@suss/recognize 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 CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and says where the two disagree.
4
4
 
5
- Write a pack as data. A pack states where a match starts, which methods
6
- count, and what each one reads or writes; this package compiles that
7
- into the recognizer hooks the adapters already call, and any adapter
8
- that implements the executor ops can run it.
5
+ Write a pack as data. A pack declares where a match starts, which
6
+ methods count, and what each one reads or writes. This package compiles
7
+ that declaration into the recognizer hooks the adapters already call,
8
+ and any adapter that implements the executor ops can run it.
9
9
 
10
10
  ```ts
11
11
  import { declaredBy, pack, storageCalls } from "@suss/recognize";
@@ -29,9 +29,9 @@ export default pack("redis", [calls], {
29
29
  });
30
30
  ```
31
31
 
32
- Nothing here imports a syntax tree, so the same declaration drives the
33
- TypeScript adapter today and the Python and Ruby adapters once they
34
- implement the ops.
32
+ This package never imports a syntax tree. The same declaration runs on
33
+ the TypeScript adapter today, and will run on the Python and Ruby
34
+ adapters once they implement the ops.
35
35
 
36
36
  ## More
37
37
 
package/dist/ast.d.cts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { CallOps } from '@suss/extractor';
2
- import { L as LinkFunction } from './chain-CVG6Jnr-.cjs';
2
+ import { L as LinkFunction } from './chain-DYU9ZdzF.cjs';
3
3
  import '@suss/behavioral-ir';
4
4
 
5
5
  /**
6
- * The floor: a link that reads the adapter's own syntax tree.
6
+ * A link that reads the adapter's own syntax tree, for a pack the ops
7
+ * cannot serve.
7
8
  *
8
- * A pack that outgrows the ops still gets the tree, and this is the
9
- * only door to it. Going through a separate import puts the decision in
10
- * the diff, and `astLink` marks the link so the pack health report says
11
- * which packs went through, the way it says which declare no version.
12
- * Allowed, observable, and slightly inconvenient.
9
+ * This is the only way a pack reaches the tree. It needs a separate
10
+ * import, so a pack that starts using it shows up in the diff. `astLink`
11
+ * also marks the link, and the pack health report lists every pack that
12
+ * used it, the same way it lists packs that declare no version.
13
13
  *
14
14
  * The node comes back as `unknown` because this package does not read
15
15
  * any one language. A pack casts it to the adapter it was written
@@ -17,8 +17,8 @@ import '@suss/behavioral-ir';
17
17
  */
18
18
 
19
19
  /**
20
- * A link answered by reading the call's own node. `read` is handed the
21
- * node and whatever the link would have been handed anyway.
20
+ * A link that reads the call's own node. `read` gets the node first,
21
+ * then the arguments the link would have received anyway.
22
22
  */
23
23
  declare function astLink<A extends unknown[], R>(read: (node: unknown, ...given: A) => R): LinkFunction<[...A, CallOps], R>;
24
24
 
package/dist/ast.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { CallOps } from '@suss/extractor';
2
- import { L as LinkFunction } from './chain-CVG6Jnr-.js';
2
+ import { L as LinkFunction } from './chain-DYU9ZdzF.js';
3
3
  import '@suss/behavioral-ir';
4
4
 
5
5
  /**
6
- * The floor: a link that reads the adapter's own syntax tree.
6
+ * A link that reads the adapter's own syntax tree, for a pack the ops
7
+ * cannot serve.
7
8
  *
8
- * A pack that outgrows the ops still gets the tree, and this is the
9
- * only door to it. Going through a separate import puts the decision in
10
- * the diff, and `astLink` marks the link so the pack health report says
11
- * which packs went through, the way it says which declare no version.
12
- * Allowed, observable, and slightly inconvenient.
9
+ * This is the only way a pack reaches the tree. It needs a separate
10
+ * import, so a pack that starts using it shows up in the diff. `astLink`
11
+ * also marks the link, and the pack health report lists every pack that
12
+ * used it, the same way it lists packs that declare no version.
13
13
  *
14
14
  * The node comes back as `unknown` because this package does not read
15
15
  * any one language. A pack casts it to the adapter it was written
@@ -17,8 +17,8 @@ import '@suss/behavioral-ir';
17
17
  */
