@suss/adapter-ruby 0.22.4 → 0.24.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/README.md +25 -248
- package/dist/index.cjs +828 -167
- package/dist/index.d.cts +91 -4
- package/dist/index.d.ts +91 -4
- package/dist/index.js +806 -144
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -288,9 +288,21 @@ var PATH_CONVENTIONS = {
|
|
|
288
288
|
railsUnderscore: underscoreConstantPath
|
|
289
289
|
};
|
|
290
290
|
function resolveConstantFile(root, qualifiedName, convention) {
|
|
291
|
-
const relative = PATH_CONVENTIONS[convention](qualifiedName)
|
|
292
|
-
const
|
|
293
|
-
|
|
291
|
+
const relative = `${PATH_CONVENTIONS[convention](qualifiedName)}.rb`;
|
|
292
|
+
for (const candidate of [root, ...autoloadDirectories(root)]) {
|
|
293
|
+
const file = import_node_path.default.join(candidate, relative);
|
|
294
|
+
if (import_node_fs.default.existsSync(file)) {
|
|
295
|
+
return file;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function autoloadDirectories(root) {
|
|
301
|
+
try {
|
|
302
|
+
return import_node_fs.default.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => import_node_path.default.join(root, entry.name)).sort();
|
|
303
|
+
} catch {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
294
306
|
}
|
|
295
307
|
|
|
296
308
|
// src/scope.ts
|
|
@@ -595,112 +607,15 @@ function inheritedStatements(ancestry, blockConfigures) {
|
|
|
595
607
|
}
|
|
596
608
|
|
|
597
609
|
// src/discovery.ts
|
|
598
|
-
var
|
|
610
|
+
var import_behavioral_ir3 = require("@suss/behavioral-ir");
|
|
599
611
|
var import_extractor5 = require("@suss/extractor");
|
|
600
612
|
|
|
601
|
-
// src/
|
|
613
|
+
// src/clientCalls.ts
|
|
602
614
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
603
|
-
var
|
|
604
|
-
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
605
|
-
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
606
|
-
function envReadEffects(root) {
|
|
607
|
-
const effects = [];
|
|
608
|
-
(0, import_extractor2.walkDescendants)(root, null, {
|
|
609
|
-
at: (node) => {
|
|
610
|
-
const read = envReadAt(node);
|
|
611
|
-
if (read !== null) {
|
|
612
|
-
effects.push(configReadEffect(read));
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor2.SKIP_CHILDREN : null
|
|
616
|
-
});
|
|
617
|
-
return effects;
|
|
618
|
-
}
|
|
619
|
-
function configReadEffect(read) {
|
|
620
|
-
return {
|
|
621
|
-
type: "interaction",
|
|
622
|
-
binding: (0, import_behavioral_ir.runtimeConfigBinding)({
|
|
623
|
-
recognition: RUBY_ENV_RECOGNITION,
|
|
624
|
-
deploymentTarget: "lambda",
|
|
625
|
-
instanceName: "<unknown>"
|
|
626
|
-
}),
|
|
627
|
-
callee: `ENV["${read.name}"]`,
|
|
628
|
-
interaction: {
|
|
629
|
-
class: "config-read",
|
|
630
|
-
name: read.name,
|
|
631
|
-
defaulted: read.defaulted
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
function envReadAt(node) {
|
|
636
|
-
if (node.type === "element_reference") {
|
|
637
|
-
return elementRead(node);
|
|
638
|
-
}
|
|
639
|
-
if (node.type === "call") {
|
|
640
|
-
return fetchRead(node);
|
|
641
|
-
}
|
|
642
|
-
return null;
|
|
643
|
-
}
|
|
644
|
-
function elementRead(node) {
|
|
645
|
-
const object = field(node, "object");
|
|
646
|
-
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
647
|
-
return null;
|
|
648
|
-
}
|
|
649
|
-
const index = node.namedChildren.find(
|
|
650
|
-
(child) => child !== null && child.id !== object.id
|
|
651
|
-
);
|
|
652
|
-
const name = index === void 0 ? null : stringLiteralValue(index);
|
|
653
|
-
if (name === null) {
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
657
|
-
}
|
|
658
|
-
function fetchRead(node) {
|
|
659
|
-
const receiver = field(node, "receiver");
|
|
660
|
-
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
661
|
-
return null;
|
|
662
|
-
}
|
|
663
|
-
const { positional } = readCallArgs(field(node, "arguments"));
|
|
664
|
-
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
665
|
-
if (name === null) {
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
669
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
670
|
-
}
|
|
671
|
-
function isEnv(node) {
|
|
672
|
-
if (node.type === "constant") {
|
|
673
|
-
return node.text === "ENV";
|
|
674
|
-
}
|
|
675
|
-
return node.type === "scope_resolution" && field(node, "scope") === null && field(node, "name")?.text === "ENV";
|
|
676
|
-
}
|
|
677
|
-
function isAssignedTo(node) {
|
|
678
|
-
const parent = node.parent;
|
|
679
|
-
return parent !== null && (parent.type === "assignment" || parent.type === "operator_assignment") && field(parent, "left")?.id === node.id;
|
|
680
|
-
}
|
|
681
|
-
function isDefaultedAt(node) {
|
|
682
|
-
let child = node;
|
|
683
|
-
let parent = node.parent;
|
|
684
|
-
while (parent !== null) {
|
|
685
|
-
if (parent.type === "parenthesized_statements") {
|
|
686
|
-
child = parent;
|
|
687
|
-
parent = parent.parent;
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
if (parent.type !== "binary" || field(parent, "operator")?.text !== "||") {
|
|
691
|
-
return false;
|
|
692
|
-
}
|
|
693
|
-
if (field(parent, "left")?.id === child.id) {
|
|
694
|
-
return true;
|
|
695
|
-
}
|
|
696
|
-
child = parent;
|
|
697
|
-
parent = parent.parent;
|
|
698
|
-
}
|
|
699
|
-
return false;
|
|
700
|
-
}
|
|
615
|
+
var import_values5 = require("@suss/values");
|
|
701
616
|
|
|
702
617
|
// src/paths/effects.ts
|
|
703
|
-
var
|
|
618
|
+
var import_extractor2 = require("@suss/extractor");
|
|
704
619
|
|
|
705
620
|
// src/paths/bareCalls.ts
|
|
706
621
|
var PARAMETER_LIST_TYPES = [
|
|
@@ -1219,7 +1134,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
|
1219
1134
|
statements.push(statement);
|
|
1220
1135
|
}
|
|
1221
1136
|
const lowered = lowerRubyBody(body, statements);
|
|
1222
|
-
const enumerated = (0,
|
|
1137
|
+
const enumerated = (0, import_extractor2.enumerateOrDegrade)(
|
|
1223
1138
|
{
|
|
1224
1139
|
statements: lowered.statements,
|
|
1225
1140
|
terminalsByStmt: lowered.terminalsByStmt
|
|
@@ -1228,7 +1143,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
|
1228
1143
|
);
|
|
1229
1144
|
return calls.map((call) => {
|
|
1230
1145
|
const statement = statementOf2.get(call.id);
|
|
1231
|
-
const conditions = (0,
|
|
1146
|
+
const conditions = (0, import_extractor2.sharedGatingConditions)(
|
|
1232
1147
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
1233
1148
|
);
|
|
1234
1149
|
return {
|
|
@@ -1242,9 +1157,120 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
|
1242
1157
|
}
|
|
1243
1158
|
|
|
1244
1159
|
// src/responseStatus.ts
|
|
1245
|
-
var
|
|
1160
|
+
var import_extractor3 = require("@suss/extractor");
|
|
1246
1161
|
var import_values4 = require("@suss/values");
|
|
1247
1162
|
|
|
1163
|
+
// src/paths/predicates.ts
|
|
1164
|
+
var OPERATORS = {
|
|
1165
|
+
"==": "eq",
|
|
1166
|
+
"!=": "neq",
|
|
1167
|
+
">": "gt",
|
|
1168
|
+
">=": "gte",
|
|
1169
|
+
"<": "lt",
|
|
1170
|
+
"<=": "lte"
|
|
1171
|
+
};
|
|
1172
|
+
var CONVERSIONS = /* @__PURE__ */ new Set(["to_i", "to_s", "to_sym"]);
|
|
1173
|
+
var opaqueOf = (node) => ({
|
|
1174
|
+
type: "opaque",
|
|
1175
|
+
sourceText: node.text,
|
|
1176
|
+
reason: "complexExpression"
|
|
1177
|
+
});
|
|
1178
|
+
function predicateOf(node) {
|
|
1179
|
+
if (node.type === "parenthesized_statements") {
|
|
1180
|
+
const inner = node.namedChildren[0];
|
|
1181
|
+
return inner == null ? opaqueOf(node) : predicateOf(inner);
|
|
1182
|
+
}
|
|
1183
|
+
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
1184
|
+
const operand2 = field(node, "operand") ?? node.namedChildren[0];
|
|
1185
|
+
return operand2 == null ? opaqueOf(node) : { type: "negation", operand: predicateOf(operand2) };
|
|
1186
|
+
}
|
|
1187
|
+
if (node.type === "binary") {
|
|
1188
|
+
return binaryOf(node);
|
|
1189
|
+
}
|
|
1190
|
+
const asked = calledMethod(node);
|
|
1191
|
+
if (asked === "nil?") {
|
|
1192
|
+
const receiver = field(node, "receiver");
|
|
1193
|
+
return receiver === null ? opaqueOf(node) : { type: "nullCheck", subject: valueRefOf(receiver), negated: false };
|
|
1194
|
+
}
|
|
1195
|
+
if (node.type === "identifier" || memberChain(node) !== null) {
|
|
1196
|
+
return {
|
|
1197
|
+
type: "truthinessCheck",
|
|
1198
|
+
subject: valueRefOf(node),
|
|
1199
|
+
negated: false
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
return opaqueOf(node);
|
|
1203
|
+
}
|
|
1204
|
+
function binaryOf(node) {
|
|
1205
|
+
const operator = field(node, "operator")?.text ?? "";
|
|
1206
|
+
const left = field(node, "left");
|
|
1207
|
+
const right = field(node, "right");
|
|
1208
|
+
if (left === null || right === null) {
|
|
1209
|
+
return opaqueOf(node);
|
|
1210
|
+
}
|
|
1211
|
+
const op = OPERATORS[operator];
|
|
1212
|
+
if (op !== void 0) {
|
|
1213
|
+
return {
|
|
1214
|
+
type: "comparison",
|
|
1215
|
+
left: valueRefOf(left),
|
|
1216
|
+
op,
|
|
1217
|
+
right: valueRefOf(right)
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
if (operator === "&&" || operator === "||" || operator === "and" || operator === "or") {
|
|
1221
|
+
return {
|
|
1222
|
+
type: "compound",
|
|
1223
|
+
op: operator === "&&" || operator === "and" ? "and" : "or",
|
|
1224
|
+
operands: [predicateOf(left), predicateOf(right)]
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
return opaqueOf(node);
|
|
1228
|
+
}
|
|
1229
|
+
function valueRefOf(node) {
|
|
1230
|
+
const literal = literalOf(node);
|
|
1231
|
+
if (literal !== null) {
|
|
1232
|
+
return literal;
|
|
1233
|
+
}
|
|
1234
|
+
const chain = memberChain(node);
|
|
1235
|
+
if (chain !== null) {
|
|
1236
|
+
const [name, ...accessChain] = chain;
|
|
1237
|
+
if (name !== void 0 && accessChain.length > 0) {
|
|
1238
|
+
return { type: "dependency", name, accessChain };
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return { type: "unresolved", sourceText: node.text };
|
|
1242
|
+
}
|
|
1243
|
+
function literalOf(node) {
|
|
1244
|
+
if (node.type === "integer") {
|
|
1245
|
+
const value = Number.parseInt(node.text, 10);
|
|
1246
|
+
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
1247
|
+
}
|
|
1248
|
+
const string = stringLiteralValue(node);
|
|
1249
|
+
if (string !== null) {
|
|
1250
|
+
return { type: "literal", value: string };
|
|
1251
|
+
}
|
|
1252
|
+
const boolean = booleanLiteralValue(node);
|
|
1253
|
+
return boolean === null ? null : { type: "literal", value: boolean };
|
|
1254
|
+
}
|
|
1255
|
+
function memberChain(node, tail = []) {
|
|
1256
|
+
if (node.type === "identifier") {
|
|
1257
|
+
return [node.text, ...tail];
|
|
1258
|
+
}
|
|
1259
|
+
const called = calledMethod(node);
|
|
1260
|
+
const receiver = field(node, "receiver");
|
|
1261
|
+
if (called === null || receiver === null || field(node, "arguments") !== null) {
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
const rest = CONVERSIONS.has(called) && tail.length === 0 ? tail : [called, ...tail];
|
|
1265
|
+
return memberChain(receiver, rest);
|
|
1266
|
+
}
|
|
1267
|
+
function calledMethod(node) {
|
|
1268
|
+
if (node.type !== "call" || field(node, "block") !== null) {
|
|
1269
|
+
return null;
|
|
1270
|
+
}
|
|
1271
|
+
return field(node, "method")?.text ?? null;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1248
1274
|
// src/values/evaluator.ts
|
|
1249
1275
|
var import_resolution2 = require("@suss/resolution");
|
|
1250
1276
|
var import_values2 = require("@suss/values");
|
|
@@ -2561,6 +2587,18 @@ var calleeRows = [
|
|
|
2561
2587
|
kind: "callee",
|
|
2562
2588
|
origin: { module: "Kernel", name: "String" },
|
|
2563
2589
|
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2590
|
+
},
|
|
2591
|
+
// A URI is the string it was parsed from, as far as a reader of paths
|
|
2592
|
+
// is concerned, and both spellings are the standard library's own.
|
|
2593
|
+
{
|
|
2594
|
+
kind: "callee",
|
|
2595
|
+
origin: { module: "Kernel", name: "URI" },
|
|
2596
|
+
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2597
|
+
},
|
|
2598
|
+
{
|
|
2599
|
+
kind: "callee",
|
|
2600
|
+
origin: { module: "URI", name: "parse" },
|
|
2601
|
+
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2564
2602
|
}
|
|
2565
2603
|
];
|
|
2566
2604
|
var rubyRows = [
|
|
@@ -2726,20 +2764,20 @@ function collectReturns(node, found) {
|
|
|
2726
2764
|
function readingOfCall(call, declaration, names) {
|
|
2727
2765
|
const argument = statusArgumentOf(call, declaration);
|
|
2728
2766
|
if (argument === null) {
|
|
2729
|
-
return declaration.defaultStatusCode === void 0 ?
|
|
2767
|
+
return declaration.defaultStatusCode === void 0 ? import_extractor3.absentReading : (0, import_extractor3.writtenReading)(declaration.defaultStatusCode, rangeOf(call));
|
|
2730
2768
|
}
|
|
2731
2769
|
const status = statusNumberOf(argument, names);
|
|
2732
2770
|
if (status === null) {
|
|
2733
|
-
return (0,
|
|
2771
|
+
return (0, import_extractor3.unreadableReading)(
|
|
2734
2772
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
2735
2773
|
rangeOf(argument)
|
|
2736
2774
|
);
|
|
2737
2775
|
}
|
|
2738
|
-
return (0,
|
|
2776
|
+
return (0, import_extractor3.writtenReading)(status, rangeOf(argument));
|
|
2739
2777
|
}
|
|
2740
2778
|
function effectsReaching(effects, conditions) {
|
|
2741
2779
|
return effects.filter(
|
|
2742
|
-
(effect) => (0,
|
|
2780
|
+
(effect) => (0, import_extractor3.guardsHoldOn)(
|
|
2743
2781
|
effect.type === "invocation" ? effect.preconditions : void 0,
|
|
2744
2782
|
conditions
|
|
2745
2783
|
)
|
|
@@ -2748,12 +2786,15 @@ function effectsReaching(effects, conditions) {
|
|
|
2748
2786
|
function conditionOf(condition) {
|
|
2749
2787
|
return {
|
|
2750
2788
|
sourceText: condition.sourceText,
|
|
2751
|
-
structured: null,
|
|
2789
|
+
structured: condition.expression === null ? null : predicateOf(condition.expression),
|
|
2752
2790
|
polarity: condition.polarity,
|
|
2753
2791
|
source: condition.source
|
|
2754
2792
|
};
|
|
2755
2793
|
}
|
|
2756
|
-
function branchOf(outcome, pattern, effects, extraEffects) {
|
|
2794
|
+
function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
2795
|
+
if (outcome.fellThrough === true && options.fallthrough === "handOn") {
|
|
2796
|
+
return handsOnBranch(outcome, effects, extraEffects);
|
|
2797
|
+
}
|
|
2757
2798
|
return {
|
|
2758
2799
|
conditions: outcome.conditions,
|
|
2759
2800
|
terminal: {
|
|
@@ -2778,7 +2819,72 @@ function branchOf(outcome, pattern, effects, extraEffects) {
|
|
|
2778
2819
|
isDefault: outcome.conditions.length === 0
|
|
2779
2820
|
};
|
|
2780
2821
|
}
|
|
2781
|
-
function
|
|
2822
|
+
function handsOnBranch(outcome, effects, extraEffects) {
|
|
2823
|
+
return {
|
|
2824
|
+
conditions: outcome.conditions,
|
|
2825
|
+
terminal: {
|
|
2826
|
+
kind: "delegate",
|
|
2827
|
+
statusCode: null,
|
|
2828
|
+
body: null,
|
|
2829
|
+
exceptionType: null,
|
|
2830
|
+
message: null,
|
|
2831
|
+
component: null,
|
|
2832
|
+
renderTree: null,
|
|
2833
|
+
delegateTarget: null,
|
|
2834
|
+
emitEvent: null,
|
|
2835
|
+
location: outcome.location
|
|
2836
|
+
},
|
|
2837
|
+
effects: effectsReaching(effects, outcome.conditions),
|
|
2838
|
+
...extraEffects === void 0 ? {} : { extraEffects },
|
|
2839
|
+
location: outcome.location,
|
|
2840
|
+
isDefault: outcome.conditions.length === 0
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function returnPathBranches(method, effects) {
|
|
2844
|
+
const body = field(method, "body");
|
|
2845
|
+
if (body === null) {
|
|
2846
|
+
return null;
|
|
2847
|
+
}
|
|
2848
|
+
const returns = collectReturns(body, []);
|
|
2849
|
+
const lowered = lowerRubyBody(body, returns, []);
|
|
2850
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2851
|
+
{
|
|
2852
|
+
statements: lowered.statements,
|
|
2853
|
+
terminalsByStmt: lowered.terminalsByStmt
|
|
2854
|
+
},
|
|
2855
|
+
returns
|
|
2856
|
+
);
|
|
2857
|
+
const branches = [];
|
|
2858
|
+
const push = (paths, at) => {
|
|
2859
|
+
for (const path7 of paths) {
|
|
2860
|
+
const conditions = path7.map(conditionOf);
|
|
2861
|
+
branches.push({
|
|
2862
|
+
conditions,
|
|
2863
|
+
terminal: {
|
|
2864
|
+
kind: "return",
|
|
2865
|
+
statusCode: null,
|
|
2866
|
+
body: null,
|
|
2867
|
+
exceptionType: null,
|
|
2868
|
+
message: null,
|
|
2869
|
+
component: null,
|
|
2870
|
+
renderTree: null,
|
|
2871
|
+
delegateTarget: null,
|
|
2872
|
+
emitEvent: null,
|
|
2873
|
+
location: at
|
|
2874
|
+
},
|
|
2875
|
+
effects: effectsReaching(effects, conditions),
|
|
2876
|
+
location: at,
|
|
2877
|
+
isDefault: conditions.length === 0
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
for (const statement of returns) {
|
|
2882
|
+
push(enumerated.byTerminal.get(statement) ?? [], rangeOf(statement));
|
|
2883
|
+
}
|
|
2884
|
+
push(enumerated.fallthrough, rangeOf(method));
|
|
2885
|
+
return branches.length === 0 ? null : branches;
|
|
2886
|
+
}
|
|
2887
|
+
function responseBranches(method, pattern, effects, extraEffects, options = {}) {
|
|
2782
2888
|
const declarations = pattern.responseStatusCalls ?? [];
|
|
2783
2889
|
const body = field(method, "body");
|
|
2784
2890
|
if (declarations.length === 0 || body === null) {
|
|
@@ -2793,7 +2899,7 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2793
2899
|
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, byName, locals, []).length === 0
|
|
2794
2900
|
);
|
|
2795
2901
|
const terminals = [...responses, ...bareReturns];
|
|
2796
|
-
const enumerated = (0,
|
|
2902
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2797
2903
|
{
|
|
2798
2904
|
statements: lowered.statements,
|
|
2799
2905
|
terminalsByStmt: lowered.terminalsByStmt
|
|
@@ -2804,7 +2910,7 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2804
2910
|
const outcomes = [];
|
|
2805
2911
|
for (const terminal of terminals) {
|
|
2806
2912
|
const declaration = declarationOf(terminal, byName, locals);
|
|
2807
|
-
const reading = declaration === void 0 ?
|
|
2913
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
2808
2914
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
2809
2915
|
outcomes.push({
|
|
2810
2916
|
conditions: path7.map(conditionOf),
|
|
@@ -2816,40 +2922,541 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2816
2922
|
for (const path7 of enumerated.fallthrough) {
|
|
2817
2923
|
outcomes.push({
|
|
2818
2924
|
conditions: path7.map(conditionOf),
|
|
2819
|
-
reading:
|
|
2820
|
-
location: rangeOf(method)
|
|
2925
|
+
reading: import_extractor3.absentReading,
|
|
2926
|
+
location: rangeOf(method),
|
|
2927
|
+
fellThrough: true
|
|
2821
2928
|
});
|
|
2822
2929
|
}
|
|
2823
2930
|
if (outcomes.length === 0) {
|
|
2824
2931
|
return null;
|
|
2825
2932
|
}
|
|
2826
2933
|
return outcomes.map(
|
|
2827
|
-
(outcome) => branchOf(outcome, pattern, effects, extraEffects)
|
|
2934
|
+
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
2828
2935
|
);
|
|
2829
2936
|
}
|
|
2830
2937
|
|
|
2831
|
-
// src/
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2938
|
+
// src/clientCalls.ts
|
|
2939
|
+
function clientCallUnits(root, pack, pattern, options) {
|
|
2940
|
+
const units = [];
|
|
2941
|
+
for (const method of methodDefinitions(root)) {
|
|
2942
|
+
const name = field(method, "name")?.text;
|
|
2943
|
+
if (name === void 0) {
|
|
2944
|
+
continue;
|
|
2945
|
+
}
|
|
2946
|
+
for (const call of callsUnder(method)) {
|
|
2947
|
+
const request = requestCall(call, method, pattern, options);
|
|
2948
|
+
if (request === null) {
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return units;
|
|
2837
2955
|
}
|
|
2838
|
-
function
|
|
2839
|
-
|
|
2956
|
+
function methodDefinitions(node, found = []) {
|
|
2957
|
+
for (const child of node.namedChildren) {
|
|
2958
|
+
if (child === null) {
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
if (child.type === "method" || child.type === "singleton_method") {
|
|
2962
|
+
found.push(child);
|
|
2963
|
+
}
|
|
2964
|
+
methodDefinitions(child, found);
|
|
2965
|
+
}
|
|
2966
|
+
return found;
|
|
2840
2967
|
}
|
|
2841
|
-
function
|
|
2842
|
-
|
|
2968
|
+
function callsUnder(node, found = []) {
|
|
2969
|
+
for (const child of node.namedChildren) {
|
|
2970
|
+
if (child === null) {
|
|
2971
|
+
continue;
|
|
2972
|
+
}
|
|
2973
|
+
if (child.type === "call") {
|
|
2974
|
+
found.push(child);
|
|
2975
|
+
}
|
|
2976
|
+
callsUnder(child, found);
|
|
2977
|
+
}
|
|
2978
|
+
return found;
|
|
2843
2979
|
}
|
|
2844
|
-
function
|
|
2845
|
-
const receiver =
|
|
2846
|
-
|
|
2980
|
+
function requestCall(call, method, pattern, options) {
|
|
2981
|
+
const receiver = field(call, "receiver");
|
|
2982
|
+
const called = field(call, "method")?.text;
|
|
2983
|
+
if (receiver === null || called === void 0) {
|
|
2847
2984
|
return null;
|
|
2848
2985
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2986
|
+
const prefix = receiverPrefix(receiver, method, pattern, options);
|
|
2987
|
+
if (prefix === null) {
|
|
2988
|
+
return null;
|
|
2851
2989
|
}
|
|
2852
|
-
|
|
2990
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
2991
|
+
const verb = pattern.verbMethodNames[called];
|
|
2992
|
+
if (verb !== void 0) {
|
|
2993
|
+
const path8 = urlIn(args, pattern, options);
|
|
2994
|
+
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
2995
|
+
}
|
|
2996
|
+
const sent = pattern.requestObject;
|
|
2997
|
+
if (sent === void 0 || sent.attribute !== called) {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
const built = requestBuilt(args.positional[0], method, sent);
|
|
3001
|
+
if (built === null) {
|
|
3002
|
+
return null;
|
|
3003
|
+
}
|
|
3004
|
+
const path7 = pathAt(built.url, options);
|
|
3005
|
+
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3006
|
+
}
|
|
3007
|
+
function requestBuilt(argument, method, sent) {
|
|
3008
|
+
const written = argument === void 0 ? null : assignedCall(argument, method) ?? argument;
|
|
3009
|
+
if (written === null || written.type !== "call") {
|
|
3010
|
+
return null;
|
|
3011
|
+
}
|
|
3012
|
+
const requestClass = field(written, "receiver")?.text;
|
|
3013
|
+
const verb = requestClass === void 0 ? void 0 : sent.constructors[requestClass];
|
|
3014
|
+
if (verb === void 0) {
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
const args = readCallArgs(field(written, "arguments"));
|
|
3018
|
+
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3019
|
+
}
|
|
3020
|
+
function assignedCall(node, method) {
|
|
3021
|
+
if (node.type !== "identifier") {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
const value = assignedValue(node.text, method);
|
|
3025
|
+
return value !== null && value.type === "call" ? value : null;
|
|
3026
|
+
}
|
|
3027
|
+
function receiverPrefix(receiver, method, pattern, options) {
|
|
3028
|
+
if (namesConstant(receiver, pattern.constantName)) {
|
|
3029
|
+
return "";
|
|
3030
|
+
}
|
|
3031
|
+
if (receiver.type !== "identifier") {
|
|
3032
|
+
return null;
|
|
3033
|
+
}
|
|
3034
|
+
const built = builtBy(receiver.text, method, pattern);
|
|
3035
|
+
if (built === null) {
|
|
3036
|
+
return null;
|
|
3037
|
+
}
|
|
3038
|
+
const keyword = pattern.builderUrlKeyword;
|
|
3039
|
+
if (keyword === void 0) {
|
|
3040
|
+
return "";
|
|
3041
|
+
}
|
|
3042
|
+
const base = readCallArgs(field(built, "arguments")).keyword[keyword];
|
|
3043
|
+
const path7 = base === void 0 ? null : (0, import_values5.pathOf)(evaluatedValue(base, options.facts));
|
|
3044
|
+
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3045
|
+
}
|
|
3046
|
+
function trimmed(path7) {
|
|
3047
|
+
return path7 === "/" ? "" : path7.replace(/\/+$/, "");
|
|
3048
|
+
}
|
|
3049
|
+
function namesConstant(receiver, constantName) {
|
|
3050
|
+
return (receiver.type === "constant" || receiver.type === "scope_resolution") && receiver.text === constantName;
|
|
3051
|
+
}
|
|
3052
|
+
function builtBy(name, method, pattern) {
|
|
3053
|
+
const builders = pattern.receiverBuilders ?? [];
|
|
3054
|
+
const value = builders.length === 0 ? null : assignedValue(name, method);
|
|
3055
|
+
if (value === null || value.type !== "call" || !builders.includes(field(value, "method")?.text ?? "")) {
|
|
3056
|
+
return null;
|
|
3057
|
+
}
|
|
3058
|
+
const receiver = field(value, "receiver");
|
|
3059
|
+
return receiver !== null && namesConstant(receiver, pattern.constantName) ? value : null;
|
|
3060
|
+
}
|
|
3061
|
+
function assignedValue(name, method) {
|
|
3062
|
+
const body = field(method, "body");
|
|
3063
|
+
if (body === null) {
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
3066
|
+
for (const statement of runStatements(body)) {
|
|
3067
|
+
if (statement.type !== "assignment") {
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
3070
|
+
if (field(statement, "left")?.text === name) {
|
|
3071
|
+
return field(statement, "right");
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
function urlIn(args, pattern, options) {
|
|
3077
|
+
const keyword = pattern.url.keyword;
|
|
3078
|
+
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3079
|
+
return pathAt(written, options);
|
|
3080
|
+
}
|
|
3081
|
+
function pathAt(written, options) {
|
|
3082
|
+
if (written === void 0) {
|
|
3083
|
+
return null;
|
|
3084
|
+
}
|
|
3085
|
+
const path7 = (0, import_values5.pathOf)(evaluatedValue(written, options.facts));
|
|
3086
|
+
return path7 === void 0 || (0, import_behavioral_ir.namesNothing)(path7) ? null : path7;
|
|
3087
|
+
}
|
|
3088
|
+
function clientUnit(method, name, request, pattern, pack, options) {
|
|
3089
|
+
const range = rangeOf(method);
|
|
3090
|
+
return {
|
|
3091
|
+
identity: {
|
|
3092
|
+
name,
|
|
3093
|
+
nameKind: "binding",
|
|
3094
|
+
kind: "client",
|
|
3095
|
+
file: options.filePath,
|
|
3096
|
+
range,
|
|
3097
|
+
span: spanOf(method),
|
|
3098
|
+
exportName: name,
|
|
3099
|
+
exportPath: [name]
|
|
3100
|
+
},
|
|
3101
|
+
boundaryBinding: (0, import_behavioral_ir.restBinding)({
|
|
3102
|
+
transport: pack.protocol,
|
|
3103
|
+
method: request.method,
|
|
3104
|
+
path: request.path,
|
|
3105
|
+
recognition: pack.name
|
|
3106
|
+
}),
|
|
3107
|
+
parameters: [],
|
|
3108
|
+
branches: callerBranches(method, range),
|
|
3109
|
+
...responseAccessors(pattern),
|
|
3110
|
+
bodyContent: "statements",
|
|
3111
|
+
dependencyCalls: [],
|
|
3112
|
+
declaredContract: null
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
function callerBranches(method, range) {
|
|
3116
|
+
const effects = invocationEffects(method);
|
|
3117
|
+
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3118
|
+
}
|
|
3119
|
+
function responseAccessors(pattern) {
|
|
3120
|
+
const response = pattern.response;
|
|
3121
|
+
if (response === void 0) {
|
|
3122
|
+
return {};
|
|
3123
|
+
}
|
|
3124
|
+
return {
|
|
3125
|
+
...response.body === void 0 ? {} : { bodyAccessors: response.body },
|
|
3126
|
+
...response.statusCode === void 0 ? {} : { statusAccessors: response.statusCode },
|
|
3127
|
+
...response.success === void 0 ? {} : { successAccessors: response.success },
|
|
3128
|
+
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery }
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
function returnBranch(range, effects) {
|
|
3132
|
+
return {
|
|
3133
|
+
conditions: [],
|
|
3134
|
+
terminal: {
|
|
3135
|
+
kind: "return",
|
|
3136
|
+
statusCode: null,
|
|
3137
|
+
body: null,
|
|
3138
|
+
exceptionType: null,
|
|
3139
|
+
message: null,
|
|
3140
|
+
component: null,
|
|
3141
|
+
renderTree: null,
|
|
3142
|
+
delegateTarget: null,
|
|
3143
|
+
emitEvent: null,
|
|
3144
|
+
location: range
|
|
3145
|
+
},
|
|
3146
|
+
effects,
|
|
3147
|
+
location: range,
|
|
3148
|
+
isDefault: true
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
// src/envReads.ts
|
|
3153
|
+
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
3154
|
+
var import_extractor4 = require("@suss/extractor");
|
|
3155
|
+
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
3156
|
+
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
3157
|
+
function envReadEffects(root) {
|
|
3158
|
+
const effects = [];
|
|
3159
|
+
(0, import_extractor4.walkDescendants)(root, null, {
|
|
3160
|
+
at: (node) => {
|
|
3161
|
+
const read = envReadAt(node);
|
|
3162
|
+
if (read !== null) {
|
|
3163
|
+
effects.push(configReadEffect(read));
|
|
3164
|
+
}
|
|
3165
|
+
},
|
|
3166
|
+
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor4.SKIP_CHILDREN : null
|
|
3167
|
+
});
|
|
3168
|
+
return effects;
|
|
3169
|
+
}
|
|
3170
|
+
function configReadEffect(read) {
|
|
3171
|
+
return {
|
|
3172
|
+
type: "interaction",
|
|
3173
|
+
binding: (0, import_behavioral_ir2.runtimeConfigBinding)({
|
|
3174
|
+
recognition: RUBY_ENV_RECOGNITION,
|
|
3175
|
+
deploymentTarget: "lambda",
|
|
3176
|
+
instanceName: "<unknown>"
|
|
3177
|
+
}),
|
|
3178
|
+
callee: `ENV["${read.name}"]`,
|
|
3179
|
+
interaction: {
|
|
3180
|
+
class: "config-read",
|
|
3181
|
+
name: read.name,
|
|
3182
|
+
defaulted: read.defaulted
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
function envReadAt(node) {
|
|
3187
|
+
if (node.type === "element_reference") {
|
|
3188
|
+
return elementRead(node);
|
|
3189
|
+
}
|
|
3190
|
+
if (node.type === "call") {
|
|
3191
|
+
return fetchRead(node);
|
|
3192
|
+
}
|
|
3193
|
+
return null;
|
|
3194
|
+
}
|
|
3195
|
+
function elementRead(node) {
|
|
3196
|
+
const object = field(node, "object");
|
|
3197
|
+
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
3198
|
+
return null;
|
|
3199
|
+
}
|
|
3200
|
+
const index = node.namedChildren.find(
|
|
3201
|
+
(child) => child !== null && child.id !== object.id
|
|
3202
|
+
);
|
|
3203
|
+
const name = index === void 0 ? null : stringLiteralValue(index);
|
|
3204
|
+
if (name === null) {
|
|
3205
|
+
return null;
|
|
3206
|
+
}
|
|
3207
|
+
return { name, defaulted: isDefaultedAt(node) };
|
|
3208
|
+
}
|
|
3209
|
+
function fetchRead(node) {
|
|
3210
|
+
const receiver = field(node, "receiver");
|
|
3211
|
+
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
3212
|
+
return null;
|
|
3213
|
+
}
|
|
3214
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
3215
|
+
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
3216
|
+
if (name === null) {
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
3220
|
+
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3221
|
+
}
|
|
3222
|
+
function isEnv(node) {
|
|
3223
|
+
if (node.type === "constant") {
|
|
3224
|
+
return node.text === "ENV";
|
|
3225
|
+
}
|
|
3226
|
+
return node.type === "scope_resolution" && field(node, "scope") === null && field(node, "name")?.text === "ENV";
|
|
3227
|
+
}
|
|
3228
|
+
function isAssignedTo(node) {
|
|
3229
|
+
const parent = node.parent;
|
|
3230
|
+
return parent !== null && (parent.type === "assignment" || parent.type === "operator_assignment") && field(parent, "left")?.id === node.id;
|
|
3231
|
+
}
|
|
3232
|
+
function isDefaultedAt(node) {
|
|
3233
|
+
let child = node;
|
|
3234
|
+
let parent = node.parent;
|
|
3235
|
+
while (parent !== null) {
|
|
3236
|
+
if (parent.type === "parenthesized_statements") {
|
|
3237
|
+
child = parent;
|
|
3238
|
+
parent = parent.parent;
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
if (parent.type !== "binary" || field(parent, "operator")?.text !== "||") {
|
|
3242
|
+
return false;
|
|
3243
|
+
}
|
|
3244
|
+
if (field(parent, "left")?.id === child.id) {
|
|
3245
|
+
return true;
|
|
3246
|
+
}
|
|
3247
|
+
child = parent;
|
|
3248
|
+
parent = parent.parent;
|
|
3249
|
+
}
|
|
3250
|
+
return false;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/filters.ts
|
|
3254
|
+
function controllerFilters(pattern, ancestry) {
|
|
3255
|
+
const forms = pattern.filters ?? [];
|
|
3256
|
+
if (forms.length === 0) {
|
|
3257
|
+
return [];
|
|
3258
|
+
}
|
|
3259
|
+
const byName = new Map(forms.map((form) => [form.name, form]));
|
|
3260
|
+
const skipNames = /* @__PURE__ */ new Map();
|
|
3261
|
+
for (const form of forms) {
|
|
3262
|
+
if (form.skippedBy !== void 0) {
|
|
3263
|
+
skipNames.set(form.skippedBy, form.name);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
const declared = [];
|
|
3267
|
+
const skips = [];
|
|
3268
|
+
for (const { statement } of inheritedStatements(ancestry)) {
|
|
3269
|
+
const called = calledName(statement);
|
|
3270
|
+
if (called === null) {
|
|
3271
|
+
continue;
|
|
3272
|
+
}
|
|
3273
|
+
const form = byName.get(called);
|
|
3274
|
+
if (form !== void 0) {
|
|
3275
|
+
declared.push(...declarationsOf(statement, form));
|
|
3276
|
+
continue;
|
|
3277
|
+
}
|
|
3278
|
+
const skipped = skipNames.get(called);
|
|
3279
|
+
if (skipped !== void 0) {
|
|
3280
|
+
skips.push(...skipsOf(statement, skipped));
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
const resolved = [];
|
|
3284
|
+
for (const declaration of applySkips(declared, skips)) {
|
|
3285
|
+
const found = methodInAncestry(ancestry, declaration.methodName);
|
|
3286
|
+
if (found.type !== "found") {
|
|
3287
|
+
continue;
|
|
3288
|
+
}
|
|
3289
|
+
resolved.push({
|
|
3290
|
+
filter: declaration.filter,
|
|
3291
|
+
methodName: declaration.methodName,
|
|
3292
|
+
method: found.method,
|
|
3293
|
+
file: found.block.file,
|
|
3294
|
+
enclosingQualifiedName: found.block.info.qualifiedName,
|
|
3295
|
+
only: declaration.only,
|
|
3296
|
+
except: declaration.except
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3299
|
+
return [
|
|
3300
|
+
...resolved.filter((one) => one.filter.onThrow !== true),
|
|
3301
|
+
...resolved.filter((one) => one.filter.onThrow === true)
|
|
3302
|
+
];
|
|
3303
|
+
}
|
|
3304
|
+
function filterCoversAction(filter, actionName) {
|
|
3305
|
+
if (filter.except.has(actionName)) {
|
|
3306
|
+
return false;
|
|
3307
|
+
}
|
|
3308
|
+
return filter.only === null || filter.only.has(actionName);
|
|
3309
|
+
}
|
|
3310
|
+
function filterReference(filter, displayPath) {
|
|
3311
|
+
return {
|
|
3312
|
+
file: displayPath,
|
|
3313
|
+
name: filter.methodName,
|
|
3314
|
+
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
function filterUnit(filter, pattern, displayPath, body) {
|
|
3318
|
+
const range = rangeOf(filter.method);
|
|
3319
|
+
const branches = responseBranches(
|
|
3320
|
+
filter.method,
|
|
3321
|
+
pattern,
|
|
3322
|
+
body.effects ?? [],
|
|
3323
|
+
body.extraEffects,
|
|
3324
|
+
{ fallthrough: "handOn" }
|
|
3325
|
+
);
|
|
3326
|
+
return {
|
|
3327
|
+
identity: {
|
|
3328
|
+
name: filter.methodName,
|
|
3329
|
+
nameKind: "binding",
|
|
3330
|
+
kind: "middleware",
|
|
3331
|
+
file: displayPath,
|
|
3332
|
+
range,
|
|
3333
|
+
span: spanOf(filter.method),
|
|
3334
|
+
exportName: filter.methodName,
|
|
3335
|
+
exportPath: [filter.enclosingQualifiedName, filter.methodName]
|
|
3336
|
+
},
|
|
3337
|
+
boundaryBinding: null,
|
|
3338
|
+
parameters: [],
|
|
3339
|
+
branches: branches ?? [handsOn(range, body)],
|
|
3340
|
+
bodyContent: body.bodyContent ?? "absent",
|
|
3341
|
+
dependencyCalls: [],
|
|
3342
|
+
declaredContract: null
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
function handsOn(range, body) {
|
|
3346
|
+
return {
|
|
3347
|
+
conditions: [],
|
|
3348
|
+
terminal: {
|
|
3349
|
+
kind: "delegate",
|
|
3350
|
+
statusCode: null,
|
|
3351
|
+
body: null,
|
|
3352
|
+
exceptionType: null,
|
|
3353
|
+
message: null,
|
|
3354
|
+
component: null,
|
|
3355
|
+
renderTree: null,
|
|
3356
|
+
delegateTarget: null,
|
|
3357
|
+
emitEvent: null,
|
|
3358
|
+
location: range
|
|
3359
|
+
},
|
|
3360
|
+
effects: body.effects ?? [],
|
|
3361
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
3362
|
+
location: range,
|
|
3363
|
+
isDefault: true
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
function calledName(statement) {
|
|
3367
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
3368
|
+
return null;
|
|
3369
|
+
}
|
|
3370
|
+
return field(statement, "method")?.text ?? null;
|
|
3371
|
+
}
|
|
3372
|
+
function declarationsOf(statement, filter) {
|
|
3373
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
3374
|
+
const keywords = filter.actionKeywords;
|
|
3375
|
+
const only = keywords === void 0 ? null : namesUnder(args.keyword[keywords.include]);
|
|
3376
|
+
const except = keywords === void 0 ? null : namesUnder(args.keyword[keywords.exclude]);
|
|
3377
|
+
return methodNamesOf(args, filter).map((methodName) => ({
|
|
3378
|
+
filter,
|
|
3379
|
+
methodName,
|
|
3380
|
+
only,
|
|
3381
|
+
except: except ?? /* @__PURE__ */ new Set()
|
|
3382
|
+
}));
|
|
3383
|
+
}
|
|
3384
|
+
function methodNamesOf(args, filter) {
|
|
3385
|
+
if (filter.methodFrom === "withKeyword") {
|
|
3386
|
+
const named2 = args.keyword.with;
|
|
3387
|
+
const value = named2 === void 0 ? null : symbolValue(named2);
|
|
3388
|
+
return value === null ? [] : [value];
|
|
3389
|
+
}
|
|
3390
|
+
return args.positional.map((arg) => symbolValue(arg)).filter((name) => name !== null);
|
|
3391
|
+
}
|
|
3392
|
+
function skipsOf(statement, filterName) {
|
|
3393
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
3394
|
+
const actions = namesUnder(args.keyword.only);
|
|
3395
|
+
return args.positional.map((arg) => symbolValue(arg)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
3396
|
+
}
|
|
3397
|
+
function applySkips(declared, skips) {
|
|
3398
|
+
const left = [];
|
|
3399
|
+
for (const declaration of declared) {
|
|
3400
|
+
const reaching = skips.filter(
|
|
3401
|
+
(skip) => skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
3402
|
+
);
|
|
3403
|
+
if (reaching.some((skip) => skip.actions === null)) {
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
const except = new Set(declaration.except);
|
|
3407
|
+
for (const skip of reaching) {
|
|
3408
|
+
for (const action of skip.actions ?? []) {
|
|
3409
|
+
except.add(action);
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
left.push({ ...declaration, except });
|
|
3413
|
+
}
|
|
3414
|
+
return left;
|
|
3415
|
+
}
|
|
3416
|
+
function namesUnder(node) {
|
|
3417
|
+
if (node === void 0) {
|
|
3418
|
+
return null;
|
|
3419
|
+
}
|
|
3420
|
+
const single = symbolValue(node);
|
|
3421
|
+
if (single !== null) {
|
|
3422
|
+
return /* @__PURE__ */ new Set([single]);
|
|
3423
|
+
}
|
|
3424
|
+
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
3425
|
+
return null;
|
|
3426
|
+
}
|
|
3427
|
+
const names = /* @__PURE__ */ new Set();
|
|
3428
|
+
for (const child of bodyStatements(node)) {
|
|
3429
|
+
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
3430
|
+
const name = symbolValue(child) ?? bare;
|
|
3431
|
+
if (name !== null) {
|
|
3432
|
+
names.add(name);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
return names;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
// src/storage.ts
|
|
3439
|
+
var import_datalog2 = require("@suss/datalog");
|
|
3440
|
+
var import_ir_core = require("@suss/ir-core");
|
|
3441
|
+
var import_resolution3 = require("@suss/resolution");
|
|
3442
|
+
function children3(node) {
|
|
3443
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
3444
|
+
}
|
|
3445
|
+
function receiverOf(node) {
|
|
3446
|
+
return node.type === "call" ? field(node, "receiver") : null;
|
|
3447
|
+
}
|
|
3448
|
+
function methodOf(node) {
|
|
3449
|
+
return field(node, "method")?.text ?? "";
|
|
3450
|
+
}
|
|
3451
|
+
function rootConstant(node) {
|
|
3452
|
+
const receiver = receiverOf(node);
|
|
3453
|
+
if (receiver === null) {
|
|
3454
|
+
return null;
|
|
3455
|
+
}
|
|
3456
|
+
if (receiver.type === "constant" || receiver.type === "scope_resolution") {
|
|
3457
|
+
return receiver;
|
|
3458
|
+
}
|
|
3459
|
+
return rootConstant(receiver);
|
|
2853
3460
|
}
|
|
2854
3461
|
var ANCESTRY_PROGRAM = (0, import_datalog2.deriveOnDemand)(
|
|
2855
3462
|
[...import_resolution3.RESOLUTION_RULES, ...import_resolution3.RESOLUTION_QUESTIONS],
|
|
@@ -3046,6 +3653,16 @@ async function discoverUnits(root, options) {
|
|
|
3046
3653
|
file: options.absoluteFile ?? options.filePath
|
|
3047
3654
|
}));
|
|
3048
3655
|
const units = [];
|
|
3656
|
+
for (const pack of options.packs) {
|
|
3657
|
+
for (const pattern of pack.clients ?? []) {
|
|
3658
|
+
units.push(
|
|
3659
|
+
...clientCallUnits(root, pack, pattern, {
|
|
3660
|
+
filePath: options.filePath,
|
|
3661
|
+
...options.facts === void 0 ? {} : { facts: options.facts }
|
|
3662
|
+
})
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3049
3666
|
for (const info of classes) {
|
|
3050
3667
|
const ownBlocks = fileBlocks.filter(
|
|
3051
3668
|
(block) => block.info.qualifiedName === info.qualifiedName
|
|
@@ -3077,7 +3694,7 @@ function unitsFor(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
|
3077
3694
|
graphqlObjectFields: (p) => graphqlObjectFieldUnits(p, pack, info, ownBlocks, fileBlocks, options),
|
|
3078
3695
|
controllerActions: (p) => controllerActionUnits(p, pack, info, ownBlocks, fileBlocks, options)
|
|
3079
3696
|
};
|
|
3080
|
-
return (0,
|
|
3697
|
+
return (0, import_behavioral_ir3.dispatchByType)(table, pattern);
|
|
3081
3698
|
}
|
|
3082
3699
|
async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
3083
3700
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
@@ -3127,7 +3744,23 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3127
3744
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
3128
3745
|
return [];
|
|
3129
3746
|
}
|
|
3747
|
+
const filters = controllerFilters(pattern, ancestry);
|
|
3130
3748
|
const units = [];
|
|
3749
|
+
for (const filter of filters) {
|
|
3750
|
+
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
3751
|
+
const raw = filterUnit(
|
|
3752
|
+
filter,
|
|
3753
|
+
pattern,
|
|
3754
|
+
displayPath,
|
|
3755
|
+
bodyOfMethod(filter.method, options)
|
|
3756
|
+
);
|
|
3757
|
+
units.push(raw);
|
|
3758
|
+
options.onReachSeed?.(raw, {
|
|
3759
|
+
file: filter.file,
|
|
3760
|
+
node: filter.method,
|
|
3761
|
+
enclosingQualifiedName: filter.enclosingQualifiedName
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3131
3764
|
for (const block of ownBlocks) {
|
|
3132
3765
|
if (block.info.bodyNode === null) {
|
|
3133
3766
|
continue;
|
|
@@ -3139,6 +3772,12 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3139
3772
|
if ((visibility.get(actionName) ?? "public") !== "public") {
|
|
3140
3773
|
continue;
|
|
3141
3774
|
}
|
|
3775
|
+
const around = filters.filter((filter) => filterCoversAction(filter, actionName)).map(
|
|
3776
|
+
(filter) => filterReference(
|
|
3777
|
+
filter,
|
|
3778
|
+
options.displayPathOf?.(filter.file) ?? filter.file
|
|
3779
|
+
)
|
|
3780
|
+
);
|
|
3142
3781
|
const raw = buildControllerActionUnit(
|
|
3143
3782
|
pack,
|
|
3144
3783
|
pattern,
|
|
@@ -3146,7 +3785,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3146
3785
|
actionName,
|
|
3147
3786
|
method,
|
|
3148
3787
|
options.filePath,
|
|
3149
|
-
options
|
|
3788
|
+
options,
|
|
3789
|
+
around
|
|
3150
3790
|
);
|
|
3151
3791
|
units.push(raw);
|
|
3152
3792
|
options.onReachSeed?.(raw, {
|
|
@@ -3158,7 +3798,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3158
3798
|
}
|
|
3159
3799
|
return units;
|
|
3160
3800
|
}
|
|
3161
|
-
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, method, filePath, bodyRead) {
|
|
3801
|
+
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, method, filePath, bodyRead, wrappers = []) {
|
|
3162
3802
|
const range = rangeOf(method);
|
|
3163
3803
|
const route = pattern.routeFor(controllerQualifiedName, actionName);
|
|
3164
3804
|
const body = bodyOfMethod(method, bodyRead);
|
|
@@ -3179,7 +3819,7 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
3179
3819
|
exportName: actionName,
|
|
3180
3820
|
exportPath: [controllerQualifiedName, actionName]
|
|
3181
3821
|
},
|
|
3182
|
-
boundaryBinding: route === null ? null : (0,
|
|
3822
|
+
boundaryBinding: route === null ? null : (0, import_behavioral_ir3.restBinding)({
|
|
3183
3823
|
transport: pack.protocol,
|
|
3184
3824
|
method: route.method,
|
|
3185
3825
|
path: route.path,
|
|
@@ -3213,7 +3853,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
3213
3853
|
],
|
|
3214
3854
|
bodyContent: body.bodyContent ?? "absent",
|
|
3215
3855
|
dependencyCalls: [],
|
|
3216
|
-
declaredContract: null
|
|
3856
|
+
declaredContract: null,
|
|
3857
|
+
...wrappers.length > 0 ? { wrappers: [...wrappers] } : {}
|
|
3217
3858
|
};
|
|
3218
3859
|
}
|
|
3219
3860
|
function routingGapUnit(pattern, gaps) {
|
|
@@ -3296,7 +3937,7 @@ function bodyOfMethod(method, bodyRead = {}) {
|
|
|
3296
3937
|
const storage = bodyRead.storage;
|
|
3297
3938
|
const extra = [
|
|
3298
3939
|
...envReadEffects(method),
|
|
3299
|
-
...storage === void 0 ? [] : storageEffects(
|
|
3940
|
+
...storage === void 0 ? [] : storageEffects(callsUnder2(method), storage)
|
|
3300
3941
|
];
|
|
3301
3942
|
return {
|
|
3302
3943
|
bodyContent: methodHasStatements(method) ? "statements" : "empty",
|
|
@@ -3305,7 +3946,7 @@ function bodyOfMethod(method, bodyRead = {}) {
|
|
|
3305
3946
|
...extra.length > 0 ? { extraEffects: extra } : {}
|
|
3306
3947
|
};
|
|
3307
3948
|
}
|
|
3308
|
-
function
|
|
3949
|
+
function callsUnder2(node, found = []) {
|
|
3309
3950
|
for (const child of node.namedChildren) {
|
|
3310
3951
|
if (child === null) {
|
|
3311
3952
|
continue;
|
|
@@ -3313,7 +3954,7 @@ function callsUnder(node, found = []) {
|
|
|
3313
3954
|
if (child.type === "call") {
|
|
3314
3955
|
found.push(child);
|
|
3315
3956
|
}
|
|
3316
|
-
|
|
3957
|
+
callsUnder2(child, found);
|
|
3317
3958
|
}
|
|
3318
3959
|
return found;
|
|
3319
3960
|
}
|
|
@@ -3340,7 +3981,7 @@ function bodyFromLookup(found, range, subject, nothingThere, bodyRead) {
|
|
|
3340
3981
|
),
|
|
3341
3982
|
none: () => nothingThere
|
|
3342
3983
|
};
|
|
3343
|
-
return (0,
|
|
3984
|
+
return (0, import_behavioral_ir3.dispatchByType)(table, found);
|
|
3344
3985
|
}
|
|
3345
3986
|
async function readFieldCall(stmt, scope, ctx, ancestry) {
|
|
3346
3987
|
if (stmt.type !== "call" || field(stmt, "receiver") !== null) {
|
|
@@ -3609,7 +4250,7 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
3609
4250
|
exportName: null,
|
|
3610
4251
|
exportPath: null
|
|
3611
4252
|
},
|
|
3612
|
-
boundaryBinding: decl.namedOnTheWire ? (0,
|
|
4253
|
+
boundaryBinding: decl.namedOnTheWire ? (0, import_behavioral_ir3.graphqlResolverBinding)({
|
|
3613
4254
|
transport: pack.protocol,
|
|
3614
4255
|
recognition: pack.name,
|
|
3615
4256
|
typeName,
|
|
@@ -3807,7 +4448,7 @@ function parseWith(language, source) {
|
|
|
3807
4448
|
// src/project.ts
|
|
3808
4449
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
3809
4450
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
3810
|
-
var
|
|
4451
|
+
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
3811
4452
|
var import_datalog4 = require("@suss/datalog");
|
|
3812
4453
|
var import_extractor10 = require("@suss/extractor");
|
|
3813
4454
|
|
|
@@ -3862,7 +4503,7 @@ function requiredRelativePath(node) {
|
|
|
3862
4503
|
}
|
|
3863
4504
|
|
|
3864
4505
|
// src/reach/closure.ts
|
|
3865
|
-
var
|
|
4506
|
+
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
3866
4507
|
var import_datalog3 = require("@suss/datalog");
|
|
3867
4508
|
var import_extractor8 = require("@suss/extractor");
|
|
3868
4509
|
|
|
@@ -4086,14 +4727,14 @@ async function reachedFunctions(seeds, options) {
|
|
|
4086
4727
|
gapHandling: "permissive"
|
|
4087
4728
|
});
|
|
4088
4729
|
summary.confidence = { source: "inferred_static", level: "low" };
|
|
4089
|
-
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(
|
|
4090
|
-
(0,
|
|
4091
|
-
(0,
|
|
4092
|
-
(0,
|
|
4730
|
+
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(import_behavioral_ir4.unfollowedCallGap));
|
|
4731
|
+
(0, import_behavioral_ir4.placeCalls)(summary, targetsByKey.get(key));
|
|
4732
|
+
(0, import_behavioral_ir4.placeArgTargets)(summary, argTargetsByKey.get(key));
|
|
4733
|
+
(0, import_behavioral_ir4.placeCalleeParameters)(summary, parameterCallsByKey.get(key));
|
|
4093
4734
|
summariesByKey.set(key, [summary]);
|
|
4094
4735
|
summaries.push(summary);
|
|
4095
4736
|
}
|
|
4096
|
-
(0,
|
|
4737
|
+
(0, import_behavioral_ir4.recordParameterGaps)(parameterCallsByKey, summariesByKey, passedPositions);
|
|
4097
4738
|
return {
|
|
4098
4739
|
summaries,
|
|
4099
4740
|
targetsByKey,
|
|
@@ -4203,7 +4844,7 @@ async function scanBody(source, ctx, options) {
|
|
|
4203
4844
|
};
|
|
4204
4845
|
const followed2 = [];
|
|
4205
4846
|
const stops = [];
|
|
4206
|
-
const placements = new
|
|
4847
|
+
const placements = new import_behavioral_ir4.TargetPlacements();
|
|
4207
4848
|
const parameterCalls = [];
|
|
4208
4849
|
const passedPositions = /* @__PURE__ */ new Set();
|
|
4209
4850
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4256,7 +4897,7 @@ async function scanBody(source, ctx, options) {
|
|
|
4256
4897
|
);
|
|
4257
4898
|
if (outcome.kind === "stopped") {
|
|
4258
4899
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
4259
|
-
if (!seen.has(stopKey) && (0,
|
|
4900
|
+
if (!seen.has(stopKey) && (0, import_behavioral_ir4.worthRecording)(outcome.reason)) {
|
|
4260
4901
|
seen.add(stopKey);
|
|
4261
4902
|
stops.push({ callee, reason: outcome.reason });
|
|
4262
4903
|
}
|
|
@@ -4300,7 +4941,7 @@ function libraryUnit(target, options) {
|
|
|
4300
4941
|
exportName: exportPath[0] ?? name,
|
|
4301
4942
|
exportPath
|
|
4302
4943
|
},
|
|
4303
|
-
boundaryBinding: (0,
|
|
4944
|
+
boundaryBinding: (0, import_behavioral_ir4.functionCallBinding)({
|
|
4304
4945
|
transport: "in-process",
|
|
4305
4946
|
recognition: "reachable"
|
|
4306
4947
|
}),
|
|
@@ -4379,6 +5020,14 @@ function inheritedMethodsIn(packs) {
|
|
|
4379
5020
|
}
|
|
4380
5021
|
return found;
|
|
4381
5022
|
}
|
|
5023
|
+
function alreadyDiscovered(seen, raw) {
|
|
5024
|
+
const key = `${raw.identity.file}::${raw.identity.name}::${raw.identity.range.start}`;
|
|
5025
|
+
if (seen.has(key)) {
|
|
5026
|
+
return true;
|
|
5027
|
+
}
|
|
5028
|
+
seen.add(key);
|
|
5029
|
+
return false;
|
|
5030
|
+
}
|
|
4382
5031
|
async function extractRubyProject(options) {
|
|
4383
5032
|
const timer = options.onTiming !== void 0 ? (0, import_extractor10.createTimer)() : (0, import_extractor10.noopTimer)();
|
|
4384
5033
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
@@ -4440,6 +5089,7 @@ async function extractRubyProject(options) {
|
|
|
4440
5089
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? import_node_path4.default.relative(options.workspaceRoot, file) : file;
|
|
4441
5090
|
const seeds = [];
|
|
4442
5091
|
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
5092
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
4443
5093
|
for (const { file, root } of parsed) {
|
|
4444
5094
|
const displayPath = displayPathOf(file);
|
|
4445
5095
|
const seedByRaw = /* @__PURE__ */ new Map();
|
|
@@ -4452,10 +5102,15 @@ async function extractRubyProject(options) {
|
|
|
4452
5102
|
cache,
|
|
4453
5103
|
...storagePatterns.length > 0 ? { storage: { facts: db, patterns: storagePatterns } } : {},
|
|
4454
5104
|
inheritedMethods,
|
|
5105
|
+
displayPathOf,
|
|
5106
|
+
facts: db,
|
|
4455
5107
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
4456
5108
|
})
|
|
4457
5109
|
);
|
|
4458
5110
|
for (const raw of rawUnits) {
|
|
5111
|
+
if (alreadyDiscovered(discovered, raw)) {
|
|
5112
|
+
continue;
|
|
5113
|
+
}
|
|
4459
5114
|
const summary = timer.time(
|
|
4460
5115
|
"summarize",
|
|
4461
5116
|
() => (0, import_extractor10.assembleSummary)(raw, { gapHandling: "permissive" })
|
|
@@ -4531,14 +5186,14 @@ async function extractRubyProject(options) {
|
|
|
4531
5186
|
for (const [key, owners] of summariesBySeed) {
|
|
4532
5187
|
for (const summary of owners) {
|
|
4533
5188
|
summary.gaps.push(
|
|
4534
|
-
...(reached.stopsByKey.get(key) ?? []).map(
|
|
5189
|
+
...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir5.unfollowedCallGap)
|
|
4535
5190
|
);
|
|
4536
|
-
(0,
|
|
4537
|
-
(0,
|
|
4538
|
-
(0,
|
|
5191
|
+
(0, import_behavioral_ir5.placeCalls)(summary, reached.targetsByKey.get(key));
|
|
5192
|
+
(0, import_behavioral_ir5.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
|
|
5193
|
+
(0, import_behavioral_ir5.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
|
|
4539
5194
|
}
|
|
4540
5195
|
}
|
|
4541
|
-
(0,
|
|
5196
|
+
(0, import_behavioral_ir5.recordParameterGaps)(
|
|
4542
5197
|
reached.parameterCallsByKey,
|
|
4543
5198
|
summariesBySeed,
|
|
4544
5199
|
reached.passedPositions
|
|
@@ -4549,21 +5204,27 @@ async function extractRubyProject(options) {
|
|
|
4549
5204
|
const idRoot = options.projectRoot ?? options.workspaceRoot;
|
|
4550
5205
|
for (const summary of summaries) {
|
|
4551
5206
|
const absoluteFile = options.workspaceRoot === void 0 ? summary.location.file : import_node_path4.default.resolve(options.workspaceRoot, summary.location.file);
|
|
4552
|
-
summary.identity.id = (0,
|
|
5207
|
+
summary.identity.id = (0, import_behavioral_ir5.summaryIdFromParts)({
|
|
4553
5208
|
workspace: void 0,
|
|
4554
5209
|
file: idRoot === void 0 ? absoluteFile : import_node_path4.default.relative(idRoot, absoluteFile),
|
|
4555
5210
|
name: summary.identity.name,
|
|
4556
5211
|
exportPath: summary.identity.exportPath
|
|
4557
5212
|
});
|
|
4558
5213
|
}
|
|
4559
|
-
(0,
|
|
4560
|
-
(0,
|
|
5214
|
+
(0, import_behavioral_ir5.disambiguateSummaryIds)(summaries);
|
|
5215
|
+
(0, import_behavioral_ir5.linkCallsToSummaries)(summaries);
|
|
5216
|
+
const composed = timer.time(
|
|
5217
|
+
"summarize",
|
|
5218
|
+
() => (0, import_extractor10.composeWrappers)(summaries, {
|
|
5219
|
+
gapHandling: options.gapHandling ?? "permissive"
|
|
5220
|
+
})
|
|
5221
|
+
);
|
|
4561
5222
|
await timer.timeAsync("cache.write", async () => {
|
|
4562
|
-
if (cacheDir === null ||
|
|
5223
|
+
if (cacheDir === null || composed.length === 0) {
|
|
4563
5224
|
return;
|
|
4564
5225
|
}
|
|
4565
5226
|
try {
|
|
4566
|
-
await extractionCache.write(cacheInput,
|
|
5227
|
+
await extractionCache.write(cacheInput, composed);
|
|
4567
5228
|
} catch {
|
|
4568
5229
|
}
|
|
4569
5230
|
});
|
|
@@ -4572,11 +5233,11 @@ async function extractRubyProject(options) {
|
|
|
4572
5233
|
packs: options.packs,
|
|
4573
5234
|
tallies,
|
|
4574
5235
|
filesWalked: options.files.length,
|
|
4575
|
-
summaries
|
|
5236
|
+
summaries: composed
|
|
4576
5237
|
})
|
|
4577
5238
|
);
|
|
4578
5239
|
options.onTiming?.(timer.report());
|
|
4579
|
-
return { summaries, facts: db };
|
|
5240
|
+
return { summaries: composed, facts: db };
|
|
4580
5241
|
}
|
|
4581
5242
|
function fileDependenciesOf(db, displayPathOf) {
|
|
4582
5243
|
const byFile = /* @__PURE__ */ new Map();
|