@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 +7 -7
- package/dist/ast.d.cts +9 -9
- package/dist/ast.d.ts +9 -9
- package/dist/{chain-CVG6Jnr-.d.cts → chain-DYU9ZdzF.d.cts} +105 -106
- package/dist/{chain-CVG6Jnr-.d.ts → chain-DYU9ZdzF.d.ts} +105 -106
- package/dist/index.cjs +235 -60
- package/dist/index.d.cts +67 -69
- package/dist/index.d.ts +67 -69
- package/dist/index.js +235 -60
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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-
|
|
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 ScopeLink, w as SelectorParamPick, x as SqlEnding, y as StartLink, z as StatedInputs, B as StatedRule, D as StorageEnding, G as SubjectLink, T as ToArgument, H as ToReceiver, J as UnitInvokeEnding } from './chain-
|
|
2
|
+
export { CallOps, ClassOps, 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-DYU9ZdzF.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 ScopeLink, w as SelectorParamPick, x as SqlEnding, y as StartLink, z as StatedInputs, B as StatedRule, D as StorageEnding, G as SubjectLink, T as ToArgument, H as ToReceiver, J as UnitInvokeEnding } from './chain-DYU9ZdzF.js';
|
|
5
5
|
import { Effect, MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -15,8 +15,8 @@ import { Effect, MessageBusSemantics, DeployableUnit } from '@suss/behavioral-ir
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* A receiver whose method one of these modules declared. Pass every
|
|
18
|
-
* module that ships the same client,
|
|
19
|
-
* one wire protocol.
|
|
18
|
+
* module that ships the same client, for example several packages that
|
|
19
|
+
* implement one wire protocol.
|
|
20
20
|
*/
|
|
21
21
|
declare function declaredBy(...importedFrom: readonly string[]): DeclaredBy;
|
|
22
22
|
/** A client made from one of these modules' exports. */
|
|
@@ -30,18 +30,18 @@ declare function constructedFrom(spec: {
|
|
|
30
30
|
declare function opsIn(ctx: unknown): CallOps | null;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* Turns a chain into the recognizer hook the adapters already call.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
* first
|
|
37
|
-
* a receiver to the library that made it walks declarations.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
35
|
+
* Every link is a guard, and the compiled hook checks the cheapest one
|
|
36
|
+
* first. Looking a method up in a table costs nothing, while following
|
|
37
|
+
* a receiver to the library that made it walks declarations. So the
|
|
38
|
+
* hook ignores the order the pack declared its links in. A link that
|
|
39
|
+
* changes what the receiver is, as a Prisma model property would, needs
|
|
40
|
+
* declared order back, and that change goes here.
|
|
41
41
|
*
|
|
42
42
|
* A chain that states a subject asks its questions of a call next to
|
|
43
|
-
* the one in hand,
|
|
44
|
-
*
|
|
43
|
+
* the one in hand, and the walk to that call is also here. DESIGN.md
|
|
44
|
+
* describes what each step reaches and why the walk is bounded.
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
47
|
/** A chain, as the hook the adapter dispatches to on every call. */
|
|
@@ -71,28 +71,27 @@ interface RanExample {
|
|
|
71
71
|
/** How a pack's test compiles one line and runs the pack over it. */
|
|
72
72
|
type RunOverCode = (code: string) => Effect[];
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
74
|
+
* Runs every example a pack declares. A declaration with no example is
|
|
75
|
+
* skipped here; `examplesMissing` lists those, and the pack health
|
|
76
|
+
* check reports them.
|
|
76
77
|
*/
|
|
77
78
|
declare function runExamples(pack: PatternPack, run: RunOverCode): RanExample[];
|
|
78
79
|
/** The declarations a pack shipped without a line of code to run. */
|
|
79
80
|
declare function examplesMissing(pack: PatternPack): string[];
|
|
80
81
|
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
-
* wire.
|
|
83
|
+
* A pack that recognizes a call putting a message on a wire.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
85
|
+
* It was written against the AWS SDK. There an operation is a command
|
|
86
|
+
* class and its arguments are one object, so the pack says which
|
|
87
|
+
* command, where the message is inside it, and which of the message's
|
|
88
|
+
* properties make up the channel:
|
|
89
89
|
*
|
|
90
90
|
* client.send(new SendMessageCommand({ QueueUrl, MessageBody }))
|
|
91
91
|
* client.send(new PutEventsCommand({ Entries: [{ EventBusName, DetailType }] }))
|
|
92
92
|
*
|
|
93
|
-
* A library
|
|
94
|
-
*
|
|
95
|
-
* belongs to the command rather than to a setting on one declaration.
|
|
93
|
+
* A library that sends one message and a batch through separate
|
|
94
|
+
* commands gets two declarations, one per command.
|
|
96
95
|
*/
|
|
97
96
|
|
|
98
97
|
interface MessageSendsSpec {
|
|
@@ -102,19 +101,19 @@ interface MessageSendsSpec {
|
|
|
102
101
|
messages: MessageLocation;
|
|
103
102
|
/** The parts of the channel, joined in the order they are written. */
|
|
104
103
|
channel: readonly ChannelPart[];
|
|
105
|
-
/** What joins the parts. Defaults to "#",
|
|
104
|
+
/** What joins the parts. Defaults to "#", as used between a bus and a subject. */
|
|
106
105
|
channelSeparator?: string;
|
|
107
106
|
/** The property a message states its body on. */
|
|
108
107
|
body?: string;
|
|
109
108
|
/**
|
|
110
|
-
* The property whose literal value
|
|
111
|
-
*
|
|
109
|
+
* The property whose literal value is recorded as the routing key. A
|
|
110
|
+
* reader can filter on it, and it stays out of the channel name.
|
|
112
111
|
*/
|
|
113
112
|
routingKey?: string;
|
|
114
113
|
/**
|
|
115
114
|
* What a reader gives back for a channel nothing in the source
|
|
116
|
-
* settles. Defaults to keeping the reference,
|
|
117
|
-
*
|
|
115
|
+
* settles. Defaults to keeping the reference, because most queues get
|
|
116
|
+
* their name at deploy time.
|
|
118
117
|
*/
|
|
119
118
|
unsettledName?: UnsettledName;
|
|
120
119
|
/** How the pack pins down the client its calls are on. */
|
|
@@ -146,15 +145,15 @@ declare function messageSends(spec: MessageSendsSpec): MessageSends;
|
|
|
146
145
|
*/
|
|
147
146
|
|
|
148
147
|
/**
|
|
149
|
-
* One thing a pack matches, as
|
|
150
|
-
*
|
|
148
|
+
* One thing a pack matches, as an entry point returns it. `storageCalls`
|
|
149
|
+
* and `sqlStatements` both return this.
|
|
151
150
|
*/
|
|
152
151
|
interface Match {
|
|
153
152
|
readonly declared: Chain<MethodMeaning>;
|
|
154
153
|
}
|
|
155
154
|
/** What a pack says about itself, beyond the calls it matches. */
|
|
156
155
|
interface PackSpec {
|
|
157
|
-
/** The languages the pack runs on, as the adapters
|
|
156
|
+
/** The languages the pack runs on, spelled as the adapters spell them. */
|
|
158
157
|
languages: string[];
|
|
159
158
|
/**
|
|
160
159
|
* The name effects from this pack are recorded under, which is the
|
|
@@ -168,24 +167,23 @@ interface PackSpec {
|
|
|
168
167
|
*/
|
|
169
168
|
version?: string;
|
|
170
169
|
/**
|
|
171
|
-
*
|
|
172
|
-
* record. An S3
|
|
173
|
-
*
|
|
170
|
+
* The pack's own protocol, when it differs from the wire its accesses
|
|
171
|
+
* record. An S3 pack records the AWS SDK as the wire on every effect,
|
|
172
|
+
* and its protocol is S3.
|
|
174
173
|
*/
|
|
175
174
|
protocol?: string;
|
|
176
175
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* imports gives it something.
|
|
176
|
+
* More modules that make a file worth reading, beyond the ones the
|
|
177
|
+
* chains match on. A helper imported by a relative path does not open
|
|
178
|
+
* the import gate, so the pack lists the library the helper imports.
|
|
181
179
|
*/
|
|
182
180
|
requiresImport?: string[];
|
|
183
181
|
/**
|
|
184
182
|
* Recognizers written against the adapter directly, run alongside the
|
|
185
|
-
* declared chains. A pack
|
|
186
|
-
* not declared yet here, and a
|
|
187
|
-
*
|
|
188
|
-
* function links, so
|
|
183
|
+
* declared chains. A pack moving to chains one call at a time keeps
|
|
184
|
+
* the calls it has not declared yet here, and a call no ending can
|
|
185
|
+
* describe stays here for good. The pack health report counts these
|
|
186
|
+
* as function links, so a reader sees how many are left.
|
|
189
187
|
*/
|
|
190
188
|
recognizers?: InvocationRecognizer[];
|
|
191
189
|
/**
|
|
@@ -198,9 +196,10 @@ interface PackSpec {
|
|
|
198
196
|
/** A pack, assembled from the calls it says it matches. */
|
|
199
197
|
declare function pack(name: string, matches: readonly Match[], spec: PackSpec): PatternPack;
|
|
200
198
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
199
|
+
* Describes a set of chains for the pack health report, for a pack that
|
|
200
|
+
* builds its `PatternPack` by hand. A pack partway through moving to
|
|
201
|
+
* declared chains keeps hand-written recognizers beside them, and this
|
|
202
|
+
* reports on the part that has moved.
|
|
204
203
|
*/
|
|
205
204
|
declare function declarationsIn(matches: readonly Match[]): PackDeclarations;
|
|
206
205
|
|
|
@@ -208,13 +207,13 @@ declare function declarationsIn(matches: readonly Match[]): PackDeclarations;
|
|
|
208
207
|
* The entry point for a pack whose calls hand the store a statement
|
|
209
208
|
* written as SQL.
|
|
210
209
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* the kind, the fields and the selector of every table the
|
|
215
|
-
* touches.
|
|
216
|
-
*
|
|
217
|
-
*
|
|
210
|
+
* With a typed client, the call's arguments show which table it
|
|
211
|
+
* reached. With a raw statement that information is in the SQL text,
|
|
212
|
+
* so the chain passes the text to `@suss/sql`. The parse gives the
|
|
213
|
+
* container, the kind, the fields and the selector of every table the
|
|
214
|
+
* statement touches. Every link before that point is one any other
|
|
215
|
+
* chain uses too, so SQL is a chain ending and needs no separate
|
|
216
|
+
* grammar.
|
|
218
217
|
*/
|
|
219
218
|
|
|
220
219
|
/** What a pack says about the store its statements reach. */
|
|
@@ -255,10 +254,10 @@ declare function sqlStatements(spec: SqlStatementsSpec): SqlStatements;
|
|
|
255
254
|
* The entry point for a pack that recognizes storage calls, and the
|
|
256
255
|
* chain it builds.
|
|
257
256
|
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
257
|
+
* A pack author writes `storageCalls` and never builds an `Effect` by
|
|
258
|
+
* hand. Each method on the returned value adds one link and returns a
|
|
259
|
+
* new chain, so by the time `pack` receives a chain it is plain data
|
|
260
|
+
* that nothing changes afterwards.
|
|
262
261
|
*/
|
|
263
262
|
|
|
264
263
|
/** What a pack says about the store its calls reach. */
|
|
@@ -271,8 +270,8 @@ interface StorageCallsSpec {
|
|
|
271
270
|
scope?: string;
|
|
272
271
|
/**
|
|
273
272
|
* How the pack pins down the client its calls are on. A pack that
|
|
274
|
-
* matches a helper a project
|
|
275
|
-
* because the name
|
|
273
|
+
* matches a helper listed in a project's own config leaves this out,
|
|
274
|
+
* because the helper's name is the only thing it has to match on.
|
|
276
275
|
*/
|
|
277
276
|
client?: ReceiverOrigin;
|
|
278
277
|
/**
|
|
@@ -294,9 +293,9 @@ interface StorageCalls {
|
|
|
294
293
|
ignoringCase?: boolean;
|
|
295
294
|
}): StorageCalls;
|
|
296
295
|
/**
|
|
297
|
-
* What a bare call of the client itself does. A store hook
|
|
298
|
-
*
|
|
299
|
-
*
|
|
296
|
+
* What a bare call of the client itself does. A store hook such as
|
|
297
|
+
* `useAppStore((s) => s.bears)` calls no method, so this meaning
|
|
298
|
+
* applies to every call.
|
|
300
299
|
*/
|
|
301
300
|
calls(meaning: StorageMethod): StorageCalls;
|
|
302
301
|
/**
|
|
@@ -336,13 +335,12 @@ interface StorageCalls {
|
|
|
336
335
|
declare function storageCalls(spec: StorageCallsSpec): StorageCalls;
|
|
337
336
|
|
|
338
337
|
/**
|
|
339
|
-
*
|
|
340
|
-
* unit by name.
|
|
338
|
+
* A pack that recognizes a call invoking one deployed unit by name.
|
|
341
339
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
340
|
+
* It was written against the AWS SDK, where an invoke looks like a
|
|
341
|
+
* message send: an operation is a command class and its arguments are
|
|
342
|
+
* one object. The pack says which command, and which of its properties
|
|
343
|
+
* identifies the unit the call reaches.
|
|
346
344
|
*
|
|
347
345
|
* client.send(new InvokeCommand({ FunctionName, Payload }))
|
|
348
346
|
*/
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ function opsIn(ctx) {
|
|
|
31
31
|
// src/compile.ts
|
|
32
32
|
var MAX_RECEIVER_HOPS = 8;
|
|
33
33
|
function compile(chain, recognition) {
|
|
34
|
+
const receiverMethods = methodsWhoseReceiversAreAsked(chain);
|
|
34
35
|
return (_call, ctx) => {
|
|
35
36
|
const ops = opsIn(ctx);
|
|
36
37
|
if (ops === null) {
|
|
@@ -51,6 +52,9 @@ function compile(chain, recognition) {
|
|
|
51
52
|
if (methods === null) {
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
55
|
+
if (receiverMethods !== null) {
|
|
56
|
+
ops.readingReceivers?.(receiverMethods);
|
|
57
|
+
}
|
|
54
58
|
for (const subject of subjectsOf(chain, ops)) {
|
|
55
59
|
const method = operationOf(subject);
|
|
56
60
|
const meaning = method === null ? null : listed(methods, method);
|
|
@@ -69,6 +73,14 @@ function compile(chain, recognition) {
|
|
|
69
73
|
return null;
|
|
70
74
|
};
|
|
71
75
|
}
|
|
76
|
+
function methodsWhoseReceiversAreAsked(chain) {
|
|
77
|
+
const start = linkIn(chain, "start");
|
|
78
|
+
const table = methodsIn(chain);
|
|
79
|
+
if (start === null || start.at.starts !== "receiver" || start.at.origin.origin !== "constructed" || linkIn(chain, "subject") !== null || table === null || table.ignoringCase) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return new Set(Object.keys(table.table));
|
|
83
|
+
}
|
|
72
84
|
function linkIn(chain, asks) {
|
|
73
85
|
const link = chain.links.find((candidate) => candidate.asks === asks);
|
|
74
86
|
return link ?? null;
|
|
@@ -102,7 +114,23 @@ function listed(link, method) {
|
|
|
102
114
|
}
|
|
103
115
|
function subjectsOf(chain, ops) {
|
|
104
116
|
const link = linkIn(chain, "subject");
|
|
105
|
-
|
|
117
|
+
if (link === null) {
|
|
118
|
+
return [ops];
|
|
119
|
+
}
|
|
120
|
+
announceArgumentReads(link.of, ops);
|
|
121
|
+
return walk([ops], link.of);
|
|
122
|
+
}
|
|
123
|
+
function announceArgumentReads(steps, ops) {
|
|
124
|
+
const first = steps[0];
|
|
125
|
+
if (first === void 0 || first.to !== "argument") {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const at = first.at;
|
|
129
|
+
if (typeof at === "number") {
|
|
130
|
+
ops.readingArguments?.(at, at);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
ops.readingArguments?.(at.from);
|
|
106
134
|
}
|
|
107
135
|
var STEP = {
|
|
108
136
|
receiver: (step, ops) => receiversOf(ops, step.method),
|
|
@@ -184,9 +212,24 @@ function accessEffect(matched, access) {
|
|
|
184
212
|
const ending = chain.ending;
|
|
185
213
|
const { input, kind, reached, unsettled } = access;
|
|
186
214
|
const stated = { input, entry: reached.entry, kind };
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
215
|
+
const listening = ops.statesSlots !== void 0;
|
|
216
|
+
const selectorRead = listening ? /* @__PURE__ */ new Map() : null;
|
|
217
|
+
const fieldsRead = listening ? /* @__PURE__ */ new Map() : null;
|
|
218
|
+
const selector = namesFor(
|
|
219
|
+
meaning.selector,
|
|
220
|
+
subject,
|
|
221
|
+
stated,
|
|
222
|
+
unsettled,
|
|
223
|
+
selectorRead
|
|
224
|
+
);
|
|
225
|
+
const fields = namesFor(
|
|
226
|
+
meaning.fields,
|
|
227
|
+
subject,
|
|
228
|
+
stated,
|
|
229
|
+
unsettled,
|
|
230
|
+
fieldsRead
|
|
231
|
+
);
|
|
232
|
+
const effect = {
|
|
190
233
|
type: "interaction",
|
|
191
234
|
binding: storageBinding({
|
|
192
235
|
recognition,
|
|
@@ -205,6 +248,69 @@ function accessEffect(matched, access) {
|
|
|
205
248
|
...selector.length > 0 ? { selector } : {}
|
|
206
249
|
}
|
|
207
250
|
};
|
|
251
|
+
const slots = [
|
|
252
|
+
...slotsRead("selector", selector, selectorRead),
|
|
253
|
+
...slotsRead("field", fields, fieldsRead)
|
|
254
|
+
];
|
|
255
|
+
if (slots.length > 0) {
|
|
256
|
+
ops.statesSlots?.(effect, slots);
|
|
257
|
+
}
|
|
258
|
+
return effect;
|
|
259
|
+
}
|
|
260
|
+
function slotsRead(slot, names, read) {
|
|
261
|
+
if (read === null) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
return [...new Set(names)].flatMap(
|
|
265
|
+
(name) => (read.get(name) ?? []).map((value) => ({ slot, name, value }))
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
function noteRead(read, name, value) {
|
|
269
|
+
const already = read.get(name);
|
|
270
|
+
if (already === void 0) {
|
|
271
|
+
read.set(name, [value]);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (!already.includes(value)) {
|
|
275
|
+
already.push(value);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function recording(value, read) {
|
|
279
|
+
return {
|
|
280
|
+
text: () => value.text(),
|
|
281
|
+
name: (unsettled) => value.name(unsettled),
|
|
282
|
+
...value.names === void 0 ? {} : { names: (cap) => value.names?.(cap) ?? null },
|
|
283
|
+
flag: () => value.flag(),
|
|
284
|
+
entries: (unsettled) => value.entries(unsettled).map((entry) => {
|
|
285
|
+
if (entry.key !== null) {
|
|
286
|
+
noteRead(read, entry.key, entry.value);
|
|
287
|
+
}
|
|
288
|
+
return { key: entry.key, value: recording(entry.value, read) };
|
|
289
|
+
}),
|
|
290
|
+
items: () => value.items().map((item) => recording(item, read)),
|
|
291
|
+
property: (name) => {
|
|
292
|
+
const inside = value.property(name);
|
|
293
|
+
if (inside === null) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
noteRead(read, name, inside);
|
|
297
|
+
return recording(inside, read);
|
|
298
|
+
},
|
|
299
|
+
asArg: () => value.asArg(),
|
|
300
|
+
parts: () => value.parts(),
|
|
301
|
+
holes: () => value.holes(),
|
|
302
|
+
...value.interpolated === void 0 ? {} : { interpolated: () => value.interpolated?.() ?? [] }
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function watched(inputs, read) {
|
|
306
|
+
if (read === null) {
|
|
307
|
+
return inputs;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
...inputs,
|
|
311
|
+
input: recording(inputs.input, read),
|
|
312
|
+
entry: inputs.entry === null ? null : recording(inputs.entry, read)
|
|
313
|
+
};
|
|
208
314
|
}
|
|
209
315
|
function reachedBy(chain, subject, unsettled) {
|
|
210
316
|
const link = linkIn(chain, "containers");
|
|
@@ -283,7 +389,7 @@ var NOTHING_STATED = {
|
|
|
283
389
|
parts: () => null,
|
|
284
390
|
holes: () => []
|
|
285
391
|
};
|
|
286
|
-
function namesFor(says, subject, stated, unsettled) {
|
|
392
|
+
function namesFor(says, subject, stated, unsettled, read = null) {
|
|
287
393
|
if (says === void 0) {
|
|
288
394
|
return [];
|
|
289
395
|
}
|
|
@@ -292,7 +398,7 @@ function namesFor(says, subject, stated, unsettled) {
|
|
|
292
398
|
}
|
|
293
399
|
if (typeof says === "function") {
|
|
294
400
|
const input2 = stated.input;
|
|
295
|
-
return input2 === null ? [] : [...says({ ...stated, input: input2 })];
|
|
401
|
+
return input2 === null ? [] : [...says(watched({ ...stated, input: input2 }, read))];
|
|
296
402
|
}
|
|
297
403
|
if ("selectorParam" in says) {
|
|
298
404
|
return [...subject.parameterReadsAt?.(says.selectorParam) ?? []];
|
|
@@ -302,7 +408,7 @@ function namesFor(says, subject, stated, unsettled) {
|
|
|
302
408
|
return namesAt(subject, pointed, unsettled);
|
|
303
409
|
}
|
|
304
410
|
const input = statedValue(subject, pointed.of) ?? NOTHING_STATED;
|
|
305
|
-
return [...pointed.by({ ...stated, input })];
|
|
411
|
+
return [...pointed.by(watched({ ...stated, input }, read))];
|
|
306
412
|
}
|
|
307
413
|
function namesAt(subject, pick, unsettled) {
|
|
308
414
|
if (pick === void 0) {
|
|
@@ -343,20 +449,27 @@ function messageSend(matched) {
|
|
|
343
449
|
}
|
|
344
450
|
const messages = messagesIn(input, ending.messages);
|
|
345
451
|
const sent = messages.length === 0 ? [NOTHING_STATED] : messages;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
452
|
+
const effects = [];
|
|
453
|
+
for (const message of sent) {
|
|
454
|
+
const channels = channelsOf({ message, input, ending });
|
|
455
|
+
for (const channel of channels) {
|
|
456
|
+
effects.push({
|
|
457
|
+
type: "interaction",
|
|
458
|
+
binding: messageBusBinding({
|
|
459
|
+
recognition,
|
|
460
|
+
messageBus: ending.wire,
|
|
461
|
+
channel
|
|
462
|
+
}),
|
|
463
|
+
callee: ops.calleeText(),
|
|
464
|
+
interaction: {
|
|
465
|
+
class: "message-send",
|
|
466
|
+
...bodyOf(message, ending),
|
|
467
|
+
...routingKeyOf(message, ending)
|
|
468
|
+
}
|
|
469
|
+
});
|
|
358
470
|
}
|
|
359
|
-
}
|
|
471
|
+
}
|
|
472
|
+
return effects;
|
|
360
473
|
}
|
|
361
474
|
function messagesIn(input, location) {
|
|
362
475
|
if (location.each === "theInput") {
|
|
@@ -365,25 +478,47 @@ function messagesIn(input, location) {
|
|
|
365
478
|
const collection = input.property(location.property);
|
|
366
479
|
return collection === null ? [] : collection.items();
|
|
367
480
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
481
|
+
var CHANNEL_CAP = 16;
|
|
482
|
+
function oneName(written, unsettled) {
|
|
483
|
+
const stated = written.name(unsettled);
|
|
484
|
+
return stated === null || stated === "" ? null : [stated];
|
|
485
|
+
}
|
|
486
|
+
function everyString(written, unsettled) {
|
|
487
|
+
const each = written.names?.(CHANNEL_CAP) ?? null;
|
|
488
|
+
if (each === null || each.length < 2 || each.includes("")) {
|
|
489
|
+
return oneName(written, unsettled);
|
|
490
|
+
}
|
|
491
|
+
return each;
|
|
492
|
+
}
|
|
493
|
+
function channelsOf(sending) {
|
|
494
|
+
const each = channelsRead(sending, "everyString");
|
|
495
|
+
return each.length > CHANNEL_CAP ? channelsRead(sending, "oneName") : each;
|
|
496
|
+
}
|
|
497
|
+
function channelsRead(sending, read) {
|
|
498
|
+
const separator = sending.ending.channelSeparator ?? "#";
|
|
499
|
+
let channels = null;
|
|
500
|
+
for (const part of sending.ending.channel) {
|
|
501
|
+
const values = partValues(part, sending, read);
|
|
502
|
+
if (values === null) {
|
|
503
|
+
return [null];
|
|
379
504
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
505
|
+
channels = channels === null ? values : crossed(channels, values, separator);
|
|
506
|
+
}
|
|
507
|
+
return channels ?? [""];
|
|
508
|
+
}
|
|
509
|
+
function crossed(heads, values, separator) {
|
|
510
|
+
return heads.flatMap(
|
|
511
|
+
(head) => values.map((value) => `${head}${separator}${value}`)
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
function partValues(part, sending, read) {
|
|
515
|
+
const holder = part.on === "theInput" ? sending.input : sending.message;
|
|
516
|
+
const written = firstWritten(holder, part.property);
|
|
517
|
+
if (written === null) {
|
|
518
|
+
return part.whenAbsent === void 0 ? null : [part.whenAbsent];
|
|
385
519
|
}
|
|
386
|
-
|
|
520
|
+
const unsettled = part.unsettled ?? sending.ending.unsettledName;
|
|
521
|
+
return read === "everyString" ? everyString(written, unsettled) : oneName(written, unsettled);
|
|
387
522
|
}
|
|
388
523
|
function firstWritten(holder, properties) {
|
|
389
524
|
for (const property of properties) {
|
|
@@ -466,28 +601,68 @@ function sqlAccess(matched) {
|
|
|
466
601
|
namesInHoles(chain, stated.value),
|
|
467
602
|
settledHoles(stated.value)
|
|
468
603
|
);
|
|
469
|
-
const accesses = readSqlAccess(statement, {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
604
|
+
const accesses = readSqlAccess(statement, {
|
|
605
|
+
dialect: ending.dialect,
|
|
606
|
+
placeholders: ops.statesSlots !== void 0
|
|
607
|
+
});
|
|
608
|
+
if (accesses.length === 0) {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
const values = placeholderValues(
|
|
612
|
+
stated,
|
|
613
|
+
subject,
|
|
614
|
+
matched.meaning.parameters
|
|
615
|
+
);
|
|
616
|
+
return accesses.map((access) => {
|
|
617
|
+
const effect = {
|
|
618
|
+
type: "interaction",
|
|
619
|
+
binding: storageBinding({
|
|
620
|
+
recognition,
|
|
621
|
+
storageSystem: ending.system,
|
|
622
|
+
...ending.transport === void 0 ? {} : { transport: ending.transport },
|
|
623
|
+
// A statement that qualifies its table says which namespace
|
|
624
|
+
// the access is in, and the pack's own scope is the fallback
|
|
625
|
+
// for a statement that leaves it out.
|
|
626
|
+
scope: access.qualifier[access.qualifier.length - 1] ?? ending.scope,
|
|
627
|
+
container: access.table
|
|
628
|
+
}),
|
|
629
|
+
callee: ops.calleeText(),
|
|
630
|
+
interaction: {
|
|
631
|
+
class: "storage-access",
|
|
632
|
+
kind: access.kind,
|
|
633
|
+
fields: access.fields,
|
|
634
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
635
|
+
operation: method
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
const slots = (access.placeholders ?? []).flatMap((one) => {
|
|
639
|
+
const value = placeholderValue(values, one.placeholder);
|
|
640
|
+
return value === null ? [] : [{ slot: SLOT_OF[one.clause], name: one.field, value }];
|
|
641
|
+
});
|
|
642
|
+
if (slots.length > 0) {
|
|
643
|
+
ops.statesSlots?.(effect, slots);
|
|
489
644
|
}
|
|
490
|
-
|
|
645
|
+
return effect;
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
var SLOT_OF = {
|
|
649
|
+
fields: "field",
|
|
650
|
+
selector: "selector"
|
|
651
|
+
};
|
|
652
|
+
function placeholderValues(stated, subject, says) {
|
|
653
|
+
const writesItsOwn = stated.parts.some((part) => /\$\d/.test(part));
|
|
654
|
+
if (stated.parts.length > 1 && !writesItsOwn) {
|
|
655
|
+
return { numbered: stated.value.interpolated?.() ?? [], named: null };
|
|
656
|
+
}
|
|
657
|
+
const picks = says === void 0 ? [] : Array.isArray(says) ? says : [says];
|
|
658
|
+
const given = picks.map((pick) => statedValue(subject, pick)).find((value) => value !== null);
|
|
659
|
+
return given === void 0 ? { numbered: [], named: null } : { numbered: given.items(), named: given };
|
|
660
|
+
}
|
|
661
|
+
function placeholderValue(values, placeholder) {
|
|
662
|
+
if (typeof placeholder === "number") {
|
|
663
|
+
return values.numbered[placeholder - 1] ?? null;
|
|
664
|
+
}
|
|
665
|
+
return values.named?.property(placeholder) ?? null;
|
|
491
666
|
}
|
|
492
667
|
function statementIn(subject, says) {
|
|
493
668
|
const picks = Array.isArray(says) ? says : [says];
|
|
@@ -679,8 +854,8 @@ function describe(chain) {
|
|
|
679
854
|
];
|
|
680
855
|
return {
|
|
681
856
|
name: declaredName(chain.ending),
|
|
682
|
-
// A method table with a rule inside
|
|
683
|
-
//
|
|
857
|
+
// A method table with a rule inside still counts as a data link. The
|
|
858
|
+
// rule is counted on its own, as a function link.
|
|
684
859
|
dataLinks: chain.links.length - links.length,
|
|
685
860
|
functionLinks: written.map((rule) => rule.asks),
|
|
686
861
|
astLinks: written.filter((rule) => rule.reachesAst).map((rule) => rule.asks),
|