18
18
 
19
19
  /**
20
- * A link answered by reading the call's own node. `read` is handed the
21
- * node and whatever the link would have been handed anyway.
20
+ * A link that reads the call's own node. `read` gets the node first,
21
+ * then the arguments the link would have received anyway.
22
22
  */
23
23
  declare function astLink<A extends unknown[], R>(read: (node: unknown, ...given: A) => R): LinkFunction<[...A, CallOps], R>;
24
24
 
@@ -2,22 +2,21 @@ import { MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
2
2
  import { ReceiverOrigin, ValueOps, CallOps, UnsettledName } from '@suss/extractor';
3
3
 
4
4
  /**
5
- * The spine every pack shares, as a chain of links.
5
+ * The chain of links every declared pack is written as.
6
6
  *
7
7
  * A pack does four jobs, and all four start the same way: match the
8
- * receiver, match the method, read the arguments, then yield. What
9
- * differs is the ending. Recognition yields effects, discovery yields a
10
- * unit, a terminal yields a response write, and a claimed callback
11
- * yields a sub-unit. Two recognition endings are built, one that asks
12
- * the call what it reached and one that reads the statement the call
13
- * was handed; the other three jobs are further members of `Ending`
14
- * with an entry in the compile table.
8
+ * receiver, match the method, read the arguments, then yield. Only the
9
+ * ending differs. Recognition yields effects, discovery yields a unit,
10
+ * a terminal yields a response write, and a claimed callback yields a
11
+ * sub-unit. Only recognition endings exist so far: storage, SQL,
12
+ * message send and unit invoke. Each of the other jobs would be one
13
+ * more member of `Ending` with an entry in the compile table.
15
14
  *
16
- * A link's answer is data wherever it can be. A link given a function
17
- * instead is code, that link alone, and pack health says which ones.
15
+ * A pack writes each link as data where it can. A link written as a
16
+ * function is code, and the pack health report lists those links.
18
17
  */
19
18
 
20
- /** A link whose answer a pack wrote as code rather than as data. */
19
+ /** A link the pack wrote as a function instead of as data. */
21
20
  interface LinkFunction<A extends unknown[], R> {
22
21
  (...args: A): R;
23
22
  /** Set when the function was built through `@suss/recognize/ast`. */
@@ -26,11 +25,10 @@ interface LinkFunction<A extends unknown[], R> {
26
25
  /**
27
26
  * What a match starts from.
28
27
  *
29
- * Most of what the shipped packs match starts from a receiver, and none
30
- * of what discovery matches does: an exported name, a file path, a
31
- * decorator, a parameter's type, a template file beside the module and
32
- * a function's return type each start somewhere else. So the start is
33
- * its own axis, and a receiver origin is one value on it.
28
+ * Every recognition chain starts from a receiver. Discovery starts
29
+ * elsewhere, for example from an exported name or a decorator. The start
30
+ * is a union of its own so a discovery ending can add a member, and a
31
+ * receiver is its only member today.
34
32
  */
35
33
  type MatchStart = FromReceiver;
36
34
  /** A match that starts from the receiver a call is on. */
@@ -48,16 +46,16 @@ interface StartLink {
48
46
  *
49
47
  * A pack that has to read a chain of calls, or a command a call was
50
48
  * handed, states the steps to it and asks the same questions there. The
51
- * steps are data, so a pack that reads three calls still declares three
52
- * links rather than one function that walks.
49
+ * steps are data, so a pack that reads three calls states three steps
50
+ * and never writes a function that walks the calls itself.
53
51
  */
54
52
  type CallStep = ToReceiver | ToArgument;
55
53
  /**
56
54
  * The call the receiver is. With a method, the walk keeps going up the
57
- * receivers until it reaches a call to that method, which is how a pack
58
- * says which hop it means where the shape varies:
55
+ * receivers until it reaches a call to that method. That lets a pack
56
+ * pick the hop it means when the distance varies:
59
57
  * `bucket(b).file(p).download()` and `bucket(b).getFiles()` put the
60
- * bucket a different distance away.
58
+ * bucket at different distances.
61
59
  */
62
60
  interface ToReceiver {
63
61
  readonly to: "receiver";
@@ -73,8 +71,8 @@ interface ToArgument {
73
71
  /**
74
72
  * Where the argument has to have come from, for a step that tries
75
73
  * several. `send(command)` takes one argument and a presigner takes
76
- * two, and the one that matters is the command the SDK declares, so
77
- * the step says so rather than reading whatever it lands on.
74
+ * two. Only the command the SDK declares matters, so the step requires
75
+ * that origin and skips any other argument.
78
76
  */
79
77
  readonly origin?: ReceiverOrigin;
80
78
  }
@@ -112,10 +110,9 @@ interface ContainerArgument {
112
110
  readonly argument: ArgumentPick;
113
111
  }
114
112
  /**
115
- * The container worked out by the pack. The call the chain is about
116
- * comes second so that a rule written over the selector alone, which is
117
- * most of them, ignores it. A rule that has to read the syntax tree
118
- * goes through `astLink`, which is what puts the call to use.
113
+ * The container worked out by the pack. The call comes second so that a
114
+ * rule over the selector alone, as most are, can leave it out. A rule
115
+ * that has to read the syntax tree gets the call through `astLink`.
119
116
  */
120
117
  interface ContainerRule {
121
118
  readonly asks: "container";
@@ -127,10 +124,10 @@ interface AccessPathLink {
127
124
  readonly argument: ArgumentPick;
128
125
  }
129
126
  /**
130
- * Which of the store's namespaces a call reached, when the call says
131
- * rather than the pack. Most clients connect to one namespace and the
132
- * pack states it once, but a BigQuery caller names the dataset on the
133
- * way to the table, so a project reading two datasets would otherwise
127
+ * Which of the store's namespaces a call reached, when the call states
128
+ * it instead of the pack. Most clients connect to one namespace and the
129
+ * pack states it once. A BigQuery caller writes the dataset on the way
130
+ * to the table, and without this a project reading two datasets would
134
131
  * record both accesses under the same scope.
135
132
  */
136
133
  interface ScopeLink {
@@ -148,47 +145,46 @@ interface ContainersLink {
148
145
  readonly asks: "containers";
149
146
  readonly in: OneArgument;
150
147
  /**
151
- * What one of them is. An entry keys the container by name and says
152
- * what the call did there, which is how a batch write is written. A
153
- * name is the container on its own, which is how a call that reads
154
- * several parameters at once is written. Defaults to an entry.
148
+ * How to read each item. An entry keys the container by name and its
149
+ * value says what the call did there, as in a batch write. A name is
150
+ * the container on its own, as in a call that reads several parameters
151
+ * at once. Defaults to an entry.
155
152
  */
156
153
  readonly each?: "entry" | "name";
157
154
  }
158
155
  /**
159
156
  * Where a call states its inputs, when it states them as one object
160
- * rather than as positional arguments. A call that states none is not
161
- * one of these calls, so the chain stops there, and a rule the pack
162
- * writes over the inputs is handed the object rather than a position to
163
- * go looking in.
157
+ * instead of as positional arguments. A call without that object does
158
+ * not match. A rule the pack writes over the inputs receives the object
159
+ * itself, so it never has to find the right position.
164
160
  */
165
161
  interface InputLink {
166
162
  readonly asks: "input";
167
163
  readonly at: OneArgument;
168
164
  }
169
165
  /**
170
- * What the values a statement interpolates come to.
166
+ * How to read the values interpolated into a statement.
171
167
  *
172
- * A query that says which table it reached by handing over the schema
173
- * object leaves the name out of the text, and a parameter in its place
174
- * does not parse. Only the pack knows the object is a table rather than
175
- * a value, so the pack says which argument of the call behind it gives
176
- * the name, and where that call had to have come from.
168
+ * A query that interpolates a schema object for its table leaves the
169
+ * table name out of the SQL text, and a parameter in its place does not
170
+ * parse. Nothing in the statement shows that the object is a table, so
171
+ * the pack says which argument of the call that built the object gives
172
+ * the name, and where that call must have come from.
177
173
  */
178
174
  interface InterpolatesLink {
179
175
  readonly asks: "interpolates";
180
- /** Which argument of the call the value was written as gives the name. */
176
+ /** Which argument of the call that built the value gives the name. */
181
177
  readonly named: ArgumentPick;
182
- /** Where the value had to have come from. Left out, every hole is read. */
178
+ /** Where the value must come from. Without it, every hole is read. */
183
179
  readonly from?: ReceiverOrigin;
184
180
  }
185
181
  /** One question in a chain, and the answer the pack gave for it. */
186
182
  type Link<TMeaning> = StartLink | SubjectLink | MethodsLink<TMeaning> | CallsLink<TMeaning> | ContainerLink | AccessPathLink | ScopeLink | ContainersLink | InputLink | InterpolatesLink;
187
183
  /**
188
- * The meaning of a bare call of the tracked client itself. A store
189
- * hook is the case: `useAppStore((s) => s.bears)` reaches for no
190
- * method, so there is no name for a methods table to list; the call
191
- * matches by what its callee was written as.
184
+ * The meaning of a bare call of the tracked client itself. A store hook
185
+ * such as `useAppStore((s) => s.bears)` calls no method, so a methods
186
+ * table has nothing to list. The call matches on how its callee was
187
+ * written.
192
188
  */
193
189
  interface CallsLink<TMeaning> {
194
190
  readonly asks: "calls";
@@ -213,8 +209,8 @@ interface OneArgument {
213
209
  readonly of?: readonly CallStep[];
214
210
  readonly at: number;
215
211
  /**
216
- * Properties of the object the argument states, tried in order, when
217
- * what the call reached is inside the argument rather than being it.
212
+ * Properties of the object the argument states, tried in order, for
213
+ * when the thing the call reached is a property of the argument.
218
214
  */
219
215
  readonly property?: readonly string[];
220
216
  }
@@ -231,29 +227,27 @@ interface ArgumentsFrom {
231
227
  * write, or whichever of the two one of its arguments asked for.
232
228
  */
233
229
  type AccessKind = "read" | "write" | KindAsAsked;
234
- /** A method the caller tells which way round it goes. */
230
+ /** A method whose argument says whether the call reads or writes. */
235
231
  interface KindAsAsked {
236
- /** Where the call says what it is for. */
232
+ /** The argument that says what the call is for. */
237
233
  readonly asks: ArgumentPick;
238
- /** What each thing the caller can ask for comes to. */
234
+ /** Whether each value that argument can take reads or writes. */
239
235
  readonly means: Readonly<Record<string, "read" | "write">>;
240
236
  /**
241
- * What it comes to when the call says nothing. Left out, a call that
242
- * asks for something the table does not list is not one of these
243
- * calls, which is how a pack reads a helper whose operations a
244
- * project lists in its own config.
237
+ * The kind to use when `means` does not list what the call asked for.
238
+ * Without it such a call does not match. A pack reading a helper whose
239
+ * operations a project lists in its own config relies on that.
245
240
  */
246
241
  readonly otherwise?: "read" | "write";
247
242
  }
248
243
  /** What a pack's own rule is handed: one value, and what the call does. */
249
244
  interface StatedInputs {
250
245
  /**
251
- * The value the rule reads. That is the object the call states its
252
- * inputs as when the chain says where with `input`, and the value the
253
- * rule was pointed at when it says where for itself. A call that
254
- * passed nothing there states nothing, rather than the rule being
255
- * skipped: a projection the caller left out is still a read of every
256
- * field there is, and only the pack knows that.
246
+ * The value the rule reads: the inputs object when the chain locates
247
+ * it with `input`, or the value the rule was pointed at otherwise.
248
+ * When the call passed nothing there, the rule still runs, over a
249
+ * value that states nothing. A caller that leaves out a projection
250
+ * still reads every field, and only the pack's rule can say so.
257
251
  */
258
252
  readonly input: ValueOps;
259
253
  /** The container's own entry, when the call reached several. */
@@ -263,19 +257,19 @@ interface StatedInputs {
263
257
  }
264
258
  /**
265
259
  * A pack's own rule over the inputs a call states, for a library that
266
- * writes what a reader wants somewhere no pick can reach.
260
+ * puts the selector or the fields somewhere no argument pick can reach.
267
261
  */
268
262
  type InputRule = LinkFunction<[StatedInputs], readonly string[]>;
269
263
  /**
270
264
  * A pack's own rule, pointed at the value it reads.
271
265
  *
272
266
  * `input` says where a call that states one request object states it,
273
- * and every rule on that chain reads the one value. A library that
274
- * spreads what a reader wants over several places needs each rule
275
- * pointed somewhere of its own: Mongoose picks documents by the first
276
- * argument and reads the fields the second asks for, and Drizzle puts
277
- * the fields, the table and the condition on three calls of one chain,
278
- * which the pick's own `of` steps reach.
267
+ * and every rule on that chain reads that one value. A library that
268
+ * spreads a request over several places needs each rule pointed at its
269
+ * own value. Mongoose picks documents by the first argument and reads
270
+ * the fields the second asks for. Drizzle puts the fields, the table
271
+ * and the condition on three calls of one chain, which the pick's own
272
+ * `of` steps reach.
279
273
  */
280
274
  interface StatedRule {
281
275
  /** Which value the rule reads. */
@@ -288,20 +282,20 @@ interface StorageMethod {
288
282
  readonly kind: AccessKind;
289
283
  /**
290
284
  * Which argument says which operation the call performs, when the
291
- * name the call goes to does not. A project's own request helper is
292
- * the case: every operation goes through the one function.
285
+ * method name does not. A project's own request helper needs this,
286
+ * because every operation goes through the one function.
293
287
  */
294
288
  readonly operation?: ArgumentPick;
295
289
  /**
296
- * What the call reached. An argument the call passes, a rule the pack
297
- * wrote, or the plain list a method whose own name settles it states
298
- * outright: `findById` picks by `_id` however the id is spelt.
290
+ * What the call reached: an argument the call passes, a rule the pack
291
+ * wrote, or a fixed list for a method whose name already settles it.
292
+ * `findById` picks by `_id` however the id is spelt.
299
293
  */
300
294
  readonly selector?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
301
295
  /**
302
- * Which fields the call touched, said the same three ways. A delete
303
- * touches the whole document and states `["*"]`, since nothing in its
304
- * arguments says so.
296
+ * Which fields the call touched, stated any of the ways a selector
297
+ * is. A delete touches the whole document and states `["*"]`, since
298
+ * nothing in its arguments says so.
305
299
  */
306
300
  readonly fields?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
307
301
  }
@@ -313,18 +307,24 @@ interface StorageMethod {
313
307
  */
314
308
  interface SqlMethod {
315
309
  /**
316
- * Where the call states the statement. The pick's steps reach a call
317
- * beside this one, which is how a pack reads a statement handed to
318
- * `execute` as a tagged template rather than written on the call.
310
+ * Where the call states the statement. The pick's steps can reach a
311
+ * call beside this one, so a pack can read a statement built as a
312
+ * tagged template and passed to `execute`.
319
313
  *
320
- * A list is tried in order until one of the picks reaches text. One
321
- * library takes the statement either way round: `query(sql)` and
322
- * `query({ query: sql })` are the same call, and the pack says both
323
- * rather than picking whichever its author happened to meet first.
314
+ * A list is tried in order until one of the picks reaches text. Some
315
+ * libraries take the statement either way: `query(sql)` and
316
+ * `query({ query: sql })` are the same call, so the pack lists both
317
+ * picks.
324
318
  */
325
319
  readonly statement: OneArgument | readonly OneArgument[];
320
+ /**
321
+ * Where the call passes the values the statement's placeholders take,
322
+ * tried in order like `statement`: `query(sql, [id])` and
323
+ * `query({ text: sql, values: [id] })`. Without it the access still
324
+ * reads, and nothing says where a column's value came from.
325
+ */
326
+ readonly parameters?: OneArgument | readonly OneArgument[];
326
327
  }
327
- /** What one method table can say a method does. */
328
328
  /** What one send method does, as the message-send ending reads it. */
329
329
  interface MessageSendMethod {
330
330
  /** Where the call states the message, or the collection of them. */
@@ -336,6 +336,7 @@ interface MessageSendMethod {
336
336
  * is and nothing else.
337
337
  */
338
338
  type UnitInvokeMethod = MessageSendMethod;
339
+ /** What one method table can say a method does. */
339
340
  type MethodMeaning = StorageMethod | SqlMethod | MessageSendMethod;
340
341
  /** What a chain produces when every link matches. */
341
342
  type Ending = StorageEnding | SqlEnding | MessageSendEnding | UnitInvokeEnding;
@@ -354,11 +355,10 @@ interface StorageEnding {
354
355
  /**
355
356
  * Every table a statement touches, as one effect each.
356
357
  *
357
- * The other ending settles what a call reached by asking the call. This
358
- * one settles it by reading the statement, so one call yields as many
359
- * effects as the statement has tables, and each of them states its own
360
- * kind: a statement that writes one table while reading another says
361
- * both.
358
+ * The other endings settle what a call reached from the call's
359
+ * arguments. This one reads the statement, so one call yields one
360
+ * effect per table, each with its own kind. A statement that writes one
361
+ * table while reading another yields a write and a read.
362
362
  */
363
363
  interface SqlEnding {
364
364
  readonly yields: "sqlAccess";
@@ -381,11 +381,10 @@ interface SqlEnding {
381
381
  * Where the messages a call sends are written.
382
382
  *
383
383
  * A library either takes one message as the call's input or takes a
384
- * collection of them under a property. Which of the two is a fact about
385
- * the command, so a library offering both spells them as two
386
- * declarations rather than one with a setting on it: SQS has
387
- * `SendMessageCommand` and `SendMessageBatchCommand`, and they are
388
- * different shapes.
384
+ * collection of them under a property. Which one applies depends on the
385
+ * command, so a library offering both gets two declarations. SQS has
386
+ * `SendMessageCommand` and `SendMessageBatchCommand`, and their inputs
387
+ * differ.
389
388
  */
390
389
  type MessageLocation = OneMessage | ManyIn;
391
390
  /** The call's input is the message. */
@@ -425,10 +424,10 @@ interface ChannelPart {
425
424
  /**
426
425
  * What a reader gives back when this part is written but nothing in
427
426
  * the source settles it, overriding the ending's `unsettledName`.
428
- * EventBridge wants both: its bus is nearly always an env var, whose
429
- * name both sides of the boundary agree on, and its subject is a
430
- * domain string, where a run-time value should leave the channel
431
- * unnamed rather than pair against everything.
427
+ * EventBridge needs both settings. Its bus is nearly always an env
428
+ * var, whose name both sides of the boundary agree on. Its subject is
429
+ * a domain string, and a value known only at run time there should
430
+ * leave the channel unnamed so it does not pair against everything.
432
431
  */
433
432
  readonly unsettled?: UnsettledName;
434
433
  }
@@ -446,9 +445,9 @@ interface MessageSendEnding {
446
445
  /** The property the message states its body on, when the pack can say. */
447
446
  readonly body?: string;
448
447
  /**
449
- * The property whose literal value rides along as the routing key.
450
- * Not part of the channel: it scopes the message for a reader without
451
- * being what the two sides pair on.
448
+ * The property whose literal value is recorded as the routing key. It
449
+ * stays out of the channel, so a reader can filter on it but the two
450
+ * sides do not pair on it.
452
451
  */
453
452
  readonly routingKey?: string;
454
453
  /**