@suss/adapter-ruby 0.33.1 → 0.34.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.d.cts CHANGED
@@ -1,15 +1,25 @@
1
1
  import { Database } from '@suss/datalog';
2
2
  import { Tree, Node } from 'web-tree-sitter';
3
3
  export { Node as RbNode, Tree as RbTree } from 'web-tree-sitter';
4
- import { TypeShape, Effect, BehavioralSummary } from '@suss/behavioral-ir';
5
- import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic, ExtractorOptions } from '@suss/extractor';
6
- import { WhyExplained } from '@suss/resolution';
4
+ import { TypeShape, RequestSpellingMetadata, Effect, BehavioralSummary } from '@suss/behavioral-ir';
5
+ import { UnwrapsByName, WhyExplained } from '@suss/resolution';
6
+ import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic, ExtractorOptions, DeclaredModule, KeptParses } from '@suss/extractor';
7
7
  import { Value } from '@suss/values';
8
8
 
9
9
  /**
10
- * A new `Parser` for every call, so that parses running at the same time do
11
- * not fight over one parser's mutable `language` field. The compiled `Language`
12
- * is loaded once per process and shared.
10
+ * Loads the Ruby grammar into web-tree-sitter and parses source with it.
11
+ *
12
+ * tree-sitter runs as WASM, so the package needs no native build step.
13
+ * The rest of the adapter never imports `web-tree-sitter` directly. It
14
+ * goes through `parseRuby` and the node helpers, so moving to a different
15
+ * Ruby grammar would change only this module. The Python adapter's
16
+ * parser works the same way.
17
+ */
18
+
19
+ /**
20
+ * Uses a new `Parser` for every call, so two parses running at once never
21
+ * share one parser's mutable `language` field. The compiled `Language` is
22
+ * loaded once per process and shared.
13
23
  */
14
24
  declare function parseRuby(source: string): Promise<Tree>;
15
25
  /**
@@ -30,6 +40,13 @@ interface Range {
30
40
  end: number;
31
41
  }
32
42
  declare function field(node: Node, name: string): Node | null;
43
+ /**
44
+ * A node's named children, and nothing for an absent node. Every read of
45
+ * children in this adapter goes through here or `allChildren`, because the
46
+ * library's `namedChildren` and `children` getters keep the array on the
47
+ * node, and the run keeps every root to the end.
48
+ */
49
+ declare function children(node: Node | null | undefined): Node[];
33
50
  declare function bodyStatements(body: Node): Node[];
34
51
  /**
35
52
  * Every statement written directly inside a branching statement's
@@ -38,40 +55,44 @@ declare function bodyStatements(body: Node): Node[];
38
55
  * included; the caller descends into that one itself.
39
56
  */
40
57
  declare function nestedStatements(stmt: Node): Node[];
41
- /** What a pack says about one call whose block runs as part of the body around it, with the fields it left out settled. */
58
+ /** A pack's declaration of one body-block call, with its optional flags defaulted. */
42
59
  interface BodyBlockKind {
43
- /** Whether the library only gives the call to a module, so a class writing the same name means something else. */
60
+ /** Whether the library offers the call only to modules. In a class, a call of the same name is something else. */
44
61
  readonly moduleOnly: boolean;
45
- /** Whether a `def` in the block declares a method on the class itself rather than on an instance. */
62
+ /** Whether a `def` in the block defines a method on the class itself instead of on an instance. */
46
63
  readonly definesClassMethods: boolean;
47
64
  }
48
65
  /**
49
- * The calls a run's packs say run their block as part of the class or
50
- * module body it is written in, by the name a project writes. Ruby
51
- * defines no such call, so this is empty until a pack declares one.
66
+ * The calls whose block runs as part of the surrounding class or module
67
+ * body, keyed by the call's name as a project writes it. Ruby has no such
68
+ * call of its own, so this is empty unless a pack declares one.
52
69
  */
53
70
  type BodyBlocks = ReadonlyMap<string, BodyBlockKind>;
54
- /** A call whose block is the thing being configured rather than a place statements run. */
71
+ /** Whether a call's block configures the call itself, so its statements are not part of the body. */
55
72
  type BlockConfigures = (call: Node) => boolean;
56
73
  /**
57
74
  * Every statement a body runs, in source order. Ruby runs a class body
58
- * like any other code, so a declaration can sit inside an `if`, a
59
- * `.each` block, a `begin` or a `class_eval`, and taking the body's own
60
- * child list finds the first spelling and loses the rest in silence.
75
+ * like any other code, so a declaration can be inside an `if`, an `.each`
76
+ * block, a `begin` or a `class_eval`. Reading only the body's direct
77
+ * children would miss those without any sign.
61
78
  *
62
- * `blockConfigures` says which calls keep their block to themselves.
63
- * `field :x, String do argument :q, String end` declares an argument on
64
- * the field, not on the class, so that block is not part of the body.
79
+ * `blockConfigures` picks out the calls whose block is not part of the
80
+ * body. `field :x, String do argument :q, String end` declares an
81
+ * argument on the field, so that block is skipped.
65
82
  */
66
83
  declare function runStatements(body: Node, blockConfigures?: BlockConfigures): Node[];
67
84
  /** The text inside a plain string node, with the quotes removed. A string with interpolation returns null. */
68
85
  declare function stringLiteralValue(node: Node): string | null;
69
- /** tree-sitter-ruby leaves the leading colon in a `simple_symbol`'s text, but not in a `hash_key_symbol`'s. */
86
+ /** A `simple_symbol`'s name without its leading colon, or null for any other node. */
70
87
  declare function symbolValue(node: Node): string | null;
71
- /** A `pair` node's key, when it is written as the bare `key:` shorthand. Null for a pair keyed by a string or an expression. */
88
+ /** A `pair` key's name when it is written as `key:`, or null for a key written as a string or an expression. */
72
89
  declare function hashKeySymbolName(node: Node): string | null;
73
90
  declare function booleanLiteralValue(node: Node): boolean | null;
