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