@suss/recognize 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/ast.d.cts +1 -1
- package/dist/ast.d.ts +1 -1
- package/dist/{chain-C7w99UMv.d.cts → chain-IogmSi0t.d.cts} +67 -8
- package/dist/{chain-C7w99UMv.d.ts → chain-IogmSi0t.d.ts} +67 -8
- package/dist/index.cjs +167 -15
- package/dist/index.d.cts +65 -8
- package/dist/index.d.ts +65 -8
- package/dist/index.js +171 -15
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -76,6 +76,15 @@ ioredis, and the declaration behind `redis.get` says everything.
|
|
|
76
76
|
`constructed` asks the receiver itself, so it still works where the
|
|
77
77
|
method is untyped.
|
|
78
78
|
|
|
79
|
+
A chain can also match a bare call of the tracked value itself, with
|
|
80
|
+
`.calls(meaning)` beside its methods table. A store hook is the case:
|
|
81
|
+
`useAppStore((s) => s.bears)` reaches for no method, so there is no
|
|
82
|
+
name for the table to list; the call matches when its callee is a
|
|
83
|
+
bound name whose written value came from the chain's origin. The
|
|
84
|
+
fields such a call reads are usually stated in a selector lambda, and
|
|
85
|
+
`fields: { selectorParam: 0 }` picks them off the lambda's parameter,
|
|
86
|
+
one per distinct first segment.
|
|
87
|
+
|
|
79
88
|
## The gradient
|
|
80
89
|
|
|
81
90
|
Expressiveness is bought link by link, and the price is printed.
|
|
@@ -111,6 +120,8 @@ result on the recognizer context under `ops`.
|
|
|
111
120
|
| `receiver()` | the call the receiver is, as ops of its own |
|
|
112
121
|
| `argument(index)` | the call that argument is, as ops of its own |
|
|
113
122
|
| `callee()` | the call the callee itself was written as |
|
|
123
|
+
| `namedCallee()` | whether the call goes to a name the program bound (optional) |
|
|
124
|
+
| `parameterReadsAt(index)` | the fields a selector lambda in that position reads off its parameter (optional) |
|
|
114
125
|
| `propertyAt(index, property, unsettled)` | what a named property of that argument says |
|
|
115
126
|
| `valueAt(index)` | the value that argument states, as `ValueOps` |
|
|
116
127
|
|
package/dist/ast.d.cts
CHANGED
package/dist/ast.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MessageBusSemantics } from '@suss/behavioral-ir';
|
|
1
|
+
import { MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
|
|
2
2
|
import { ReceiverOrigin, ValueOps, CallOps, UnsettledName } from '@suss/extractor';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -136,6 +136,13 @@ interface AccessPathLink {
|
|
|
136
136
|
interface ContainersLink {
|
|
137
137
|
readonly asks: "containers";
|
|
138
138
|
readonly in: OneArgument;
|
|
139
|
+
/**
|
|
140
|
+
* What one of them is. An entry keys the container by name and says
|
|
141
|
+
* what the call did there, which is how a batch write is written. A
|
|
142
|
+
* name is the container on its own, which is how a call that reads
|
|
143
|
+
* several parameters at once is written. Defaults to an entry.
|
|
144
|
+
*/
|
|
145
|
+
readonly each?: "entry" | "name";
|
|
139
146
|
}
|
|
140
147
|
/**
|
|
141
148
|
* Where a call states its inputs, when it states them as one object
|
|
@@ -165,12 +172,30 @@ interface InterpolatesLink {
|
|
|
165
172
|
readonly from?: ReceiverOrigin;
|
|
166
173
|
}
|
|
167
174
|
/** One question in a chain, and the answer the pack gave for it. */
|
|
168
|
-
type Link<TMeaning> = StartLink | SubjectLink | MethodsLink<TMeaning> | ContainerLink | AccessPathLink | ContainersLink | InputLink | InterpolatesLink;
|
|
175
|
+
type Link<TMeaning> = StartLink | SubjectLink | MethodsLink<TMeaning> | CallsLink<TMeaning> | ContainerLink | AccessPathLink | ContainersLink | InputLink | InterpolatesLink;
|
|
176
|
+
/**
|
|
177
|
+
* The meaning of a bare call of the tracked client itself. A store
|
|
178
|
+
* hook is the case: `useAppStore((s) => s.bears)` reaches for no
|
|
179
|
+
* method, so there is no name for a methods table to list; the call
|
|
180
|
+
* matches by what its callee was written as.
|
|
181
|
+
*/
|
|
182
|
+
interface CallsLink<TMeaning> {
|
|
183
|
+
readonly asks: "calls";
|
|
184
|
+
readonly meaning: TMeaning;
|
|
185
|
+
}
|
|
169
186
|
/**
|
|
170
187
|
* Which argument or arguments say what a call reached, and where to go
|
|
171
188
|
* looking. Without `of` the argument belongs to the chain's subject.
|
|
172
189
|
*/
|
|
173
190
|
type ArgumentPick = OneArgument | ArgumentsFrom;
|
|
191
|
+
/**
|
|
192
|
+
* The fields a selector lambda reads off its parameter, one per
|
|
193
|
+
* distinct first segment: `(s) => s.bears.count` reads `bears`. The
|
|
194
|
+
* value says which argument the lambda is passed as.
|
|
195
|
+
*/
|
|
196
|
+
interface SelectorParamPick {
|
|
197
|
+
readonly selectorParam: number;
|
|
198
|
+
}
|
|
174
199
|
/** The argument in one position. */
|
|
175
200
|
interface OneArgument {
|
|
176
201
|
/** The steps to the call the argument belongs to. */
|
|
@@ -261,13 +286,13 @@ interface StorageMethod {
|
|
|
261
286
|
* wrote, or the plain list a method whose own name settles it states
|
|
262
287
|
* outright: `findById` picks by `_id` however the id is spelt.
|
|
263
288
|
*/
|
|
264
|
-
readonly selector?: readonly string[] | ArgumentPick | InputRule | StatedRule;
|
|
289
|
+
readonly selector?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
|
|
265
290
|
/**
|
|
266
291
|
* Which fields the call touched, said the same three ways. A delete
|
|
267
292
|
* touches the whole document and states `["*"]`, since nothing in its
|
|
268
293
|
* arguments says so.
|
|
269
294
|
*/
|
|
270
|
-
readonly fields?: readonly string[] | ArgumentPick | InputRule | StatedRule;
|
|
295
|
+
readonly fields?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
|
|
271
296
|
}
|
|
272
297
|
/**
|
|
273
298
|
* What one method of a client that takes a statement written as SQL
|
|
@@ -289,9 +314,15 @@ interface MessageSendMethod {
|
|
|
289
314
|
/** Where the call states the message, or the collection of them. */
|
|
290
315
|
readonly input: OneArgument;
|
|
291
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* What one invoke method does. An invoke states its whole request as
|
|
319
|
+
* one object the same way a send does, so both say where that object
|
|
320
|
+
* is and nothing else.
|
|
321
|
+
*/
|
|
322
|
+
type UnitInvokeMethod = MessageSendMethod;
|
|
292
323
|
type MethodMeaning = StorageMethod | SqlMethod | MessageSendMethod;
|
|
293
324
|
/** What a chain produces when every link matches. */
|
|
294
|
-
type Ending = StorageEnding | SqlEnding | MessageSendEnding;
|
|
325
|
+
type Ending = StorageEnding | SqlEnding | MessageSendEnding | UnitInvokeEnding;
|
|
295
326
|
/** A storage access: one call, one thing it read or wrote. */
|
|
296
327
|
interface StorageEnding {
|
|
297
328
|
readonly yields: "storageAccess";
|
|
@@ -359,8 +390,13 @@ interface ManyIn {
|
|
|
359
390
|
* the same way, so a pack states the parts in the order they join.
|
|
360
391
|
*/
|
|
361
392
|
interface ChannelPart {
|
|
362
|
-
/**
|
|
363
|
-
|
|
393
|
+
/**
|
|
394
|
+
* The properties the message may state this part on, tried in order.
|
|
395
|
+
* A library that lets one destination be written more than one way
|
|
396
|
+
* lists each spelling: an SNS publish writes its topic as `TopicArn`
|
|
397
|
+
* or as `TargetArn`, and the two are the same part of the channel.
|
|
398
|
+
*/
|
|
399
|
+
readonly property: readonly string[];
|
|
364
400
|
/**
|
|
365
401
|
* Where the property is written. A batch command states the queue
|
|
366
402
|
* once beside the list of messages, so its channel part reads off
|
|
@@ -407,6 +443,29 @@ interface MessageSendEnding {
|
|
|
407
443
|
*/
|
|
408
444
|
readonly unsettledName: UnsettledName;
|
|
409
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* A call that hands a payload to one deployed unit, named on the call.
|
|
448
|
+
*
|
|
449
|
+
* The unit's name is read off the call's own input rather than off a
|
|
450
|
+
* message, because there is one callee per call however many messages a
|
|
451
|
+
* send takes. A name written as a fully-qualified cloud id is reduced
|
|
452
|
+
* to the resource segment, so an ARN and a bare name come out the same.
|
|
453
|
+
*/
|
|
454
|
+
interface UnitInvokeEnding {
|
|
455
|
+
readonly yields: "unitInvoke";
|
|
456
|
+
/** The platform running the unit, in the IR's deployable-unit words. */
|
|
457
|
+
readonly platform: DeployableUnit["deploymentTarget"];
|
|
458
|
+
/** The properties the call may name the unit on, tried in order. */
|
|
459
|
+
readonly named: readonly string[];
|
|
460
|
+
/** The property the call states its payload on, when the pack can say. */
|
|
461
|
+
readonly payload?: string;
|
|
462
|
+
/**
|
|
463
|
+
* What a reader gives back for a name nothing in the source settles.
|
|
464
|
+
* A function name usually arrives through an env var, whose name both
|
|
465
|
+
* sides agree on, so `"reference"` keeps it.
|
|
466
|
+
*/
|
|
467
|
+
readonly unsettledName: UnsettledName;
|
|
468
|
+
}
|
|
410
469
|
/**
|
|
411
470
|
* A pack's declaration for one kind of call: the links, the ending, and
|
|
412
471
|
* a line of code it matches.
|
|
@@ -418,4 +477,4 @@ interface Chain<TMeaning = MethodMeaning> {
|
|
|
418
477
|
readonly example: string | null;
|
|
419
478
|
}
|
|
420
479
|
|
|
421
|
-
export type { ArgumentPick as A,
|
|
480
|
+
export type { ArgumentPick as A, StorageEnding as B, Chain as C, SubjectLink as D, Ending as E, FromReceiver as F, ToReceiver as G, UnitInvokeEnding as H, InterpolatesLink as I, KindAsAsked as K, LinkFunction as L, MethodMeaning as M, OneArgument as O, SqlMethod as S, ToArgument as T, UnitInvokeMethod as U, MessageSendMethod as a, MessageLocation as b, ChannelPart as c, CallStep as d, StorageMethod as e, AccessKind as f, AccessPathLink as g, ArgumentsFrom as h, CallsLink as i, ContainerArgument as j, ContainerLink as k, ContainerRule as l, ContainersLink as m, InputLink as n, InputRule as o, Link as p, ManyIn as q, MatchStart as r, MessageSendEnding as s, MethodsLink as t, OneMessage as u, SelectorParamPick as v, SqlEnding as w, StartLink as x, StatedInputs as y, StatedRule as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MessageBusSemantics } from '@suss/behavioral-ir';
|
|
1
|
+
import { MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
|
|
2
2
|
import { ReceiverOrigin, ValueOps, CallOps, UnsettledName } from '@suss/extractor';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -136,6 +136,13 @@ interface AccessPathLink {
|
|
|
136
136
|
interface ContainersLink {
|
|
137
137
|
readonly asks: "containers";
|
|
138
138
|
readonly in: OneArgument;
|
|
139
|
+
/**
|
|
140
|
+
* What one of them is. An entry keys the container by name and says
|
|
141
|
+
* what the call did there, which is how a batch write is written. A
|
|
142
|
+
* name is the container on its own, which is how a call that reads
|
|
143
|
+
* several parameters at once is written. Defaults to an entry.
|
|
144
|
+
*/
|
|
145
|
+
readonly each?: "entry" | "name";
|
|
139
146
|
}
|
|
140
147
|
/**
|
|
141
148
|
* Where a call states its inputs, when it states them as one object
|
|
@@ -165,12 +172,30 @@ interface InterpolatesLink {
|
|
|
165
172
|
readonly from?: ReceiverOrigin;
|
|
166
173
|
}
|
|
167
174
|
/** One question in a chain, and the answer the pack gave for it. */
|
|
168
|
-
type Link<TMeaning> = StartLink | SubjectLink | MethodsLink<TMeaning> | ContainerLink | AccessPathLink | ContainersLink | InputLink | InterpolatesLink;
|
|
175
|
+
type Link<TMeaning> = StartLink | SubjectLink | MethodsLink<TMeaning> | CallsLink<TMeaning> | ContainerLink | AccessPathLink | ContainersLink | InputLink | InterpolatesLink;
|
|
176
|
+
/**
|
|
177
|
+
* The meaning of a bare call of the tracked client itself. A store
|
|
178
|
+
* hook is the case: `useAppStore((s) => s.bears)` reaches for no
|
|
179
|
+
* method, so there is no name for a methods table to list; the call
|
|
180
|
+
* matches by what its callee was written as.
|
|
181
|
+
*/
|
|
182
|
+
interface CallsLink<TMeaning> {
|
|
183
|
+
readonly asks: "calls";
|
|
184
|
+
readonly meaning: TMeaning;
|
|
185
|
+
}
|
|
169
186
|
/**
|
|
170
187
|
* Which argument or arguments say what a call reached, and where to go
|
|
171
188
|
* looking. Without `of` the argument belongs to the chain's subject.
|
|
172
189
|
*/
|
|
173
190
|
type ArgumentPick = OneArgument | ArgumentsFrom;
|
|
191
|
+
/**
|
|
192
|
+
* The fields a selector lambda reads off its parameter, one per
|
|
193
|
+
* distinct first segment: `(s) => s.bears.count` reads `bears`. The
|
|
194
|
+
* value says which argument the lambda is passed as.
|
|
195
|
+
*/
|
|
196
|
+
interface SelectorParamPick {
|
|
197
|
+
readonly selectorParam: number;
|
|
198
|
+
}
|
|
174
199
|
/** The argument in one position. */
|
|
175
200
|
interface OneArgument {
|
|
176
201
|
/** The steps to the call the argument belongs to. */
|
|
@@ -261,13 +286,13 @@ interface StorageMethod {
|
|
|
261
286
|
* wrote, or the plain list a method whose own name settles it states
|
|
262
287
|
* outright: `findById` picks by `_id` however the id is spelt.
|
|
263
288
|
*/
|
|
264
|
-
readonly selector?: readonly string[] | ArgumentPick | InputRule | StatedRule;
|
|
289
|
+
readonly selector?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
|
|
265
290
|
/**
|
|
266
291
|
* Which fields the call touched, said the same three ways. A delete
|
|
267
292
|
* touches the whole document and states `["*"]`, since nothing in its
|
|
268
293
|
* arguments says so.
|
|
269
294
|
*/
|
|
270
|
-
readonly fields?: readonly string[] | ArgumentPick | InputRule | StatedRule;
|
|
295
|
+
readonly fields?: readonly string[] | ArgumentPick | SelectorParamPick | InputRule | StatedRule;
|
|
271
296
|
}
|
|
272
297
|
/**
|
|
273
298
|
* What one method of a client that takes a statement written as SQL
|
|
@@ -289,9 +314,15 @@ interface MessageSendMethod {
|
|
|
289
314
|
/** Where the call states the message, or the collection of them. */
|
|
290
315
|
readonly input: OneArgument;
|
|
291
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* What one invoke method does. An invoke states its whole request as
|
|
319
|
+
* one object the same way a send does, so both say where that object
|
|
320
|
+
* is and nothing else.
|
|
321
|
+
*/
|
|
322
|
+
type UnitInvokeMethod = MessageSendMethod;
|
|
292
323
|
type MethodMeaning = StorageMethod | SqlMethod | MessageSendMethod;
|
|
293
324
|
/** What a chain produces when every link matches. */
|
|
294
|
-
type Ending = StorageEnding | SqlEnding | MessageSendEnding;
|
|
325
|
+
type Ending = StorageEnding | SqlEnding | MessageSendEnding | UnitInvokeEnding;
|
|
295
326
|
/** A storage access: one call, one thing it read or wrote. */
|
|
296
327
|
interface StorageEnding {
|
|
297
328
|
readonly yields: "storageAccess";
|
|
@@ -359,8 +390,13 @@ interface ManyIn {
|
|
|
359
390
|
* the same way, so a pack states the parts in the order they join.
|
|
360
391
|
*/
|
|
361
392
|
interface ChannelPart {
|
|
362
|
-
/**
|
|
363
|
-
|
|
393
|
+
/**
|
|
394
|
+
* The properties the message may state this part on, tried in order.
|
|
395
|
+
* A library that lets one destination be written more than one way
|
|
396
|
+
* lists each spelling: an SNS publish writes its topic as `TopicArn`
|
|
397
|
+
* or as `TargetArn`, and the two are the same part of the channel.
|
|
398
|
+
*/
|
|
399
|
+
readonly property: readonly string[];
|
|
364
400
|
/**
|
|
365
401
|
* Where the property is written. A batch command states the queue
|
|
366
402
|
* once beside the list of messages, so its channel part reads off
|
|
@@ -407,6 +443,29 @@ interface MessageSendEnding {
|
|
|
407
443
|
*/
|
|
408
444
|
readonly unsettledName: UnsettledName;
|
|
409
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* A call that hands a payload to one deployed unit, named on the call.
|
|
448
|
+
*
|
|
449
|
+
* The unit's name is read off the call's own input rather than off a
|
|
450
|
+
* message, because there is one callee per call however many messages a
|
|
451
|
+
* send takes. A name written as a fully-qualified cloud id is reduced
|
|
452
|
+
* to the resource segment, so an ARN and a bare name come out the same.
|
|
453
|
+
*/
|
|
454
|
+
interface UnitInvokeEnding {
|
|
455
|
+
readonly yields: "unitInvoke";
|
|
456
|
+
/** The platform running the unit, in the IR's deployable-unit words. */
|
|
457
|
+
readonly platform: DeployableUnit["deploymentTarget"];
|
|
458
|
+
/** The properties the call may name the unit on, tried in order. */
|
|
459
|
+
readonly named: readonly string[];
|
|
460
|
+
/** The property the call states its payload on, when the pack can say. */
|
|
461
|
+
readonly payload?: string;
|
|
462
|
+
/**
|
|
463
|
+
* What a reader gives back for a name nothing in the source settles.
|
|
464
|
+
* A function name usually arrives through an env var, whose name both
|
|
465
|
+
* sides agree on, so `"reference"` keeps it.
|
|
466
|
+
*/
|
|
467
|
+
readonly unsettledName: UnsettledName;
|
|
468
|
+
}
|
|
410
469
|
/**
|
|
411
470
|
* A pack's declaration for one kind of call: the links, the ending, and
|
|
412
471
|
* a line of code it matches.
|
|
@@ -418,4 +477,4 @@ interface Chain<TMeaning = MethodMeaning> {
|
|
|
418
477
|
readonly example: string | null;
|
|
419
478
|
}
|
|
420
479
|
|
|
421
|
-
export type { ArgumentPick as A,
|
|
480
|
+
export type { ArgumentPick as A, StorageEnding as B, Chain as C, SubjectLink as D, Ending as E, FromReceiver as F, ToReceiver as G, UnitInvokeEnding as H, InterpolatesLink as I, KindAsAsked as K, LinkFunction as L, MethodMeaning as M, OneArgument as O, SqlMethod as S, ToArgument as T, UnitInvokeMethod as U, MessageSendMethod as a, MessageLocation as b, ChannelPart as c, CallStep as d, StorageMethod as e, AccessKind as f, AccessPathLink as g, ArgumentsFrom as h, CallsLink as i, ContainerArgument as j, ContainerLink as k, ContainerRule as l, ContainersLink as m, InputLink as n, InputRule as o, Link as p, ManyIn as q, MatchStart as r, MessageSendEnding as s, MethodsLink as t, OneMessage as u, SelectorParamPick as v, SqlEnding as w, StartLink as x, StatedInputs as y, StatedRule as z };
|
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,8 @@ __export(index_exports, {
|
|
|
30
30
|
pack: () => pack,
|
|
31
31
|
runExamples: () => runExamples,
|
|
32
32
|
sqlStatements: () => sqlStatements,
|
|
33
|
-
storageCalls: () => storageCalls
|
|
33
|
+
storageCalls: () => storageCalls,
|
|
34
|
+
unitInvokes: () => unitInvokes
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(index_exports);
|
|
36
37
|
|
|
@@ -64,8 +65,22 @@ var MAX_RECEIVER_HOPS = 8;
|
|
|
64
65
|
function compile(chain, recognition) {
|
|
65
66
|
return (_call, ctx) => {
|
|
66
67
|
const ops = opsIn(ctx);
|
|
68
|
+
if (ops === null) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const bare = bareCall(chain, ops);
|
|
72
|
+
if (bare !== null) {
|
|
73
|
+
return YIELD[chain.ending.yields]({
|
|
74
|
+
ops,
|
|
75
|
+
subject: ops,
|
|
76
|
+
method: bare.method,
|
|
77
|
+
meaning: bare.meaning,
|
|
78
|
+
chain,
|
|
79
|
+
recognition
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
const methods = methodsIn(chain);
|
|
68
|
-
if (
|
|
83
|
+
if (methods === null) {
|
|
69
84
|
return null;
|
|
70
85
|
}
|
|
71
86
|
for (const subject of subjectsOf(chain, ops)) {
|
|
@@ -90,6 +105,21 @@ function linkIn(chain, asks) {
|
|
|
90
105
|
const link = chain.links.find((candidate) => candidate.asks === asks);
|
|
91
106
|
return link ?? null;
|
|
92
107
|
}
|
|
108
|
+
function bareCall(chain, ops) {
|
|
109
|
+
const link = linkIn(chain, "calls");
|
|
110
|
+
const start = linkIn(chain, "start");
|
|
111
|
+
if (link === null || start === null || start.at.starts !== "receiver") {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (ops.method() !== null || ops.namedCallee?.() !== true) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const written = ops.callee();
|
|
118
|
+
if (written === null || !written.isFrom(start.at.origin)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return { method: ops.calleeText(), meaning: link.meaning };
|
|
122
|
+
}
|
|
93
123
|
function methodsIn(chain) {
|
|
94
124
|
return linkIn(chain, "methods");
|
|
95
125
|
}
|
|
@@ -163,7 +193,8 @@ function startsHere(chain, subject) {
|
|
|
163
193
|
var YIELD = {
|
|
164
194
|
storageAccess,
|
|
165
195
|
sqlAccess,
|
|
166
|
-
messageSend
|
|
196
|
+
messageSend,
|
|
197
|
+
unitInvoke
|
|
167
198
|
};
|
|
168
199
|
function storageAccess(matched) {
|
|
169
200
|
const { subject, chain } = matched;
|
|
@@ -194,7 +225,7 @@ function accessEffect(matched, access) {
|
|
|
194
225
|
storageSystem: ending.system,
|
|
195
226
|
...ending.transport === void 0 ? {} : { transport: ending.transport },
|
|
196
227
|
scope: ending.scope,
|
|
197
|
-
container: reached.
|
|
228
|
+
container: reached.stated ? reached.container : containerOf(chain, selector, subject, unsettled),
|
|
198
229
|
accessPath: accessPathOf(chain, subject, unsettled)
|
|
199
230
|
}),
|
|
200
231
|
callee: ops.calleeText(),
|
|
@@ -209,11 +240,24 @@ function accessEffect(matched, access) {
|
|
|
209
240
|
}
|
|
210
241
|
function reachedBy(chain, subject, unsettled) {
|
|
211
242
|
const link = linkIn(chain, "containers");
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
243
|
+
const alone = [{ container: null, entry: null, stated: false }];
|
|
244
|
+
const stated = link === null ? null : statedValue(subject, link.in);
|
|
245
|
+
if (link === null || stated === null) {
|
|
246
|
+
return alone;
|
|
247
|
+
}
|
|
248
|
+
if (link.each === "name") {
|
|
249
|
+
const named = stated.items().map((item) => ({
|
|
250
|
+
container: item.name(unsettled),
|
|
251
|
+
entry: null,
|
|
252
|
+
stated: true
|
|
253
|
+
}));
|
|
254
|
+
return named.length === 0 ? alone : named;
|
|
255
|
+
}
|
|
256
|
+
return stated.entries(unsettled).map((entry) => ({
|
|
257
|
+
container: entry.key,
|
|
258
|
+
entry: entry.value,
|
|
259
|
+
stated: true
|
|
260
|
+
}));
|
|
217
261
|
}
|
|
218
262
|
function kindOf(kind, subject, unsettled) {
|
|
219
263
|
if (typeof kind === "string") {
|
|
@@ -278,6 +322,9 @@ function namesFor(says, subject, stated, unsettled) {
|
|
|
278
322
|
const input2 = stated.input;
|
|
279
323
|
return input2 === null ? [] : [...says({ ...stated, input: input2 })];
|
|
280
324
|
}
|
|
325
|
+
if ("selectorParam" in says) {
|
|
326
|
+
return [...subject.parameterReadsAt?.(says.selectorParam) ?? []];
|
|
327
|
+
}
|
|
281
328
|
const pointed = says;
|
|
282
329
|
if (!("by" in pointed)) {
|
|
283
330
|
return namesAt(subject, pointed, unsettled);
|
|
@@ -350,7 +397,7 @@ function channelOf(message, input, ending) {
|
|
|
350
397
|
const parts = [];
|
|
351
398
|
for (const part of ending.channel) {
|
|
352
399
|
const holder = part.on === "theInput" ? input : message;
|
|
353
|
-
const written = holder
|
|
400
|
+
const written = firstWritten(holder, part.property);
|
|
354
401
|
if (written === null) {
|
|
355
402
|
if (part.whenAbsent === void 0) {
|
|
356
403
|
return null;
|
|
@@ -366,6 +413,15 @@ function channelOf(message, input, ending) {
|
|
|
366
413
|
}
|
|
367
414
|
return parts.join(ending.channelSeparator ?? "#");
|
|
368
415
|
}
|
|
416
|
+
function firstWritten(holder, properties) {
|
|
417
|
+
for (const property of properties) {
|
|
418
|
+
const written = holder.property(property);
|
|
419
|
+
if (written !== null) {
|
|
420
|
+
return written;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
369
425
|
function bodyOf(message, ending) {
|
|
370
426
|
if (ending.body === void 0) {
|
|
371
427
|
return {};
|
|
@@ -382,6 +438,50 @@ function routingKeyOf(message, ending) {
|
|
|
382
438
|
const stated = message.property(ending.routingKey)?.text() ?? null;
|
|
383
439
|
return stated === null || stated === "" ? {} : { routingKey: stated };
|
|
384
440
|
}
|
|
441
|
+
function unitInvoke(matched) {
|
|
442
|
+
const { ops, subject, chain, recognition } = matched;
|
|
443
|
+
const ending = chain.ending;
|
|
444
|
+
const input = statedValue(
|
|
445
|
+
subject,
|
|
446
|
+
matched.meaning.input
|
|
447
|
+
);
|
|
448
|
+
if (input === null) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
return [
|
|
452
|
+
{
|
|
453
|
+
type: "interaction",
|
|
454
|
+
binding: (0, import_behavioral_ir.unitInvocationBinding)({
|
|
455
|
+
recognition,
|
|
456
|
+
deploymentTarget: ending.platform,
|
|
457
|
+
instanceName: invokedUnitIn(input, ending)
|
|
458
|
+
}),
|
|
459
|
+
callee: ops.calleeText(),
|
|
460
|
+
interaction: {
|
|
461
|
+
class: "unit-invoke",
|
|
462
|
+
...payloadOf(input, ending)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
];
|
|
466
|
+
}
|
|
467
|
+
function invokedUnitIn(input, ending) {
|
|
468
|
+
for (const property of ending.named) {
|
|
469
|
+
const stated = input.property(property)?.name(ending.unsettledName) ?? null;
|
|
470
|
+
if (stated !== null && stated !== "") {
|
|
471
|
+
return (0, import_behavioral_ir.resourceNameIn)(stated);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
function payloadOf(input, ending) {
|
|
477
|
+
if (ending.payload === void 0) {
|
|
478
|
+
return {};
|
|
479
|
+
}
|
|
480
|
+
const stated = (0, import_extractor.unwrapJsonStringify)(
|
|
481
|
+
input.property(ending.payload)?.asArg() ?? null
|
|
482
|
+
);
|
|
483
|
+
return stated === null ? {} : { payload: stated };
|
|
484
|
+
}
|
|
385
485
|
function sqlAccess(matched) {
|
|
386
486
|
const { ops, subject, method, chain, recognition } = matched;
|
|
387
487
|
const ending = chain.ending;
|
|
@@ -485,7 +585,8 @@ function chainFrom(declared) {
|
|
|
485
585
|
var DISPATCHED_ON = {
|
|
486
586
|
storageAccess: "invocation",
|
|
487
587
|
sqlAccess: "access",
|
|
488
|
-
messageSend: "invocation"
|
|
588
|
+
messageSend: "invocation",
|
|
589
|
+
unitInvoke: "invocation"
|
|
489
590
|
};
|
|
490
591
|
function walkedBy(chains, walk2, recognizedAs) {
|
|
491
592
|
return chains.filter((chain) => DISPATCHED_ON[chain.ending.yields] === walk2).map((chain) => compile(chain, recognizedAs));
|
|
@@ -509,6 +610,7 @@ function pack(name, matches, spec) {
|
|
|
509
610
|
...spec.recognizers ?? []
|
|
510
611
|
],
|
|
511
612
|
...access.length === 0 ? {} : { accessRecognizers: access },
|
|
613
|
+
...spec.projectHelpers === void 0 ? {} : { projectHelpers: spec.projectHelpers },
|
|
512
614
|
declarations: declarationsIn(matches)
|
|
513
615
|
};
|
|
514
616
|
}
|
|
@@ -519,10 +621,19 @@ function wireOf(ending) {
|
|
|
519
621
|
if (ending.yields === "messageSend") {
|
|
520
622
|
return ending.wire;
|
|
521
623
|
}
|
|
624
|
+
if (ending.yields === "unitInvoke") {
|
|
625
|
+
return ending.platform;
|
|
626
|
+
}
|
|
522
627
|
return ending.transport ?? ending.system;
|
|
523
628
|
}
|
|
524
629
|
function declaredName(ending) {
|
|
525
|
-
|
|
630
|
+
if (ending.yields === "messageSend") {
|
|
631
|
+
return ending.wire;
|
|
632
|
+
}
|
|
633
|
+
if (ending.yields === "unitInvoke") {
|
|
634
|
+
return ending.platform;
|
|
635
|
+
}
|
|
636
|
+
return ending.system;
|
|
526
637
|
}
|
|
527
638
|
function protocolOf(chains) {
|
|
528
639
|
const wires = new Set(chains.map((chain) => wireOf(chain.ending)));
|
|
@@ -591,8 +702,15 @@ function rulesIn(chain) {
|
|
|
591
702
|
const link = chain.links.find(
|
|
592
703
|
(candidate) => candidate.asks === "methods"
|
|
593
704
|
);
|
|
705
|
+
const calls = chain.links.find(
|
|
706
|
+
(candidate) => candidate.asks === "calls"
|
|
707
|
+
);
|
|
708
|
+
const meanings = [
|
|
709
|
+
...Object.values(link?.table ?? {}),
|
|
710
|
+
...calls === void 0 ? [] : [calls.meaning]
|
|
711
|
+
];
|
|
594
712
|
const written = /* @__PURE__ */ new Map();
|
|
595
|
-
for (const method of
|
|
713
|
+
for (const method of meanings) {
|
|
596
714
|
for (const asks of ["selector", "fields"]) {
|
|
597
715
|
const rule = ruleFor(method[asks]);
|
|
598
716
|
if (rule !== null) {
|
|
@@ -672,15 +790,48 @@ function chainFrom3(declared) {
|
|
|
672
790
|
table,
|
|
673
791
|
ignoringCase: options?.ignoringCase ?? false
|
|
674
792
|
}),
|
|
793
|
+
calls: (meaning) => adding({ asks: "calls", meaning }),
|
|
675
794
|
container: (says) => adding(
|
|
676
795
|
typeof says === "function" ? { asks: "container", from: says } : { asks: "container", argument: says }
|
|
677
796
|
),
|
|
678
797
|
accessPath: (says) => adding({ asks: "accessPath", argument: says }),
|
|
679
|
-
containersIn: (says) => adding({
|
|
798
|
+
containersIn: (says, options) => adding({
|
|
799
|
+
asks: "containers",
|
|
800
|
+
in: says,
|
|
801
|
+
each: options?.each ?? "entry"
|
|
802
|
+
}),
|
|
680
803
|
input: (says) => adding({ asks: "input", at: says }),
|
|
681
804
|
example: (code) => chainFrom3({ ...declared, example: code })
|
|
682
805
|
};
|
|
683
806
|
}
|
|
807
|
+
|
|
808
|
+
// src/unitInvokes.ts
|
|
809
|
+
function unitInvokes(spec) {
|
|
810
|
+
const ending = {
|
|
811
|
+
yields: "unitInvoke",
|
|
812
|
+
platform: spec.platform,
|
|
813
|
+
named: spec.named,
|
|
814
|
+
...spec.payload === void 0 ? {} : { payload: spec.payload },
|
|
815
|
+
unsettledName: spec.unsettledName ?? "reference"
|
|
816
|
+
};
|
|
817
|
+
return chainFrom4({
|
|
818
|
+
links: chainStart(spec.client),
|
|
819
|
+
ending,
|
|
820
|
+
example: null
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
function chainFrom4(declared) {
|
|
824
|
+
const adding = (link) => chainFrom4({ ...declared, links: [...declared.links, link] });
|
|
825
|
+
return {
|
|
826
|
+
declared,
|
|
827
|
+
methods: (table, options) => adding({
|
|
828
|
+
asks: "methods",
|
|
829
|
+
table,
|
|
830
|
+
ignoringCase: options?.ignoringCase ?? false
|
|
831
|
+
}),
|
|
832
|
+
example: (code) => chainFrom4({ ...declared, example: code })
|
|
833
|
+
};
|
|
834
|
+
}
|
|
684
835
|
// Annotate the CommonJS export names for ESM import in node:
|
|
685
836
|
0 && (module.exports = {
|
|
686
837
|
compile,
|
|
@@ -693,5 +844,6 @@ function chainFrom3(declared) {
|
|
|
693
844
|
pack,
|
|
694
845
|
runExamples,
|
|
695
846
|
sqlStatements,
|
|
696
|
-
storageCalls
|
|
847
|
+
storageCalls,
|
|
848
|
+
unitInvokes
|
|
697
849
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, PackDeclarations } from '@suss/extractor';
|
|
2
|
-
export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, OpsCarrier, PackDeclarations, PatternPack, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
|
|
3
|
-
import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-
|
|
4
|
-
export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as
|
|
5
|
-
import { Effect, MessageBusSemantics } from '@suss/behavioral-ir';
|
|
1
|
+
import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, ProjectHelpers, PackDeclarations } from '@suss/extractor';
|
|
2
|
+
export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, HelperDeclarations, HelperSink, HelperValue, OpsCarrier, PackDeclarations, PatternPack, ProjectHelper, ProjectHelpers, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
|
|
3
|
+
import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument, U as UnitInvokeMethod } from './chain-IogmSi0t.cjs';
|
|
4
|
+
export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as CallsLink, j as ContainerArgument, k as ContainerLink, l as ContainerRule, m as ContainersLink, E as Ending, F as FromReceiver, n as InputLink, o as InputRule, K as KindAsAsked, p as Link, q as ManyIn, r as MatchStart, s as MessageSendEnding, t as MethodsLink, u as OneMessage, v as SelectorParamPick, w as SqlEnding, x as StartLink, y as StatedInputs, z as StatedRule, B as StorageEnding, D as SubjectLink, T as ToArgument, G as ToReceiver, H as UnitInvokeEnding } from './chain-IogmSi0t.cjs';
|
|
5
|
+
import { Effect, MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The small builders a pack writes its origins with, and the vocabulary
|
|
@@ -188,6 +188,12 @@ interface PackSpec {
|
|
|
188
188
|
* function links, so the cost of staying here is visible.
|
|
189
189
|
*/
|
|
190
190
|
recognizers?: InvocationRecognizer[];
|
|
191
|
+
/**
|
|
192
|
+
* Functions the project wrote in front of this library, read once
|
|
193
|
+
* across the project before extraction. What the pack makes of them
|
|
194
|
+
* joins the chains above for the rest of the run.
|
|
195
|
+
*/
|
|
196
|
+
projectHelpers?: ProjectHelpers;
|
|
191
197
|
}
|
|
192
198
|
/** A pack, assembled from the calls it says it matches. */
|
|
193
199
|
declare function pack(name: string, matches: readonly Match[], spec: PackSpec): PatternPack;
|
|
@@ -287,6 +293,12 @@ interface StorageCalls {
|
|
|
287
293
|
methods(table: Readonly<Record<string, StorageMethod>>, options?: {
|
|
288
294
|
ignoringCase?: boolean;
|
|
289
295
|
}): StorageCalls;
|
|
296
|
+
/**
|
|
297
|
+
* What a bare call of the client itself does. A store hook is the
|
|
298
|
+
* case: `useAppStore((s) => s.bears)` reaches for no method, so the
|
|
299
|
+
* one meaning here is the whole answer.
|
|
300
|
+
*/
|
|
301
|
+
calls(meaning: StorageMethod): StorageCalls;
|
|
290
302
|
/**
|
|
291
303
|
* Which container a call's selector belongs to, as an argument or as
|
|
292
304
|
* the pack's own rule. Without this the first name a call reached is
|
|
@@ -297,9 +309,12 @@ interface StorageCalls {
|
|
|
297
309
|
accessPath(says: ArgumentPick): StorageCalls;
|
|
298
310
|
/**
|
|
299
311
|
* Where a call that reaches several containers at once states them.
|
|
300
|
-
* Each entry becomes one effect, against the container its key says
|
|
312
|
+
* Each entry becomes one effect, against the container its key says,
|
|
313
|
+
* or against the name itself where the call lists names alone.
|
|
301
314
|
*/
|
|
302
|
-
containersIn(says: OneArgument
|
|
315
|
+
containersIn(says: OneArgument, options?: {
|
|
316
|
+
each?: "entry" | "name";
|
|
317
|
+
}): StorageCalls;
|
|
303
318
|
/**
|
|
304
319
|
* Where a call states its inputs, when it states them as one object.
|
|
305
320
|
* A call that states none does not match, and a rule the pack wrote
|
|
@@ -314,4 +329,46 @@ interface StorageCalls {
|
|
|
314
329
|
/** A pack that recognizes calls against a store. */
|
|
315
330
|
declare function storageCalls(spec: StorageCallsSpec): StorageCalls;
|
|
316
331
|
|
|
317
|
-
|
|
332
|
+
/**
|
|
333
|
+
* unitInvokes.ts: a pack that recognizes a call invoking one deployed
|
|
334
|
+
* unit by name.
|
|
335
|
+
*
|
|
336
|
+
* The AWS SDK shape is the one this was written against, and it is the
|
|
337
|
+
* same shape a send has: an operation is a command class and its
|
|
338
|
+
* arguments are one object, so the pack says which command and which of
|
|
339
|
+
* its properties says which unit the call reaches.
|
|
340
|
+
*
|
|
341
|
+
* client.send(new InvokeCommand({ FunctionName, Payload }))
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
interface UnitInvokesSpec {
|
|
345
|
+
/** The platform running the unit, in the IR's deployable-unit words. */
|
|
346
|
+
platform: DeployableUnit["deploymentTarget"];
|
|
347
|
+
/** The properties the call may name the unit on, tried in order. */
|
|
348
|
+
named: readonly string[];
|
|
349
|
+
/** The property the call states its payload on. */
|
|
350
|
+
payload?: string;
|
|
351
|
+
/**
|
|
352
|
+
* What a reader gives back for a name nothing settles. Defaults to
|
|
353
|
+
* keeping the reference, since a function name reaches the code
|
|
354
|
+
* through an env var far more often than it is written out.
|
|
355
|
+
*/
|
|
356
|
+
unsettledName?: UnsettledName;
|
|
357
|
+
/** How the pack pins down the client its calls are on. */
|
|
358
|
+
client?: ReceiverOrigin;
|
|
359
|
+
}
|
|
360
|
+
/** A chain over invokes, and the links that can still be added. */
|
|
361
|
+
interface UnitInvokes {
|
|
362
|
+
/** Which methods invoke, and where each one states its request. */
|
|
363
|
+
methods(table: Readonly<Record<string, UnitInvokeMethod>>, options?: {
|
|
364
|
+
ignoringCase?: boolean;
|
|
365
|
+
}): UnitInvokes;
|
|
366
|
+
/** A line of code this matches, which the pack's tests run. */
|
|
367
|
+
example(code: string): UnitInvokes;
|
|
368
|
+
/** The links and the ending, as data. */
|
|
369
|
+
readonly declared: Chain<UnitInvokeMethod>;
|
|
370
|
+
}
|
|
371
|
+
/** A pack that recognizes a call invoking a deployed unit. */
|
|
372
|
+
declare function unitInvokes(spec: UnitInvokesSpec): UnitInvokes;
|
|
373
|
+
|
|
374
|
+
export { ArgumentPick, CallStep, Chain, ChannelPart, InterpolatesLink, LinkFunction, type Match, MessageLocation, MessageSendMethod, type MessageSends, type MessageSendsSpec, MethodMeaning, OneArgument, type PackSpec, type RanExample, type RunOverCode, SqlMethod, type SqlStatements, type SqlStatementsSpec, type StorageCalls, type StorageCallsSpec, StorageMethod, UnitInvokeMethod, type UnitInvokes, type UnitInvokesSpec, compile, constructedFrom, declarationsIn, declaredBy, examplesMissing, messageSends, opsIn, pack, runExamples, sqlStatements, storageCalls, unitInvokes };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, PackDeclarations } from '@suss/extractor';
|
|
2
|
-
export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, OpsCarrier, PackDeclarations, PatternPack, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
|
|
3
|
-
import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument } from './chain-
|
|
4
|
-
export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as
|
|
5
|
-
import { Effect, MessageBusSemantics } from '@suss/behavioral-ir';
|
|
1
|
+
import { ConstructedFrom, DeclaredBy, CallOps, InvocationRecognizer, PatternPack, UnsettledName, ReceiverOrigin, ProjectHelpers, PackDeclarations } from '@suss/extractor';
|
|
2
|
+
export { CallOps, ConstructedFrom, DeclaredBy, DeclaredMatch, HelperDeclarations, HelperSink, HelperValue, OpsCarrier, PackDeclarations, PatternPack, ProjectHelper, ProjectHelpers, ReceiverOrigin, UnsettledName, ValueEntry, ValueOps } from '@suss/extractor';
|
|
3
|
+
import { C as Chain, M as MethodMeaning, a as MessageSendMethod, b as MessageLocation, c as ChannelPart, S as SqlMethod, I as InterpolatesLink, d as CallStep, e as StorageMethod, A as ArgumentPick, L as LinkFunction, O as OneArgument, U as UnitInvokeMethod } from './chain-IogmSi0t.js';
|
|
4
|
+
export { f as AccessKind, g as AccessPathLink, h as ArgumentsFrom, i as CallsLink, j as ContainerArgument, k as ContainerLink, l as ContainerRule, m as ContainersLink, E as Ending, F as FromReceiver, n as InputLink, o as InputRule, K as KindAsAsked, p as Link, q as ManyIn, r as MatchStart, s as MessageSendEnding, t as MethodsLink, u as OneMessage, v as SelectorParamPick, w as SqlEnding, x as StartLink, y as StatedInputs, z as StatedRule, B as StorageEnding, D as SubjectLink, T as ToArgument, G as ToReceiver, H as UnitInvokeEnding } from './chain-IogmSi0t.js';
|
|
5
|
+
import { Effect, MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The small builders a pack writes its origins with, and the vocabulary
|
|
@@ -188,6 +188,12 @@ interface PackSpec {
|
|
|
188
188
|
* function links, so the cost of staying here is visible.
|
|
189
189
|
*/
|
|
190
190
|
recognizers?: InvocationRecognizer[];
|
|
191
|
+
/**
|
|
192
|
+
* Functions the project wrote in front of this library, read once
|
|
193
|
+
* across the project before extraction. What the pack makes of them
|
|
194
|
+
* joins the chains above for the rest of the run.
|
|
195
|
+
*/
|
|
196
|
+
projectHelpers?: ProjectHelpers;
|
|
191
197
|
}
|
|
192
198
|
/** A pack, assembled from the calls it says it matches. */
|
|
193
199
|
declare function pack(name: string, matches: readonly Match[], spec: PackSpec): PatternPack;
|
|
@@ -287,6 +293,12 @@ interface StorageCalls {
|
|
|
287
293
|
methods(table: Readonly<Record<string, StorageMethod>>, options?: {
|
|
288
294
|
ignoringCase?: boolean;
|
|
289
295
|
}): StorageCalls;
|
|
296
|
+
/**
|
|
297
|
+
* What a bare call of the client itself does. A store hook is the
|
|
298
|
+
* case: `useAppStore((s) => s.bears)` reaches for no method, so the
|
|
299
|
+
* one meaning here is the whole answer.
|
|
300
|
+
*/
|
|
301
|
+
calls(meaning: StorageMethod): StorageCalls;
|
|
290
302
|
/**
|
|
291
303
|
* Which container a call's selector belongs to, as an argument or as
|
|
292
304
|
* the pack's own rule. Without this the first name a call reached is
|
|
@@ -297,9 +309,12 @@ interface StorageCalls {
|
|
|
297
309
|
accessPath(says: ArgumentPick): StorageCalls;
|
|
298
310
|
/**
|
|
299
311
|
* Where a call that reaches several containers at once states them.
|
|
300
|
-
* Each entry becomes one effect, against the container its key says
|
|
312
|
+
* Each entry becomes one effect, against the container its key says,
|
|
313
|
+
* or against the name itself where the call lists names alone.
|
|
301
314
|
*/
|
|
302
|
-
containersIn(says: OneArgument
|
|
315
|
+
containersIn(says: OneArgument, options?: {
|
|
316
|
+
each?: "entry" | "name";
|
|
317
|
+
}): StorageCalls;
|
|
303
318
|
/**
|
|
304
319
|
* Where a call states its inputs, when it states them as one object.
|
|
305
320
|
* A call that states none does not match, and a rule the pack wrote
|
|
@@ -314,4 +329,46 @@ interface StorageCalls {
|
|
|
314
329
|
/** A pack that recognizes calls against a store. */
|
|
315
330
|
declare function storageCalls(spec: StorageCallsSpec): StorageCalls;
|
|
316
331
|
|
|
317
|
-
|
|
332
|
+
/**
|
|
333
|
+
* unitInvokes.ts: a pack that recognizes a call invoking one deployed
|
|
334
|
+
* unit by name.
|
|
335
|
+
*
|
|
336
|
+
* The AWS SDK shape is the one this was written against, and it is the
|
|
337
|
+
* same shape a send has: an operation is a command class and its
|
|
338
|
+
* arguments are one object, so the pack says which command and which of
|
|
339
|
+
* its properties says which unit the call reaches.
|
|
340
|
+
*
|
|
341
|
+
* client.send(new InvokeCommand({ FunctionName, Payload }))
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
interface UnitInvokesSpec {
|
|
345
|
+
/** The platform running the unit, in the IR's deployable-unit words. */
|
|
346
|
+
platform: DeployableUnit["deploymentTarget"];
|
|
347
|
+
/** The properties the call may name the unit on, tried in order. */
|
|
348
|
+
named: readonly string[];
|
|
349
|
+
/** The property the call states its payload on. */
|
|
350
|
+
payload?: string;
|
|
351
|
+
/**
|
|
352
|
+
* What a reader gives back for a name nothing settles. Defaults to
|
|
353
|
+
* keeping the reference, since a function name reaches the code
|
|
354
|
+
* through an env var far more often than it is written out.
|
|
355
|
+
*/
|
|
356
|
+
unsettledName?: UnsettledName;
|
|
357
|
+
/** How the pack pins down the client its calls are on. */
|
|
358
|
+
client?: ReceiverOrigin;
|
|
359
|
+
}
|
|
360
|
+
/** A chain over invokes, and the links that can still be added. */
|
|
361
|
+
interface UnitInvokes {
|
|
362
|
+
/** Which methods invoke, and where each one states its request. */
|
|
363
|
+
methods(table: Readonly<Record<string, UnitInvokeMethod>>, options?: {
|
|
364
|
+
ignoringCase?: boolean;
|
|
365
|
+
}): UnitInvokes;
|
|
366
|
+
/** A line of code this matches, which the pack's tests run. */
|
|
367
|
+
example(code: string): UnitInvokes;
|
|
368
|
+
/** The links and the ending, as data. */
|
|
369
|
+
readonly declared: Chain<UnitInvokeMethod>;
|
|
370
|
+
}
|
|
371
|
+
/** A pack that recognizes a call invoking a deployed unit. */
|
|
372
|
+
declare function unitInvokes(spec: UnitInvokesSpec): UnitInvokes;
|
|
373
|
+
|
|
374
|
+
export { ArgumentPick, CallStep, Chain, ChannelPart, InterpolatesLink, LinkFunction, type Match, MessageLocation, MessageSendMethod, type MessageSends, type MessageSendsSpec, MethodMeaning, OneArgument, type PackSpec, type RanExample, type RunOverCode, SqlMethod, type SqlStatements, type SqlStatementsSpec, type StorageCalls, type StorageCallsSpec, StorageMethod, UnitInvokeMethod, type UnitInvokes, type UnitInvokesSpec, compile, constructedFrom, declarationsIn, declaredBy, examplesMissing, messageSends, opsIn, pack, runExamples, sqlStatements, storageCalls, unitInvokes };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/compile.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
messageBusBinding,
|
|
4
|
+
resourceNameIn,
|
|
5
|
+
storageBinding,
|
|
6
|
+
unitInvocationBinding
|
|
7
|
+
} from "@suss/behavioral-ir";
|
|
3
8
|
import { unwrapJsonStringify } from "@suss/extractor";
|
|
4
9
|
import { readSqlAccess, sqlFromParts } from "@suss/sql";
|
|
5
10
|
|
|
@@ -28,8 +33,22 @@ var MAX_RECEIVER_HOPS = 8;
|
|
|
28
33
|
function compile(chain, recognition) {
|
|
29
34
|
return (_call, ctx) => {
|
|
30
35
|
const ops = opsIn(ctx);
|
|
36
|
+
if (ops === null) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const bare = bareCall(chain, ops);
|
|
40
|
+
if (bare !== null) {
|
|
41
|
+
return YIELD[chain.ending.yields]({
|
|
42
|
+
ops,
|
|
43
|
+
subject: ops,
|
|
44
|
+
method: bare.method,
|
|
45
|
+
meaning: bare.meaning,
|
|
46
|
+
chain,
|
|
47
|
+
recognition
|
|
48
|
+
});
|
|
49
|
+
}
|
|
31
50
|
const methods = methodsIn(chain);
|
|
32
|
-
if (
|
|
51
|
+
if (methods === null) {
|
|
33
52
|
return null;
|
|
34
53
|
}
|
|
35
54
|
for (const subject of subjectsOf(chain, ops)) {
|
|
@@ -54,6 +73,21 @@ function linkIn(chain, asks) {
|
|
|
54
73
|
const link = chain.links.find((candidate) => candidate.asks === asks);
|
|
55
74
|
return link ?? null;
|
|
56
75
|
}
|
|
76
|
+
function bareCall(chain, ops) {
|
|
77
|
+
const link = linkIn(chain, "calls");
|
|
78
|
+
const start = linkIn(chain, "start");
|
|
79
|
+
if (link === null || start === null || start.at.starts !== "receiver") {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (ops.method() !== null || ops.namedCallee?.() !== true) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const written = ops.callee();
|
|
86
|
+
if (written === null || !written.isFrom(start.at.origin)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { method: ops.calleeText(), meaning: link.meaning };
|
|
90
|
+
}
|
|
57
91
|
function methodsIn(chain) {
|
|
58
92
|
return linkIn(chain, "methods");
|
|
59
93
|
}
|
|
@@ -127,7 +161,8 @@ function startsHere(chain, subject) {
|
|
|
127
161
|
var YIELD = {
|
|
128
162
|
storageAccess,
|
|
129
163
|
sqlAccess,
|
|
130
|
-
messageSend
|
|
164
|
+
messageSend,
|
|
165
|
+
unitInvoke
|
|
131
166
|
};
|
|
132
167
|
function storageAccess(matched) {
|
|
133
168
|
const { subject, chain } = matched;
|
|
@@ -158,7 +193,7 @@ function accessEffect(matched, access) {
|
|
|
158
193
|
storageSystem: ending.system,
|
|
159
194
|
...ending.transport === void 0 ? {} : { transport: ending.transport },
|
|
160
195
|
scope: ending.scope,
|
|
161
|
-
container: reached.
|
|
196
|
+
container: reached.stated ? reached.container : containerOf(chain, selector, subject, unsettled),
|
|
162
197
|
accessPath: accessPathOf(chain, subject, unsettled)
|
|
163
198
|
}),
|
|
164
199
|
callee: ops.calleeText(),
|
|
@@ -173,11 +208,24 @@ function accessEffect(matched, access) {
|
|
|
173
208
|
}
|
|
174
209
|
function reachedBy(chain, subject, unsettled) {
|
|
175
210
|
const link = linkIn(chain, "containers");
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
211
|
+
const alone = [{ container: null, entry: null, stated: false }];
|
|
212
|
+
const stated = link === null ? null : statedValue(subject, link.in);
|
|
213
|
+
if (link === null || stated === null) {
|
|
214
|
+
return alone;
|
|
215
|
+
}
|
|
216
|
+
if (link.each === "name") {
|
|
217
|
+
const named = stated.items().map((item) => ({
|
|
218
|
+
container: item.name(unsettled),
|
|
219
|
+
entry: null,
|
|
220
|
+
stated: true
|
|
221
|
+
}));
|
|
222
|
+
return named.length === 0 ? alone : named;
|
|
223
|
+
}
|
|
224
|
+
return stated.entries(unsettled).map((entry) => ({
|
|
225
|
+
container: entry.key,
|
|
226
|
+
entry: entry.value,
|
|
227
|
+
stated: true
|
|
228
|
+
}));
|
|
181
229
|
}
|
|
182
230
|
function kindOf(kind, subject, unsettled) {
|
|
183
231
|
if (typeof kind === "string") {
|
|
@@ -242,6 +290,9 @@ function namesFor(says, subject, stated, unsettled) {
|
|
|
242
290
|
const input2 = stated.input;
|
|
243
291
|
return input2 === null ? [] : [...says({ ...stated, input: input2 })];
|
|
244
292
|
}
|
|
293
|
+
if ("selectorParam" in says) {
|
|
294
|
+
return [...subject.parameterReadsAt?.(says.selectorParam) ?? []];
|
|
295
|
+
}
|
|
245
296
|
const pointed = says;
|
|
246
297
|
if (!("by" in pointed)) {
|
|
247
298
|
return namesAt(subject, pointed, unsettled);
|
|
@@ -314,7 +365,7 @@ function channelOf(message, input, ending) {
|
|
|
314
365
|
const parts = [];
|
|
315
366
|
for (const part of ending.channel) {
|
|
316
367
|
const holder = part.on === "theInput" ? input : message;
|
|
317
|
-
const written = holder
|
|
368
|
+
const written = firstWritten(holder, part.property);
|
|
318
369
|
if (written === null) {
|
|
319
370
|
if (part.whenAbsent === void 0) {
|
|
320
371
|
return null;
|
|
@@ -330,6 +381,15 @@ function channelOf(message, input, ending) {
|
|
|
330
381
|
}
|
|
331
382
|
return parts.join(ending.channelSeparator ?? "#");
|
|
332
383
|
}
|
|
384
|
+
function firstWritten(holder, properties) {
|
|
385
|
+
for (const property of properties) {
|
|
386
|
+
const written = holder.property(property);
|
|
387
|
+
if (written !== null) {
|
|
388
|
+
return written;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
333
393
|
function bodyOf(message, ending) {
|
|
334
394
|
if (ending.body === void 0) {
|
|
335
395
|
return {};
|
|
@@ -346,6 +406,50 @@ function routingKeyOf(message, ending) {
|
|
|
346
406
|
const stated = message.property(ending.routingKey)?.text() ?? null;
|
|
347
407
|
return stated === null || stated === "" ? {} : { routingKey: stated };
|
|
348
408
|
}
|
|
409
|
+
function unitInvoke(matched) {
|
|
410
|
+
const { ops, subject, chain, recognition } = matched;
|
|
411
|
+
const ending = chain.ending;
|
|
412
|
+
const input = statedValue(
|
|
413
|
+
subject,
|
|
414
|
+
matched.meaning.input
|
|
415
|
+
);
|
|
416
|
+
if (input === null) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
return [
|
|
420
|
+
{
|
|
421
|
+
type: "interaction",
|
|
422
|
+
binding: unitInvocationBinding({
|
|
423
|
+
recognition,
|
|
424
|
+
deploymentTarget: ending.platform,
|
|
425
|
+
instanceName: invokedUnitIn(input, ending)
|
|
426
|
+
}),
|
|
427
|
+
callee: ops.calleeText(),
|
|
428
|
+
interaction: {
|
|
429
|
+
class: "unit-invoke",
|
|
430
|
+
...payloadOf(input, ending)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
function invokedUnitIn(input, ending) {
|
|
436
|
+
for (const property of ending.named) {
|
|
437
|
+
const stated = input.property(property)?.name(ending.unsettledName) ?? null;
|
|
438
|
+
if (stated !== null && stated !== "") {
|
|
439
|
+
return resourceNameIn(stated);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
function payloadOf(input, ending) {
|
|
445
|
+
if (ending.payload === void 0) {
|
|
446
|
+
return {};
|
|
447
|
+
}
|
|
448
|
+
const stated = unwrapJsonStringify(
|
|
449
|
+
input.property(ending.payload)?.asArg() ?? null
|
|
450
|
+
);
|
|
451
|
+
return stated === null ? {} : { payload: stated };
|
|
452
|
+
}
|
|
349
453
|
function sqlAccess(matched) {
|
|
350
454
|
const { ops, subject, method, chain, recognition } = matched;
|
|
351
455
|
const ending = chain.ending;
|
|
@@ -449,7 +553,8 @@ function chainFrom(declared) {
|
|
|
449
553
|
var DISPATCHED_ON = {
|
|
450
554
|
storageAccess: "invocation",
|
|
451
555
|
sqlAccess: "access",
|
|
452
|
-
messageSend: "invocation"
|
|
556
|
+
messageSend: "invocation",
|
|
557
|
+
unitInvoke: "invocation"
|
|
453
558
|
};
|
|
454
559
|
function walkedBy(chains, walk2, recognizedAs) {
|
|
455
560
|
return chains.filter((chain) => DISPATCHED_ON[chain.ending.yields] === walk2).map((chain) => compile(chain, recognizedAs));
|
|
@@ -473,6 +578,7 @@ function pack(name, matches, spec) {
|
|
|
473
578
|
...spec.recognizers ?? []
|
|
474
579
|
],
|
|
475
580
|
...access.length === 0 ? {} : { accessRecognizers: access },
|
|
581
|
+
...spec.projectHelpers === void 0 ? {} : { projectHelpers: spec.projectHelpers },
|
|
476
582
|
declarations: declarationsIn(matches)
|
|
477
583
|
};
|
|
478
584
|
}
|
|
@@ -483,10 +589,19 @@ function wireOf(ending) {
|
|
|
483
589
|
if (ending.yields === "messageSend") {
|
|
484
590
|
return ending.wire;
|
|
485
591
|
}
|
|
592
|
+
if (ending.yields === "unitInvoke") {
|
|
593
|
+
return ending.platform;
|
|
594
|
+
}
|
|
486
595
|
return ending.transport ?? ending.system;
|
|
487
596
|
}
|
|
488
597
|
function declaredName(ending) {
|
|
489
|
-
|
|
598
|
+
if (ending.yields === "messageSend") {
|
|
599
|
+
return ending.wire;
|
|
600
|
+
}
|
|
601
|
+
if (ending.yields === "unitInvoke") {
|
|
602
|
+
return ending.platform;
|
|
603
|
+
}
|
|
604
|
+
return ending.system;
|
|
490
605
|
}
|
|
491
606
|
function protocolOf(chains) {
|
|
492
607
|
const wires = new Set(chains.map((chain) => wireOf(chain.ending)));
|
|
@@ -555,8 +670,15 @@ function rulesIn(chain) {
|
|
|
555
670
|
const link = chain.links.find(
|
|
556
671
|
(candidate) => candidate.asks === "methods"
|
|
557
672
|
);
|
|
673
|
+
const calls = chain.links.find(
|
|
674
|
+
(candidate) => candidate.asks === "calls"
|
|
675
|
+
);
|
|
676
|
+
const meanings = [
|
|
677
|
+
...Object.values(link?.table ?? {}),
|
|
678
|
+
...calls === void 0 ? [] : [calls.meaning]
|
|
679
|
+
];
|
|
558
680
|
const written = /* @__PURE__ */ new Map();
|
|
559
|
-
for (const method of
|
|
681
|
+
for (const method of meanings) {
|
|
560
682
|
for (const asks of ["selector", "fields"]) {
|
|
561
683
|
const rule = ruleFor(method[asks]);
|
|
562
684
|
if (rule !== null) {
|
|
@@ -636,15 +758,48 @@ function chainFrom3(declared) {
|
|
|
636
758
|
table,
|
|
637
759
|
ignoringCase: options?.ignoringCase ?? false
|
|
638
760
|
}),
|
|
761
|
+
calls: (meaning) => adding({ asks: "calls", meaning }),
|
|
639
762
|
container: (says) => adding(
|
|
640
763
|
typeof says === "function" ? { asks: "container", from: says } : { asks: "container", argument: says }
|
|
641
764
|
),
|
|
642
765
|
accessPath: (says) => adding({ asks: "accessPath", argument: says }),
|
|
643
|
-
containersIn: (says) => adding({
|
|
766
|
+
containersIn: (says, options) => adding({
|
|
767
|
+
asks: "containers",
|
|
768
|
+
in: says,
|
|
769
|
+
each: options?.each ?? "entry"
|
|
770
|
+
}),
|
|
644
771
|
input: (says) => adding({ asks: "input", at: says }),
|
|
645
772
|
example: (code) => chainFrom3({ ...declared, example: code })
|
|
646
773
|
};
|
|
647
774
|
}
|
|
775
|
+
|
|
776
|
+
// src/unitInvokes.ts
|
|
777
|
+
function unitInvokes(spec) {
|
|
778
|
+
const ending = {
|
|
779
|
+
yields: "unitInvoke",
|
|
780
|
+
platform: spec.platform,
|
|
781
|
+
named: spec.named,
|
|
782
|
+
...spec.payload === void 0 ? {} : { payload: spec.payload },
|
|
783
|
+
unsettledName: spec.unsettledName ?? "reference"
|
|
784
|
+
};
|
|
785
|
+
return chainFrom4({
|
|
786
|
+
links: chainStart(spec.client),
|
|
787
|
+
ending,
|
|
788
|
+
example: null
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
function chainFrom4(declared) {
|
|
792
|
+
const adding = (link) => chainFrom4({ ...declared, links: [...declared.links, link] });
|
|
793
|
+
return {
|
|
794
|
+
declared,
|
|
795
|
+
methods: (table, options) => adding({
|
|
796
|
+
asks: "methods",
|
|
797
|
+
table,
|
|
798
|
+
ignoringCase: options?.ignoringCase ?? false
|
|
799
|
+
}),
|
|
800
|
+
example: (code) => chainFrom4({ ...declared, example: code })
|
|
801
|
+
};
|
|
802
|
+
}
|
|
648
803
|
export {
|
|
649
804
|
compile,
|
|
650
805
|
constructedFrom,
|
|
@@ -656,5 +811,6 @@ export {
|
|
|
656
811
|
pack,
|
|
657
812
|
runExamples,
|
|
658
813
|
sqlStatements,
|
|
659
|
-
storageCalls
|
|
814
|
+
storageCalls,
|
|
815
|
+
unitInvokes
|
|
660
816
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/recognize",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Write a pack as data: a chain of named links that any language adapter with the executor ops can run.",
|
|
6
6
|
"keywords": [
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@suss/behavioral-ir": "0.
|
|
53
|
-
"@suss/extractor": "0.
|
|
54
|
-
"@suss/sql": "0.
|
|
52
|
+
"@suss/behavioral-ir": "0.21.0",
|
|
53
|
+
"@suss/extractor": "0.21.0",
|
|
54
|
+
"@suss/sql": "0.21.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"tsup": "*",
|