74
- /** Keyword arguments turn up as `pair` nodes directly among the argument list's children, rather than wrapped in a hash node. */
91
+ /**
92
+ * A call's arguments, split into positional and keyword. tree-sitter puts
93
+ * keyword arguments in the argument list as `pair` nodes, with no hash
94
+ * node around them. Only keys written as `key:` are read.
95
+ */
75
96
  interface CallArgs {
76
97
  positional: Node[];
77
98
  keyword: Record<string, Node>;
@@ -79,83 +100,96 @@ interface CallArgs {
79
100
  declare function readCallArgs(argumentList: Node | null): CallArgs;
80
101
 
81
102
  /**
82
- * Finds the file behind a constant.
103
+ * Finds the file that defines a constant.
83
104
  *
84
- * A Ruby codebase locates that file by a naming convention rather than through a
85
- * load graph a static reader could follow, so we build one path from the
86
- * constant's own name and look for it under the configured root and the
105
+ * A Ruby codebase finds that file by a naming convention, with no load
106
+ * graph a static reader could follow. So the adapter builds one path from
107
+ * the constant's name and looks for it under the configured root and the
87
108
  * directories Rails autoloads from. No other spelling is tried.
88
109
  */
89
- /** A pack picks a convention by name, and the code for each one lives here. */
110
+ /** A pack picks a convention by name, and `PATH_CONVENTIONS` maps each name to its code. */
90
111
  type ConstantPathConvention = "railsUnderscore";
91
112
  /**
92
- * Ported from ActiveSupport's own `String#underscore`, which is what
93
- * Rails autoloading runs a constant path through. An acronym the
94
- * project registers with the inflector is one word to it, so with
95
- * `ActivityPub` registered the path is `activitypub`, not
96
- * `activity_pub`.
113
+ * A port of ActiveSupport's `String#underscore`, which Rails autoloading
114
+ * runs a constant path through. The inflector treats an acronym the
115
+ * project registers as one word, so with `ActivityPub` registered the
116
+ * path is `activitypub` and never `activity_pub`.
97
117
  */
98
118
  declare function underscoreConstantPath(qualifiedName: string, acronyms?: readonly string[]): string;
99
119
  /**
100
- * Null when there is no file at that path. Rails autoloads from every
101
- * directory directly under `app`, so `ApplicationController` is
102
- * `app/controllers/application_controller.rb` and not
103
- * `app/application_controller.rb`; those directories are tried after
104
- * the root itself, in name order, and no other spelling is tried.
120
+ * What constant lookups have read from the disk during one run. A run
121
+ * looks the same names up many times, and files do not change while it
122
+ * reads them, so each directory list and each answer is read once. Each
123
+ * run makes its own, so the MCP server and watch mode see a file added
124
+ * since the last run.
125
+ */
126
+ interface ConstantFileCache {
127
+ /** The directories a constant is looked for in under each root, in the order they are tried. */
128
+ readonly autoloadDirectories: Map<string, readonly string[]>;
129
+ /** The file each root and relative path resolved to, or null. */
130
+ readonly resolved: Map<string, string | null>;
131
+ }
132
+ declare function createConstantFileCache(): ConstantFileCache;
133
+ /**
134
+ * The file that defines a constant under `root`, or null when there is
135
+ * none. Rails autoloads from every directory directly under `app`, and
136
+ * from each `concerns` directory under those, so `ApplicationController`
137
+ * is `app/controllers/application_controller.rb` and `Archivable` can be
138
+ * `app/models/concerns/archivable.rb`. The root is tried first, then the
139
+ * directories under it in name order, then their `concerns` directories
140
+ * in the same order. Rails' own autoload glob lists them in that order.
141
+ * Paths a project adds, such as `lib`, come last.
105
142
  */
106
- declare function resolveConstantFile(root: string, qualifiedName: string, convention: ConstantPathConvention, acronyms?: readonly string[]): string | null;
143
+ declare function resolveConstantFile(root: string, qualifiedName: string, convention: ConstantPathConvention, acronyms?: readonly string[], cache?: ConstantFileCache, autoloadRoots?: readonly string[]): string | null;
107
144
 
108
145
  /**
109
- * The method names a class gets under a name the source computes.
146
+ * The methods a class defines under a name the source computes.
110
147
  *
111
148
  * `KEYS.each { |key| define_method(key) { ... } }` defines one method
112
149
  * per element of `KEYS`, and a reader of `def` nodes sees none of them.
113
- * Reading the names lets a lookup for a name none of them defines carry
114
- * on up the ancestry instead of stopping at the class.
150
+ * Knowing the names lets a lookup for any other name continue up the
151
+ * ancestry instead of stopping at the class.
115
152
  *
116
- * Which calls those are, and what each name is written as, arrive as
117
- * `definesMethodFrom` and `nameTurnsOn`, so this reads no source of its
118
- * own. Each name goes through the shared value evaluator, which follows
119
- * names and constants across the whole run. A name read only in part
153
+ * The calls and their name expressions come in as `definesMethodFrom`
154
+ * and `nameTurnsOn` facts, so this module never reads source itself. Each
155
+ * name goes through the shared value evaluator, which follows names and
156
+ * constants across the whole run. A name that settles only in part
120
157
  * becomes a pattern the whole name has to match.
121
158
  */
122
159
 
123
160
  /** What the dynamic definitions in one class define. */
124
161
  interface DefinedNames {
125
- /** Every name this reader read one as defining. */
162
+ /** Every name a dynamic definition settled on. */
126
163
  readonly names: ReadonlySet<string>;
127
- /** What a name read only in part has to match for the class to be defining it. */
164
+ /** One pattern per name that settled only in part. A name matching one may be defined by the class. */
128
165
  readonly patterns: readonly RegExp[];
129
- /** Whether one of them was given a name this reader could not read. */
166
+ /** Whether any definition's name settled on nothing at all. */
130
167
  readonly unreadable: boolean;
131
168
  }
132
- /** Every class's reading, by the key the value facts give the class node. */
169
+ /** What each class defines dynamically, keyed by the class node's value fact key. */
133
170
  type DynamicNames = ReadonlyMap<string, DefinedNames>;
134
171
 
135
172
  /**
136
173
  * Resolves Ruby constant paths against lexical nesting.
137
174
  *
138
- * A compound path referenced, like `Data::Record`, is read as absolute. A
139
- * bare name is relative to the nesting it is written inside, and so is a
140
- * compound name `class` or `module` opens. `require` is never resolved,
141
- * because Rails autoloads by naming convention rather than through a load graph
142
- * a static reader could follow, so a constant that nesting alone cannot qualify
143
- * keeps whatever name the source wrote and callers treat it as unresolved.
175
+ * A compound reference such as `Data::Record` is read as absolute. A bare
176
+ * name is relative to the nesting it is written inside, and so is a
177
+ * compound name that `class` or `module` opens. `require` is never
178
+ * resolved, because Rails autoloads by naming convention. A constant that
179
+ * nesting alone cannot qualify keeps the name the source wrote.
144
180
  *
145
- * Ruby's own `Module.nesting` puts a newly opened class or module on the front
146
- * of the chain already in effect, whether the name used to open it was bare or
147
- * compound, because nesting follows the class and module keywords in the text
148
- * and not the form of the name. So even when a class is opened with a compound
149
- * name, as in `module Api; class Types::CampaignType; end; end`, Ruby still
150
- * looks up through `Api` inside that body. That matters, because a bare
151
- * reference in there is resolved by trying each level of the chain, and dropping
152
- * `Api` would miss a class the file itself defines.
181
+ * Ruby's `Module.nesting` puts each newly opened class or module on the
182
+ * front of the chain, whether its name was bare or compound. Inside
183
+ * `module Api; class Types::CampaignType`, a bare reference is still
184
+ * looked up through `Api`.
153
185
  */
154
186
 
155
187
  /**
156
- * Qualifies a name against the innermost level of `nesting` and no further. It
157
- * returns null for a computed expression or a variable, which callers treat as
158
- * unresolved. `shadowingClassFor` is the one place that walks every level.
188
+ * Qualifies a name against the innermost level of `nesting` only, which
189
+ * gives the name a `class` or `module` keyword defines. Returns null for
190
+ * a computed expression or a variable, which callers treat as
191
+ * unresolved. To find the class a reference refers to, use
192
+ * `constantRefCandidates`.
159
193
  */
160
194
  declare function qualifyConstantRef(node: Node, nesting: readonly string[]): string | null;
161
195
  interface ClassInfo {
@@ -182,11 +216,18 @@ interface ClassInfo {
182
216
  declare function walkClasses(root: Node, visit: (info: ClassInfo) => void): void;
183
217
  /** The same walk including `module` declarations, for a lookup that has to reach a module's own methods. */
184
218
  declare function walkDefinitions(root: Node, visit: (info: ClassInfo) => void): void;
185
- /** A pack picks a derivation by name, and the code for each one lives here. */
219
+ /** A pack picks a convention by name, and `TYPE_NAME_CONVENTIONS` maps each name to its code. */
186
220
  type GraphqlTypeNameConvention = "stripTypeSuffix";
187
- /** We never read a per-class name override, so this is always the name the convention derives. */
221
+ /** The GraphQL type name the convention derives from a class name. A name a class sets for itself is not read. */
188
222
  declare function graphqlTypeNameFromQualified(qualifiedName: string, convention: GraphqlTypeNameConvention): string;
189
223
 
224
+ /**
225
+ * The ancestor chain a lookup walks to find the method behind a class,
226
+ * in the order Ruby's `Module#ancestors` gives. A reopened class is one
227
+ * place in the chain, an included module's own chain goes in as a unit,
228
+ * and an ancestor already in the chain is not added again.
229
+ */
230
+
190
231
  /** One class or module body a walk reached, with the definitions its own file makes, since a bare constant is shadowed per file. */
191
232
  interface ReachedBody {
192
233
  info: ClassInfo;
@@ -214,7 +255,7 @@ type AncestorEntry = {
214
255
  };
215
256
  /** A class and everything it inherits from, in Ruby's own method-lookup order. */
216
257
  type Ancestry = readonly AncestorEntry[];
217
- /** What reading a class body takes beyond the body: the run's facts, and what its packs declare about a block written in one. */
258
+ /** What reading a class body needs besides the body: the run's facts, and what the packs declare about blocks written in one. */
218
259
  interface BodyReading {
219
260
  readonly facts?: Database | undefined;
220
261
  readonly bodyBlocks?: BodyBlocks | undefined;
@@ -225,6 +266,8 @@ interface BodyReading {
225
266
  interface AncestorLookup {
226
267
  /** Directory the constant-to-path convention resolves an ancestor's name against. */
227
268
  root: string;
269
+ /** Further directories tried after `root`, each as the top of the namespace. */
270
+ autoloadRoots?: readonly string[];
228
271
  pathConvention: ConstantPathConvention;
229
272
  /** Acronyms the convention keeps as one word, `ActivityPub` to `activitypub`. */
230
273
  acronyms?: readonly string[];
@@ -233,6 +276,8 @@ interface AncestorLookup {
233
276
  * ends a walk with nothing left unfollowed.
234
277
  */
235
278
  ancestryRootClassNames: readonly string[];
279
+ /** What this run's constant lookups have already read from the disk. */
280
+ constantFiles: ConstantFileCache;
236
281
  parsedFile(absPath: string): Promise<Node | null>;
237
282
  /**
238
283
  * The blocks the file being read defines under a name, consulted
@@ -242,32 +287,34 @@ interface AncestorLookup {
242
287
  localDefinition?(qualifiedName: string): ReachedBody[] | null;
243
288
  }
244
289
  /**
245
- * Every block a file defines under `qualifiedName`, or null when the
246
- * constant-to-path convention points at no file, no file is there, or
247
- * the file defines nothing by that name.
290
+ * Every block a file defines under `qualifiedName`, or null when no file
291
+ * the constant-to-path convention points at defines it. A class nested
292
+ * in its namespace's own file, `Order::NotPaid` written inside
293
+ * `order.rb`, is found there, since loading `Order` defines it.
248
294
  */
249
295
  declare function reachDefinition(qualifiedName: string, lookup: AncestorLookup): Promise<ReachedBody[] | null>;
250
296
  declare function ancestryOf(name: string, blocks: readonly ReachedBody[], lookup: AncestorLookup): Promise<Ancestry>;
251
297
  /** What searching an ancestry for one method name came to. */
252
298
  type MethodLookup =
253
- /** `block` is which ancestor's own body the method is written in, so a caller that needs its file can get there. */
299
+ /** `block` is the ancestor body the method is written in, which gives a caller its file. */
254
300
  {
255
301
  type: "found";
256
302
  method: Node;
257
303
  block: ReachedBody;
258
304
  }
259
305
  /**
260
- * `reason` completes "could be answered by a method ...".
261
- * `cause` says why the search stopped, since a caller deciding
262
- * whether to look further needs more than the sentence: `unreadAncestor`
263
- * is a base whose file this run could not open, and `dynamicDefine`
264
- * is a `define_method` call that defines this name, or one whose own
265
- * name this reader could not read.
306
+ * `reason` finishes the sentence "could be answered by a method ...".
307
+ * `cause` gives the reason as a value, for a caller deciding whether
308
+ * to look further. `unreadAncestor` is a base whose file the run could
309
+ * not open, and `dynamicDefine` is a `define_method` call that defines
310
+ * this name or whose own name did not settle.
266
311
  */
267
312
  | {
268
313
  type: "unsettled";
269
314
  reason: string;
270
315
  cause: "unreadAncestor" | "dynamicDefine";
316
+ /** True when an ancestor past the unread one defines it in the project. */
317
+ definedLater?: boolean;
271
318
  } | {
272
319
  type: "none";
273
320
  };
@@ -283,56 +330,240 @@ declare function inheritedStatements(ancestry: Ancestry, blockConfigures?: Block
283
330
  }>;
284
331
 
285
332
  /**
286
- * The Ruby adapter's own pattern-pack contract.
333
+ * The contract a Ruby pack implements.
287
334
  *
288
- * Match shapes stay per-language until a second implementation shows what is
289
- * actually shared, so this is Ruby's own and not the TypeScript or Python
290
- * adapter's. Everything a library defines, meaning its call names, keywords,
291
- * scalars, and conventions, arrives as pack data and is never hardcoded here.
335
+ * Each language adapter keeps its own pack contract until a second
336
+ * implementation shows which parts can be shared. Anything a library
337
+ * defines, such as its call names, keywords and scalars, comes in as pack
338
+ * data. This package hardcodes none of it.
292
339
  */
293
340
 
294
341
  interface RubyPack {
295
342
  name: string;
296
343
  /**
297
- * Pack version stamp, which feeds the cache invalidation key. Bump on
298
- * any change that affects discovered units or extracted summaries.
299
- * The CLI folds a hash of the loaded pack file and its config into
300
- * this stamp on top, so a pack run through the CLI invalidates on an
301
- * edit whether or not it declares a version of its own.
344
+ * Part of the cache key. Bump it on any change that affects discovered
345
+ * units or extracted summaries. The CLI also adds a hash of the loaded
346
+ * pack file and its config to the key, so a pack run through the CLI
347
+ * re-extracts after an edit even when it declares no version.
302
348
  */
303
349
  version?: string;
304
350
  /**
305
- * Files under the project this pack reads that are not among the
306
- * `.rb` files a run walks, given the files the run is about to walk.
307
- * Their content feeds the same cache key the pack's own config does,
308
- * so an edit to one of them re-extracts instead of handing back the
309
- * previous answer.
351
+ * Files outside the walked `.rb` set whose content this pack reads,
352
+ * given the files the run is about to walk. Their content goes into the
353
+ * cache key, so editing one of them forces a re-extract.
310
354
  */
311
355
  discoveryInputs?: (files: readonly string[]) => string[];
312
- /** Wire protocol for the produced boundary bindings, e.g. "http-graphql". */
356
+ /** Wire protocol for the boundary bindings the pack produces, e.g. "http-graphql". */
313
357
  protocol: string;
314
358
  discovery: RubyDiscoveryPattern[];
315
- /** The calls the library gives a project for making a request. */
359
+ /** The calls the library offers a project for making a request. */
316
360
  clients?: RbClientCall[];
317
- /** What the library's own database calls look like. The README says how one is matched. */
361
+ /** The library's model calls that read or write the database. */
318
362
  storage?: RbStoragePattern[];
319
- /** The calls the library gives a project for handing the store a statement it wrote itself. */
363
+ /** The calls the library offers for sending the store a statement the project wrote itself. */
320
364
  rawSql?: RbRawSqlPattern[];
321
- /** Calls the library gives a project for reading a model through a batching loader, rather than on the model itself. */
365
+ /**
366
+ * The methods an action calls with no receiver to read the request,
367
+ * such as Rails' `params` and `request`. An action is handed no
368
+ * parameter for the request, so without these a value read off one is
369
+ * not known to come from the request.
370
+ */
371
+ requestAccessors?: string[];
372
+ /** Where an action reads each part of the request, recorded on every action the pack finds. */
373
+ requestSpelling?: RequestSpellingMetadata;
374
+ /** Calls that read a model through a batching loader, where the model is an argument instead of the receiver. */
322
375
  loaders?: RbLoaderPattern[];
323
- /** Calls the library gives a class or module body whose block runs as part of that body. */
376
+ /** Calls in a class or module body whose block runs as part of that body. */
324
377
  bodyBlocks?: RbBodyBlock[];
325
- /** What the project taught the library's inflector, which comes ahead of the adapter's own defaults. */
378
+ /** Words and rules the project added to the library's inflector. They are tried before the adapter's defaults. */
326
379
  inflections?: RbInflections;
380
+ /** Methods the library offers that hand back the argument at `argument`, keyed on the module the callee comes from. */
381
+ transparentWrappers?: UnwrapsByName[];
382
+ /** How the library builds test records from named factories. */
383
+ factories?: RbFactories[];
384
+ /** How a class-based test runner declares tests, as Minitest does. */
385
+ testClasses?: RbTestClasses[];
386
+ /** How the library writes a test, so each one becomes a `test` unit a PRD scenario can list under `coveredBy`. */
387
+ tests?: RbTestCases[];
388
+ }
389
+ /**
390
+ * Tests written as nested blocks, the way RSpec writes them:
391
+ * `describe Order do; it "cancels" do ... end; end`. Each example is a
392
+ * `test` unit whose name is its group titles and its own. Every call
393
+ * listed here is one the library defines; the adapter reads the nesting.
394
+ */
395
+ interface RbTestCases {
396
+ /** Base names of the files the runner loads, where `*` matches any run of characters: `*_spec.rb`. */
397
+ filePatterns: string[];
398
+ /** The constant a top-level group may be opened on, `RSpec` in `RSpec.describe`. */
399
+ receiver: string;
400
+ /** Calls that open a group of examples: `describe`, `context`. */
401
+ groupNames: string[];
402
+ /** Calls that open a group whose examples do not run: `xdescribe`, `xcontext`. */
403
+ skippedGroupNames: string[];
404
+ /** Calls that open a group other groups include by name, whose examples belong to whoever includes them: `shared_examples`. */
405
+ sharedGroupNames: string[];
406
+ /** Calls that include a shared group by name into the group they are written in: `it_behaves_like`, `include_context`. */
407
+ sharedIncludes: RbSharedInclude[];
408
+ /** Calls that declare one example: `it`, `specify`. */
409
+ exampleNames: string[];
410
+ /** Calls that declare an example that does not run: `xit`, `skip`. */
411
+ skippedExampleNames: string[];
412
+ /** Calls that, written as a statement in an example or a `before` block, mark the example as not run: `skip`, `pending`. */
413
+ skipStatements: string[];
414
+ /** Metadata keys that mark a group or an example as not run, written `:skip` or `skip: true`. */
415
+ skipMetadata: string[];
416
+ /** Hooks whose block runs before every example in the group: `before`. */
417
+ beforeHooks: string[];
418
+ /** Calls that define a value an example reads by name, run when it is first read: `let`. */
419
+ lazyValues: string[];
420
+ /** The same, run before every example whether it reads the value or not: `let!`. */
421
+ eagerValues: string[];
422
+ /** Calls that define the group's subject, lazily and eagerly: `subject`, `subject!`. */
423
+ subjectNames: {
424
+ lazy: string[];
425
+ eager: string[];
426
+ };
427
+ /** Calls an example makes on itself to read the subject: `subject`, `is_expected`. */
428
+ subjectReads: string[];
429
+ /** The one of those that gives back the subject itself: `subject`. A group given a class has that class's `new` as its subject unless it says otherwise. */
430
+ subjectValue: string;
431
+ /** The call that gives back the class the nearest group around was given: `described_class`. */
432
+ describedClass: string;
433
+ /**
434
+ * Methods the library gives an example to call on itself to check or
435
+ * set something up, such as `expect`, `eq` and `allow`. A call made
436
+ * through one is no call into project code.
437
+ */
438
+ runnerMethods: string[];
439
+ /** The matchers the library makes up from a predicate method, as `be_cancelled` calls `cancelled?` on what the expectation is about. */
440
+ predicateMatchers: RbPredicateMatcher[];
441
+ /** How an example states an expectation, which a predicate matcher is given to. */
442
+ expectations: RbExpectations;
443
+ /** How the library replaces something for the length of an example. */
444
+ mocks: RbTestMocks;
445
+ /** Read only these files, matched on whole path segments from the end. Left out, every file matching `filePatterns` is read. */
446
+ files?: string[];
447
+ }
448
+ /**
449
+ * Tests written as a class, the way Minitest and Rails' test cases write
450
+ * them: a class that extends one of `baseClassNames`, with a method per
451
+ * test or a `test "..." do` block per test. Each becomes a `test` unit
452
+ * named by the class and then the test's method name.
453
+ */
454
+ interface RbTestClasses {
455
+ /** Base names of the files the runner loads, where `*` matches any run of characters: `*_test.rb`. */
456
+ filePatterns: string[];
457
+ /** The library's test case classes, as a project writes them: `Minitest::Test`, `ActiveSupport::TestCase`. */
458
+ baseClassNames: string[];
459
+ /** What a test method's name starts with: `test_`. */
460
+ testMethodPrefix: string;
461
+ /** Calls that declare a test as a block, given its description: `test`. */
462
+ testBlockMethods: string[];
463
+ /** How the library turns a block test's description into the method it defines: `test_` in front, each run of spaces as `_`. */
464
+ blockTestName: {
465
+ prefix: string;
466
+ spacesAs: string;
467
+ };
468
+ /** Calls whose block runs before each test: `setup`. */
469
+ setupBlockMethods: string[];
470
+ /** Methods the runner calls before each test: `setup`. */
471
+ setupMethodNames: string[];
472
+ /** Calls that, written as a statement in a test, mark it as not run: `skip`. */
473
+ skipStatements: string[];
474
+ /** Prefixes of the checks the runner gives a test, which call no project code: `assert`, `refute`. */
475
+ assertionPrefixes: string[];
476
+ /** Calls that replace a method on the object they are called on, given its name first: `stub`, `stubs`, `expects`. */
477
+ stubMethods: string[];
478
+ /** Read only these files, matched on whole path segments from the end. */
479
+ files?: string[];
480
+ }
481
+ /**
482
+ * How a test data library builds a record from a factory it looks up by
483
+ * name, so a value a test builds with `create(:order)` or
484
+ * `Fabricate(:order)` is known to be one of the factory's class. The
485
+ * adapter reads the definitions wherever the run finds them, and the
486
+ * builds in the files it reads as tests.
487
+ */
488
+ interface RbFactories {
489
+ /** Calls that define a factory, given its name first: `factory`, `Fabricator`. */
490
+ definitionMethods: string[];
491
+ /** Keywords a definition gives its class under, as a constant or a string: `class`, `class_name`. */
492
+ classKeywords: string[];
493
+ /** Keywords a definition gives the factory it builds on under: `parent`, `from`. A name no factory has is read as a class. */
494
+ parentKeywords: string[];
495
+ /** Whether a definition written in another's block builds on it, as a nested FactoryBot factory does. */
496
+ nestedDefinitionsInherit: boolean;
497
+ /** Calls that build one record from the factory their first argument names. */
498
+ builders: RbFactoryBuilder[];
499
+ }
500
+ /** One build call: `create`, or `Fabricate.build` with `receiver` set. */
501
+ interface RbFactoryBuilder {
502
+ method: string;
503
+ /** The constant the call is made on, when it is made on one. */
504
+ receiver?: string;
505
+ }
506
+ /**
507
+ * A matcher the library makes up from a predicate method: a matcher
508
+ * written `prefix` and a name calls `methodPrefix`, that name and
509
+ * `methodSuffix` on the value the expectation is about, so `be_local`
510
+ * calls `local?` and `have_key` calls `has_key?`.
511
+ */
512
+ interface RbPredicateMatcher {
513
+ prefix: string;
514
+ methodPrefix: string;
515
+ methodSuffix: string;
327
516
  }
328
517
  /**
329
- * The words and rules a project registered with its library's
330
- * inflector. The library says where a project writes them and how; what
331
- * arrives here is the reading, so the adapter applies them without
332
- * knowing any of that.
518
+ * How an example states an expectation: `expect(order).to be_open`, on
519
+ * the argument of a `starts` call, or `is_expected.to be_open`, on the
520
+ * subject through `onSubject`. `runs` are the calls given the matcher.
521
+ */
522
+ interface RbExpectations {
523
+ starts: string[];
524
+ onSubject: string;
525
+ runs: string[];
526
+ }
527
+ /**
528
+ * One way a group includes a shared group. With `nestedTitle`, the shared
529
+ * examples run in a group of their own titled that and then the shared
530
+ * group's name, as `it_behaves_like "x"` runs them under "behaves like x".
531
+ * Without it they run in the including group itself.
532
+ */
533
+ interface RbSharedInclude {
534
+ method: string;
535
+ nestedTitle?: string;
536
+ }
537
+ /**
538
+ * The library's mock spellings. `allow(Order).to receive(:cancel)`
539
+ * replaces one method: `targets` opens it, `expectations` joins it, and
540
+ * `messages` says which method. `stub_const("Order", ...)` replaces a
541
+ * whole constant. A double is a fake object the test hands over, and it
542
+ * replaces nothing the test would otherwise reach unless the test makes
543
+ * it the constant with `constantDoubleMethod`.
544
+ */
545
+ interface RbTestMocks {
546
+ /** `allow`, `expect`, `allow_any_instance_of`. */
547
+ targets: string[];
548
+ /** `to`. */
549
+ expectations: string[];
550
+ /** `receive`, `receive_messages`. */
551
+ messages: string[];
552
+ /** `stub_const`. */
553
+ constantStubs: string[];
554
+ /** `instance_double`, `class_double`. */
555
+ doubles: string[];
556
+ /** `as_stubbed_const`, which makes a double the constant it stands in for. */
557
+ constantDoubleMethod: string;
558
+ }
559
+ /**
560
+ * The words and rules a project registered with its library's inflector.
561
+ * Where a project writes them depends on the library, so the pack reads
562
+ * them and passes the result here. The adapter applies them the same way
563
+ * for every library.
333
564
  */
334
565
  interface RbInflections {
335
- /** Words a constant name keeps whole: `API` gives `APIToken` rather than `ApiToken`. */
566
+ /** Words a constant name keeps in capitals: with `API`, `api_token` becomes `APIToken`. */
336
567
  acronyms?: string[];
337
568
  /** A plural and the singular it comes from: `["people", "person"]`. */
338
569
  irregular?: Array<[plural: string, singular: string]>;
@@ -342,26 +573,26 @@ interface RbInflections {
342
573
  singular?: Array<[rule: string, replacement: string]>;
343
574
  }
344
575
  /**
345
- * A receiverless call a library gives a class or module body, whose
346
- * block runs as part of that body rather than keeping what it declares
347
- * to itself. Ruby has no such call of its own, so a run whose packs
348
- * declare nothing here never opens a block out into the body.
576
+ * A receiverless call in a class or module body whose block runs as part
577
+ * of that body, so what the block declares belongs to the class. Ruby has
578
+ * no such call of its own. When no pack declares one, every block is read
579
+ * as an ordinary block.
349
580
  */
350
581
  interface RbBodyBlock {
351
582
  /** The call as a project writes it, `included`. */
352
583
  name: string;
353
- /** Set when the library only gives the call to a module, as a concern's own `included` is. */
584
+ /** Set when the library offers the call only to modules, as with a concern's `included`. */
354
585
  moduleOnly?: boolean;
355
- /** Set when a `def` in the block declares a method on the class itself rather than on an instance. */
586
+ /** Set when a `def` in the block defines a method on the class itself instead of on an instance. */
356
587
  definesClassMethods?: boolean;
357
588
  }
358
589
  /**
359
- * The calls a library gives a project for making a request. A method
360
- * that makes one is a client of the boundary that call states, and gets
361
- * a unit bound to its method and path.
590
+ * The calls a library offers a project for making a request. A method
591
+ * that makes one becomes a client unit, bound to the request method and
592
+ * path of the call.
362
593
  */
363
594
  interface RbClientCall {
364
- /** The constant the calls hang on, as a project writes it: `Faraday`, `Net::HTTP`. */
595
+ /** The constant the calls are made on, as a project writes it: `Faraday`, `Net::HTTP`. */
365
596
  constantName: string;
366
597
  /** Method names that state the request method themselves: `get` means GET. */
367
598
  verbMethodNames: Record<string, string>;
@@ -372,11 +603,11 @@ interface RbClientCall {
372
603
  };
373
604
  /** Methods on the constant that build a value taking the same calls, `new` for a Faraday connection. */
374
605
  receiverBuilders?: string[];
375
- /** The keyword such a builder takes the base URL under, which comes in front of the path of a call on it. */
606
+ /** The keyword a builder takes its base URL under. The base URL goes in front of the path of each call on the built value. */
376
607
  builderUrlKeyword?: string;
377
- /** What the response object gives a caller, so a test on one of its members counts as a test on a status. */
608
+ /** The members of the response object, so a test on one of them counts as a test on the status. */
378
609
  response?: RbClientResponse;
379
- /** A call that sends a request object built somewhere else, which is how Net::HTTP sends anything with a body. */
610
+ /** A call that sends a request object built separately. Net::HTTP sends any request with a body this way. */
380
611
  requestObject?: {
381
612
  /** The method that takes the request object, `request`. */
382
613
  attribute: string;
@@ -387,9 +618,9 @@ interface RbClientCall {
387
618
  };
388
619
  }
389
620
  /**
390
- * The members a library's response object gives a caller. A caller that
391
- * tests one of them is saying which statuses it handles, and the
392
- * checker compares that against what the other side produces.
621
+ * The members of a library's response object. A test on one of them shows
622
+ * which statuses the caller handles, and the checker compares that with
623
+ * what the provider sends.
393
624
  */
394
625
  interface RbClientResponse {
395
626
  /** Members whose value is the status: `status` for Faraday, `code` for Net::HTTP. */
@@ -400,18 +631,21 @@ interface RbClientResponse {
400
631
  body?: string[];
401
632
  /** Whether a refused request comes back as a response or raises where it was made. */
402
633
  failureDelivery?: "response" | "exception";
634
+ /** Whether the client follows a redirect itself or hands the 3xx back. */
635
+ redirectDelivery?: "followed" | "response";
403
636
  }
404
637
  /**
405
- * Ruby writes no return type, so a call is matched by what its receiver
406
- * inherits from. A model is a class that reaches one of these base classes,
407
- * whether the project declares an intermediate one or not.
638
+ * Ruby code declares no return types, so a call is matched by what its
639
+ * receiver inherits from. A model is any class whose ancestry reaches one
640
+ * of `baseClasses`, including through a base class the project declares
641
+ * in between.
408
642
  *
409
- * `reads` and `writes` are the whole of what the library does to the
410
- * database. A method in neither list is the project's own or the
411
- * language's, and the reach walk follows it instead.
643
+ * `reads` and `writes` list every database call the library makes. A
644
+ * method in neither list belongs to the project or the language, and the
645
+ * reach walk follows it.
412
646
  */
413
647
  interface RbStoragePattern {
414
- /** Base classes the library gives a model, `ActiveRecord::Base` for Rails. */
648
+ /** Base classes the library provides for models, `ActiveRecord::Base` for Rails. */
415
649
  baseClasses: string[];
416
650
  /** Methods the library defines that change what is stored. */
417
651
  writes: string[];
@@ -421,146 +655,157 @@ interface RbStoragePattern {
421
655
  givesBack: string[];
422
656
  /**
423
657
  * Methods on the model that take a statement the project wrote itself,
424
- * and where each one takes it. The tables come from the statement, so
425
- * one of these says nothing about the model's own container, and the
426
- * statement is read in the dialect of `storageSystem`.
658
+ * and where each one takes it. The tables come from the statement
659
+ * instead of the model's own container, and the statement is read in
660
+ * the dialect of `storageSystem`.
427
661
  */
428
662
  statements?: Record<string, RbArgumentPlace>;
429
663
  /** The token the library writes where a bind value goes, ActiveRecord's `?`. */
430
664
  bindPlaceholder?: string;
431
- /** The methods the library runs of its own accord when a write happens, `after_commit :sync_search`. */
665
+ /** The methods the library runs by itself around a write, as registered by `after_commit :sync_search`. */
432
666
  callbacks?: RbModelCallbacks;
433
- /** Methods that pick rows by the primary key when they are given a positional argument, and the column that is. */
667
+ /** Methods that pick rows by the primary key when given a positional argument, and that key's column. */
434
668
  byPrimaryKey?: RbPrimaryKeyLookup;
435
669
  /** Read methods whose symbol arguments are the columns they ask for. */
436
670
  columnArguments?: string[];
437
- /** How the library declares that one model reaches another, when it has such a thing. */
671
+ /** The calls a model uses to declare an association with another model, when the library has them. */
438
672
  associations?: RbAssociationCalls;
439
- /** Which database is behind the connection, which the project settles. */
673
+ /**
674
+ * The calls a model body makes to define a class method from a lambda,
675
+ * as `scope :recent, -> { order(:created_at) }` defines `recent`. The
676
+ * first argument is the method's name and the lambda after it runs.
677
+ */
678
+ scopes?: string[];
679
+ /** Which database is behind the connection. The project decides this, so the pack passes it in. */
440
680
  storageSystem: "postgresql" | "mysql" | "sqlite";
681
+ /** The exception each of these methods raises when the row is missing or does not save, ActiveRecord's `RecordNotFound` from `find`. */
682
+ raises?: Array<{
683
+ exception: string;
684
+ methods: string[];
685
+ }>;
441
686
  }
442
687
  /**
443
- * A library whose calls take SQL the project wrote, rather than
444
- * building it from a model. Ruby writes no types, so the receiver is
445
- * typed by following it back to the library call that produced it: the
446
- * chain starts at `constantName`, one of `clientBuilders` gives back a
447
- * client, and `addressing` calls narrow that client to a part of the
448
- * store before the statement or the row call arrives.
688
+ * A library whose calls take SQL the project wrote, instead of building
689
+ * it from a model. Ruby code declares no types, so the adapter finds the
690
+ * receiver's type by following it back to the library call that produced
691
+ * it. The chain starts at `constantName`, one of `clientBuilders` returns
692
+ * a client, and `addressing` calls narrow that client to part of the store
693
+ * before the statement or row call.
449
694
  */
450
695
  interface RbRawSqlPattern {
451
696
  /** The constant the library's calls start at, `PG` or `Google::Cloud::Bigquery`. */
452
697
  constantName: string;
453
698
  /**
454
- * Base classes whose subclasses hand a client out the same way the
455
- * constant does, `ActiveRecord::Base` for Rails. A project writes
456
- * `Account.connection` and a bare `connection` inside the model as
457
- * readily as the base class itself, and all three reach one store.
699
+ * Base classes whose subclasses return a client the same way the
700
+ * constant does, `ActiveRecord::Base` for Rails. `Account.connection`
701
+ * and a bare `connection` inside the model reach the same store as the
702
+ * base class itself.
458
703
  */
459
704
  baseClasses?: string[];
460
- /** Methods on that constant that give back a client, `connect` for the pg gem. */
705
+ /** Methods on that constant that return a client, `connect` for the pg gem. */
461
706
  clientBuilders: string[];
462
- /** Calls that narrow a client to part of the store and give back something the rest of the calls can be made on. */
707
+ /** Calls that narrow a client to part of the store and return a value the later calls are made on. */
463
708
  addressing?: Record<string, RbAddressingCall>;
464
709
  /** Calls that take a statement, and where each one takes it. */
465
710
  statements?: Record<string, RbArgumentPlace>;
466
- /** The token the library writes where a bind value goes, when it is not the one the dialect itself reads. */
711
+ /** The token the library writes where a bind value goes, when the dialect's parser does not read it. */
467
712
  bindPlaceholder?: string;
468
713
  /** Calls that read or write rows of an addressed container with no statement, `insert` on a BigQuery table. */
469
714
  rowCalls?: Record<string, RbRowCall>;
470
- /** Which store is behind the calls, in the words OpenTelemetry's semantic conventions use: `postgresql`, `gcp.bigquery`. */
715
+ /** Which store is behind the calls, in OpenTelemetry's semantic convention names: `postgresql`, `gcp.bigquery`. */
471
716
  storageSystem: string;
472
717
  /**
473
- * Which dialect the statements are written in, which is a separate
474
- * answer from the store: BigQuery's store is `gcp.bigquery` and its
475
- * statements are `bigquery`, and a default here would read the wrong
476
- * tables without saying so.
718
+ * Which dialect the statements are written in. This can differ from the
719
+ * store: BigQuery's store is `gcp.bigquery` and its dialect is
720
+ * `bigquery`. There is no default, because a wrong dialect would read
721
+ * the wrong tables without any warning.
477
722
  */
478
723
  dialect: string;
479
- /** Which namespace the calls reach when neither the chain nor the table name says. Defaults to "default". */
724
+ /** The namespace the calls reach when neither the chain nor the table name gives one. Defaults to "default". */
480
725
  scope?: string;
481
726
  }
482
- /** A call that says which part of the store the calls after it reach, `bigquery.dataset("core")`. */
727
+ /** A call that selects which part of the store the later calls reach, `bigquery.dataset("core")`. */
483
728
  interface RbAddressingCall extends RbArgumentPlace {
484
- /** Which part of the address this call states. */
729
+ /** Which part of the address this call gives. */
485
730
  says: "scope" | "container";
486
731
  }
487
732
  /** A call that reads or writes rows without a statement. */
488
733
  interface RbRowCall {
489
734
  kind: "read" | "write";
490
735
  /**
491
- * Where the call may say which container, for a library that gives
492
- * the same call on the container and one level above it: BigQuery's
493
- * `table.insert(rows)` and `dataset.insert("accounts", rows)`. The
494
- * chain's own container is used when this argument settles on no
495
- * string, which is what a row argument does.
736
+ * Where the call can take the container, for a library that offers the
737
+ * same call on the container and one level above it: BigQuery's
738
+ * `table.insert(rows)` and `dataset.insert("accounts", rows)`. When this
739
+ * argument does not settle on a string, as with a rows argument, the
740
+ * chain's own container is used.
496
741
  */
497
742
  container?: RbArgumentPlace;
498
743
  }
499
- /** Where a call writes one of its arguments. A call that takes it both ways is matched on the keyword first. */
744
+ /** Where a call takes one of its arguments. When a call passes it both ways, the keyword wins. */
500
745
  interface RbArgumentPlace {
501
746
  /** The index of the positional argument. */
502
747
  at: number;
503
- /** The keyword the argument may travel under instead. */
748
+ /** The keyword the argument can be passed under instead. */
504
749
  keyword?: string;
505
750
  }
506
- /** Which methods take the primary key positionally, and what that column is called. */
507
751
  /**
508
- * The methods a model registers in its own class body for the library
509
- * to run when a write happens. A body that writes through the model
510
- * runs them too, so their effects belong to that body.
752
+ * The methods a model registers in its class body for the library to
753
+ * run when a write happens. A body that writes through the model runs
754
+ * them too, so their effects belong to that body.
511
755
  *
512
- * `events` is the library's own vocabulary for what a write is, and
513
- * both halves of this are keyed by it: a write method says which events
514
- * it runs, and a registering call says which events it covers.
756
+ * Both tables are keyed by the library's own event names: a write method
757
+ * lists the events it fires, and a registering call lists the events it
758
+ * covers.
515
759
  */
516
760
  interface RbModelCallbacks {
517
- /** Each write method the pattern declares, and the events it runs. */
761
+ /** Each write method the pattern declares, and the events it fires. */
518
762
  eventOf: Record<string, string[]>;
519
- /** Each class-body call that registers a callback, and the events it covers when the call narrows to none. */
763
+ /** Each class-body call that registers a callback, and the events it covers when the call does not narrow them. */
520
764
  registeredBy: Record<string, string[]>;
521
765
  /** The keyword that narrows one registration to some of those events, `on`. */
522
766
  eventKeyword: string;
523
767
  }
768
+ /** Which methods take the primary key positionally, and what that column is called. */
524
769
  interface RbPrimaryKeyLookup {
525
770
  methods: string[];
526
- /** The column the library uses unless a model says another. */
771
+ /** The column the library uses unless a model sets another. */
527
772
  column: string;
528
773
  }
529
774
  /**
530
- * The class-body calls a library gives a model for declaring an
531
- * association, and where one says which class it reaches. The two lists
532
- * are apart because the target's name is the association's singularised
533
- * for a plural call and the association's as written for a singular one,
534
- * and only the library knows which of its calls is which.
775
+ * The class-body calls a model uses to declare an association, and the
776
+ * keyword that gives the target class. The calls are split into two
777
+ * lists because a plural call's target is the singularised association
778
+ * name, and a singular call's target is the name as written. Only the
779
+ * library knows which of its calls is which.
535
780
  */
536
781
  interface RbAssociationCalls {
537
782
  /** Calls written in the singular, `belongs_to :account` and `has_one :profile`. */
538
783
  singular: string[];
539
784
  /** Calls written in the plural, `has_many :statuses`. */
540
785
  plural: string[];
541
- /** The keyword whose value is the target class's name instead, `class_name`. */
786
+ /** The keyword that gives the target class's name explicitly, `class_name`. */
542
787
  classNameKeyword: string;
543
788
  }
544
789
  /**
545
- * A loader that batches reads of a model on the caller's behalf, graphql-ruby's
546
- * dataloader for one. The call that picks what to load is given the model as
547
- * a constant argument, so the read is recorded against that model the same
548
- * way a call on the model itself would be, and a storage pattern from another
549
- * pack in the run says whether the constant is a model at all.
790
+ * A loader that batches reads of a model for the caller, such as
791
+ * graphql-ruby's dataloader. The call that picks what to load gets the
792
+ * model as a constant argument, so the read is recorded against that
793
+ * model as if the call were made on it. A storage pattern from another
794
+ * pack in the run decides whether the constant is a model at all.
550
795
  */
551
796
  interface RbLoaderPattern {
552
- /** The receiverless call that gives the loader, `dataloader`. */
797
+ /** The receiverless call that returns the loader, `dataloader`. */
553
798
  loader: string;
554
799
  /** The method on the loader that picks a source and its arguments, `with`. */
555
800
  pick: string;
556
801
  /** The methods on a picked source that perform the read, `load` and `load_all`. */
557
802
  reads: string[];
558
- /** Receiverless calls that pick and read in one, whose arguments include the model, `dataload` and `dataload_record`. */
803
+ /** Receiverless calls that pick and read in one call, with the model among the arguments: `dataload` and `dataload_record`. */
559
804
  shortcuts: string[];
560
805
  /**
561
806
  * Where `pick` takes the project's own source class, and the method
562
- * the library runs on it. The read happens in that method, so what it
563
- * reaches belongs to every body that loads through it.
807
+ * the library runs on it. The read happens in that method, so whatever
808
+ * it reaches is reported on every body that loads through it.
564
809
  */
565
810
  source?: RbLoaderSource;
566
811
  }
@@ -572,73 +817,102 @@ interface RbLoaderSource {
572
817
  method: string;
573
818
  }
574
819
  type RubyDiscoveryPattern = GraphqlObjectFields | ControllerActions;
575
- /** A class whose ancestry reaches one of `baseClassNames` or `ancestryRootClassNames` is a controller, and every instance method it defines directly is one of its actions, bound by `routeFor` to the method and path a project's own routing gives it, or discovered with no boundary binding when `routeFor` finds none. */
820
+ /** One route to a controller action. */
821
+ interface ControllerRoute {
822
+ method: string;
823
+ path: string;
824
+ /** True for a route served with no `.{format}` in a framework that adds one; see `ControllerActions.optionalFormat`. */
825
+ formatless?: true;
826
+ }
827
+ /**
828
+ * A class whose ancestry reaches one of `baseClassNames` or
829
+ * `ancestryRootClassNames` is a controller, and every instance method it
830
+ * defines directly is an action. `routesFor` binds each action to the
831
+ * methods and paths the project's routing gives it, one unit per route.
832
+ * An action with no route is still discovered, with no boundary binding.
833
+ */
576
834
  interface ControllerActions {
577
835
  type: "controllerActions";
578
- /** A base a project's controllers extend, `ApplicationController` say. The base itself is not a controller. */
836
+ /** Bases a project's controllers extend, such as `ApplicationController`. A base is not itself a controller. */
579
837
  baseClassNames: string[];
580
- /** The directory a bare superclass name is looked up under, the project's own layout. */
838
+ /** The directory a bare superclass name is looked up under, which depends on the project's layout. */
581
839
  root: string;
840
+ /** Other directories the project autoloads constants from, each read as the top of the namespace, after `root`. */
841
+ autoloadRoots?: string[];
842
+ /**
843
+ * The library's own exception classes, with the classes each one
844
+ * inherits from, so a handler for a base class counts. `status` is
845
+ * what the library sends when an action raises one and no handler
846
+ * rescues it, Rails' `rescue_responses`.
847
+ */
848
+ libraryExceptions?: Record<string, LibraryException>;
582
849
  pathConvention: ConstantPathConvention;
583
- /** Acronyms the project registers with the inflector, which the path convention keeps as one word: `ActivityPub` is `activitypub`, not `activity_pub`. */
850
+ /** Acronyms the project registers with the inflector. The path convention keeps each one as one word: `ActivityPub` becomes `activitypub`. */
584
851
  acronyms?: string[];
585
852
  /** The library's own classes a project's controller chain ends at. A class extending one directly is a controller too. */
586
853
  ancestryRootClassNames: string[];
587
- /** Status code a wire response gets when the action does not say otherwise. */
854
+ /** The status a response gets when the action does not set one. */
588
855
  defaultStatusCode: number;
589
- /** The receiverless calls the library gives an action for writing a wire status. Leave it out and every action reports the default. */
856
+ /** The receiverless calls an action uses to send a status. Without them, every action reports the default. */
590
857
  responseStatusCalls?: RbStatusCall[];
591
- /** The number behind each name the library accepts where a status number could go, Rack's own symbol table for Rails. */
858
+ /** The number for each status name the library accepts in place of a number. For Rails this is Rack's symbol table. */
592
859
  statusCodeNames?: Record<string, number>;
860
+ /** The call whose block responds differently for each format the request asks for, Rails' `respond_to`. */
861
+ formatSwitch?: RbFormatSwitch;
593
862
  /**
594
- * The methods every controller gets without defining them, which for
595
- * Rails is `params`, `render` and the rest of what `ActionController`
596
- * mixes in. An action's effect list is there to show what the action
597
- * reaches in the project, and nothing in the project defines these,
598
- * so a receiverless call to one of them is left off that list.
863
+ * Methods every controller inherits from the library, which for Rails
864
+ * are `params`, `render` and the rest of what `ActionController` mixes
865
+ * in. Nothing in the project defines them, so a receiverless call to
866
+ * one is left off the action's effect list.
599
867
  */
600
868
  inheritedMethodNames?: string[];
601
- /** Absolute path of the file this pattern's own routing came from, for the one gap `routingGaps` may report. */
869
+ /** Absolute path of the routes file this pattern read, used in the gap `routingGaps` can report. */
602
870
  routesFile: string;
603
- /** The method and path a project's own routing gives one controller's action, or null when that action has none. */
604
- routeFor: (controllerQualifiedName: string, actionName: string) => {
605
- method: string;
606
- path: string;
607
- } | null;
608
- /** One message per routing declaration kind this pattern's reading left uncovered. A pure read: calling it again gives the same list. */
871
+ /**
872
+ * Every method and path the project's routing gives one controller
873
+ * action, in the order they are declared, or none.
874
+ */
875
+ routesFor: (controllerQualifiedName: string, actionName: string) => readonly ControllerRoute[];
876
+ /**
877
+ * Whether the framework also serves a route with `.{format}` after its
878
+ * path, as Rails serves `/search.json` from `get "search"`. A route
879
+ * marked `formatless`, and one whose path ends in a slash, get none.
880
+ */
881
+ optionalFormat?: boolean;
882
+ /** One message for each kind of routing declaration the pattern could not read. Calling it again returns the same list. */
609
883
  routingGaps?: () => readonly string[];
610
- /** The class-level calls the library gives a controller for running one of its own methods around every action, Rails' `before_action` and `rescue_from`. */
884
+ /** The class-level calls that run one of the controller's own methods around its actions, Rails' `before_action` and `rescue_from`. */
611
885
  filters?: RbControllerFilter[];
612
886
  }
613
887
  /**
614
- * A call in a controller's class body naming a method the library runs
615
- * around the action, and which actions that reaches. A class inherits
616
- * what its ancestors declared, so a filter on `ApplicationController`
617
- * reaches every action in the project.
888
+ * A class-body call that registers a method for the library to run around
889
+ * a controller's actions. A class inherits what its ancestors register, so
890
+ * a filter on `ApplicationController` applies to every action in the
891
+ * project.
618
892
  */
619
893
  interface RbControllerFilter {
620
894
  /** The call as a controller writes it, `before_action`. */
621
895
  name: string;
622
896
  /**
623
- * Where the call names the method: `argument` for the leading symbol
897
+ * Where the call takes the method: `argument` for the leading symbol
624
898
  * of `before_action :require_login`, `withKeyword` for the `with:` of
625
899
  * `rescue_from ActiveRecord::RecordNotFound, with: :not_found`.
626
900
  */
627
901
  methodFrom: "argument" | "withKeyword";
628
902
  /** Set when the library runs the method only for an action that raised, as `rescue_from` does. */
629
903
  onThrow?: boolean;
630
- /** The name of the call that takes a filter back off, `skip_before_action` for `before_action`. */
904
+ /** The call that removes the filter again, `skip_before_action` for `before_action`. */
631
905
  skippedBy?: string;
632
- /** The keywords that narrow a filter to some of the actions, Rails' `only` and `except`. */
906
+ /** The keywords that limit a filter to some of the actions, Rails' `only` and `except`. */
633
907
  actionKeywords?: {
634
908
  include: string;
635
909
  exclude: string;
636
910
  };
637
911
  }
638
912
  /**
639
- * One call an action writes to send a response, and where that call takes
640
- * the status. A call may take it both ways, and then the keyword wins,
641
- * as it does in Rails for `head :ok, status: :created`.
913
+ * A call an action makes to send a response, and where that call takes
914
+ * the status. When a call passes the status both ways the keyword wins,
915
+ * as in Rails for `head :ok, status: :created`.
642
916
  */
643
917
  interface RbStatusCall {
644
918
  /** The call's own name as an action writes it, `render` for Rails. */
@@ -648,54 +922,83 @@ interface RbStatusCall {
648
922
  /** The index of the positional argument giving the status, 0 for Rails' `head :no_content`. */
649
923
  statusArgument?: number;
650
924
  /**
651
- * The status this call sends when the action writes none. Rails'
652
- * `redirect_to` sends 302 where its `render` sends the controller's own
653
- * default, so a default declared here wins over `defaultStatusCode`.
925
+ * The status this call sends when the action passes none. Rails'
926
+ * `redirect_to` sends 302, while `render` sends the controller default,
927
+ * so a default declared here takes precedence over `defaultStatusCode`.
654
928
  */
655
929
  defaultStatusCode?: number;
930
+ /** The keyword whose value the library sends as a JSON body, `json` for Rails' `render json:`. */
931
+ bodyKeyword?: string;
932
+ /**
933
+ * Methods that turn the value they are called on into that same JSON,
934
+ * so `render json: { ok: true }.to_json` sends the hash. Only a call
935
+ * with no arguments counts, since `as_json(only: ...)` drops keys.
936
+ */
937
+ bodySerializers?: string[];
938
+ }
939
+ /** One of a library's exception classes. */
940
+ interface LibraryException {
941
+ /** Every class it inherits from, nearest first, up to `Exception`. */
942
+ ancestors: string[];
943
+ /** The status the library responds with when nothing rescues it. */
944
+ status?: number;
945
+ }
946
+ /**
947
+ * A receiverless call whose block responds one way per format the request
948
+ * asks for. Each call on the block's parameter, `format.json { ... }`, is
949
+ * one branch, and the request runs the first one whose format it accepts.
950
+ * When none does, the library raises.
951
+ */
952
+ interface RbFormatSwitch {
953
+ /** The call's own name, `respond_to` for Rails. */
954
+ name: string;
955
+ /** The format that accepts every request, `any` for Rails. */
956
+ matchesEvery?: string;
656
957
  }
657
958
  /** A class or module whose ancestry reaches one of `baseClassNames` declares GraphQL fields through DSL calls in its own body. */
658
959
  interface GraphqlObjectFields {
659
960
  type: "graphqlObjectFields";
660
- /** The base classes the library itself generates. The walk crosses a project's own intermediate bases on its own, so config only has to add a base with another name. */
961
+ /** The base classes the library's generator writes. The walk follows a project's own intermediate bases, so config only needs to add a base with a different name. */
661
962
  baseClassNames: string[];
662
- /** The directory a wiring keyword's referenced class is looked up under. That is the project's own layout, not the library's. */
963
+ /** The directory a wiring keyword's referenced class is looked up under, which depends on the project's layout. */
663
964
  root: string;
965
+ /** Other directories the project autoloads constants from, each read as the top of the namespace, after `root`. */
966
+ autoloadRoots?: string[];
664
967
  pathConvention: ConstantPathConvention;
665
- /** Acronyms the project registers with the inflector, which the path convention keeps as one word. */
968
+ /** Acronyms the project registers with the inflector. The path convention keeps each one as one word. */
666
969
  acronyms?: string[];
667
970
  /** The DSL call declaring one schema field in an object type's body. */
668
971
  fieldCallName: string;
669
972
  /** The DSL call declaring a referenced class's own return type. */
670
973
  typeCallName: string;
671
974
  argumentCallName: string;
672
- /** Keywords whose value gives a class to read the declared contract from, one hop away. They are tried in the order listed. */
975
+ /** Keywords whose value is a class to read the declared contract from, one hop away. They are tried in the order listed. */
673
976
  wiringKeywords: string[];
674
- /** The method a wired class defines to resolve the field it is wired to. Library-defined. */
977
+ /** The method a wired class defines to resolve the field it is wired to. */
675
978
  resolverMethodName: string;
676
- /** The library's own classes that a project's class chain ends at. Library-defined. */
979
+ /** The library's own classes that a project's class chain ends at. */
677
980
  ancestryRootClassNames: string[];
678
- /** Keyword on an argument call saying whether the argument is required. Library-defined. */
981
+ /** The keyword on an argument call that says whether the argument is required. */
679
982
  requiredKeyword: string;
680
- /** What an argument means when it does not write the required keyword at all. */
983
+ /** Whether an argument is required when its call does not pass the required keyword. */
681
984
  requiredDefault: boolean;
682
985
  /** The keyword that overrides `camelizeDefault` for a single name. */
683
986
  camelizeKeyword: string;
684
- /** Whether a symbol's snake_case name is exposed camelCased when the call itself does not say. */
987
+ /** Whether a snake_case symbol is exposed in camelCase when the call does not say. */
685
988
  camelizeDefault: boolean;
686
- /** Only the names the library itself accepts in a type position belong here. */
989
+ /** The scalar names the library accepts in a type position, and the shape of each. List only the library's own. */
687
990
  scalars: Record<string, TypeShape>;
688
- /** Module prefixes the same scalars can also be written under when someone spells out the full path. */
991
+ /** Module prefixes a project can write in front of those scalar names when it spells out the full path. */
689
992
  scalarNamePrefixes: string[];
690
993
  typeNameConvention: GraphqlTypeNameConvention;
691
994
  /**
692
995
  * A base class that changes a mutation's wire contract. When a wired
693
- * class's ancestry reaches `ancestorClassName`, the library wraps
694
- * every declared argument into one input-object argument called
996
+ * class's ancestry reaches `ancestorClassName`, the library wraps every
997
+ * declared argument into one input-object argument called
695
998
  * `argumentName` and adds `extraFields` to it. graphql-ruby's
696
- * RelayClassicMutation is the class this describes: on the wire the
697
- * mutation takes a single required `input`, whose fields are the
698
- * declared arguments plus an optional `clientMutationId`.
999
+ * RelayClassicMutation does this: on the wire the mutation takes a
1000
+ * single required `input`, whose fields are the declared arguments plus
1001
+ * an optional `clientMutationId`.
699
1002
  */
700
1003
  argumentWrapping?: {
701
1004
  ancestorClassName: string;
@@ -708,79 +1011,166 @@ interface GraphqlObjectFields {
708
1011
  }
709
1012
 
710
1013
  /**
711
- * storage.ts: which calls in a body talk to the database, for Ruby.
1014
+ * Finds the calls in a Ruby body that read or write the database.
712
1015
  *
713
- * One rule covers both ways a call is written. It is database work when the
714
- * pack lists its method as a read or a write, the class behind its receiver
715
- * reaches a base class the pack lists, and the project does not declare that
716
- * method itself. A chain counts once, at the outermost call the library
717
- * defines, and every other call is left to the reach walk. The README says
718
- * why ancestry.
1016
+ * A call is database work when the pack lists its method as a read or a
1017
+ * write, the class behind its receiver reaches a base class the pack
1018
+ * lists, and the project does not declare that method itself. A chain
1019
+ * counts once, at the outermost call the library defines. Every other
1020
+ * call is left to the reach walk.
719
1021
  *
720
- * The class behind the receiver comes from the constant bindings, or from the
721
- * resolution rules. A loader pattern adds calls given the model, and a raw
722
- * SQL pattern adds calls handed a statement, both reported through here.
1022
+ * The receiver's class comes from the constant bindings when the chain
1023
+ * starts at a constant, and from the resolution rules otherwise. Loader
1024
+ * calls and raw SQL calls are reported through here as well.
723
1025
  */
724
1026
 
725
1027
  interface RbStorageOptions {
726
1028
  readonly facts: Database;
727
1029
  readonly patterns: readonly RbStoragePattern[];
728
1030
  readonly loaders?: readonly RbLoaderPattern[];
729
- /** What a pack says about calls that take SQL the project wrote itself. */
1031
+ /** The pack declarations for calls that take SQL the project wrote itself. */
730
1032
  readonly rawSql?: readonly RbRawSqlPattern[];
1033
+ /** Told the value each effect's columns are given, when somebody listens. */
1034
+ readonly statesSlots?: (effect: Effect, slots: readonly SlotValue[]) => void;
1035
+ }
1036
+ /** A column a storage effect writes or picks rows by, and the value passed for it. */
1037
+ interface SlotValue {
1038
+ slot: "field" | "selector";
1039
+ name: string;
1040
+ value: Node;
731
1041
  }
732
1042
  /**
733
1043
  * Whether the recognizer records this call as database work, so a walk
734
- * need not report it as a gap. `enclosing` is the method the call is
735
- * written in, which is what tells one body's locals from the next.
1044
+ * does not need to report it as a gap. `enclosing` is the method the call
1045
+ * is written in, which keeps one method's locals apart from another's.
736
1046
  */
737
1047
  declare function storageClaims(call: Node, file: string, options: RbStorageOptions, enclosing?: Node | null): boolean;
738
1048
  /**
739
- * The database work a body does, one effect per chain. A chain is one thing
740
- * the code does, so `Order.where(id: 1).first` counts once. `file` is the
741
- * absolute path the calls were read from, which the constant bindings key on.
742
- * `enclosing` is the method the calls were read from, or null outside one.
1049
+ * The database work a body does, one effect per chain, so
1050
+ * `Order.where(id: 1).first` counts once. `file` is the absolute path the
1051
+ * calls were read from, which the constant bindings are keyed on.
1052
+ * `enclosing` is the method the calls are in, or null outside one.
743
1053
  */
744
1054
  declare function storageEffects(calls: readonly Node[], file: string, options: RbStorageOptions, enclosing?: Node | null): Effect[];
745
1055
 
746
- /** The methods a pack declared the library defines itself, so a receiverless call to one of them is not work the project does. */
1056
+ /**
1057
+ * The calls a body makes, as invocation effects. A call written under an
1058
+ * `if` records that test as a precondition, and the IR treats a call
1059
+ * with preconditions as one that does not always run.
1060
+ */
1061
+
1062
+ /** Methods the pack says the library defines, so a receiverless call to one of them is not project work. */
747
1063
  type InheritedMethods = ReadonlySet<string>;
748
1064
 
749
- /** Parsed files by absolute path, so a class that several fields refer to is only parsed once. */
1065
+ /**
1066
+ * The shared groups a run's test files define, and where each one is
1067
+ * included. RSpec's `shared_examples "x"` is written once, often in a
1068
+ * support file, and `it_behaves_like "x"` in a spec runs its examples
1069
+ * there. An include finds its shared group by name, in its own file
1070
+ * first and then anywhere in the run.
1071
+ *
1072
+ * A name read inside a shared group that the group does not define
1073
+ * comes from whoever includes it. The value facts key such a name on
1074
+ * the shared group, and `emitSharedGroupBinds` binds it to what each
1075
+ * include site has under that name. Two includers that give it two
1076
+ * values leave it unsettled, since which one ran is not known.
1077
+ */
1078
+
1079
+ interface SharedDefinition {
1080
+ readonly file: string;
1081
+ readonly call: Node;
1082
+ readonly name: string;
1083
+ }
1084
+ interface IncludeSite {
1085
+ readonly file: string;
1086
+ readonly call: Node;
1087
+ /** The groups around the include, outermost first. */
1088
+ readonly groups: readonly Node[];
1089
+ /** Whether the shared examples run in a group of their own, as `it_behaves_like` runs them. */
1090
+ readonly nested: boolean;
1091
+ readonly definition: SharedDefinition;
1092
+ }
1093
+ /** A test file and the pattern that reads it. */
1094
+ interface PatternedFile {
1095
+ readonly file: string;
1096
+ readonly root: Node;
1097
+ readonly pattern: RbTestCases;
1098
+ }
1099
+ declare class SharedGroupIndex {
1100
+ private readonly db;
1101
+ private readonly definitions;
1102
+ private readonly definitionsByNode;
1103
+ private readonly sitesByNode;
1104
+ private readonly sitesByDefinition;
1105
+ private readonly filesByTree;
1106
+ constructor(db: Database, files: readonly PatternedFile[]);
1107
+ /** The include this call makes, or null when it makes none or names no shared group the run defines. */
1108
+ includedAt(call: Node): IncludeSite | null;
1109
+ /** The shared group this call defines, or null. */
1110
+ definitionAt(call: Node): SharedDefinition | null;
1111
+ includersOf(definition: SharedDefinition): readonly IncludeSite[];
1112
+ /** The file a node from a test or support file is written in. */
1113
+ fileOf(node: Node): string | null;
1114
+ private recordIncludes;
1115
+ private recordInclude;
1116
+ private nameOf;
1117
+ private noteRead;
1118
+ }
1119
+
1120
+ /**
1121
+ * Discovers the units a Ruby file defines: GraphQL fields declared through
1122
+ * a class DSL, controller actions, and client calls.
1123
+ *
1124
+ * A field's boundary binding and declared contract come from the
1125
+ * arguments of its own DSL call. A field written with one of the pack's
1126
+ * wiring keywords declares no type of its own, so its contract and the
1127
+ * method behind it are read from the class the keyword points at, one hop
1128
+ * away. DESIGN.md describes what a field's summary says about the method
1129
+ * behind it and where the reading stops.
1130
+ */
1131
+
1132
+ /**
1133
+ * What one run has read from the disk: parsed files by absolute path, so
1134
+ * a class that several fields refer to is only parsed once, and what its
1135
+ * constant lookups have found.
1136
+ */
750
1137
  interface FileCache {
751
1138
  get(absPath: string): Promise<Node | null>;
1139
+ readonly constantFiles: ConstantFileCache;
752
1140
  }
753
- declare function createFileCache(parse: (source: string) => Promise<Node>, readFile: (absPath: string) => string | null): FileCache;
1141
+ declare function createFileCache(parse: (source: string, absPath: string) => Promise<Node>, readFile: (absPath: string) => string | null): FileCache;
754
1142
  /**
755
- * What the run's packs said that reading a method body needs. An
756
- * options object that already declares both fields, `DiscoveryOptions`
757
- * among them, is one of these and can be passed straight through.
1143
+ * What reading a method body needs from the run's packs. Any options
1144
+ * object that has these fields, `DiscoveryOptions` among them, can be
1145
+ * passed straight through.
758
1146
  */
759
1147
  interface BodyReadOptions {
760
- /** The run's own facts, which the value evaluator reads a name through. Absent in a test that builds one file by hand. */
1148
+ /** The run's facts, which the value evaluator reads names through. Absent in a test that builds one file by hand. */
761
1149
  readonly facts?: Database | undefined;
762
- /** What a pack needs to say a call talks to the database. Absent when no pack does. */
1150
+ /** The storage patterns that decide whether a call reads or writes the database. Absent when no pack declares any. */
763
1151
  readonly storage?: RbStorageOptions | undefined;
764
- /** The methods every pack in the run said its own library defines, which are left off an effect list. */
1152
+ /** The methods the run's packs declare their libraries define. Calls to them are left off an effect list. */
765
1153
  readonly inheritedMethods?: InheritedMethods | undefined;
766
- /** The calls every pack in the run said run their block as part of the body around it. */
1154
+ /** The calls whose block the run's packs declare runs as part of the surrounding body. */
767
1155
  readonly bodyBlocks?: BodyBlocks | undefined;
768
- /** What each class defines under a name the source computes, by class key. */
1156
+ /** The methods each class defines under a name computed at run time, by class key. */
769
1157
  readonly dynamicNames?: DynamicNames | undefined;
770
1158
  }
771
1159
  interface DiscoveryOptions extends BodyReadOptions {
772
1160
  packs: RubyPack[];
773
1161
  /** Repo-relative or absolute path recorded on each summary's `location.file`. */
774
1162
  filePath: string;
775
- /** Absolute path of the file being read, for a block's own `ReachedBody.file`. Falls back to `filePath` when nothing was written to disk. */
1163
+ /** Absolute path of the file being read, used for `ReachedBody.file`. Falls back to `filePath` for a source that is not on disk. */
776
1164
  absoluteFile?: string;
777
- /** What a summary's `location.file` says for a file other than the one being read, which a controller's filters need when an ancestor defines them. */
1165
+ /** The `location.file` to record for another file, needed when a controller's filters come from an ancestor in another file. */
778
1166
  displayPathOf?: (absolute: string) => string;
779
1167
  cache: FileCache;
780
- /** Called once per discovered unit whose own body is a method this run can follow calls out of, so the reach walk has a place to start. */
1168
+ /** Called once for each discovered unit whose body is a method, so the reach walk can start from it. */
781
1169
  onReachSeed?: (raw: RawCodeStructure, seed: ReachSeed) => void;
1170
+ /** The shared groups the run's test files define, when a pack reads tests. */
1171
+ sharedGroups?: SharedGroupIndex | undefined;
782
1172
  }
783
- /** The method behind a discovered unit's own body, and where it lives, so the reach walk can start there the way it starts at a `def` it found directly. */
1173
+ /** The method behind a discovered unit, and where it is, so the reach walk can start there as it does at any `def`. */
784
1174
  interface ReachSeed {
785
1175
  readonly file: string;
786
1176
  readonly node: Node;
@@ -790,16 +1180,16 @@ interface ReachSeed {
790
1180
  declare function discoverUnits(root: Node, options: DiscoveryOptions): Promise<RawCodeStructure[]>;
791
1181
 
792
1182
  /**
793
- * associations.ts: reading `has_many :statuses` out of a model's body.
1183
+ * Reads association declarations such as `has_many :statuses` out of a
1184
+ * model's body.
794
1185
  *
795
- * A pack says which calls declare an association. The target is the
796
- * class the `class_name:` keyword gives, or the association's own name
797
- * put through ActiveSupport's default inflections when the declaration
798
- * gives none. That inflected name is written nowhere in the source, so
799
- * the declaration comes with a constant reference of its own and the
800
- * constant bindings settle it from the nesting the model is written in:
801
- * a `has_many :statuses` inside `Admin::Account` reaches `Admin::Status`
802
- * before it reaches `Status`.
1186
+ * A pack says which calls declare an association. The target is the class
1187
+ * `class_name:` gives, or the association's name put through the
1188
+ * inflections when the call gives none. The source never writes that
1189
+ * inflected name, so each declaration gets a constant reference of its
1190
+ * own, and the constant bindings settle it from the model's nesting. A
1191
+ * `has_many :statuses` inside `Admin::Account` reaches `Admin::Status`
1192
+ * before `Status`.
803
1193
  */
804
1194
 
805
1195
  /** One association a class or module body declares. */
@@ -812,11 +1202,17 @@ interface AssociationDeclaration {
812
1202
  readonly target: ConstantReference;
813
1203
  }
814
1204
 
815
- /** A definition every file in the run can see, under the name it is written as. */
1205
+ /**
1206
+ * Binds each Ruby constant reference to the definition behind it. Ruby
1207
+ * has no imports, so there is no `imports` fact to follow, and a
1208
+ * reference is bound directly to its definition.
1209
+ */
1210
+
1211
+ /** A constant definition, which every file in the run can see, under its qualified name. */
816
1212
  interface ConstantDefinition {
817
1213
  readonly qualifiedName: string;
818
1214
  readonly key: string;
819
- /** `class`/`module` opens a constant every file may open again; `X = ...` sets one. */
1215
+ /** A `class` or `module` declaration, which any file may reopen, or an `X = ...` assignment. */
820
1216
  readonly kind: "declaration" | "assignment";
821
1217
  }
822
1218
  /** A constant read somewhere, with the nesting it was read inside. */
@@ -831,69 +1227,79 @@ interface FileConstants {
831
1227
  readonly filePath: string;
832
1228
  readonly definitions: readonly ConstantDefinition[];
833
1229
  readonly references: readonly ConstantReference[];
834
- /** Empty unless a pack in the run said what an association declaration looks like. */
1230
+ /** Empty unless a pack in the run declares association calls. */
835
1231
  readonly associations: readonly AssociationDeclaration[];
836
1232
  }
837
1233
  /**
838
- * Every constant this file defines and every one it reads. One walk, because
839
- * the nesting a reference is written inside is what the walk already knows.
840
- * An association's target is read here too: the name it inflects to has no
841
- * node in the source, and the nesting is what settles which class it means.
1234
+ * Every constant this file defines and every one it reads, in one walk
1235
+ * that tracks the nesting as it goes. An association's target is
1236
+ * collected here too, because the inflected name has no node in the
1237
+ * source and only the nesting says which class it means.
842
1238
  */
843
1239
  declare function collectFileConstants(filePath: string, root: Node, associationCalls?: readonly RbAssociationCalls[], inflections?: RbInflections): FileConstants;
844
1240
  /**
845
- * Link every constant read anywhere in the run to the definition behind it,
846
- * looking outwards from the nesting it was read inside the way Ruby does.
1241
+ * Binds every constant read anywhere in the run to its definition,
1242
+ * looking outwards from the nesting it was read inside, the way Ruby
1243
+ * does.
847
1244
  *
848
- * `binds(refKey, defKey)` is the link itself. `rbConstantFrom(from, to)`
849
- * is the same link at file level, which is the closest thing to an
850
- * import graph a language without imports has.
1245
+ * `binds(refKey, defKey)` records the binding. `importsFile(from, to)`
1246
+ * records the same link between the two files, since reading a constant
1247
+ * another file defines is the nearest thing Ruby has to an import.
851
1248
  *
852
- * `declaresAssociation` is emitted here as well, because an association's
853
- * target is one of those references and the shared rules read the pair
854
- * together.
1249
+ * `declaresAssociation` is emitted here too, because an association's
1250
+ * target is one of these references and the shared rules join the two.
855
1251
  */
856
1252
  declare function emitConstantBindings(db: Database, perFile: Iterable<FileConstants>): void;
857
1253
 
858
1254
  /**
859
- * A node's identity across the whole run. The end is part of it because a
1255
+ * Emits the value facts that the rules in `@suss/resolution` join, for a
1256
+ * Ruby file. The relations and their columns are defined by that package.
1257
+ * The package DESIGN.md describes where Ruby differs from the other
1258
+ * adapters: names keyed on their scope, a bare name that runs a method,
1259
+ * mixins recorded as `extends`, and instance variables as class properties.
1260
+ */
1261
+
1262
+ /**
1263
+ * A node's key across the whole run. It includes the end offset because a
860
1264
  * call and its receiver start at the same offset.
861
1265
  */
862
1266
  declare function nodeId(filePath: string, node: Node): string;
863
1267
  /**
864
- * The key a read of this expression joins on, for a caller that has an
865
- * expression in hand and wants to ask the rules about it. `enclosing` is
866
- * the method the expression is written in, or null outside one.
1268
+ * The key to ask the rules about when reading this expression. `enclosing`
1269
+ * is the method the expression is written in, or null outside one.
1270
+ * `parent` is the node the expression is written in, when the caller has
1271
+ * it, since reading a node's parent off the tree costs a walk from the root.
867
1272
  *
868
- * Two Ruby spellings are keyed on a node rather than on a name. Ruby
869
- * gives `receiver.method` no node of its own, so the method name is
870
- * where the callee is keyed, and a name Ruby runs as a method is the
871
- * call itself.
1273
+ * Two kinds of name are keyed on their node instead of on the name. The
1274
+ * tree has no node for the `receiver.method` part of a call, so the
1275
+ * callee is keyed on the method name's node. A bare name that Ruby runs
1276
+ * as a method is the call itself.
872
1277
  */
873
- declare function readKey(filePath: string, written: Node, enclosing: Node | null): string;
1278
+ declare function readKey(filePath: string, written: Node, enclosing: Node | null, parent?: Node | null): string;
874
1279
  /**
875
- * Walk a file and emit the value facts. A method is walked in its own right,
876
- * so a body's returns and calls belong to the method that wrote them.
1280
+ * Walks a file and emits its value facts. Each method is walked on its
1281
+ * own, so the returns and calls in a body are recorded against the method
1282
+ * they are written in.
877
1283
  */
878
- declare function emitValueFacts(db: Database, filePath: string, root: Node, bodyBlocks?: BodyBlocks): void;
1284
+ declare function emitValueFacts(db: Database, filePath: string, root: Node, bodyBlocks?: BodyBlocks, scopeCalls?: ReadonlySet<string>): void;
879
1285
 
880
1286
  /**
881
- * The session behind `suss ask why` on a Ruby project: a tree-sitter
882
- * parse of the asked-about source, a way to point at the expression
883
- * somebody spelled, and the witness proof of what it resolved to,
884
- * rendered through `@suss/resolution`'s phrases.
1287
+ * The session behind `suss ask why` on a Ruby project. It parses every
1288
+ * file under the root with tree-sitter, finds the expression a question
1289
+ * points at, and renders the witness proof of what that expression
1290
+ * resolved to through `@suss/resolution`'s phrases.
885
1291
  *
886
- * It parses every file under the root and emits the same value and
887
- * constant facts `extractRubyProject` does, keeping a location for
888
- * every fact key so a proof's atoms can point back at source. A
889
- * handle this session hands back pairs a tree-sitter node with the
890
- * file it came from, since a node alone does not say which file
891
- * parsed it.
1292
+ * It emits its facts through the same `RunFacts` an extraction over the
1293
+ * same packs uses, and keeps a location for every fact key so each atom
1294
+ * of a proof can point back at source. A handle pairs a tree-sitter node
1295
+ * with its file, since a node alone does not say which file it came from.
892
1296
  */
893
1297
 
894
1298
  interface RubyWhySessionOptions {
895
- /** The project root, which paths in every answer come out relative to. */
1299
+ /** The project root. Paths in every answer are relative to it. */
896
1300
  dir: string;
1301
+ /** The packs the extraction ran with. A hop only a pack declares, such as `Account.find` giving back an Account, is explained only when that pack is here. */
1302
+ packs?: readonly RubyPack[];
897
1303
  }
898
1304
  /** A found node, paired with the file it was parsed from. */
899
1305
  interface RubyValueHandle {
@@ -918,8 +1324,8 @@ declare class RubyWhySession {
918
1324
  findCallee(file: string, startLine: number, endLine: number, calleeText: string): RubyValueHandle | null;
919
1325
  /**
920
1326
  * Why `value` resolves to what it does: the witness proof, flattened
921
- * to the chain and rendered. Null when the value does not resolve,
922
- * which the caller says in its own words.
1327
+ * to the chain and rendered. Null when the value does not resolve, and
1328
+ * the caller reports that itself.
923
1329
  */
924
1330
  explain(value: RubyValueHandle, options?: {
925
1331
  maxDepth?: number;
@@ -927,38 +1333,59 @@ declare class RubyWhySession {
927
1333
  private locate;
928
1334
  private rootOf;
929
1335
  private pathOf;
930
- /** A file path said relative to the root, or as given when it is outside the root. */
1336
+ /** A file path relative to the root, or unchanged when it is outside the root. */
931
1337
  private displayPath;
932
1338
  }
933
1339
 
934
1340
  /**
935
- * facts.ts: what discovery hands to the shared fact store.
936
- *
937
- * This is the Layer 1 contract: discover units, emit summaries, emit
938
- * these facts.
939
- *
940
- * `entry` reuses the existing relation name and shape, where the unit
941
- * is a pack-discovered entry point, so a Ruby-discovered field is an
942
- * entry the same way a Python route or a TypeScript handler is.
1341
+ * One evaluator per project, over the parsed files and the resolution
1342
+ * facts. A reader passes in a node and gets back the abstract value it
1343
+ * comes down to. A route path or a prefix is then built from that value,
1344
+ * so every way of writing it gives the same result.
943
1345
  *
944
- * `rbRequires(from, to)` records a `require_relative` whose target is a
945
- * file in the run. A plain `require` goes through the load path, which
946
- * this run does not know, so it records nothing.
1346
+ * The facts key a node by file and span, so the evaluator keeps every
1347
+ * file's root to find the node a fact refers to. Without facts, as in a
1348
+ * unit test or when a routes file is read on its own, the evaluator
1349
+ * still follows names within the file through the engine's own scope
1350
+ * walk.
947
1351
  */
948
1352
 
1353
+ interface EvaluatedFile {
1354
+ readonly file: string;
1355
+ readonly root: Node;
1356
+ }
1357
+ interface ProjectNodes {
1358
+ readonly files: readonly EvaluatedFile[];
1359
+ /** Every method definition by its node key, for a resolved callee. */
1360
+ readonly definitions: ReadonlyMap<string, Node>;
1361
+ }
1362
+ /** Registers the parsed project, so reads through `db` can follow the facts back to nodes. */
1363
+ declare function bindEvaluator(db: Database, nodes: ProjectNodes): void;
1364
+ /** Values to use for the parameters of the block or method around `node`, as a caller would supply them. */
1365
+ type ParameterBindings = ReadonlyMap<string, string>;
949
1366
  /**
950
- * The name is part of the key because the range is measured in lines, two
951
- * units can start on the same line, and `entry` is a set, so keying on the range
952
- * alone would drop one of them.
1367
+ * The abstract value `node` comes down to, through the facts when `db`
1368
+ * was bound. With a site, the value when its receiver is the instance
1369
+ * that site made. That evaluation is not memoized.
1370
+ */
1371
+ declare function evaluatedValue(node: Node, db?: Database, bindings?: ParameterBindings, site?: string): Value;
1372
+ /** The one string `node` comes down to, or null when it does not settle on one. */
1373
+ declare function stringValueOf(node: Node, db?: Database, bindings?: ParameterBindings): string | null;
1374
+ /** Every method a file defines, keyed the way the facts key it, for `bindEvaluator`. */
1375
+ declare function methodDefinitionsIn(file: string, root: Node): Map<string, Node>;
1376
+
1377
+ /**
1378
+ * Runs the Ruby adapter over a project: discovers units, emits summaries
1379
+ * in the shared IR, and emits facts.
1380
+ *
1381
+ * It parses every file it is given, emits the run's facts into one
1382
+ * shared `Database`, runs discovery over each file, walks what each unit
1383
+ * reaches, and hands every unit to `assembleSummary` from
1384
+ * `@suss/extractor`. The Python and TypeScript adapters use the same
1385
+ * assembly step, so gap detection is shared across all three languages.
1386
+ * With a cache, a run after an edit replays the files and methods whose
1387
+ * inputs did not change, through the records in `reuse.ts`.
953
1388
  */
954
- declare function unitKey(filePath: string, range: {
955
- start: number;
956
- end: number;
957
- }, name: string): string;
958
- declare function emitEntryFact(db: Database, filePath: string, range: {
959
- start: number;
960
- end: number;
961
- }, name: string): void;
962
1389
 
963
1390
  interface ExtractRubyOptions {
964
1391
  /** Absolute paths of the files to parse and extract. */
@@ -976,9 +1403,22 @@ interface ExtractRubyOptions {
976
1403
  onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
977
1404
  /** Absolute. `<projectRoot>/.suss/cache` by default; `null` turns it off. */
978
1405
  cacheDir?: string | null;
979
- /** What to do with gaps, which composing a controller's filters can add one of. */
1406
+ /** How to handle gaps. Composing a controller's filters into its actions can add one. */
980
1407
  gapHandling?: ExtractorOptions["gapHandling"];
1408
+ /** The modules the project lists in `suss.json`, with absolute paths. */
1409
+ modules?: readonly DeclaredModule[];
1410
+ /** Parses an earlier run in this process left, for a file whose text is unchanged. */
1411
+ keptParses?: KeptRubyParses;
981
1412
  }
1413
+ type KeptRubyParses = KeptParses<Node>;
1414
+ /** A holder for parses between runs, which frees each tree it lets go of. */
1415
+ declare function keptRubyParses(): KeptRubyParses;
1416
+ /**
1417
+ * Parses these files into `kept` before any run needs them. A run served
1418
+ * whole from the cache parses nothing, so a process that keeps parses
1419
+ * calls this while nobody is waiting.
1420
+ */
1421
+ declare function parseRubyAhead(files: readonly string[], kept: KeptRubyParses): Promise<void>;
982
1422
  interface ExtractRubyResult {
983
1423
  summaries: BehavioralSummary[];
984
1424
  facts: Database;
@@ -991,25 +1431,24 @@ interface FileFactsOptions {
991
1431
  }
992
1432
  /**
993
1433
  * The facts for a single parsed file, with the evaluator bound to them,
994
- * for a caller that has one file and no project. A pack's own tests need
995
- * these: what built a receiver is an answer the rules give, and without
996
- * facts they have nothing to give it from. A project run emits the same
997
- * facts across every file at once, so a name written in another file
998
- * resolves there and never here.
1434
+ * for a caller with one file and no project, such as a pack's tests. The
1435
+ * rules need facts to work out what built a receiver. A project run emits
1436
+ * the same facts for every file at once, so a name defined in another
1437
+ * file resolves there but not here.
999
1438
  */
1000
1439
  declare function factsForFile(options: FileFactsOptions): Database;
1001
1440
  declare function extractRubyProject(options: ExtractRubyOptions): Promise<ExtractRubyResult>;
1002
- /** Every `.rb` file under `root`, depth-first, skipping the usual non-source directories. */
1441
+ /** Every `.rb` file under `root`, sorted, skipping vendored, temporary and VCS directories. */
1003
1442
  declare function findRubyFiles(root: string): string[];
1004
1443
 
1005
1444
  /**
1006
1445
  * Evidence for drafting a graphql-ruby `extends-base` dependency stub:
1007
- * every `require`/`require_relative` of the asked package, and every
1008
- * project class whose superclass is spelled from it. Discovery
1009
- * only recognizes a class through `baseClassNames`' literal names (see
1010
- * discovery.ts's `reachesConfiguredBase`), so a project's own base
1011
- * under a renamed namespace, or one that extends the library's own
1012
- * class directly, has to state the exact name.
1446
+ * every `require` or `require_relative` of the requested package, and
1447
+ * every project class whose superclass name is spelled from it.
1448
+ * Discovery only recognizes a class by the literal names in
1449
+ * `baseClassNames`, so a project's own base under a renamed namespace,
1450
+ * or a class that extends the library's class directly, has to be
1451
+ * listed by its exact name.
1013
1452
  */
1014
1453
  interface RubyRequireSite {
1015
1454
  target: string;
@@ -1043,68 +1482,30 @@ declare function rubyStubEvidence(options: RubyStubEvidenceOptions): Promise<Rub
1043
1482
  interface TypeReadContext {
1044
1483
  /** The `Module.nesting` chain in effect, innermost first. */
1045
1484
  nesting: readonly string[];
1046
- /** Every class the surrounding file defines, by qualified name, so we can tell when one shadows a scalar. */
1485
+ /** Every class the surrounding file defines, by qualified name. A project class shadows a scalar with the same name. */
1047
1486
  knownClasses: ReadonlySet<string>;
1048
1487
  scalars: Readonly<Record<string, TypeShape>>;
1049
1488
  scalarNamePrefixes: readonly string[];
1050
1489
  typeNameConvention: GraphqlTypeNameConvention;
1051
1490
  }
1052
1491
  /**
1053
- * Null means the field abstains. Callers must not fall back to `unknown`, which
1054
- * would read as a contract nobody actually declared.
1492
+ * Returns null when the type cannot be read, and the field then abstains.
1493
+ * A caller must not fall back to `unknown`, because that would report a
1494
+ * contract nobody declared.
1055
1495
  *
1056
- * A bare `constant` is checked against `ctx.nesting` before the scalar table,
1057
- * because a project class at some level of `Module.nesting` is what Ruby itself
1058
- * finds first. A compound `scope_resolution` path is absolute and cannot be
1059
- * shadowed, so it skips that check.
1496
+ * A bare `constant` is checked against `ctx.nesting` before the scalar
1497
+ * table, because Ruby finds a project class at any level of
1498
+ * `Module.nesting` first. A compound `scope_resolution` path is absolute
1499
+ * and cannot be shadowed, so it skips that check.
1060
1500
  */
1061
1501
  declare function typeShapeFromNode(node: Node, ctx: TypeReadContext): TypeShape | null;
1062
1502
 
1063
1503
  /**
1064
- * One evaluator per project, over the parsed files and the resolution
1065
- * facts. A reader hands in a node and gets back the abstract value it
1066
- * comes down to; a route path or a prefix is then spelled from that
1067
- * value rather than read off one syntax shape.
1068
- *
1069
- * The facts key a node by file and span, so the evaluator keeps every
1070
- * file's root to find the node a fact refers to. Without facts,
1071
- * which is how a unit test or a routes file on its own runs, the
1072
- * evaluator still follows names within the file through the engine's
1073
- * own scope walk.
1074
- */
1075
-
1076
- interface EvaluatedFile {
1077
- readonly file: string;
1078
- readonly root: Node;
1079
- }
1080
- interface ProjectNodes {
1081
- readonly files: readonly EvaluatedFile[];
1082
- /** Every method definition by its node key, for a resolved callee. */
1083
- readonly definitions: ReadonlyMap<string, Node>;
1084
- }
1085
- /** Register the parsed project, so reads through `db` can follow the facts back to nodes. */
1086
- declare function bindEvaluator(db: Database, nodes: ProjectNodes): void;
1087
- /** Strings to read for the parameters of the block or method `node` is written in, the way a caller would supply them. */
1088
- type ParameterBindings = ReadonlyMap<string, string>;
1089
- /**
1090
- * The abstract value `node` comes down to, through the facts when `db`
1091
- * was bound. With a site, what it comes down to when the receiver
1092
- * behind it is the instance that site made; that run is not memoized.
1093
- */
1094
- declare function evaluatedValue(node: Node, db?: Database, bindings?: ParameterBindings, site?: string): Value;
1095
- /** The one string `node` comes down to, or null when it does not settle on one. */
1096
- declare function stringValueOf(node: Node, db?: Database, bindings?: ParameterBindings): string | null;
1097
- /** Every method a file defines, keyed the way the facts key it, for `bindEvaluator`. */
1098
- declare function methodDefinitionsIn(file: string, root: Node): Map<string, Node>;
1099
-
1100
- /**
1101
- * version.ts: this adapter's own half of the cache key.
1102
- *
1103
- * `ADAPTER_VERSION` is the hand-bumped semver. Bump it on any change
1104
- * that affects extraction output: IR shape, discovery semantics,
1105
- * terminal classification, anything that would invalidate previously
1106
- * cached summaries.
1504
+ * The adapter's part of the cache key. Bump `ADAPTER_VERSION` by hand on
1505
+ * any change to extraction output, such as a change to the IR, to which
1506
+ * units are discovered, or to how a terminal is classified. A summary
1507
+ * cached under the old version is then thrown away.
1107
1508
  */
1108
1509
  declare const ADAPTER_VERSION = "0.1.0";
1109
1510
 
1110
- export { ADAPTER_VERSION, type AncestorEntry, type AncestorLookup, type Ancestry, type CallArgs, type ClassInfo, type ConstantPathConvention, type ControllerActions, type DiscoveryOptions, type EvaluatedFile, type ExtractRubyOptions, type ExtractRubyResult, type FileCache, type FileConstants, type FileFactsOptions, type GraphqlObjectFields, type GraphqlTypeNameConvention, type MethodLookup, type ParameterBindings, type ProjectNodes, type Range, type RbAddressingCall, type RbArgumentPlace, type RbBodyBlock, type RbInflections, type RbRawSqlPattern, type RbRowCall, type RbStatusCall, type RbStorageOptions, type RbStoragePattern, type ReachedBody, type RubyDiscoveryPattern, type RubyExtendsSite, type RubyPack, type RubyRequireSite, type RubyStubEvidence, type RubyStubEvidenceOptions, type RubyValueHandle, RubyWhySession, type RubyWhySessionOptions, type TypeReadContext, ancestryOf, bindEvaluator, bodyStatements, booleanLiteralValue, collectFileConstants, createFileCache, discoverUnits, emitConstantBindings, emitEntryFact, emitValueFacts, evaluatedValue, extractRubyProject, factsForFile, field, findRubyFiles, graphqlTypeNameFromQualified, hashKeySymbolName, inheritedStatements, methodDefinitionsIn, methodInAncestry, nestedStatements, nodeId, parseRuby, parseRubySync, preloadRubyGrammar, qualifyConstantRef, reachDefinition, readCallArgs, readKey, resolveConstantFile, rubyStubEvidence, runStatements, storageClaims, storageEffects, stringLiteralValue, stringValueOf, symbolValue, typeShapeFromNode, underscoreConstantPath, unitKey, walkClasses, walkDefinitions };
1511
+ export { ADAPTER_VERSION, type AncestorEntry, type AncestorLookup, type Ancestry, type CallArgs, type ClassInfo, type ConstantFileCache, type ConstantPathConvention, type ControllerActions, type ControllerRoute, type DiscoveryOptions, type EvaluatedFile, type ExtractRubyOptions, type ExtractRubyResult, type FileCache, type FileConstants, type FileFactsOptions, type GraphqlObjectFields, type GraphqlTypeNameConvention, type KeptRubyParses, type LibraryException, type MethodLookup, type ParameterBindings, type ProjectNodes, type Range, type RbAddressingCall, type RbArgumentPlace, type RbBodyBlock, type RbExpectations, type RbFactories, type RbFactoryBuilder, type RbFormatSwitch, type RbInflections, type RbPredicateMatcher, type RbRawSqlPattern, type RbRowCall, type RbSharedInclude, type RbStatusCall, type RbStorageOptions, type RbStoragePattern, type RbTestCases, type RbTestClasses, type RbTestMocks, type ReachedBody, type RubyDiscoveryPattern, type RubyExtendsSite, type RubyPack, type RubyRequireSite, type RubyStubEvidence, type RubyStubEvidenceOptions, type RubyValueHandle, RubyWhySession, type RubyWhySessionOptions, type TypeReadContext, ancestryOf, bindEvaluator, bodyStatements, booleanLiteralValue, children, collectFileConstants, createConstantFileCache, createFileCache, discoverUnits, emitConstantBindings, emitValueFacts, evaluatedValue, extractRubyProject, factsForFile, field, findRubyFiles, graphqlTypeNameFromQualified, hashKeySymbolName, inheritedStatements, keptRubyParses, methodDefinitionsIn, methodInAncestry, nestedStatements, nodeId, parseRuby, parseRubyAhead, parseRubySync, preloadRubyGrammar, qualifyConstantRef, reachDefinition, readCallArgs, readKey, resolveConstantFile, rubyStubEvidence, runStatements, storageClaims, storageEffects, stringLiteralValue, stringValueOf, symbolValue, typeShapeFromNode, underscoreConstantPath, walkClasses, walkDefinitions };