@suss/adapter-ruby 0.32.0 → 0.33.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.js CHANGED
@@ -62,12 +62,19 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
62
62
  "module",
63
63
  "singleton_class"
64
64
  ]);
65
+ var PROGRAM_TYPE = "program";
66
+ var MODULE_SCOPE_STOPS = /* @__PURE__ */ new Set([
67
+ ...OWN_BODY_TYPES,
68
+ "block",
69
+ "do_block"
70
+ ]);
65
71
  var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
66
72
  var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
67
- function enclosingMethod(node) {
73
+ var LAMBDA_TYPE = "lambda";
74
+ function enclosingDefinition(node) {
68
75
  let current = node.parent;
69
76
  while (current !== null) {
70
- if (METHOD_TYPES.has(current.type)) {
77
+ if (METHOD_TYPES.has(current.type) || current.type === LAMBDA_TYPE) {
71
78
  return current;
72
79
  }
73
80
  current = current.parent;
@@ -341,7 +348,7 @@ import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
341
348
  import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
342
349
 
343
350
  // src/values/evaluator.ts
344
- import { nodeOfKey } from "@suss/resolution";
351
+ import { nodeOfKey, writtenValuesByKey } from "@suss/resolution";
345
352
  import { Evaluator, force, literalOf, text } from "@suss/values";
346
353
 
347
354
  // src/facts/resolve.ts
@@ -401,11 +408,14 @@ var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
401
408
  function resolveValues(db, keys) {
402
409
  askResolution(db, keys, "wanted", RUBY_PROGRAM);
403
410
  }
404
- function resolveEnvSites(db, sites) {
405
- askResolution(db, sites, "wantedEnvSite", RUBY_PROGRAM);
411
+ function resolveEnvObjects(db, objects) {
412
+ askResolution(db, objects, "wantedEnvObject", RUBY_PROGRAM);
406
413
  }
407
414
  function resolvedFunctions(db, key) {
408
- return db.lookup("wantedResolves", 0, key).map((row) => String(row[1]));
415
+ return [
416
+ ...db.lookup("wantedResolves", 0, key),
417
+ ...db.lookup("wantedGivesBack", 0, key)
418
+ ].map((row) => String(row[1]));
409
419
  }
410
420
  function writtenValueOf(db, key) {
411
421
  resolveValues(db, [key]);
@@ -505,12 +515,15 @@ var BINDS_A_NAME = {
505
515
  PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
506
516
  )
507
517
  };
508
- function localNamesIn(definitionNode) {
518
+ var DESCENDS_EVERYWHERE = /* @__PURE__ */ new Set();
519
+ function localNamesIn(definitionNode, stops = DESCENDS_EVERYWHERE) {
509
520
  const names = /* @__PURE__ */ new Set();
510
521
  const visit = (node) => {
511
522
  BINDS_A_NAME[node.type]?.(node, names);
512
523
  for (const child of bodyStatements(node)) {
513
- visit(child);
524
+ if (!stops.has(child.type)) {
525
+ visit(child);
526
+ }
514
527
  }
515
528
  };
516
529
  visit(definitionNode);
@@ -523,11 +536,12 @@ function spellsAName(node) {
523
536
  }
524
537
  return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
525
538
  }
539
+ var KEYWORD_LITERALS = /* @__PURE__ */ new Set(["__FILE__", "__LINE__", "__ENCODING__"]);
526
540
  function isBareMethodCall(node, locals) {
527
541
  if (node.type !== "identifier" || locals.has(node.text)) {
528
542
  return false;
529
543
  }
530
- return !spellsAName(node);
544
+ return !KEYWORD_LITERALS.has(node.text) && !spellsAName(node);
531
545
  }
532
546
 
533
547
  // src/facts/locals.ts
@@ -678,25 +692,46 @@ function namesOfFile(tree) {
678
692
  cache.file ??= scopeNames(/* @__PURE__ */ new Set(), tree.rootNode);
679
693
  return cache.file;
680
694
  }
695
+ function scopesAround(enclosing) {
696
+ const chain = [];
697
+ let current = enclosing;
698
+ while (current !== null && current.type === LAMBDA_TYPE) {
699
+ chain.push(current);
700
+ current = enclosingDefinition(current);
701
+ }
702
+ chain.push(current);
703
+ return chain;
704
+ }
705
+ var namesOfScope = (scope, node) => scope === null ? namesOfFile(node.tree) : namesOfMethod(scope);
681
706
  function ownerOfName(node, name, enclosing) {
682
707
  if (node.type === "constant") {
683
708
  return null;
684
709
  }
685
- const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
686
- if (names.blockParams.has(name)) {
687
- const block = blockDeclaring(node, name);
688
- if (block !== null) {
689
- return block;
710
+ for (const scope of scopesAround(enclosing)) {
711
+ const names = namesOfScope(scope, node);
712
+ if (names.blockParams.has(name)) {
713
+ const block = blockDeclaring(node, name);
714
+ if (block !== null) {
715
+ return block;
716
+ }
717
+ }
718
+ if (scope !== null && names.locals.has(name)) {
719
+ return scope;
690
720
  }
691
721
  }
692
- return enclosing !== null && names.locals.has(name) ? enclosing : null;
722
+ return null;
693
723
  }
694
724
  function isLocalName(node, name, enclosing) {
695
- const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
696
- if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
697
- return true;
725
+ for (const scope of scopesAround(enclosing)) {
726
+ const names = namesOfScope(scope, node);
727
+ if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
728
+ return true;
729
+ }
730
+ if (names.locals.has(name)) {
731
+ return true;
732
+ }
698
733
  }
699
- return names.locals.has(name);
734
+ return false;
700
735
  }
701
736
  function blockDeclaring(node, name) {
702
737
  let current = node.parent;
@@ -887,19 +922,33 @@ function calleeKeyOf(filePath, node, enclosing) {
887
922
  }
888
923
  return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
889
924
  }
925
+ function invokedKeyOf(filePath, node, enclosing) {
926
+ const invoked = invokedValueOf(node);
927
+ return invoked === null ? null : readKey(filePath, invoked, enclosing);
928
+ }
890
929
  function emitCall(emitter, call) {
891
- add(emitter, "writtenValue", nodeId(emitter.filePath, call));
930
+ const callKey = nodeId(emitter.filePath, call);
931
+ add(emitter, "writtenValue", callKey);
932
+ const invoked = invokedValueOf(call);
933
+ if (invoked !== null) {
934
+ add(emitter, "call", callKey, valueKey(emitter, invoked));
935
+ }
936
+ if (!emitMessageSent(emitter, call, callKey) && invoked === null) {
937
+ return;
938
+ }
939
+ emitCallArguments(emitter, call, callKey);
940
+ }
941
+ function emitMessageSent(emitter, call, callKey) {
892
942
  const method = call.type === "identifier" ? call : field(call, "method");
893
943
  const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
894
944
  if (method === null || calleeKey === null) {
895
- return;
945
+ return false;
896
946
  }
897
- const receiver = field(call, "receiver");
898
- const callKey = nodeId(emitter.filePath, call);
899
947
  add(emitter, "call", callKey, calleeKey);
900
948
  if (!emitter.insideMethod) {
901
949
  add(emitter, "callOutsideMethod", callKey);
902
950
  }
951
+ const receiver = field(call, "receiver");
903
952
  if (receiver !== null) {
904
953
  add(
905
954
  emitter,
@@ -911,6 +960,21 @@ function emitCall(emitter, call) {
911
960
  } else if (emitter.selfKey !== null) {
912
961
  add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
913
962
  }
963
+ return true;
964
+ }
965
+ var INVOKE_METHOD = "call";
966
+ function invokedValueOf(call) {
967
+ if (call.type !== "call") {
968
+ return null;
969
+ }
970
+ const receiver = field(call, "receiver");
971
+ if (receiver === null) {
972
+ return null;
973
+ }
974
+ const method = field(call, "method");
975
+ return method === null || method.text === INVOKE_METHOD ? receiver : null;
976
+ }
977
+ function emitCallArguments(emitter, call, callKey) {
914
978
  const args = field(call, "arguments");
915
979
  let position = 0;
916
980
  for (const argument of args === null ? [] : children(args)) {
@@ -1030,12 +1094,58 @@ function walkExpressions(node, emitter, visit, turns = []) {
1030
1094
  );
1031
1095
  }
1032
1096
  }
1097
+ var WRITTEN_KEY_TYPES = /* @__PURE__ */ new Set([
1098
+ "string",
1099
+ "integer",
1100
+ "simple_symbol",
1101
+ "bare_string",
1102
+ "bare_symbol"
1103
+ ]);
1104
+ function emitKeyedRead(emitter, site, container, key) {
1105
+ if (WRITTEN_KEY_TYPES.has(key.type)) {
1106
+ return;
1107
+ }
1108
+ add(
1109
+ emitter,
1110
+ "readsKeyed",
1111
+ nodeId(emitter.filePath, site),
1112
+ valueKey(emitter, container),
1113
+ valueKey(emitter, key)
1114
+ );
1115
+ }
1116
+ function emitKeyedElement(emitter, node) {
1117
+ const object = field(node, "object");
1118
+ if (object === null || isWriteTarget(node)) {
1119
+ return;
1120
+ }
1121
+ const index = node.namedChildren.find(
1122
+ (child) => child !== null && child.id !== object.id
1123
+ );
1124
+ if (index !== void 0) {
1125
+ emitKeyedRead(emitter, node, object, index);
1126
+ }
1127
+ }
1128
+ function emitKeyedFetch(emitter, call) {
1129
+ const receiver = field(call, "receiver");
1130
+ if (receiver === null || field(call, "method")?.text !== "fetch") {
1131
+ return;
1132
+ }
1133
+ const { positional } = readCallArgs(field(call, "arguments"));
1134
+ const key = positional[0];
1135
+ if (key !== void 0) {
1136
+ emitKeyedRead(emitter, call, receiver, key);
1137
+ }
1138
+ }
1033
1139
  function emitExpressionFacts(emitter, node) {
1034
1140
  emitDynamicDefinition(emitter, node, []);
1035
1141
  walkExpressions(node, emitter, (child, turns) => {
1036
1142
  emitDynamicDefinition(emitter, child, turns);
1037
1143
  if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
1038
1144
  emitCall(emitter, child);
1145
+ emitKeyedFetch(emitter, child);
1146
+ }
1147
+ if (child.type === "element_reference") {
1148
+ emitKeyedElement(emitter, child);
1039
1149
  }
1040
1150
  if (ARRAY_TYPES.has(child.type)) {
1041
1151
  emitArray(emitter, child);
@@ -1159,6 +1269,25 @@ function assignedValueOf(node) {
1159
1269
  }
1160
1270
  return field(node, "right");
1161
1271
  }
1272
+ function emitLambdasIn(emitter, body) {
1273
+ for (const child of children(body)) {
1274
+ if (child.type === LAMBDA_TYPE) {
1275
+ emitMethodFacts(emitter, child);
1276
+ continue;
1277
+ }
1278
+ if (OWN_BODY_TYPES.has(child.type)) {
1279
+ continue;
1280
+ }
1281
+ emitLambdasIn(emitter, child);
1282
+ }
1283
+ }
1284
+ function definitionBody(definition) {
1285
+ const body = field(definition, "body");
1286
+ if (body === null || definition.type !== LAMBDA_TYPE) {
1287
+ return body;
1288
+ }
1289
+ return field(body, "body");
1290
+ }
1162
1291
  function emitMethodFacts(emitter, method) {
1163
1292
  const funcKey = nodeId(emitter.filePath, method);
1164
1293
  add(emitter, "func", funcKey);
@@ -1173,7 +1302,7 @@ function emitMethodFacts(emitter, method) {
1173
1302
  position += 1;
1174
1303
  }
1175
1304
  const inside = { ...emitter, enclosing: method };
1176
- const body = field(method, "body");
1305
+ const body = definitionBody(method);
1177
1306
  if (body === null) {
1178
1307
  return funcKey;
1179
1308
  }
@@ -1205,6 +1334,7 @@ function emitMethodFacts(emitter, method) {
1205
1334
  }
1206
1335
  emitExpressionFacts(inside, body);
1207
1336
  emitScopeWrites(inside, method, body);
1337
+ emitLambdasIn(inside, body);
1208
1338
  return funcKey;
1209
1339
  }
1210
1340
  function isConstruction(value) {
@@ -1364,12 +1494,15 @@ function emitClassFacts(emitter, cls) {
1364
1494
  add(emitter, "initializes", classKey, funcKey);
1365
1495
  }
1366
1496
  }
1367
- const methodBody = field(statement, "body");
1368
- if (methodBody !== null) {
1369
- emitInstanceStores(within, funcKey, methodBody, stored);
1497
+ const methodBody2 = field(statement, "body");
1498
+ if (methodBody2 !== null) {
1499
+ emitInstanceStores(within, funcKey, methodBody2, stored);
1370
1500
  }
1371
1501
  }
1372
1502
  emitInstanceWrites(within, classKey, collected);
1503
+ if (body !== null) {
1504
+ emitLambdasIn(within, body);
1505
+ }
1373
1506
  return classKey;
1374
1507
  }
1375
1508
  function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
@@ -1405,6 +1538,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
1405
1538
  walk(root);
1406
1539
  emitExpressionFacts(emitter, root);
1407
1540
  emitScopeWrites(emitter, null, root);
1541
+ emitLambdasIn(emitter, root);
1408
1542
  }
1409
1543
 
1410
1544
  // src/values/lowering.ts
@@ -2424,6 +2558,24 @@ function writtenNodeOf(node, db, site) {
2424
2558
  const context = db === void 0 ? void 0 : contexts.get(db);
2425
2559
  return context === void 0 ? null : context.writtenTo(node, site);
2426
2560
  }
2561
+ function askWrittenValues(nodes, db) {
2562
+ if (db === void 0 || !contexts.has(db)) {
2563
+ return;
2564
+ }
2565
+ const keys = /* @__PURE__ */ new Set();
2566
+ for (const node of nodes) {
2567
+ const file = filesByTree.get(node.tree);
2568
+ if (file !== void 0) {
2569
+ keys.add(readKey(file, node, enclosingDefinition(node)));
2570
+ }
2571
+ }
2572
+ if (keys.size === 0) {
2573
+ return;
2574
+ }
2575
+ const asked = [...keys];
2576
+ resolveValues(db, asked);
2577
+ writtenValuesByKey(db, asked, (more) => resolveValues(db, more));
2578
+ }
2427
2579
  function evaluatedValue(node, db, bindings, site) {
2428
2580
  const evaluator = evaluatorFor(node, db);
2429
2581
  if (bindings === void 0) {
@@ -2504,7 +2656,7 @@ function contextOver(db, nodes) {
2504
2656
  if (file === null) {
2505
2657
  return null;
2506
2658
  }
2507
- const key = readKey(file, node, enclosingMethod(node));
2659
+ const key = readKey(file, node, enclosingDefinition(node));
2508
2660
  const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
2509
2661
  return answer === null ? null : nodeOfKey(rootsByFile, answer);
2510
2662
  },
@@ -2514,7 +2666,7 @@ function contextOver(db, nodes) {
2514
2666
  if (file === null || method === null) {
2515
2667
  return null;
2516
2668
  }
2517
- const key = field(call, "receiver") === null ? readKey(file, method, enclosingMethod(call)) : nodeId(file, method);
2669
+ const key = field(call, "receiver") === null ? readKey(file, method, enclosingDefinition(call)) : nodeId(file, method);
2518
2670
  resolveValues(db, [key]);
2519
2671
  const resolved = resolvedFunctions(db, key);
2520
2672
  const settled = resolved.length === 1 ? resolved[0] : void 0;
@@ -2666,8 +2818,8 @@ function qualifyConstantRef(node, nesting) {
2666
2818
  return compoundName(node);
2667
2819
  }
2668
2820
  if (node.type === "constant") {
2669
- const innermost = nesting[0] ?? null;
2670
- return innermost === null ? node.text : `${innermost}::${node.text}`;
2821
+ const innermost2 = nesting[0] ?? null;
2822
+ return innermost2 === null ? node.text : `${innermost2}::${node.text}`;
2671
2823
  }
2672
2824
  return null;
2673
2825
  }
@@ -2724,9 +2876,9 @@ function ownIdentity(nameNode, outerNesting) {
2724
2876
  return { qualifiedName, bodyNesting: [qualifiedName, ...outerNesting] };
2725
2877
  }
2726
2878
  function definedName(nameNode, outerNesting) {
2727
- const innermost = outerNesting[0] ?? null;
2728
- if (nameNode.type === "scope_resolution" && innermost !== null && !nameNode.text.startsWith("::")) {
2729
- return `${innermost}::${nameNode.text}`;
2879
+ const innermost2 = outerNesting[0] ?? null;
2880
+ if (nameNode.type === "scope_resolution" && innermost2 !== null && !nameNode.text.startsWith("::")) {
2881
+ return `${innermost2}::${nameNode.text}`;
2730
2882
  }
2731
2883
  return qualifyConstantRef(nameNode, outerNesting);
2732
2884
  }
@@ -3354,24 +3506,38 @@ function isCall(node, locals, inherited) {
3354
3506
  }
3355
3507
  return isBareMethodCall(node, locals) && !RAISE_NAMES2.has(node.text) && !inherited.has(node.text);
3356
3508
  }
3357
- function collectCalls(node, locals, inherited, found) {
3509
+ function collectCalls(node, read, inherited, found) {
3358
3510
  for (const child of children2(node)) {
3359
- if (OWN_BODY_TYPES.has(child.type)) {
3511
+ if (read.stops.has(child.type)) {
3360
3512
  continue;
3361
3513
  }
3362
- if (isCall(child, locals, inherited)) {
3514
+ if (isCall(child, read.locals, inherited)) {
3363
3515
  found.push(child);
3364
3516
  }
3365
- collectCalls(child, locals, inherited, found);
3517
+ collectCalls(child, read, inherited, found);
3366
3518
  }
3367
3519
  return found;
3368
3520
  }
3369
- function bodyCalls(definitionNode, inherited = NO_INHERITED_METHODS) {
3521
+ function methodBody(definitionNode) {
3370
3522
  const body = field(definitionNode, "body");
3371
3523
  if (body === null) {
3372
- return [];
3524
+ return null;
3373
3525
  }
3374
- return collectCalls(body, localNamesIn(definitionNode), inherited, []);
3526
+ return {
3527
+ body,
3528
+ locals: localNamesIn(definitionNode),
3529
+ stops: OWN_BODY_TYPES
3530
+ };
3531
+ }
3532
+ function moduleScopeBody(root) {
3533
+ return {
3534
+ body: root,
3535
+ locals: localNamesIn(root, MODULE_SCOPE_STOPS),
3536
+ stops: MODULE_SCOPE_STOPS
3537
+ };
3538
+ }
3539
+ function bodyCalls(read, inherited = NO_INHERITED_METHODS) {
3540
+ return collectCalls(read.body, read, inherited, []);
3375
3541
  }
3376
3542
  function calleeText(call) {
3377
3543
  const receiver = field(call, "receiver");
@@ -3415,14 +3581,23 @@ function enclosingStatement(call, body) {
3415
3581
  return current;
3416
3582
  }
3417
3583
  function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
3418
- const body = field(definitionNode, "body");
3419
- if (body === null) {
3584
+ const read = methodBody(definitionNode);
3585
+ if (read === null) {
3420
3586
  return [];
3421
3587
  }
3422
- const calls = callsReported(
3423
- bodyCalls(definitionNode, inherited),
3424
- keepsArglessCall
3588
+ return effectsOfBody(read, inherited, keepsArglessCall, facts);
3589
+ }
3590
+ function moduleScopeInvocationEffects(root, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
3591
+ return effectsOfBody(
3592
+ moduleScopeBody(root),
3593
+ inherited,
3594
+ keepsArglessCall,
3595
+ facts
3425
3596
  );
3597
+ }
3598
+ function effectsOfBody(read, inherited, keepsArglessCall, facts) {
3599
+ const body = read.body;
3600
+ const calls = callsReported(bodyCalls(read, inherited), keepsArglessCall);
3426
3601
  if (calls.length === 0) {
3427
3602
  return [];
3428
3603
  }
@@ -3834,6 +4009,57 @@ function clientCallUnits(root, pack, pattern, options) {
3834
4009
  }
3835
4010
  return units;
3836
4011
  }
4012
+ function askClientCallReads(root, patterns, options) {
4013
+ const facts = options.facts;
4014
+ if (facts === void 0 || patterns.length === 0) {
4015
+ return;
4016
+ }
4017
+ const calls = [];
4018
+ for (const method of methodDefinitions(root)) {
4019
+ calls.push(...callsUnder(method));
4020
+ }
4021
+ if (patterns.some((pattern) => (pattern.receiverBuilders ?? []).length > 0)) {
4022
+ askWrittenValues(receiversOf(calls), facts);
4023
+ }
4024
+ askWrittenValues(requestArguments(calls, patterns, options), facts);
4025
+ }
4026
+ function receiversOf(calls) {
4027
+ const found = [];
4028
+ for (const call of calls) {
4029
+ const receiver = field(call, "receiver");
4030
+ if (receiver !== null) {
4031
+ found.push(receiver);
4032
+ }
4033
+ }
4034
+ return found;
4035
+ }
4036
+ function requestArguments(calls, patterns, options) {
4037
+ const found = [];
4038
+ for (const call of calls) {
4039
+ const receiver = field(call, "receiver");
4040
+ const called = field(call, "method")?.text;
4041
+ if (receiver === null || called === void 0) {
4042
+ continue;
4043
+ }
4044
+ for (const pattern of patterns) {
4045
+ if (!isLibraryReceiver(receiver, pattern, options)) {
4046
+ continue;
4047
+ }
4048
+ const argument = requestArgument(call, called, pattern);
4049
+ if (argument !== void 0) {
4050
+ found.push(argument);
4051
+ }
4052
+ }
4053
+ }
4054
+ return found;
4055
+ }
4056
+ function requestArgument(call, called, pattern) {
4057
+ const args = readCallArgs(field(call, "arguments"));
4058
+ if (pattern.verbMethodNames[called] !== void 0) {
4059
+ return urlNodeIn(args, pattern);
4060
+ }
4061
+ return pattern.requestObject?.attribute === called ? args.positional[0] : void 0;
4062
+ }
3837
4063
  function methodDefinitions(node, found = []) {
3838
4064
  for (const child of node.namedChildren) {
3839
4065
  if (child === null) {
@@ -3958,9 +4184,11 @@ function builderCallBehind(receiver, pattern, facts) {
3958
4184
  return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
3959
4185
  }
3960
4186
  function urlIn(args, pattern, options, site) {
4187
+ return pathAt(urlNodeIn(args, pattern), options, site);
4188
+ }
4189
+ function urlNodeIn(args, pattern) {
3961
4190
  const keyword = pattern.url.keyword;
3962
- const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
3963
- return pathAt(written, options, site);
4191
+ return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
3964
4192
  }
3965
4193
  function pathAt(written, options, site) {
3966
4194
  if (written === void 0) {
@@ -4082,11 +4310,11 @@ function namedParameters(db) {
4082
4310
  }
4083
4311
  const named2 = /* @__PURE__ */ new Map();
4084
4312
  namedByDb.set(db, named2);
4085
- const sites = db.facts("readsEnvNamed").map((row) => String(row[0]));
4086
- if (sites.length === 0) {
4313
+ const objects = db.facts("environmentObject").map((row) => String(row[0]));
4314
+ if (objects.length === 0) {
4087
4315
  return named2;
4088
4316
  }
4089
- resolveEnvSites(db, sites);
4317
+ resolveEnvObjects(db, objects);
4090
4318
  for (const row of db.facts("wantedParamNamesEnv")) {
4091
4319
  const found = named2.get(String(row[0]));
4092
4320
  if (found === void 0) {
@@ -4097,38 +4325,44 @@ function namedParameters(db) {
4097
4325
  }
4098
4326
  return named2;
4099
4327
  }
4100
- function emitEnvNameFacts(db, file, root) {
4101
- for (const site of envNameSites(root)) {
4102
- const key = nodeId(file, site.read);
4103
- db.add("readsEnvNamed", [
4104
- key,
4105
- readKey(file, site.name, enclosingMethod(site.name))
4106
- ]);
4107
- if (site.defaulted) {
4108
- db.add(ENV_DEFAULTED, [key]);
4109
- }
4110
- }
4111
- }
4112
- function envNameSites(root) {
4113
- const sites = [];
4328
+ function emitEnvFacts(db, file, root) {
4114
4329
  walkDescendants2(root, null, {
4115
4330
  at: (node) => {
4331
+ if (isEnv(node) && handsOnward(node)) {
4332
+ db.add("environmentObject", [
4333
+ readKey(file, node, enclosingDefinition(node))
4334
+ ]);
4335
+ }
4116
4336
  const site = envSiteAt(node);
4117
- if (site !== null && stringLiteralValue(site.name) === null) {
4118
- sites.push(site);
4337
+ if (site !== null && site.defaulted && stringLiteralValue(site.name) === null) {
4338
+ db.add(ENV_DEFAULTED, [nodeId(file, site.read)]);
4119
4339
  }
4120
4340
  },
4121
4341
  into: () => null
4122
4342
  });
4123
- return sites;
4343
+ }
4344
+ function handsOnward(node) {
4345
+ const parent = node.parent;
4346
+ if (parent === null) {
4347
+ return true;
4348
+ }
4349
+ if (parent.type === "element_reference" || parent.type === "call") {
4350
+ const site = envSiteAt(parent);
4351
+ return site === null || stringLiteralValue(site.name) === null;
4352
+ }
4353
+ return true;
4124
4354
  }
4125
4355
  function helperReads(calls, facts) {
4126
4356
  const { db, file, named: named2 } = facts;
4127
4357
  const callees = new NodeMap();
4128
4358
  for (const call of calls) {
4129
- const key = calleeKeyOf(file, call, enclosingMethod(call));
4130
- if (key !== null) {
4131
- callees.set(call, key);
4359
+ const enclosing = enclosingDefinition(call);
4360
+ const keys = [
4361
+ calleeKeyOf(file, call, enclosing),
4362
+ invokedKeyOf(file, call, enclosing)
4363
+ ].filter((key) => key !== null);
4364
+ if (keys.length > 0) {
4365
+ callees.set(call, keys);
4132
4366
  }
4133
4367
  }
4134
4368
  const reads = new NodeMap();
@@ -4137,7 +4371,7 @@ function helperReads(calls, facts) {
4137
4371
  }
4138
4372
  resolveValues(
4139
4373
  db,
4140
- [...callees].map(([, callee]) => callee)
4374
+ [...callees].flatMap(([, keys]) => keys)
4141
4375
  );
4142
4376
  for (const [call, callee] of callees) {
4143
4377
  const args = namingArguments(db, named2, call, callee);
@@ -4151,10 +4385,10 @@ function helperReads(calls, facts) {
4151
4385
  }
4152
4386
  return reads;
4153
4387
  }
4154
- function namingArguments(db, named2, call, callee) {
4388
+ function namingArguments(db, named2, call, callees) {
4155
4389
  const { positional, keyword } = readCallArgs(field(call, "arguments"));
4156
4390
  const found = [];
4157
- for (const func of resolvedFunctions(db, callee)) {
4391
+ for (const func of callees.flatMap((key) => resolvedFunctions(db, key))) {
4158
4392
  for (const row of db.lookup("paramOf", 0, func)) {
4159
4393
  const sites = named2.get(String(row[2]));
4160
4394
  const argument = positional[Number(row[1])];
@@ -4418,6 +4652,9 @@ function methodNamesOf(args, filter, facts) {
4418
4652
  const value = named2 === void 0 ? null : stringValueOf(named2, facts);
4419
4653
  return value === null ? [] : [value];
4420
4654
  }
4655
+ return symbolArgumentNames(args, facts);
4656
+ }
4657
+ function symbolArgumentNames(args, facts) {
4421
4658
  return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
4422
4659
  }
4423
4660
  function skipsOf(statement, filterName, facts) {
@@ -4456,19 +4693,315 @@ function actionsUnder(node, facts) {
4456
4693
  }
4457
4694
 
4458
4695
  // src/storage.ts
4459
- import { storageBinding } from "@suss/ir-core";
4696
+ import { storageBinding as storageBinding2 } from "@suss/ir-core";
4697
+ import { askResolution as askResolution3 } from "@suss/resolution";
4698
+
4699
+ // src/baseClass.ts
4460
4700
  import { askResolution as askResolution2 } from "@suss/resolution";
4461
- function children3(node) {
4462
- return node.namedChildren.filter((child) => child !== null);
4701
+ function classBehind(facts, file, constant2) {
4702
+ const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
4703
+ const bound = new Set(
4704
+ facts.lookup("binds", 0, key).map((row) => String(row[1]))
4705
+ );
4706
+ return bound.size === 1 ? [...bound][0] : void 0;
4707
+ }
4708
+ function reachesBase(facts, classKey, bases) {
4709
+ askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
4710
+ return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
4463
4711
  }
4712
+ function enclosingClassKey(node, file) {
4713
+ let current = node.parent;
4714
+ while (current !== null) {
4715
+ if (NESTING_TYPES.has(current.type)) {
4716
+ return nodeId(file, current);
4717
+ }
4718
+ current = current.parent;
4719
+ }
4720
+ return null;
4721
+ }
4722
+
4723
+ // src/loaders.ts
4464
4724
  function receiverOf(node) {
4465
4725
  return node.type === "call" ? field(node, "receiver") : null;
4466
4726
  }
4467
4727
  function methodOf(node) {
4468
4728
  return field(node, "method")?.text ?? "";
4469
4729
  }
4730
+ function isLoader(node, loader) {
4731
+ if (node === null) {
4732
+ return false;
4733
+ }
4734
+ if (node.type === "identifier") {
4735
+ return node.text === loader.loader;
4736
+ }
4737
+ return node.type === "call" && receiverOf(node) === null && methodOf(node) === loader.loader;
4738
+ }
4739
+ function loaderPick(call, loader) {
4740
+ const method = methodOf(call);
4741
+ const receiver = receiverOf(call);
4742
+ if (receiver === null) {
4743
+ return loader.shortcuts.includes(method) ? call : null;
4744
+ }
4745
+ if (!loader.reads.includes(method) || receiver.type !== "call") {
4746
+ return null;
4747
+ }
4748
+ const picks = methodOf(receiver) === loader.pick && isLoader(receiverOf(receiver), loader);
4749
+ return picks ? receiver : null;
4750
+ }
4751
+ function pickedSource(call, loader) {
4752
+ const source = loader.source;
4753
+ const pick = source === void 0 ? null : loaderPick(call, loader);
4754
+ if (source === void 0 || pick === null || methodOf(pick) !== loader.pick) {
4755
+ return null;
4756
+ }
4757
+ const args = field(pick, "arguments");
4758
+ const given = args === null ? void 0 : args.namedChildren[source.at];
4759
+ return given?.type === "constant" || given?.type === "scope_resolution" ? given : null;
4760
+ }
4761
+
4762
+ // src/rawSql.ts
4763
+ import { storageBinding } from "@suss/ir-core";
4764
+ import { readSqlAccess, splitQualifiedTable, sqlFromParts } from "@suss/sql";
4765
+ import { piecesOf as piecesOf2 } from "@suss/values";
4766
+ var MOST_HOPS = 8;
4767
+ var NOWHERE = { scope: null, container: null };
4768
+ function rawSqlEffects(call, options) {
4769
+ for (const pattern of options.patterns) {
4770
+ const effects = patternEffects(call, pattern, options);
4771
+ if (effects.length > 0) {
4772
+ return effects;
4773
+ }
4774
+ }
4775
+ return [];
4776
+ }
4777
+ function patternEffects(call, pattern, options) {
4778
+ const receiver = field(call, "receiver");
4779
+ if (receiver === null) {
4780
+ return [];
4781
+ }
4782
+ const address = addressBehind(receiver, pattern, options, 0);
4783
+ if (address === null) {
4784
+ return [];
4785
+ }
4786
+ const method = field(call, "method")?.text ?? "";
4787
+ const args = readCallArgs(field(call, "arguments"));
4788
+ const place = pattern.statements?.[method];
4789
+ if (place !== void 0) {
4790
+ const statement = statementAt(
4791
+ args,
4792
+ place,
4793
+ options.facts,
4794
+ pattern.bindPlaceholder
4795
+ );
4796
+ return statementEffects(call, statement, address, pattern);
4797
+ }
4798
+ return rowCallEffects(call, args, address, pattern, options);
4799
+ }
4800
+ function statementEffects(call, statement, address, store) {
4801
+ if (statement === null) {
4802
+ return [];
4803
+ }
4804
+ return readSqlAccess(statement, { dialect: store.dialect }).map(
4805
+ (access) => effectOf(
4806
+ call,
4807
+ store,
4808
+ {
4809
+ scope: innermost(access.qualifier) ?? address.scope,
4810
+ container: access.table
4811
+ },
4812
+ {
4813
+ kind: access.kind,
4814
+ fields: access.fields,
4815
+ selector: access.selector
4816
+ }
4817
+ )
4818
+ );
4819
+ }
4820
+ function innermost(qualifier) {
4821
+ return qualifier[qualifier.length - 1] ?? null;
4822
+ }
4823
+ function rowCallEffects(call, args, address, pattern, options) {
4824
+ const method = field(call, "method")?.text ?? "";
4825
+ const rowCall = pattern.rowCalls?.[method];
4826
+ if (rowCall === void 0) {
4827
+ return [];
4828
+ }
4829
+ const given = rowCall.container === void 0 ? null : namedTable(stringAt(args, rowCall.container, options.facts));
4830
+ const reached = {
4831
+ scope: given?.scope ?? address.scope,
4832
+ container: given?.container ?? address.container
4833
+ };
4834
+ if (reached.container === null) {
4835
+ return [];
4836
+ }
4837
+ return [
4838
+ effectOf(call, pattern, reached, {
4839
+ kind: rowCall.kind,
4840
+ fields: [],
4841
+ selector: []
4842
+ })
4843
+ ];
4844
+ }
4845
+ function namedTable(name) {
4846
+ const split = name === null ? null : splitQualifiedTable(name);
4847
+ return split === null ? null : { scope: innermost(split.qualifier), container: split.table };
4848
+ }
4849
+ function effectOf(call, store, address, access) {
4850
+ const operation = field(call, "method")?.text ?? "";
4851
+ return {
4852
+ type: "interaction",
4853
+ binding: storageBinding({
4854
+ recognition: "ruby-raw-sql",
4855
+ storageSystem: store.storageSystem,
4856
+ scope: address.scope ?? store.scope ?? "default",
4857
+ container: address.container
4858
+ }),
4859
+ callee: call.text,
4860
+ interaction: {
4861
+ class: "storage-access",
4862
+ kind: access.kind,
4863
+ fields: access.fields,
4864
+ operation,
4865
+ ...access.selector.length > 0 ? { selector: access.selector } : {}
4866
+ }
4867
+ };
4868
+ }
4869
+ function addressBehind(receiver, pattern, options, hops) {
4870
+ if (hops > MOST_HOPS) {
4871
+ return null;
4872
+ }
4873
+ if (receiver.type !== "call") {
4874
+ const written = writtenNodeOf(receiver, options.facts);
4875
+ if (written !== null && written.id !== receiver.id) {
4876
+ return addressBehind(written, pattern, options, hops + 1);
4877
+ }
4878
+ return receiver.type === "identifier" && buildsOnOwnClass(receiver, receiver.text, pattern, options) ? NOWHERE : null;
4879
+ }
4880
+ const method = field(receiver, "method")?.text ?? "";
4881
+ const inner = field(receiver, "receiver");
4882
+ if (inner === null) {
4883
+ return buildsOnOwnClass(receiver, method, pattern, options) ? NOWHERE : null;
4884
+ }
4885
+ if (namesConstant2(inner, pattern.constantName) || namesSubclass(inner, pattern, options)) {
4886
+ return pattern.clientBuilders.includes(method) ? NOWHERE : null;
4887
+ }
4888
+ const addressing = pattern.addressing?.[method];
4889
+ if (addressing === void 0 || inner === null) {
4890
+ return null;
4891
+ }
4892
+ const behind = addressBehind(inner, pattern, options, hops + 1);
4893
+ if (behind === null) {
4894
+ return null;
4895
+ }
4896
+ const named2 = stringAt(
4897
+ readCallArgs(field(receiver, "arguments")),
4898
+ addressing,
4899
+ options.facts
4900
+ );
4901
+ if (addressing.says === "scope") {
4902
+ return { scope: named2 ?? behind.scope, container: behind.container };
4903
+ }
4904
+ const table = namedTable(named2);
4905
+ return {
4906
+ scope: table?.scope ?? behind.scope,
4907
+ container: table?.container ?? behind.container
4908
+ };
4909
+ }
4910
+ function namesConstant2(receiver, constantName2) {
4911
+ if (receiver.type === "constant") {
4912
+ return receiver.text === constantName2;
4913
+ }
4914
+ return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
4915
+ }
4916
+ function namesSubclass(receiver, pattern, options) {
4917
+ const bases = pattern.baseClasses ?? [];
4918
+ const facts = options.facts;
4919
+ const file = options.file;
4920
+ if (bases.length === 0 || facts === void 0 || file === void 0 || receiver.type !== "constant" && receiver.type !== "scope_resolution") {
4921
+ return false;
4922
+ }
4923
+ const classKey = classBehind(facts, file, receiver);
4924
+ return classKey !== void 0 && reachesBase(facts, classKey, bases);
4925
+ }
4926
+ function buildsOnOwnClass(node, method, pattern, options) {
4927
+ const bases = pattern.baseClasses ?? [];
4928
+ const facts = options.facts;
4929
+ const file = options.file;
4930
+ if (bases.length === 0 || facts === void 0 || file === void 0 || !pattern.clientBuilders.includes(method)) {
4931
+ return false;
4932
+ }
4933
+ const classKey = enclosingClassKey(node, file);
4934
+ return classKey !== null && reachesBase(facts, classKey, bases);
4935
+ }
4936
+ function argumentAt(args, place) {
4937
+ const keyword = place.keyword;
4938
+ return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[place.at];
4939
+ }
4940
+ function stringAt(args, place, facts) {
4941
+ const written = argumentAt(args, place);
4942
+ if (written === void 0) {
4943
+ return null;
4944
+ }
4945
+ const pieces = piecesOf2(evaluatedValue(written, facts));
4946
+ const only = pieces.length === 1 ? pieces[0] : void 0;
4947
+ if (only === void 0 || only.kind !== "text" || only.options.length !== 1) {
4948
+ return null;
4949
+ }
4950
+ return only.options[0] ?? null;
4951
+ }
4952
+ function statementAt(args, place, facts, bindPlaceholder) {
4953
+ const written = argumentAt(args, place);
4954
+ if (written === void 0) {
4955
+ return null;
4956
+ }
4957
+ const parts = literalParts(statementValue(evaluatedValue(written, facts)));
4958
+ if (parts === null) {
4959
+ return null;
4960
+ }
4961
+ const statement = sqlFromParts(bindsSplit(parts, bindPlaceholder));
4962
+ return statement.trim() === "" ? null : statement;
4963
+ }
4964
+ function bindsSplit(parts, bindPlaceholder) {
4965
+ if (bindPlaceholder === void 0 || bindPlaceholder === "") {
4966
+ return [...parts];
4967
+ }
4968
+ return parts.flatMap((part) => part.split(bindPlaceholder));
4969
+ }
4970
+ function statementValue(value) {
4971
+ if (value.kind !== "sequence") {
4972
+ return value;
4973
+ }
4974
+ const first = value.items[0];
4975
+ return first === void 0 ? value : first.value;
4976
+ }
4977
+ function literalParts(value) {
4978
+ if (value.kind !== "string") {
4979
+ return null;
4980
+ }
4981
+ const parts = [""];
4982
+ for (const piece of value.pieces) {
4983
+ const only = piece.kind === "text" ? piece.options : [];
4984
+ if (only.length === 1) {
4985
+ parts[parts.length - 1] += only[0] ?? "";
4986
+ continue;
4987
+ }
4988
+ parts.push("");
4989
+ }
4990
+ return parts;
4991
+ }
4992
+
4993
+ // src/storage.ts
4994
+ function children3(node) {
4995
+ return node.namedChildren.filter((child) => child !== null);
4996
+ }
4997
+ function receiverOf2(node) {
4998
+ return node.type === "call" ? field(node, "receiver") : null;
4999
+ }
5000
+ function methodOf2(node) {
5001
+ return field(node, "method")?.text ?? "";
5002
+ }
4470
5003
  function rootConstant(node) {
4471
- const receiver = receiverOf(node);
5004
+ const receiver = receiverOf2(node);
4472
5005
  if (receiver === null) {
4473
5006
  return null;
4474
5007
  }
@@ -4483,17 +5016,6 @@ function isConstant(node) {
4483
5016
  function constantName(constant2) {
4484
5017
  return constant2.type === "constant" ? constant2.text : compoundName(constant2);
4485
5018
  }
4486
- function reachesBase(facts, classKey, bases) {
4487
- askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
4488
- return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
4489
- }
4490
- function classBehind(facts, file, constant2) {
4491
- const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
4492
- const bound = new Set(
4493
- facts.lookup("binds", 0, key).map((row) => String(row[1]))
4494
- );
4495
- return bound.size === 1 ? [...bound][0] : void 0;
4496
- }
4497
5019
  function modelPattern(constant2, file, options) {
4498
5020
  const classKey = classBehind(options.facts, file, constant2);
4499
5021
  if (classKey === void 0) {
@@ -4508,7 +5030,7 @@ function chainLinks(call) {
4508
5030
  let current = call;
4509
5031
  while (current !== null && current.type === "call") {
4510
5032
  links.push(current);
4511
- current = receiverOf(current);
5033
+ current = receiverOf2(current);
4512
5034
  }
4513
5035
  return links;
4514
5036
  }
@@ -4521,9 +5043,17 @@ function kindOfCall(pattern, method) {
4521
5043
  }
4522
5044
  return void 0;
4523
5045
  }
5046
+ function libraryCallOf(pattern, method) {
5047
+ const place = pattern.statements?.[method];
5048
+ if (place !== void 0) {
5049
+ return { how: "statement", place };
5050
+ }
5051
+ const kind = kindOfCall(pattern, method);
5052
+ return kind === void 0 ? void 0 : { how: "rows", kind };
5053
+ }
4524
5054
  function someLibraryDefines(options, method) {
4525
5055
  return options.patterns.some(
4526
- (pattern) => kindOfCall(pattern, method) !== void 0
5056
+ (pattern) => libraryCallOf(pattern, method) !== void 0
4527
5057
  );
4528
5058
  }
4529
5059
  function argumentsOf2(call) {
@@ -4558,7 +5088,7 @@ function hasPositionalArgument(call, facts) {
4558
5088
  );
4559
5089
  }
4560
5090
  function columnsAskedFor(call, pattern, facts) {
4561
- if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
5091
+ if (!(pattern.columnArguments ?? []).includes(methodOf2(call))) {
4562
5092
  return [];
4563
5093
  }
4564
5094
  return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
@@ -4568,14 +5098,14 @@ function primaryKeySelector(call, pattern, facts) {
4568
5098
  if (byPrimaryKey === void 0) {
4569
5099
  return [];
4570
5100
  }
4571
- const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
5101
+ const picks = byPrimaryKey.methods.includes(methodOf2(call)) && hasPositionalArgument(call, facts);
4572
5102
  return picks ? [byPrimaryKey.column] : [];
4573
5103
  }
4574
5104
  function selectorOf(call, pattern, facts) {
4575
5105
  const picked = [];
4576
5106
  for (const link of chainLinks(call)) {
4577
5107
  picked.push(...primaryKeySelector(link, pattern, facts));
4578
- if (pattern.reads.includes(methodOf(link))) {
5108
+ if (pattern.reads.includes(methodOf2(link))) {
4579
5109
  picked.push(...keywordKeys(link, facts));
4580
5110
  }
4581
5111
  }
@@ -4592,10 +5122,10 @@ function fieldsOf(call, pattern, kind, facts) {
4592
5122
  ];
4593
5123
  }
4594
5124
  function storageEffect(call, container, pattern, kind, selector, fields) {
4595
- const operation = methodOf(call);
5125
+ const operation = methodOf2(call);
4596
5126
  return {
4597
5127
  type: "interaction",
4598
- binding: storageBinding({
5128
+ binding: storageBinding2({
4599
5129
  recognition: "ruby-storage",
4600
5130
  storageSystem: pattern.storageSystem,
4601
5131
  scope: "default",
@@ -4612,7 +5142,7 @@ function storageEffect(call, container, pattern, kind, selector, fields) {
4612
5142
  };
4613
5143
  }
4614
5144
  function classSettledOn(facts, key) {
4615
- askResolution2(facts, [key], "wanted", RUBY_PROGRAM);
5145
+ askResolution3(facts, [key], "wanted", RUBY_PROGRAM);
4616
5146
  const objects = new Set(
4617
5147
  facts.lookup("wantedObjectOf", 0, key).map((row) => String(row[1]))
4618
5148
  );
@@ -4644,7 +5174,7 @@ function receiverClass(call, file, options, enclosing) {
4644
5174
  if (constant2 !== null) {
4645
5175
  return constantReceiver(constant2, file, options.facts);
4646
5176
  }
4647
- const receiver = receiverOf(call);
5177
+ const receiver = receiverOf2(call);
4648
5178
  if (receiver === null) {
4649
5179
  return void 0;
4650
5180
  }
@@ -4652,60 +5182,91 @@ function receiverClass(call, file, options, enclosing) {
4652
5182
  }
4653
5183
  function libraryCallIn(call, options) {
4654
5184
  return chainLinks(call).find(
4655
- (link) => someLibraryDefines(options, methodOf(link))
5185
+ (link) => someLibraryDefines(options, methodOf2(link))
4656
5186
  ) ?? null;
4657
5187
  }
5188
+ function modelStatementEffects(call, place, pattern, options) {
5189
+ const args = readCallArgs(field(call, "arguments"));
5190
+ return statementEffects(
5191
+ call,
5192
+ statementAt(args, place, options.facts, pattern.bindPlaceholder),
5193
+ NOWHERE,
5194
+ { storageSystem: pattern.storageSystem, dialect: pattern.storageSystem }
5195
+ );
5196
+ }
5197
+ function callbacksRunBy(facts, classKey, pattern, writeMethod) {
5198
+ const events = pattern.callbacks?.eventOf[writeMethod] ?? [];
5199
+ if (events.length === 0) {
5200
+ return [];
5201
+ }
5202
+ askResolution3(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
5203
+ const found = /* @__PURE__ */ new Map();
5204
+ for (const row of facts.lookup("wantedCallbackMethod", 0, classKey)) {
5205
+ if (events.includes(String(row[1]))) {
5206
+ const key = String(row[3]);
5207
+ found.set(key, { name: String(row[2]), key });
5208
+ }
5209
+ }
5210
+ return [...found.values()];
5211
+ }
5212
+ function callbackEffect(callback) {
5213
+ return {
5214
+ type: "invocation",
5215
+ callee: callback.name,
5216
+ args: [],
5217
+ async: false
5218
+ };
5219
+ }
4658
5220
  function modelCallEffects(call, file, options, enclosing) {
4659
5221
  const worked = libraryCallIn(call, options);
4660
5222
  if (worked === null) {
4661
5223
  return [];
4662
5224
  }
4663
- const method = methodOf(worked);
5225
+ const method = methodOf2(worked);
4664
5226
  const target = receiverClass(worked, file, options, enclosing);
4665
5227
  if (target === void 0) {
4666
5228
  return [];
4667
5229
  }
4668
5230
  for (const pattern of options.patterns) {
4669
- const kind = kindOfCall(pattern, method);
4670
- if (kind === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
5231
+ const library = libraryCallOf(pattern, method);
5232
+ if (library === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
4671
5233
  continue;
4672
5234
  }
4673
5235
  if (projectDeclares(options.facts, target.classKey, method)) {
4674
5236
  return [];
4675
5237
  }
5238
+ if (library.how === "statement") {
5239
+ return modelStatementEffects(worked, library.place, pattern, options);
5240
+ }
5241
+ const ran = library.kind === "write" ? callbacksRunBy(options.facts, target.classKey, pattern, method) : [];
4676
5242
  return [
4677
5243
  storageEffect(
4678
5244
  worked,
4679
5245
  target.container,
4680
5246
  pattern,
4681
- kind,
5247
+ library.kind,
4682
5248
  selectorOf(worked, pattern, options.facts),
4683
- fieldsOf(worked, pattern, kind, options.facts)
4684
- )
5249
+ fieldsOf(worked, pattern, library.kind, options.facts)
5250
+ ),
5251
+ ...ran.map(callbackEffect)
4685
5252
  ];
4686
5253
  }
4687
5254
  return [];
4688
5255
  }
4689
- function isLoader(node, loader) {
4690
- if (node === null) {
4691
- return false;
4692
- }
4693
- if (node.type === "identifier") {
4694
- return node.text === loader.loader;
4695
- }
4696
- return node.type === "call" && receiverOf(node) === null && methodOf(node) === loader.loader;
4697
- }
4698
- function loaderPick(call, loader) {
4699
- const method = methodOf(call);
4700
- const receiver = receiverOf(call);
4701
- if (receiver === null) {
4702
- return loader.shortcuts.includes(method) ? call : null;
5256
+ function callbacksReached(call, file, options, enclosing = null) {
5257
+ const worked = libraryCallIn(call, options);
5258
+ const target = worked === null ? void 0 : receiverClass(worked, file, options, enclosing);
5259
+ if (worked === null || target === void 0) {
5260
+ return [];
4703
5261
  }
4704
- if (!loader.reads.includes(method) || receiver.type !== "call") {
4705
- return null;
5262
+ const method = methodOf2(worked);
5263
+ for (const pattern of options.patterns) {
5264
+ if (libraryCallOf(pattern, method)?.how !== "rows" || kindOfCall(pattern, method) !== "write" || !reachesBase(options.facts, target.classKey, pattern.baseClasses) || projectDeclares(options.facts, target.classKey, method)) {
5265
+ continue;
5266
+ }
5267
+ return callbacksRunBy(options.facts, target.classKey, pattern, method);
4706
5268
  }
4707
- const picks = methodOf(receiver) === loader.pick && isLoader(receiverOf(receiver), loader);
4708
- return picks ? receiver : null;
5269
+ return [];
4709
5270
  }
4710
5271
  function loaderCallEffects(call, file, options) {
4711
5272
  const effects = [];
@@ -4731,10 +5292,24 @@ function loaderCallEffects(call, file, options) {
4731
5292
  }
4732
5293
  function effectsOfCall(call, file, options, enclosing) {
4733
5294
  const onModel = modelCallEffects(call, file, options, enclosing);
4734
- return onModel.length > 0 ? onModel : loaderCallEffects(call, file, options);
5295
+ if (onModel.length > 0) {
5296
+ return onModel;
5297
+ }
5298
+ const throughLoader = loaderCallEffects(call, file, options);
5299
+ if (throughLoader.length > 0) {
5300
+ return throughLoader;
5301
+ }
5302
+ return rawSqlEffects(call, {
5303
+ facts: options.facts,
5304
+ patterns: options.rawSql ?? [],
5305
+ file
5306
+ });
4735
5307
  }
4736
5308
  function storageClaims(call, file, options, enclosing = null) {
4737
- return options.patterns.length > 0 && effectsOfCall(call, file, options, enclosing).length > 0;
5309
+ return saysAnything(options) && effectsOfCall(call, file, options, enclosing).length > 0;
5310
+ }
5311
+ function saysAnything(options) {
5312
+ return options.patterns.length > 0 || (options.rawSql ?? []).length > 0;
4738
5313
  }
4739
5314
  function receiverKeysToAsk(chains, file, options, enclosing) {
4740
5315
  const keys = [];
@@ -4743,7 +5318,7 @@ function receiverKeysToAsk(chains, file, options, enclosing) {
4743
5318
  if (worked === null || rootConstant(worked) !== null) {
4744
5319
  continue;
4745
5320
  }
4746
- const receiver = receiverOf(worked);
5321
+ const receiver = receiverOf2(worked);
4747
5322
  if (receiver !== null) {
4748
5323
  keys.push(readKey(file, receiver, enclosing));
4749
5324
  }
@@ -4751,18 +5326,18 @@ function receiverKeysToAsk(chains, file, options, enclosing) {
4751
5326
  return keys;
4752
5327
  }
4753
5328
  function storageEffects(calls, file, options, enclosing = null) {
4754
- if (options.patterns.length === 0) {
5329
+ if (!saysAnything(options)) {
4755
5330
  return [];
4756
5331
  }
4757
5332
  const partOfOne = /* @__PURE__ */ new Set();
4758
5333
  for (const call of calls) {
4759
- const receiver = receiverOf(call);
5334
+ const receiver = receiverOf2(call);
4760
5335
  if (receiver !== null) {
4761
5336
  partOfOne.add(receiver.id);
4762
5337
  }
4763
5338
  }
4764
5339
  const chains = calls.filter((call) => !partOfOne.has(call.id));
4765
- askResolution2(
5340
+ askResolution3(
4766
5341
  options.facts,
4767
5342
  receiverKeysToAsk(chains, file, options, enclosing),
4768
5343
  "wanted",
@@ -4887,14 +5462,18 @@ async function discoverUnits(root, options) {
4887
5462
  file: options.absoluteFile ?? options.filePath
4888
5463
  }));
4889
5464
  const units = [];
5465
+ const clientOptions = {
5466
+ filePath: options.filePath,
5467
+ ...options.facts === void 0 ? {} : { facts: options.facts }
5468
+ };
5469
+ askClientCallReads(
5470
+ root,
5471
+ options.packs.flatMap((pack) => pack.clients ?? []),
5472
+ clientOptions
5473
+ );
4890
5474
  for (const pack of options.packs) {
4891
5475
  for (const pattern of pack.clients ?? []) {
4892
- units.push(
4893
- ...clientCallUnits(root, pack, pattern, {
4894
- filePath: options.filePath,
4895
- ...options.facts === void 0 ? {} : { facts: options.facts }
4896
- })
4897
- );
5476
+ units.push(...clientCallUnits(root, pack, pattern, clientOptions));
4898
5477
  }
4899
5478
  }
4900
5479
  for (const info of classes) {
@@ -6008,6 +6587,47 @@ function buildRubyExtractionReport(args) {
6008
6587
  });
6009
6588
  }
6010
6589
 
6590
+ // src/facts/callbacks.ts
6591
+ function callbacksIn(patterns) {
6592
+ return patterns.flatMap(
6593
+ (pattern) => pattern.callbacks === void 0 ? [] : [pattern.callbacks]
6594
+ );
6595
+ }
6596
+ function emitClassCallbacks(db, file, classNode, declared) {
6597
+ const body = field(classNode, "body");
6598
+ if (body === null || declared.length === 0) {
6599
+ return;
6600
+ }
6601
+ const classKey = nodeId(file, classNode);
6602
+ for (const statement of runStatements(body)) {
6603
+ if (statement.type !== "call" || field(statement, "receiver") !== null) {
6604
+ continue;
6605
+ }
6606
+ const called = field(statement, "method")?.text ?? "";
6607
+ const args = readCallArgs(field(statement, "arguments"));
6608
+ for (const callbacks of declared) {
6609
+ const events = eventsOf(args, called, callbacks, db);
6610
+ for (const event of events) {
6611
+ for (const method of symbolArgumentNames(args, db)) {
6612
+ db.add("classCallback", [classKey, event, method]);
6613
+ }
6614
+ }
6615
+ }
6616
+ }
6617
+ }
6618
+ function eventsOf(args, called, callbacks, db) {
6619
+ const covered = callbacks.registeredBy[called];
6620
+ if (covered === void 0) {
6621
+ return [];
6622
+ }
6623
+ const written = args.keyword[callbacks.eventKeyword];
6624
+ const narrowed = written === void 0 ? null : stringValueOf(written, db);
6625
+ if (narrowed === null) {
6626
+ return covered;
6627
+ }
6628
+ return covered.filter((event) => event === narrowed);
6629
+ }
6630
+
6011
6631
  // src/facts.ts
6012
6632
  import path3 from "path";
6013
6633
  import { SKIP_CHILDREN as SKIP_CHILDREN3, walkDescendants as walkDescendants3 } from "@suss/extractor";
@@ -6124,6 +6744,10 @@ function calleeSpellings(calls, ctx) {
6124
6744
  };
6125
6745
  }
6126
6746
  function resolveCallee(call, site, ctx, read) {
6747
+ const throughLoader = loaderSourceCallee(call, site, ctx);
6748
+ if (throughLoader !== null) {
6749
+ return throughLoader;
6750
+ }
6127
6751
  const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site);
6128
6752
  if (spelling.kind === "stopped") {
6129
6753
  return spelling;
@@ -6133,6 +6757,21 @@ function resolveCallee(call, site, ctx, read) {
6133
6757
  }
6134
6758
  return asCallee(spelling, outcomeFor(spelling.key, ctx, read), site, ctx);
6135
6759
  }
6760
+ function loaderSourceCallee(call, site, ctx) {
6761
+ for (const loader of ctx.loaders) {
6762
+ const constant2 = pickedSource(call, loader);
6763
+ const source = loader.source;
6764
+ if (constant2 === null || source === void 0) {
6765
+ continue;
6766
+ }
6767
+ const classKey = classBehind(ctx.facts, site.file, constant2);
6768
+ const qualifiedName = classKey === void 0 ? void 0 : ctx.classNames.get(classKey);
6769
+ if (qualifiedName !== void 0) {
6770
+ return methodOnAncestryOf(qualifiedName, source.method, ctx);
6771
+ }
6772
+ }
6773
+ return null;
6774
+ }
6136
6775
  function outcomeFor(key, ctx, read) {
6137
6776
  return read?.outcomes.get(key) ?? calleeOutcomeOf(ctx.facts, key, RUBY_PROGRAM);
6138
6777
  }
@@ -6314,6 +6953,7 @@ async function reachedFunctions(seeds, options) {
6314
6953
  const parameterCallsByKey = /* @__PURE__ */ new Map();
6315
6954
  const propertyReadsByKey = /* @__PURE__ */ new Map();
6316
6955
  const passedPositions = /* @__PURE__ */ new Set();
6956
+ const followedKeys = /* @__PURE__ */ new Set();
6317
6957
  for (const seed of seeds) {
6318
6958
  seedKeys.add(seed.key);
6319
6959
  functionByKey.set(seed.key, {
@@ -6361,6 +7001,9 @@ async function reachedFunctions(seeds, options) {
6361
7001
  for (const position of scan.passedPositions) {
6362
7002
  passedPositions.add(position);
6363
7003
  }
7004
+ if (scan.followed.length > 0) {
7005
+ followedKeys.add(key);
7006
+ }
6364
7007
  for (const target of scan.followed) {
6365
7008
  const calleeKey = keyOf(target);
6366
7009
  if (!functionByKey.has(calleeKey)) {
@@ -6397,7 +7040,8 @@ async function reachedFunctions(seeds, options) {
6397
7040
  argTargetsByKey,
6398
7041
  parameterCallsByKey,
6399
7042
  propertyReadsByKey,
6400
- passedPositions
7043
+ passedPositions,
7044
+ followedKeys
6401
7045
  };
6402
7046
  }
6403
7047
  function keyOf(target) {
@@ -6436,13 +7080,9 @@ function methodReferenceSymbol(node) {
6436
7080
  return argChildren.length === 1 && argChildren[0]?.type === "simple_symbol" ? argChildren[0] : null;
6437
7081
  }
6438
7082
  function bodyOf2(source, options) {
6439
- const site = {
6440
- file: source.file,
6441
- method: source.node,
6442
- owner: keyOf(source),
6443
- enclosingQualifiedName: source.enclosingQualifiedName
6444
- };
6445
- const written = field(source.node, "body") === null ? [] : bodyCalls(source.node, options.inheritedMethods);
7083
+ const site = siteOf2(source);
7084
+ const read = readableBodyOf(source.node);
7085
+ const written = read === null ? [] : bodyCalls(read, options.inheritedMethods);
6446
7086
  const calls = callsReported(
6447
7087
  written,
6448
7088
  (call) => mightReadAsACall(call, site, options.context)
@@ -6454,6 +7094,17 @@ function bodyOf2(source, options) {
6454
7094
  written: calls.map((call) => ({ call, site }))
6455
7095
  };
6456
7096
  }
7097
+ function readableBodyOf(node) {
7098
+ return node.type === PROGRAM_TYPE ? moduleScopeBody(node) : methodBody(node);
7099
+ }
7100
+ function siteOf2(source) {
7101
+ return {
7102
+ file: source.file,
7103
+ method: source.node.type === PROGRAM_TYPE ? null : source.node,
7104
+ owner: keyOf(source),
7105
+ enclosingQualifiedName: source.enclosingQualifiedName
7106
+ };
7107
+ }
6457
7108
  function callsMade(asked, site, ctx, spellings) {
6458
7109
  return asked.filter(
6459
7110
  (call) => !isArglessReceiverCall(call) || readsAsACall(call, site, ctx, spellings)
@@ -6461,7 +7112,7 @@ function callsMade(asked, site, ctx, spellings) {
6461
7112
  }
6462
7113
  function scanBody(source, ctx, options, read) {
6463
7114
  const displayPathOf = options.displayPathOf;
6464
- if (field(source.node, "body") === null) {
7115
+ if (readableBodyOf(source.node) === null) {
6465
7116
  return EMPTY_SCAN;
6466
7117
  }
6467
7118
  const site = read.site;
@@ -6504,9 +7155,34 @@ function scanBody(source, ctx, options, read) {
6504
7155
  }
6505
7156
  }
6506
7157
  };
7158
+ const followCallback = (name, key) => {
7159
+ const target = ctx.definitions.get(key);
7160
+ if (target === void 0) {
7161
+ return;
7162
+ }
7163
+ const targetKey = keyOf(target);
7164
+ if (!seen.has(targetKey)) {
7165
+ seen.add(targetKey);
7166
+ followed2.push(target);
7167
+ }
7168
+ placements.place(name, {
7169
+ file: displayPathOf(target.file),
7170
+ span: spanOf(target.node)
7171
+ });
7172
+ };
6507
7173
  for (const call of calls) {
6508
7174
  const callee = calleeText(call);
6509
7175
  const outcome = resolveCallee(call, site, ctx, read.spellings);
7176
+ if (options.storage !== void 0) {
7177
+ for (const callback of callbacksReached(
7178
+ call,
7179
+ source.file,
7180
+ options.storage,
7181
+ site.method
7182
+ )) {
7183
+ followCallback(callback.name, callback.key);
7184
+ }
7185
+ }
6510
7186
  const placed = outcome.kind === "followed" ? {
6511
7187
  file: displayPathOf(outcome.target.file),
6512
7188
  span: spanOf(outcome.target.node)
@@ -6639,7 +7315,7 @@ function parameterName(param) {
6639
7315
 
6640
7316
  // src/reach/context.ts
6641
7317
  var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
6642
- async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
7318
+ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map(), loaders = []) {
6643
7319
  const blocksByQualifiedName = /* @__PURE__ */ new Map();
6644
7320
  const classes = [];
6645
7321
  const classNames = /* @__PURE__ */ new Map();
@@ -6715,7 +7391,8 @@ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dyna
6715
7391
  classNames,
6716
7392
  definitions,
6717
7393
  bodyBlocks,
6718
- dynamicNames
7394
+ dynamicNames,
7395
+ loaders
6719
7396
  };
6720
7397
  }
6721
7398
  function namedChildren(node) {
@@ -6783,7 +7460,7 @@ function factsForFile(options) {
6783
7460
  const db = new Database2();
6784
7461
  const bodyBlocks = bodyBlocksIn(options.packs);
6785
7462
  emitValueFacts(db, options.file, options.root, bodyBlocks);
6786
- emitEnvNameFacts(db, options.file, options.root);
7463
+ emitEnvFacts(db, options.file, options.root);
6787
7464
  emitConstantBindings(db, [
6788
7465
  collectFileConstants(
6789
7466
  options.file,
@@ -6800,6 +7477,19 @@ function factsForFile(options) {
6800
7477
  addPackWords(db, packWordsOf(options.packs));
6801
7478
  return db;
6802
7479
  }
7480
+ function moduleInitUnit(options) {
7481
+ const raw = moduleInitStructure({
7482
+ name: options.name,
7483
+ file: options.file,
7484
+ range: options.range,
7485
+ effects: options.effects
7486
+ });
7487
+ const branch = raw.branches[0];
7488
+ if (branch !== void 0) {
7489
+ branch.effects = options.calls;
7490
+ }
7491
+ return raw;
7492
+ }
6803
7493
  function alreadyDiscovered(seen, raw) {
6804
7494
  const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
6805
7495
  const boundary = raw.boundaryBinding === null ? "" : boundaryKey(raw.boundaryBinding) ?? "";
@@ -6855,7 +7545,7 @@ async function extractRubyProject(options) {
6855
7545
  }
6856
7546
  parsed.push({ file, root });
6857
7547
  emitValueFacts(db, file, root, bodyBlocks);
6858
- emitEnvNameFacts(db, file, root);
7548
+ emitEnvFacts(db, file, root);
6859
7549
  for (const [key, method] of methodDefinitionsIn(file, root)) {
6860
7550
  definitions.set(key, method);
6861
7551
  }
@@ -6872,6 +7562,15 @@ async function extractRubyProject(options) {
6872
7562
  }
6873
7563
  bindEvaluator(db, { files: parsed, definitions });
6874
7564
  addPackWords(db, packWordsOf(options.packs));
7565
+ const callbacks = callbacksIn(
7566
+ options.packs.flatMap((pack) => pack.storage ?? [])
7567
+ );
7568
+ for (const { file, root } of parsed) {
7569
+ walkDefinitions(
7570
+ root,
7571
+ (info) => emitClassCallbacks(db, file, info.node, callbacks)
7572
+ );
7573
+ }
6875
7574
  return readDynamicNames(
6876
7575
  db,
6877
7576
  new Map(parsed.map(({ file, root }) => [file, root]))
@@ -6879,11 +7578,17 @@ async function extractRubyProject(options) {
6879
7578
  });
6880
7579
  const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
6881
7580
  const loaderPatterns = options.packs.flatMap((pack) => pack.loaders ?? []);
6882
- const storage = storagePatterns.length > 0 ? { facts: db, patterns: storagePatterns, loaders: loaderPatterns } : void 0;
7581
+ const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
7582
+ const storage = storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
7583
+ facts: db,
7584
+ patterns: storagePatterns,
7585
+ loaders: loaderPatterns,
7586
+ rawSql: rawSqlPatterns
7587
+ } : void 0;
6883
7588
  const inheritedMethods = inheritedMethodsIn(options.packs);
6884
7589
  const reachContext = await timer.timeAsync(
6885
7590
  "discover",
6886
- () => buildReachContext(parsed, db, bodyBlocks, dynamicNames)
7591
+ () => buildReachContext(parsed, db, bodyBlocks, dynamicNames, loaderPatterns)
6887
7592
  );
6888
7593
  const displayPathOf = (file) => options.workspaceRoot !== void 0 ? path4.relative(options.workspaceRoot, file) : file;
6889
7594
  const seeds = [];
@@ -6933,26 +7638,34 @@ async function extractRubyProject(options) {
6933
7638
  });
6934
7639
  }
6935
7640
  }
6936
- const loadTimeReads = timer.time(
6937
- "discover",
6938
- () => envReadEffects(root, { db, file })
6939
- );
6940
- if (loadTimeReads.length > 0) {
6941
- const summary = timer.time(
6942
- "summarize",
6943
- () => assembleSummary2(
6944
- moduleInitStructure({
6945
- name: path4.basename(displayPath),
6946
- file: displayPath,
6947
- range: rangeOf(root),
6948
- effects: loadTimeReads
6949
- }),
6950
- { gapHandling: "permissive" }
6951
- )
6952
- );
6953
- summary.confidence = { source: "inferred_static", level: "low" };
6954
- found.push({ summary });
6955
- }
7641
+ const moduleKey = nodeId(file, root);
7642
+ seedKeys.add(moduleKey);
7643
+ seeds.push({
7644
+ key: moduleKey,
7645
+ file,
7646
+ node: root,
7647
+ enclosingQualifiedName: null
7648
+ });
7649
+ const loadTime = timer.time("discover", () => ({
7650
+ reads: envReadEffects(root, { db, file }),
7651
+ calls: moduleScopeInvocationEffects(
7652
+ root,
7653
+ inheritedMethods,
7654
+ EVERY_ARGLESS_CALL,
7655
+ db
7656
+ )
7657
+ }));
7658
+ found.push({
7659
+ raw: moduleInitUnit({
7660
+ name: path4.basename(displayPath),
7661
+ file: displayPath,
7662
+ range: rangeOf(root),
7663
+ effects: loadTime.reads,
7664
+ calls: loadTime.calls
7665
+ }),
7666
+ seedKey: moduleKey,
7667
+ onlyIfItReaches: loadTime.reads.length === 0
7668
+ });
6956
7669
  }
6957
7670
  for (const pack of options.packs) {
6958
7671
  for (const pattern of pack.discovery) {
@@ -6990,6 +7703,9 @@ async function extractRubyProject(options) {
6990
7703
  continue;
6991
7704
  }
6992
7705
  const { raw, seedKey } = entry;
7706
+ if (entry.onlyIfItReaches === true && (seedKey === null || !reached.followedKeys.has(seedKey))) {
7707
+ continue;
7708
+ }
6993
7709
  if (seedKey !== null) {
6994
7710
  dropPropertyReads(raw, reached.propertyReadsByKey.get(seedKey));
6995
7711
  }
@@ -7137,7 +7853,7 @@ function width(node) {
7137
7853
  return node.endIndex - node.startIndex;
7138
7854
  }
7139
7855
  function askedKey(value) {
7140
- const enclosing = enclosingMethod(value.node);
7856
+ const enclosing = enclosingDefinition(value.node);
7141
7857
  return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
7142
7858
  }
7143
7859
  var RubyWhySession = class {
@@ -7352,6 +8068,8 @@ export {
7352
8068
  resolveConstantFile,
7353
8069
  rubyStubEvidence,
7354
8070
  runStatements,
8071
+ storageClaims,
8072
+ storageEffects,
7355
8073
  stringLiteralValue,
7356
8074
  stringValueOf,
7357
8075
  symbolValue,