@suss/adapter-ruby 0.33.1 → 0.34.0

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