@suss/adapter-ruby 0.20.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -72,12 +72,37 @@ function field(node, name) {
72
72
  function bodyStatements(body) {
73
73
  return body.namedChildren.filter((child) => child !== null);
74
74
  }
75
+ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
76
+ "method",
77
+ "singleton_method",
78
+ "lambda",
79
+ "class",
80
+ "module",
81
+ "singleton_class"
82
+ ]);
83
+ var ARGUMENT_LIST_TYPE = "argument_list";
84
+ function runStatements(body, blockConfigures = () => false) {
85
+ const found = [];
86
+ const readsItsChildren = (node) => {
87
+ if (OWN_BODY_TYPES.has(node.type) || node.type === ARGUMENT_LIST_TYPE) {
88
+ return false;
89
+ }
90
+ return !(node.type === "call" && blockConfigures(node));
91
+ };
92
+ (0, import_extractor.walkDescendants)(body, null, {
93
+ at: (node) => {
94
+ found.push(node);
95
+ },
96
+ into: (node) => readsItsChildren(node) ? null : import_extractor.SKIP_CHILDREN
97
+ });
98
+ return found;
99
+ }
75
100
  function symbolValue(node) {
76
101
  return node.type === "simple_symbol" ? node.text.slice(1) : null;
77
102
  }
78
103
  function instanceMethodsByName(body) {
79
104
  const methods = /* @__PURE__ */ new Map();
80
- for (const stmt of bodyStatements(body)) {
105
+ for (const stmt of runStatements(body)) {
81
106
  if (stmt.type !== "method") {
82
107
  continue;
83
108
  }
@@ -90,7 +115,7 @@ function instanceMethodsByName(body) {
90
115
  }
91
116
  function bareCallArgumentGroups(body, name) {
92
117
  const groups = [];
93
- for (const stmt of bodyStatements(body)) {
118
+ for (const stmt of runStatements(body)) {
94
119
  if (stmt.type !== "call" || field(stmt, "receiver") !== null) {
95
120
  continue;
96
121
  }
@@ -193,7 +218,7 @@ function walkClasses(root, visit) {
193
218
  });
194
219
  }
195
220
  function walkDefinitions(root, visit) {
196
- walkBody(bodyStatements(root), [], visit);
221
+ walkBody(runStatements(root), [], visit);
197
222
  }
198
223
  function walkBody(statements, nesting, visit) {
199
224
  for (const stmt of statements) {
@@ -232,7 +257,7 @@ function visitClass(node, nesting, visit) {
232
257
  bodyNesting: identity.bodyNesting
233
258
  });
234
259
  if (bodyNode !== null) {
235
- walkBody(bodyStatements(bodyNode), identity.bodyNesting, visit);
260
+ walkBody(runStatements(bodyNode), identity.bodyNesting, visit);
236
261
  }
237
262
  }
238
263
  function visitModule(node, nesting, visit) {
@@ -251,7 +276,7 @@ function visitModule(node, nesting, visit) {
251
276
  bodyNesting: identity.bodyNesting
252
277
  });
253
278
  if (bodyNode !== null) {
254
- walkBody(bodyStatements(bodyNode), identity.bodyNesting, visit);
279
+ walkBody(runStatements(bodyNode), identity.bodyNesting, visit);
255
280
  }
256
281
  }
257
282
  function stripTypeSuffixName(qualifiedName) {
@@ -407,7 +432,7 @@ function definitionIn(blocks, name) {
407
432
  }
408
433
  return { method, dynamic };
409
434
  }
410
- function inheritedStatements(ancestry) {
435
+ function inheritedStatements(ancestry, blockConfigures) {
411
436
  const statements = [];
412
437
  for (const entry of [...ancestry].reverse()) {
413
438
  if (entry.type !== "bodies") {
@@ -418,7 +443,7 @@ function inheritedStatements(ancestry) {
418
443
  if (body === null) {
419
444
  continue;
420
445
  }
421
- for (const statement of bodyStatements(body)) {
446
+ for (const statement of runStatements(body, blockConfigures)) {
422
447
  statements.push({ block, statement });
423
448
  }
424
449
  }
@@ -644,14 +669,6 @@ function lowerRubyBody(body, terminals) {
644
669
  }
645
670
 
646
671
  // src/paths/effects.ts
647
- var DECLARATION_TYPES = /* @__PURE__ */ new Set([
648
- "method",
649
- "singleton_method",
650
- "lambda",
651
- "class",
652
- "module",
653
- "singleton_class"
654
- ]);
655
672
  var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
656
673
  var BLOCK_TYPES = /* @__PURE__ */ new Set([
657
674
  "body_statement",
@@ -699,7 +716,7 @@ function isRaise2(node) {
699
716
  }
700
717
  function bodyCalls(node, found = []) {
701
718
  for (const child of children(node)) {
702
- if (DECLARATION_TYPES.has(child.type)) {
719
+ if (OWN_BODY_TYPES.has(child.type)) {
703
720
  continue;
704
721
  }
705
722
  if (child.type === "call" && !isPropertyRead(child) && !isRaise2(child)) {
@@ -15302,7 +15319,7 @@ function date4(params) {
15302
15319
  // ../../../node_modules/zod/v4/classic/external.js
15303
15320
  config(en_default());
15304
15321
 
15305
- // ../../ir-core/dist/chunk-52CH76PE.js
15322
+ // ../../ir-core/dist/chunk-PPQWK62O.js
15306
15323
  function gqlIdentityKey(typeName, fieldName) {
15307
15324
  return `gql:${typeName}.${fieldName}`;
15308
15325
  }
@@ -15312,6 +15329,9 @@ function fnIdentityKey(packageName, exportPath) {
15312
15329
  function busIdentityKey(messageBus, subject) {
15313
15330
  return `bus:${messageBus} ${subject}`;
15314
15331
  }
15332
+ function unitIdentityKey(deploymentTarget, instanceName) {
15333
+ return `unit:${deploymentTarget} ${instanceName}`;
15334
+ }
15315
15335
  function metricIdentityKey(metricSystem, metricType) {
15316
15336
  return `metric:${metricSystem} ${metricType}`;
15317
15337
  }
@@ -15585,6 +15605,42 @@ var metricSemantics = defineBoundarySemantics({
15585
15605
  }
15586
15606
  }
15587
15607
  });
15608
+ var HOLE_SPLIT = /(\{[^}]*\})/;
15609
+ function isHole(piece) {
15610
+ return piece.startsWith("{") && piece.endsWith("}");
15611
+ }
15612
+ function parseBoundaryName(name) {
15613
+ const parts = name.split(HOLE_SPLIT).filter((piece) => piece !== "").map(
15614
+ (piece) => isHole(piece) ? { type: "hole", label: piece.slice(1, -1) } : { type: "text", text: piece }
15615
+ );
15616
+ const only = parts.length === 1 ? parts[0] : void 0;
15617
+ if (only !== void 0 && only.type === "hole") {
15618
+ return { type: "reference", path: only.label.split(".") };
15619
+ }
15620
+ if (parts.some((part) => part.type === "hole")) {
15621
+ return { type: "pattern", parts };
15622
+ }
15623
+ return { type: "literal", value: name };
15624
+ }
15625
+ function patternHole(label) {
15626
+ return `{${label}}`;
15627
+ }
15628
+ function namesNothing(name) {
15629
+ return parseBoundaryName(name).type === "reference";
15630
+ }
15631
+ function referenceOf(name) {
15632
+ if (name.type !== "reference") {
15633
+ return null;
15634
+ }
15635
+ const root = name.path[0];
15636
+ if (root === void 0 || name.path.some((part) => part === "")) {
15637
+ return null;
15638
+ }
15639
+ return { root, fields: name.path.slice(1) };
15640
+ }
15641
+ function referenceFromName(name) {
15642
+ return referenceOf(parseBoundaryName(name));
15643
+ }
15588
15644
  var SCHEME_ORIGIN = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/]*/;
15589
15645
  var PROTOCOL_RELATIVE_ORIGIN = /^\/\/[^/]*/;
15590
15646
  function pathAfterOrigin(text) {
@@ -15611,9 +15667,9 @@ var RestSemanticsSchema = external_exports.object({
15611
15667
  declaredResponses: external_exports.array(external_exports.number()).optional()
15612
15668
  });
15613
15669
  var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
15614
- function variableOf(label) {
15615
- const parts = label.split(".");
15616
- return parts[parts.length - 1] ?? label;
15670
+ function baseUrlReference(semantics) {
15671
+ const label = OPENING_HOLE.exec(semantics.path ?? "")?.[1];
15672
+ return label === void 0 ? null : referenceFromName(patternHole(label));
15617
15673
  }
15618
15674
  function normalizePath(path4) {
15619
15675
  let normalized = path4.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
@@ -15724,6 +15780,14 @@ var restSemantics = defineBoundarySemantics({
15724
15780
  }
15725
15781
  return routePathAdmits(semantics.path, path4) ? "match" : "nomatch";
15726
15782
  },
15783
+ /**
15784
+ * A route the registration pattern admits is inside it. A route
15785
+ * whose path this source never gave is left outside, because
15786
+ * nothing here can show it is one the pattern covers.
15787
+ */
15788
+ withinScope(semantics, scope) {
15789
+ return semantics.path !== null && routePathAdmits(scope, semantics.path);
15790
+ },
15727
15791
  /**
15728
15792
  * A call whose base URL the deployment fills in, resolved to the
15729
15793
  * path it reaches.
@@ -15739,22 +15803,20 @@ var restSemantics = defineBoundarySemantics({
15739
15803
  * route parameter, and `/orders/{id}` means every id rather than a
15740
15804
  * variable somebody set.
15741
15805
  */
15742
- groundName(semantics, deployedAs) {
15743
- if (semantics.path === null) {
15744
- return null;
15745
- }
15746
- const opening = OPENING_HOLE.exec(semantics.path);
15747
- if (opening === null) {
15806
+ groundName(semantics, deployment) {
15807
+ const reference = baseUrlReference(semantics);
15808
+ if (reference === null) {
15748
15809
  return null;
15749
15810
  }
15750
- const [, label, rest] = opening;
15751
- const base = deployedAs(variableOf(label));
15811
+ const base = deployment.setTo(reference);
15752
15812
  if (base === null) {
15753
15813
  return null;
15754
15814
  }
15815
+ const rest = OPENING_HOLE.exec(semantics.path ?? "")?.[2] ?? "";
15755
15816
  const grounded = pathAfterOrigin(`${base}${rest}`);
15756
15817
  return { ...semantics, path: grounded === "" ? "/" : grounded };
15757
15818
  },
15819
+ nameReference: baseUrlReference,
15758
15820
  ruleBoundary: {
15759
15821
  // "METHOD /path": one leading token, then an absolute path.
15760
15822
  claims(raw) {
@@ -15830,6 +15892,9 @@ var StorageSemanticsSchema = external_exports.object({
15830
15892
  */
15831
15893
  accessPath: external_exports.string().nullable()
15832
15894
  });
15895
+ function containerReference(semantics) {
15896
+ return semantics.container === null ? null : referenceFromName(semantics.container);
15897
+ }
15833
15898
  var storageSemantics = defineBoundarySemantics({
15834
15899
  name: "storage",
15835
15900
  schema: StorageSemanticsSchema,
@@ -15845,7 +15910,100 @@ var storageSemantics = defineBoundarySemantics({
15845
15910
  /** A query returns rows or items, not a status and a body. */
15846
15911
  exchangesHttpResponses: false,
15847
15912
  reportsUnpairedItself: false,
15848
- identityKey: () => null
15913
+ identityKey: () => null,
15914
+ displayLabel: storageLabel,
15915
+ /**
15916
+ * A container the source states as a variable is whatever the
15917
+ * deployment sets that variable to. `{SUBSCRIBERS_TABLE}` in the
15918
+ * code and `prod-subscribers-v1` in the manifest are one table.
15919
+ */
15920
+ groundName(semantics, deployment) {
15921
+ const reference = containerReference(semantics);
15922
+ if (reference === null) {
15923
+ return null;
15924
+ }
15925
+ const name = deployment.setTo(reference);
15926
+ return name === null ? null : { ...semantics, container: name };
15927
+ },
15928
+ nameReference: containerReference
15929
+ }
15930
+ });
15931
+ function storageLabel(semantics) {
15932
+ return `${semantics.storageSystem}:${storageContainerLabel(semantics)}`;
15933
+ }
15934
+ function storageContainerLabel(semantics) {
15935
+ const container = semantics.container ?? "<unnamed container>";
15936
+ const addressed = semantics.accessPath === null ? container : `${container}#${semantics.accessPath}`;
15937
+ return semantics.scope === "default" ? addressed : `${semantics.scope}/${addressed}`;
15938
+ }
15939
+ var UnitInvocationSemanticsSchema = DeployableUnitSchema.extend({
15940
+ name: external_exports.literal("unit-invocation"),
15941
+ instanceName: DeployableUnitSchema.shape.instanceName.nullable()
15942
+ });
15943
+ function calleeReference(semantics) {
15944
+ return semantics.instanceName === null ? null : referenceFromName(semantics.instanceName);
15945
+ }
15946
+ var unitInvocationSemantics = defineBoundarySemantics({
15947
+ name: "unit-invocation",
15948
+ schema: UnitInvocationSemanticsSchema,
15949
+ // faas.name is the deployed function's own name and instanceName is
15950
+ // the deployment template's logical id, which is a different string.
15951
+ // Runtime config leaves the pair off for the same reason.
15952
+ semconv: {},
15953
+ behavior: {
15954
+ /**
15955
+ * A synchronous invoke gives back whatever the handler returned,
15956
+ * which is a value rather than a status and a body. A handler's
15957
+ * return only becomes an HTTP response under a proxy integration,
15958
+ * and that route gets a REST binding instead of this one.
15959
+ */
15960
+ exchangesHttpResponses: false,
15961
+ /**
15962
+ * A unit nothing in the run invokes is the ordinary case, since
15963
+ * most services are reached from a stack suss never read. So an
15964
+ * uninvoked unit stays in the generic unmatched list rather than
15965
+ * being reported as a problem here.
15966
+ */
15967
+ reportsUnpairedItself: false,
15968
+ /**
15969
+ * A callee the source states only as a variable is not a name for
15970
+ * anything until the deployment fills it in, so it keys as nothing
15971
+ * rather than as a bucket that would agree with itself alone.
15972
+ */
15973
+ identityKey(semantics) {
15974
+ if (semantics.instanceName === null || namesNothing(semantics.instanceName)) {
15975
+ return null;
15976
+ }
15977
+ return unitIdentityKey(
15978
+ semantics.deploymentTarget,
15979
+ semantics.instanceName
15980
+ );
15981
+ },
15982
+ displayLabel(semantics) {
15983
+ if (semantics.instanceName === null) {
15984
+ return `unit:${semantics.deploymentTarget} (named at runtime)`;
15985
+ }
15986
+ return unitIdentityKey(
15987
+ semantics.deploymentTarget,
15988
+ semantics.instanceName
15989
+ );
15990
+ },
15991
+ /**
15992
+ * A call that reads its callee's name out of the environment
15993
+ * reaches whichever resource the template wires that variable to.
15994
+ * The logical id is the answer rather than the deployed name,
15995
+ * because the invoked unit's own summary is keyed by the logical
15996
+ * id and neither side ever writes the other's string.
15997
+ */
15998
+ groundName(semantics, deployment) {
15999
+ const reference = calleeReference(semantics);
16000
+ if (reference === null) {
16001
+ return null;
16002
+ }
16003
+ const logicalId = deployment.pointsAt(reference);
16004
+ return logicalId === null ? null : { ...semantics, instanceName: logicalId };
16005
+ },
16006
+ nameReference: calleeReference
15849
16007
  }
15850
16008
  });
15851
16009
  var SemanticsSchema = external_exports.discriminatedUnion("name", [
@@ -15856,7 +16014,8 @@ var SemanticsSchema = external_exports.discriminatedUnion("name", [
15856
16014
  runtimeConfigSemantics.schema,
15857
16015
  storageSemantics.schema,
15858
16016
  messageBusSemantics.schema,
15859
- metricSemantics.schema
16017
+ metricSemantics.schema,
16018
+ unitInvocationSemantics.schema
15860
16019
  ]);
15861
16020
  var DEFINITIONS = [
15862
16021
  restSemantics,
@@ -15866,7 +16025,8 @@ var DEFINITIONS = [
15866
16025
  runtimeConfigSemantics,
15867
16026
  storageSemantics,
15868
16027
  messageBusSemantics,
15869
- metricSemantics
16028
+ metricSemantics,
16029
+ unitInvocationSemantics
15870
16030
  ];
15871
16031
  var BY_NAME = new Map(
15872
16032
  DEFINITIONS.map((d) => [d.name, d])
@@ -15956,6 +16116,13 @@ var TypeShapeSchema = external_exports.lazy(
15956
16116
  );
15957
16117
 
15958
16118
  // ../../ir-core/dist/index.js
16119
+ var RelationSchema = external_exports.enum([
16120
+ "provides",
16121
+ "reads",
16122
+ "writes",
16123
+ "invokes"
16124
+ ]);
16125
+ var EffectRelationSchema = RelationSchema.exclude(["provides"]);
15959
16126
  var SuppressionSideSchema = external_exports.object({
15960
16127
  summary: external_exports.string().optional(),
15961
16128
  transitionId: external_exports.string().optional()
@@ -16144,9 +16311,9 @@ var RESOLUTION_RULES = [
16144
16311
  [(0, import_datalog2.lit)("invokes", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("ret"))],
16145
16312
  "call result"
16146
16313
  ),
16147
- // Where a walk gets to, and whether it ran a call on the way. A value
16148
- // step keeps whatever the rest of the walk was, and a result step
16149
- // makes the whole walk one that ran a call.
16314
+ // Where a walk gets to, and whether it ran a call on the way. The walk
16315
+ // so far comes first, so demand stays on the value asked about; the
16316
+ // README says what the other order cost.
16150
16317
  (0, import_datalog2.rule)(
16151
16318
  "reaches",
16152
16319
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
@@ -16156,27 +16323,27 @@ var RESOLUTION_RULES = [
16156
16323
  "reaches",
16157
16324
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
16158
16325
  [
16159
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
16160
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))
16326
+ (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
16327
+ (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))
16161
16328
  ]
16162
16329
  ),
16163
- // Two rules rather than one leaving the rest of the walk's kind free.
16164
- // A free kind is a second question about the same relation, and a
16330
+ // Two rules rather than one leaving the next step's kind free. A free
16331
+ // kind is a second question about the same relation, and a
16165
16332
  // demand-driven run then derives both to answer either.
16166
16333
  (0, import_datalog2.rule)(
16167
16334
  "reaches",
16168
16335
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
16169
16336
  [
16170
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
16171
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), VALUE_STEP)
16337
+ (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
16338
+ (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), VALUE_STEP)
16172
16339
  ]
16173
16340
  ),
16174
16341
  (0, import_datalog2.rule)(
16175
16342
  "reaches",
16176
16343
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
16177
16344
  [
16178
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
16179
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), RESULT_STEP)
16345
+ (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
16346
+ (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), RESULT_STEP)
16180
16347
  ]
16181
16348
  ),
16182
16349
  // What a value comes down to: the walk that never ran a call, stopped
@@ -16522,29 +16689,14 @@ var RESOLUTION_QUESTIONS = [
16522
16689
  // here demands reaches or callsInto. The chains are closures with
16523
16690
  // no depth bound: an alias run of any length resolves.
16524
16691
  (0, import_datalog2.rule)("callOriginChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedCallOrigin", (0, import_datalog2.variable)("x"))]),
16692
+ // The chain with at least one hop taken, which is what tells a
16693
+ // destructured binding apart from asking about a node directly. The
16694
+ // whole chain is the seed plus those hops.
16525
16695
  (0, import_datalog2.rule)(
16526
16696
  "callOriginChain",
16527
16697
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
16528
- [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
16529
- ),
16530
- (0, import_datalog2.rule)(
16531
- "callOriginChain",
16532
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
16533
- [
16534
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
16535
- (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
16536
- ]
16537
- ),
16538
- (0, import_datalog2.rule)(
16539
- "callOriginChain",
16540
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
16541
- [
16542
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
16543
- (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
16544
- ]
16698
+ [(0, import_datalog2.lit)("callOriginChainStepped", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
16545
16699
  ),
16546
- // The chain with at least one hop taken, which is what tells a
16547
- // destructured binding apart from asking about a node directly.
16548
16700
  (0, import_datalog2.rule)(
16549
16701
  "callOriginChainStepped",
16550
16702
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
@@ -16675,6 +16827,23 @@ var RESOLUTION_QUESTIONS = [
16675
16827
  (0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
16676
16828
  ]
16677
16829
  ),
16830
+ // Subject identity, its own demand class; the README's subject
16831
+ // section says why the origin pair rides on the written-value walk.
16832
+ (0, import_datalog2.rule)(
16833
+ "wantedSubjectWritten",
16834
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
16835
+ [(0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"))]
16836
+ ),
16837
+ (0, import_datalog2.rule)(
16838
+ "wantedSubjectConstruction",
16839
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
16840
+ [
16841
+ (0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")),
16842
+ (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")),
16843
+ (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
16844
+ (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
16845
+ ]
16846
+ ),
16678
16847
  // The export table of one module, keyed by the module asked about.
16679
16848
  // The moduleExport rules flatten re-export chains of any length, and
16680
16849
  // the demand cone stays on export facts alone.
@@ -17022,7 +17191,10 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
17022
17191
  const knownClasses = ownBlocks[0]?.knownClasses ?? /* @__PURE__ */ new Set();
17023
17192
  const scope = { nesting: info.bodyNesting, knownClasses };
17024
17193
  const declsByName = /* @__PURE__ */ new Map();
17025
- for (const stmt of bodyStatements(info.bodyNode)) {
17194
+ for (const stmt of runStatements(
17195
+ info.bodyNode,
17196
+ blockConfiguresCall(pattern)
17197
+ )) {
17026
17198
  const decl = await readFieldCall(stmt, scope, ctx, ancestry);
17027
17199
  if (decl !== null) {
17028
17200
  declsByName.set(decl.fieldName, decl);
@@ -17032,6 +17204,17 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
17032
17204
  (decl) => buildFieldUnit(pack, typeName, decl, options.filePath)
17033
17205
  );
17034
17206
  }
17207
+ function blockConfiguresCall(pattern) {
17208
+ const names = /* @__PURE__ */ new Set([
17209
+ pattern.fieldCallName,
17210
+ pattern.typeCallName,
17211
+ pattern.argumentCallName
17212
+ ]);
17213
+ return (call) => {
17214
+ const method = field(call, "method")?.text;
17215
+ return field(call, "receiver") === null && method !== void 0 && names.has(method);
17216
+ };
17217
+ }
17035
17218
  function branchesFor(body, range) {
17036
17219
  if (body.effects === void 0 && body.extraEffects === void 0) {
17037
17220
  return [];
@@ -17107,10 +17290,13 @@ async function readFieldCall(stmt, scope, ctx, ancestry) {
17107
17290
  }
17108
17291
  const callArgs = readCallArgs(field(stmt, "arguments"));
17109
17292
  const nameArg = callArgs.positional[0];
17110
- const symbol2 = nameArg !== void 0 ? symbolValue(nameArg) : null;
17111
- if (symbol2 === null) {
17293
+ if (nameArg === void 0) {
17112
17294
  return null;
17113
17295
  }
17296
+ const symbol2 = symbolValue(nameArg);
17297
+ if (symbol2 === null) {
17298
+ return computedNameDeclaration(stmt, nameArg);
17299
+ }
17114
17300
  const read = await readFieldShape(
17115
17301
  symbol2,
17116
17302
  callArgs,
@@ -17121,11 +17307,29 @@ async function readFieldCall(stmt, scope, ctx, ancestry) {
17121
17307
  );
17122
17308
  return {
17123
17309
  fieldName: resolvedName(symbol2, callArgs, ctx.pattern),
17310
+ namedOnTheWire: true,
17124
17311
  node: stmt,
17125
17312
  contract: read.contract,
17126
17313
  body: read.body
17127
17314
  };
17128
17315
  }
17316
+ function computedNameDeclaration(stmt, nameArg) {
17317
+ const range = rangeOf(stmt);
17318
+ return {
17319
+ fieldName: nameArg.text,
17320
+ namedOnTheWire: false,
17321
+ node: stmt,
17322
+ contract: null,
17323
+ body: {
17324
+ readings: [
17325
+ (0, import_extractor3.unreadableReading)(
17326
+ `This field is named by ${nameArg.text}, which is worked out when the class body runs, so the name the schema exposes it under was not read here`,
17327
+ range
17328
+ )
17329
+ ]
17330
+ }
17331
+ };
17332
+ }
17129
17333
  function wiringReference(callArgs, wiringKeywords) {
17130
17334
  for (const keyword of wiringKeywords) {
17131
17335
  const ref = callArgs.keyword[keyword];
@@ -17250,7 +17454,10 @@ function readClassContract(ancestry, pattern) {
17250
17454
  args: /* @__PURE__ */ new Map()
17251
17455
  };
17252
17456
  const handlers = classCallHandlers(pattern);
17253
- for (const { block, statement } of inheritedStatements(ancestry)) {
17457
+ for (const { block, statement } of inheritedStatements(
17458
+ ancestry,
17459
+ blockConfiguresCall(pattern)
17460
+ )) {
17254
17461
  if (statement.type !== "call" || field(statement, "receiver") !== null) {
17255
17462
  continue;
17256
17463
  }
@@ -17341,12 +17548,12 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
17341
17548
  exportName: null,
17342
17549
  exportPath: null
17343
17550
  },
17344
- boundaryBinding: (0, import_behavioral_ir.graphqlResolverBinding)({
17551
+ boundaryBinding: decl.namedOnTheWire ? (0, import_behavioral_ir.graphqlResolverBinding)({
17345
17552
  transport: pack.protocol,
17346
17553
  recognition: pack.name,
17347
17554
  typeName,
17348
17555
  fieldName: decl.fieldName
17349
- }),
17556
+ }) : null,
17350
17557
  parameters,
17351
17558
  branches: branchesFor(decl.body, rangeOf(decl.node)),
17352
17559
  ...decl.body.bodyContent !== void 0 ? { bodyContent: decl.body.bodyContent } : {},
@@ -17417,14 +17624,6 @@ function nodeId(filePath, node) {
17417
17624
  function nameId(filePath, name) {
17418
17625
  return `${filePath}#${name}`;
17419
17626
  }
17420
- var DECLARATION_TYPES2 = /* @__PURE__ */ new Set([
17421
- "method",
17422
- "singleton_method",
17423
- "lambda",
17424
- "class",
17425
- "module",
17426
- "singleton_class"
17427
- ]);
17428
17627
  var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
17429
17628
  "string",
17430
17629
  "integer",
@@ -17562,7 +17761,7 @@ function emitHash(emitter, hash2) {
17562
17761
  }
17563
17762
  function walkExpressions(node, visit) {
17564
17763
  for (const child of children3(node)) {
17565
- if (DECLARATION_TYPES2.has(child.type)) {
17764
+ if (OWN_BODY_TYPES.has(child.type)) {
17566
17765
  continue;
17567
17766
  }
17568
17767
  visit(child);
@@ -17620,7 +17819,7 @@ function emitMethodFacts(emitter, method) {
17620
17819
  }
17621
17820
  const recordNested = (node) => {
17622
17821
  for (const child of children3(node)) {
17623
- if (DECLARATION_TYPES2.has(child.type)) {
17822
+ if (OWN_BODY_TYPES.has(child.type)) {
17624
17823
  add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
17625
17824
  continue;
17626
17825
  }
package/dist/index.d.cts CHANGED
@@ -4,6 +4,16 @@ import { Database } from '@suss/datalog';
4
4
  import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
5
5
  import { RawCodeStructure } from '@suss/extractor';
6
6
 
7
+ /**
8
+ * A new `Parser` for every call, so that parses running at the same time do
9
+ * not fight over one parser's mutable `language` field. The compiled `Language`
10
+ * is loaded once per process and shared.
11
+ */
12
+ declare function parseRuby(source: string): Promise<Tree>;
13
+
14
+ /** A call whose block is the thing being configured rather than a place statements run. */
15
+ type BlockConfigures = (call: Node) => boolean;
16
+
7
17
  /**
8
18
  * Finds the file behind a constant.
9
19
  *
@@ -19,13 +29,6 @@ declare function underscoreConstantPath(qualifiedName: string): string;
19
29
  /** Null when there is no file at that path. We do not fall back to another root or another spelling. */
20
30
  declare function resolveConstantFile(root: string, qualifiedName: string, convention: ConstantPathConvention): string | null;
21
31
 
22
- /**
23
- * A new `Parser` for every call, so that parses running at the same time do
24
- * not fight over one parser's mutable `language` field. The compiled `Language`
25
- * is loaded once per process and shared.
26
- */
27
- declare function parseRuby(source: string): Promise<Tree>;
28
-
29
32
  /**
30
33
  * Resolves Ruby constant paths against lexical nesting.
31
34
  *
@@ -65,7 +68,7 @@ interface ClassInfo {
65
68
  /** `Module.nesting` in effect inside this class's own body, innermost first. */
66
69
  bodyNesting: readonly string[];
67
70
  }
68
- /** Every `class` declaration reachable from `root` through direct module/class nesting, in source order. */
71
+ /** Every `class` declaration reachable from `root` through module/class nesting, in source order. */
69
72
  declare function walkClasses(root: Node, visit: (info: ClassInfo) => void): void;
70
73
  /** The same walk including `module` declarations, for a lookup that has to reach a module's own methods. */
71
74
  declare function walkDefinitions(root: Node, visit: (info: ClassInfo) => void): void;
@@ -134,7 +137,7 @@ type MethodLookup = {
134
137
  */
135
138
  declare function methodInAncestry(ancestry: Ancestry, name: string): MethodLookup;
136
139
  /** Every statement of every body reached, most distant ancestor first, so a nearer declaration overwrites what it inherits. */
137
- declare function inheritedStatements(ancestry: Ancestry): Array<{
140
+ declare function inheritedStatements(ancestry: Ancestry, blockConfigures?: BlockConfigures): Array<{
138
141
  block: ReachedBody;
139
142
  statement: Node;
140
143
  }>;