@suss/extractor 0.20.0 → 0.21.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 +47 -0
- package/dist/index.cjs +403 -84
- package/dist/index.d.cts +386 -15
- package/dist/index.d.ts +386 -15
- package/dist/index.js +391 -79
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -58,6 +58,53 @@ const summary = assembleSummary(raw);
|
|
|
58
58
|
// summary.transitions[0].output.type === "response"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
## Composing the wrappers around a unit
|
|
62
|
+
|
|
63
|
+
A route's wire behaviour is not only what its own body does. Middleware, error handlers and validation hooks produce responses for it without appearing in it, which is why a service whose auth middleware returns 401 used to look like every route disagreed with its contract.
|
|
64
|
+
|
|
65
|
+
A wrapper is a meta-function: it takes a unit and returns a unit. Its own body says what it does once you know which call is the continuation, so the adapter reads the call to that parameter as a `delegate` terminal. A path through the wrapper that reaches it hands control to the wrapped unit; a path that ends first responds on its own.
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
composed = the wrapper's short circuits
|
|
69
|
+
+ (its pass-throughs x the wrapped unit's transitions)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`composeWrappers` runs that over a whole run's summaries. It reads the `wrappers` metadata a unit records, finds each wrapper's summary by file and name, and folds them innermost first, the way `mw1(mw2(handler))` reads. A wrapper the framework only calls on a throw goes on last and replaces the paths that ended by throwing with its own response. Every transition a wrapper contributed records which one, under `wrappers.from`, so a reader asking why a route returns 401 lands in the middleware.
|
|
73
|
+
|
|
74
|
+
What it does not read:
|
|
75
|
+
|
|
76
|
+
- **Anything after the continuation returns.** A middleware that inspects the response on the way back out is read up to the `next()` call and no further.
|
|
77
|
+
- **A wrapper whose continuation the pack does not declare, or whose call the walk cannot see.** Nothing says where control passes on, so the wrapper's outcomes are reported beside the unit's own rather than around them.
|
|
78
|
+
- **A throw the walk never saw.** An error handler composes over the paths that end by throwing, so a route whose 500 comes from a call the walk could not follow still does not report 500.
|
|
79
|
+
- **Registration order against route order.** A wrapper registered after a route still composes into it.
|
|
80
|
+
|
|
81
|
+
Composition multiplies paths, so it is capped by the same `MAX_PATHS` budget path enumeration uses. Past it the two sides are reported side by side and the unit gets a gap saying so.
|
|
82
|
+
|
|
83
|
+
## Which branch an effect belongs to
|
|
84
|
+
|
|
85
|
+
An adapter finds the calls a body makes once, and it finds the branches separately. Something then has to say which branch each call runs on. `guardsHoldOn` and `runsBefore` in `effectGuards.ts` are that decision, and both adapters that make it call these rather than keeping a copy.
|
|
86
|
+
|
|
87
|
+
A call belongs to a branch when two things are true of it.
|
|
88
|
+
|
|
89
|
+
- **No guard the call is written under was recorded the other way around on the branch.** A call inside `if (flag)` stays off every branch that recorded `!flag`. A call written above every guard has none, so it reaches all of them.
|
|
90
|
+
- **The call is written no later than the terminal's last line.** A call written after an early return did not run on that early return's path. The terminal's last line and not its first, because `return new Promise((resolve) => resolve(read()))` runs the call inside its own expression.
|
|
91
|
+
|
|
92
|
+
Neither test is enough on its own, and the two ways to get it wrong are both ordinary code.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const found = await dynamo.send(new GetItemCommand({ TableName: "Invoices" }));
|
|
96
|
+
if (!found.Item) {
|
|
97
|
+
return { statusCode: 404, body: JSON.stringify({ error: "no invoice" }) };
|
|
98
|
+
}
|
|
99
|
+
return { statusCode: 200, body: JSON.stringify({ invoice: found.Item }) };
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The read is under no guard, so guards alone put it on both branches, which is right. Source order alone would too. But `JSON.stringify` inside the 404 return is under `!found.Item`, and its line comes before the 200 return, so source order alone would put it on the 200 branch, where it never ran. Guards catch that. Turn the example around, put the read after the 404 return, and guards alone put it on the 404 branch, where it also never ran. Source order catches that.
|
|
103
|
+
|
|
104
|
+
The first test asks whether the branch contradicts the guard, not whether the branch repeats it. That matters because a branch often cannot speak about a guard at all. The path engine opacifies a loop, so a terminal after the loop records nothing about an `if` inside the loop body, and the same goes for a call inside a `catch` or inside a callback the walk descended into. The commonest shape in this repo is an accumulator: a loop that pushes into an array under a condition, then returns the array. Requiring the branch to repeat the guard takes those pushes off the only branch there is, and the unit comes out calling nothing at all. So silence on the branch leaves the effect in place, and only a guard written down the other way around takes it off.
|
|
105
|
+
|
|
106
|
+
Comparison is by the condition's polarity and the text it was written as, the same key a transition id is built from. An adapter therefore has to spell a guard on an effect the way it spells the same guard on a branch, or the two never meet and the guard is treated as silence.
|
|
107
|
+
|
|
61
108
|
## Coverage
|
|
62
109
|
|
|
63
110
|

|
package/dist/index.cjs
CHANGED
|
@@ -22,33 +22,45 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
IdMap: () => IdMap,
|
|
24
24
|
IdSet: () => IdSet,
|
|
25
|
+
MAX_PATHS: () => MAX_PATHS,
|
|
25
26
|
PathBudgetExceeded: () => PathBudgetExceeded,
|
|
27
|
+
SKIP_CHILDREN: () => SKIP_CHILDREN,
|
|
26
28
|
UnmodeledFlow: () => UnmodeledFlow,
|
|
27
29
|
absentReading: () => absentReading,
|
|
28
30
|
ambiguousReading: () => ambiguousReading,
|
|
29
31
|
andThenReading: () => andThenReading,
|
|
30
32
|
assembleSummary: () => assembleSummary,
|
|
31
33
|
assessConfidence: () => assessConfidence,
|
|
34
|
+
composeWrappers: () => composeWrappers,
|
|
35
|
+
configuredCallOption: () => configuredCallOption,
|
|
32
36
|
detectGaps: () => detectGaps,
|
|
33
37
|
effectToIR: () => effectToIR,
|
|
34
38
|
enumerateOrDegrade: () => enumerateOrDegrade,
|
|
35
39
|
enumerateStructuredPaths: () => enumerateStructuredPaths,
|
|
36
40
|
firstWrittenReading: () => firstWrittenReading,
|
|
41
|
+
guardsHoldOn: () => guardsHoldOn,
|
|
37
42
|
httpRouteDiscovery: () => httpRouteDiscovery,
|
|
38
43
|
makeTransitionId: () => makeTransitionId,
|
|
39
44
|
mapReading: () => mapReading,
|
|
40
45
|
paramToInput: () => paramToInput,
|
|
46
|
+
rawConditionToPredicate: () => rawConditionToPredicate,
|
|
41
47
|
registrationHelperDiscovery: () => registrationHelperDiscovery,
|
|
48
|
+
routeHelperIndex: () => routeHelperIndex,
|
|
49
|
+
runsBefore: () => runsBefore,
|
|
50
|
+
scopeOption: () => scopeOption,
|
|
42
51
|
sharedGatingConditions: () => sharedGatingConditions,
|
|
52
|
+
storageSystemOption: () => storageSystemOption,
|
|
43
53
|
terminalToOutput: () => terminalToOutput,
|
|
44
54
|
unreadableReading: () => unreadableReading,
|
|
45
55
|
unwrapJsonStringify: () => unwrapJsonStringify,
|
|
46
56
|
valueToReadFurtherFrom: () => valueToReadFurtherFrom,
|
|
57
|
+
walkDescendants: () => walkDescendants,
|
|
58
|
+
wrapperDiscovery: () => wrapperDiscovery,
|
|
47
59
|
writtenReading: () => writtenReading
|
|
48
60
|
});
|
|
49
61
|
module.exports = __toCommonJS(index_exports);
|
|
50
62
|
var import_node_crypto = require("crypto");
|
|
51
|
-
var
|
|
63
|
+
var import_behavioral_ir3 = require("@suss/behavioral-ir");
|
|
52
64
|
|
|
53
65
|
// src/inputReads.ts
|
|
54
66
|
function inputReadsOf(args) {
|
|
@@ -140,80 +152,8 @@ function mergeInputReads(derived, syntactic) {
|
|
|
140
152
|
return [...found.values()];
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
// src/
|
|
144
|
-
|
|
145
|
-
const kind = opts.kind ?? "handler";
|
|
146
|
-
const calls = opts.importNames.map((importName) => ({
|
|
147
|
-
kind,
|
|
148
|
-
match: {
|
|
149
|
-
type: "registrationCall",
|
|
150
|
-
importModule: opts.importModule,
|
|
151
|
-
importName,
|
|
152
|
-
registrationChain: [...opts.methods]
|
|
153
|
-
},
|
|
154
|
-
bindingExtraction: {
|
|
155
|
-
// `.all` registers every method, so it records "*", which the pairing
|
|
156
|
-
// engine treats as agreeing with any method at all.
|
|
157
|
-
method: {
|
|
158
|
-
type: "fromRegistration",
|
|
159
|
-
position: "methodName",
|
|
160
|
-
nameMap: { all: "*" }
|
|
161
|
-
},
|
|
162
|
-
path: { type: "fromArgument", position: 0 }
|
|
163
|
-
},
|
|
164
|
-
...opts.mount !== void 0 ? { mount: opts.mount } : {},
|
|
165
|
-
requiresImport: [opts.importModule]
|
|
166
|
-
}));
|
|
167
|
-
if (opts.importNames.length === 0) {
|
|
168
|
-
return calls;
|
|
169
|
-
}
|
|
170
|
-
return [
|
|
171
|
-
...calls,
|
|
172
|
-
// Routes registered in a loop over an array of specs, a shape
|
|
173
|
-
// registration-call discovery cannot see. The receiver comes from
|
|
174
|
-
// the same import declaration as the calls above, so a loop that
|
|
175
|
-
// never touches this library's routable is left alone.
|
|
176
|
-
{
|
|
177
|
-
kind,
|
|
178
|
-
match: {
|
|
179
|
-
type: "registrationLoop",
|
|
180
|
-
elementShape: LOOP_ELEMENT_SHAPE,
|
|
181
|
-
receiver: {
|
|
182
|
-
importModule: opts.importModule,
|
|
183
|
-
importNames: [...opts.importNames]
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
requiresImport: [opts.importModule]
|
|
187
|
-
}
|
|
188
|
-
];
|
|
189
|
-
}
|
|
190
|
-
var LOOP_ELEMENT_SHAPE = {
|
|
191
|
-
methodKey: "method",
|
|
192
|
-
pathKey: "path",
|
|
193
|
-
handlerKey: "handler"
|
|
194
|
-
};
|
|
195
|
-
function registrationHelperDiscovery(helpers, kind = "handler") {
|
|
196
|
-
return helpers.map((helper) => ({
|
|
197
|
-
kind,
|
|
198
|
-
match: {
|
|
199
|
-
type: "registrationTemplate",
|
|
200
|
-
helperName: helper.helperName,
|
|
201
|
-
...helper.importModule !== void 0 ? { importModule: helper.importModule } : {},
|
|
202
|
-
registrations: helper.registrations.map((one) => ({ ...one }))
|
|
203
|
-
}
|
|
204
|
-
}));
|
|
205
|
-
}
|
|
206
|
-
function unwrapJsonStringify(body) {
|
|
207
|
-
if (body === null || typeof body !== "object") {
|
|
208
|
-
return body;
|
|
209
|
-
}
|
|
210
|
-
const candidate = body;
|
|
211
|
-
if (candidate.kind !== "call" || candidate.callee !== "JSON.stringify") {
|
|
212
|
-
return body;
|
|
213
|
-
}
|
|
214
|
-
const inner = candidate.args?.[0];
|
|
215
|
-
return inner ?? body;
|
|
216
|
-
}
|
|
155
|
+
// src/composeWrappers.ts
|
|
156
|
+
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
217
157
|
|
|
218
158
|
// src/paths/enumeratePaths.ts
|
|
219
159
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
@@ -630,23 +570,372 @@ function reasonToDegrade(error) {
|
|
|
630
570
|
return null;
|
|
631
571
|
}
|
|
632
572
|
|
|
633
|
-
// src/
|
|
573
|
+
// src/composeWrappers.ts
|
|
574
|
+
var BUDGET_GAP = {
|
|
575
|
+
type: "unreadOutcome",
|
|
576
|
+
conditions: [],
|
|
577
|
+
consequence: "unknown",
|
|
578
|
+
description: `Composing the wrappers registered around this unit would have gone past the path budget of ${MAX_PATHS}, so what each of them produces is reported beside this unit's own outcomes rather than under the conditions that reach it`
|
|
579
|
+
};
|
|
580
|
+
function composeWrappers(summaries) {
|
|
581
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
582
|
+
for (const summary of summaries) {
|
|
583
|
+
const key = summaryKey(summary.location.file, summary.identity.name);
|
|
584
|
+
if (!byKey.has(key)) {
|
|
585
|
+
byKey.set(key, summary);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return summaries.map((summary) => composeOne(summary, byKey));
|
|
589
|
+
}
|
|
590
|
+
function summaryKey(file, name) {
|
|
591
|
+
return `${file}::${name}`;
|
|
592
|
+
}
|
|
593
|
+
function composeOne(summary, byKey) {
|
|
594
|
+
const recorded = (0, import_behavioral_ir2.readWrapperMetadata)(summary);
|
|
595
|
+
const applied = recorded?.applied ?? [];
|
|
596
|
+
const covering = applied.filter(
|
|
597
|
+
(reference) => coversUnit(reference, summary)
|
|
598
|
+
);
|
|
599
|
+
const narrowed = covering.length === applied.length ? summary : {
|
|
600
|
+
...summary,
|
|
601
|
+
metadata: (0, import_behavioral_ir2.withWrapperMetadata)(summary.metadata, {
|
|
602
|
+
applied: covering
|
|
603
|
+
})
|
|
604
|
+
};
|
|
605
|
+
const wrappers = covering.flatMap((reference) => {
|
|
606
|
+
const found = byKey.get(summaryKey(reference.file, reference.name));
|
|
607
|
+
return found === void 0 ? [] : [{ reference, summary: found }];
|
|
608
|
+
});
|
|
609
|
+
if (wrappers.length === 0) {
|
|
610
|
+
return narrowed;
|
|
611
|
+
}
|
|
612
|
+
let composition = {
|
|
613
|
+
transitions: summary.transitions,
|
|
614
|
+
degraded: false
|
|
615
|
+
};
|
|
616
|
+
for (const wrapper of [...wrappers].reverse()) {
|
|
617
|
+
if (wrapper.reference.onThrow === true) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
composition = merge(composition, applyWrapper(wrapper, composition));
|
|
621
|
+
}
|
|
622
|
+
for (const wrapper of wrappers) {
|
|
623
|
+
if (wrapper.reference.onThrow !== true) {
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
composition = merge(composition, applyThrowWrapper(wrapper, composition));
|
|
627
|
+
}
|
|
628
|
+
if (composition.transitions === summary.transitions) {
|
|
629
|
+
return narrowed;
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
...narrowed,
|
|
633
|
+
transitions: withDistinctIds(composition.transitions),
|
|
634
|
+
gaps: composition.degraded ? [...summary.gaps, BUDGET_GAP] : summary.gaps
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function merge(before, step) {
|
|
638
|
+
return {
|
|
639
|
+
transitions: step.transitions,
|
|
640
|
+
degraded: before.degraded || step.degraded
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function coversUnit(reference, summary) {
|
|
644
|
+
if (reference.scope === void 0) {
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
647
|
+
const binding = summary.identity.boundaryBinding;
|
|
648
|
+
return binding !== null && (0, import_behavioral_ir2.withinScope)(binding, reference.scope);
|
|
649
|
+
}
|
|
650
|
+
function applyWrapper(wrapper, inner) {
|
|
651
|
+
const shortCircuits = attribute(
|
|
652
|
+
wrapper.summary.transitions.filter((t) => t.output.type !== "delegate"),
|
|
653
|
+
wrapper.reference
|
|
654
|
+
);
|
|
655
|
+
const passThroughs = wrapper.summary.transitions.filter(
|
|
656
|
+
(t) => t.output.type === "delegate"
|
|
657
|
+
);
|
|
658
|
+
if (passThroughs.length === 0 || shortCircuits.length + passThroughs.length * inner.transitions.length > MAX_PATHS) {
|
|
659
|
+
return {
|
|
660
|
+
transitions: [...shortCircuits, ...inner.transitions],
|
|
661
|
+
degraded: passThroughs.length > 0
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
const continued = passThroughs.flatMap(
|
|
665
|
+
(passThrough) => inner.transitions.map((transition) => splice(passThrough, transition))
|
|
666
|
+
);
|
|
667
|
+
return { transitions: [...shortCircuits, ...continued], degraded: false };
|
|
668
|
+
}
|
|
669
|
+
function applyThrowWrapper(wrapper, inner) {
|
|
670
|
+
const thrown = inner.transitions.filter((t) => t.output.type === "throw");
|
|
671
|
+
if (thrown.length === 0) {
|
|
672
|
+
return inner;
|
|
673
|
+
}
|
|
674
|
+
const rest = inner.transitions.filter((t) => t.output.type !== "throw");
|
|
675
|
+
const handled = wrapper.summary.transitions;
|
|
676
|
+
if (rest.length + thrown.length * handled.length > MAX_PATHS) {
|
|
677
|
+
return {
|
|
678
|
+
transitions: [
|
|
679
|
+
...inner.transitions,
|
|
680
|
+
...attribute(handled, wrapper.reference)
|
|
681
|
+
],
|
|
682
|
+
degraded: true
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
const composed = thrown.flatMap(
|
|
686
|
+
(transition) => attribute(
|
|
687
|
+
handled.map((handler) => splice(transition, handler)),
|
|
688
|
+
wrapper.reference
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
return { transitions: [...rest, ...composed], degraded: false };
|
|
692
|
+
}
|
|
693
|
+
function splice(outer, inner) {
|
|
694
|
+
return {
|
|
695
|
+
...inner,
|
|
696
|
+
id: `${inner.id}:via:${outer.id}`,
|
|
697
|
+
conditions: [...outer.conditions, ...inner.conditions],
|
|
698
|
+
effects: [...outer.effects, ...inner.effects],
|
|
699
|
+
isDefault: outer.isDefault && inner.isDefault
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function attribute(transitions, reference) {
|
|
703
|
+
return transitions.map((transition) => ({
|
|
704
|
+
...transition,
|
|
705
|
+
metadata: (0, import_behavioral_ir2.withWrapperMetadata)(transition.metadata, { from: reference })
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
function withDistinctIds(transitions) {
|
|
709
|
+
const seen = /* @__PURE__ */ new Map();
|
|
710
|
+
return transitions.map((transition) => {
|
|
711
|
+
const taken = seen.get(transition.id);
|
|
712
|
+
seen.set(transition.id, (taken ?? 0) + 1);
|
|
713
|
+
return taken === void 0 ? transition : { ...transition, id: `${transition.id}#${taken + 1}` };
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/effectGuards.ts
|
|
634
718
|
var keyOf = (condition) => `${condition.polarity}:${condition.sourceText}`;
|
|
719
|
+
var opposite = (condition) => `${condition.polarity === "positive" ? "negative" : "positive"}:${condition.sourceText}`;
|
|
720
|
+
function guardsHoldOn(preconditions, conditions) {
|
|
721
|
+
if (preconditions === void 0 || preconditions.length === 0) {
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
const onBranch = new Set(conditions.map(keyOf));
|
|
725
|
+
return preconditions.every(
|
|
726
|
+
(precondition) => !onBranch.has(opposite(precondition))
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
function runsBefore(effectLine, terminalEndLine) {
|
|
730
|
+
return effectLine <= terminalEndLine;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/packHelpers.ts
|
|
734
|
+
function httpRouteDiscovery(opts) {
|
|
735
|
+
const kind = opts.kind ?? "handler";
|
|
736
|
+
const calls = opts.importNames.map((importName) => ({
|
|
737
|
+
kind,
|
|
738
|
+
match: {
|
|
739
|
+
type: "registrationCall",
|
|
740
|
+
importModule: opts.importModule,
|
|
741
|
+
importName,
|
|
742
|
+
registrationChain: [...opts.methods]
|
|
743
|
+
},
|
|
744
|
+
bindingExtraction: {
|
|
745
|
+
// `.all` registers every method, so it records "*", which the pairing
|
|
746
|
+
// engine treats as agreeing with any method at all.
|
|
747
|
+
method: {
|
|
748
|
+
type: "fromRegistration",
|
|
749
|
+
position: "methodName",
|
|
750
|
+
nameMap: { all: "*" }
|
|
751
|
+
},
|
|
752
|
+
path: { type: "fromArgument", position: 0 }
|
|
753
|
+
},
|
|
754
|
+
...opts.mount !== void 0 ? { mount: opts.mount } : {},
|
|
755
|
+
requiresImport: [opts.importModule]
|
|
756
|
+
}));
|
|
757
|
+
if (opts.importNames.length === 0) {
|
|
758
|
+
return calls;
|
|
759
|
+
}
|
|
760
|
+
return [
|
|
761
|
+
...calls,
|
|
762
|
+
// Routes registered in a loop over an array of specs, a shape
|
|
763
|
+
// registration-call discovery cannot see. The receiver comes from
|
|
764
|
+
// the same import declaration as the calls above, so a loop that
|
|
765
|
+
// never touches this library's routable is left alone.
|
|
766
|
+
{
|
|
767
|
+
kind,
|
|
768
|
+
match: {
|
|
769
|
+
type: "registrationLoop",
|
|
770
|
+
elementShape: LOOP_ELEMENT_SHAPE,
|
|
771
|
+
receiver: {
|
|
772
|
+
importModule: opts.importModule,
|
|
773
|
+
importNames: [...opts.importNames]
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
requiresImport: [opts.importModule]
|
|
777
|
+
}
|
|
778
|
+
];
|
|
779
|
+
}
|
|
780
|
+
function wrapperDiscovery(opts) {
|
|
781
|
+
return opts.importNames.flatMap(
|
|
782
|
+
(importName) => opts.wraps.map((wraps) => ({
|
|
783
|
+
kind: opts.kind ?? "middleware",
|
|
784
|
+
match: {
|
|
785
|
+
type: "registrationCall",
|
|
786
|
+
importModule: opts.importModule,
|
|
787
|
+
importName,
|
|
788
|
+
registrationChain: []
|
|
789
|
+
},
|
|
790
|
+
wraps: { ...wraps },
|
|
791
|
+
requiresImport: [opts.importModule]
|
|
792
|
+
}))
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
var LOOP_ELEMENT_SHAPE = {
|
|
796
|
+
methodKey: "method",
|
|
797
|
+
pathKey: "path",
|
|
798
|
+
handlerKey: "handler"
|
|
799
|
+
};
|
|
800
|
+
function registrationHelperDiscovery(helpers, kind = "handler") {
|
|
801
|
+
return helpers.map((helper) => ({
|
|
802
|
+
kind,
|
|
803
|
+
match: {
|
|
804
|
+
type: "registrationTemplate",
|
|
805
|
+
helperName: helper.helperName,
|
|
806
|
+
...helper.importModule !== void 0 ? { importModule: helper.importModule } : {},
|
|
807
|
+
...helper.subject !== void 0 ? { subject: helper.subject } : {},
|
|
808
|
+
registrations: helper.registrations.map((one) => ({ ...one }))
|
|
809
|
+
}
|
|
810
|
+
}));
|
|
811
|
+
}
|
|
812
|
+
function routeHelperIndex(opts) {
|
|
813
|
+
const methods = new Set(
|
|
814
|
+
opts.methods.map(
|
|
815
|
+
(method) => method.startsWith(".") ? method.slice(1) : method
|
|
816
|
+
)
|
|
817
|
+
);
|
|
818
|
+
return {
|
|
819
|
+
find: { by: "subject" },
|
|
820
|
+
declare: (helpers) => ({
|
|
821
|
+
discovery: registrationHelperDiscovery(
|
|
822
|
+
helpers.flatMap((helper) => routesRegisteredBy(helper, methods, opts)),
|
|
823
|
+
opts.kind ?? "handler"
|
|
824
|
+
)
|
|
825
|
+
})
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
function routesRegisteredBy(helper, methods, opts) {
|
|
829
|
+
const registrations = [];
|
|
830
|
+
let subjectArgument;
|
|
831
|
+
for (const sink of helper.sinks) {
|
|
832
|
+
const registration = routeRegisteredBy(helper, sink, methods);
|
|
833
|
+
if (registration === null) {
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
registrations.push(registration);
|
|
837
|
+
if (sink.receiver.as === "parameter") {
|
|
838
|
+
subjectArgument = sink.receiver.position;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (registrations.length === 0 || subjectArgument === void 0) {
|
|
842
|
+
return [];
|
|
843
|
+
}
|
|
844
|
+
return [
|
|
845
|
+
{
|
|
846
|
+
helperName: helper.name,
|
|
847
|
+
importModule: helper.file,
|
|
848
|
+
subject: {
|
|
849
|
+
argument: subjectArgument,
|
|
850
|
+
importModule: opts.importModule,
|
|
851
|
+
importNames: [...opts.importNames]
|
|
852
|
+
},
|
|
853
|
+
registrations
|
|
854
|
+
}
|
|
855
|
+
];
|
|
856
|
+
}
|
|
857
|
+
function routeRegisteredBy(helper, sink, methods) {
|
|
858
|
+
if (sink.method === null || !methods.has(sink.method)) {
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
const { receiver } = sink;
|
|
862
|
+
if (receiver.as !== "parameter" || receiver.property !== void 0 || !helper.subjectParameters.includes(receiver.position)) {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
const pathTemplate = sink.arguments[0];
|
|
866
|
+
const handler = sink.arguments[sink.arguments.length - 1];
|
|
867
|
+
if (sink.arguments.length < 2 || pathTemplate === void 0 || pathTemplate.as !== "text" || handler === void 0) {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
const handlerArg = handlerSlot(handler);
|
|
871
|
+
return handlerArg === null ? null : {
|
|
872
|
+
// `.all` registers every method, which the pairing engine
|
|
873
|
+
// spells as a wildcard.
|
|
874
|
+
method: sink.method === "all" ? "*" : sink.method.toUpperCase(),
|
|
875
|
+
pathTemplate: pathTemplate.text,
|
|
876
|
+
handlerArg
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function handlerSlot(value) {
|
|
880
|
+
if (value.as !== "parameter") {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
return value.property === void 0 ? `{${value.position}}` : `{${value.position}}.${value.property}`;
|
|
884
|
+
}
|
|
885
|
+
function unwrapJsonStringify(body) {
|
|
886
|
+
if (body === null || typeof body !== "object") {
|
|
887
|
+
return body;
|
|
888
|
+
}
|
|
889
|
+
const candidate = body;
|
|
890
|
+
if (candidate.kind !== "call" || candidate.callee !== "JSON.stringify") {
|
|
891
|
+
return body;
|
|
892
|
+
}
|
|
893
|
+
const inner = candidate.args?.[0];
|
|
894
|
+
return inner ?? body;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// src/packOptions.ts
|
|
898
|
+
var import_zod = require("zod");
|
|
899
|
+
var storageSystemOption = import_zod.z.enum([
|
|
900
|
+
"postgresql",
|
|
901
|
+
"mysql",
|
|
902
|
+
"sqlite"
|
|
903
|
+
]);
|
|
904
|
+
var scopeOption = import_zod.z.string();
|
|
905
|
+
var configuredCallOption = import_zod.z.object({
|
|
906
|
+
/** Module that declares the receiver's type. */
|
|
907
|
+
module: import_zod.z.string(),
|
|
908
|
+
/** Type name of the receiver, as exported from that module. */
|
|
909
|
+
receiver: import_zod.z.string(),
|
|
910
|
+
/** Method that performs the send. */
|
|
911
|
+
method: import_zod.z.string(),
|
|
912
|
+
/** Argument index carrying the subject. */
|
|
913
|
+
subjectArg: import_zod.z.number(),
|
|
914
|
+
/**
|
|
915
|
+
* Which argument the message body is. Left out when the method has no
|
|
916
|
+
* single body argument, as a batch method taking a list of entries
|
|
917
|
+
* does, and then no body is reported.
|
|
918
|
+
*/
|
|
919
|
+
bodyArg: import_zod.z.number().optional()
|
|
920
|
+
}).strict();
|
|
921
|
+
|
|
922
|
+
// src/paths/gatingConditions.ts
|
|
923
|
+
var keyOf2 = (condition) => `${condition.polarity}:${condition.sourceText}`;
|
|
635
924
|
function sharedGatingConditions(paths, readPredicate) {
|
|
636
925
|
const [first, ...rest] = paths ?? [];
|
|
637
926
|
if (first === void 0) {
|
|
638
927
|
return [];
|
|
639
928
|
}
|
|
640
|
-
const shared = new Set(first.map(
|
|
929
|
+
const shared = new Set(first.map(keyOf2));
|
|
641
930
|
for (const path of rest) {
|
|
642
|
-
const here = new Set(path.map(
|
|
931
|
+
const here = new Set(path.map(keyOf2));
|
|
643
932
|
for (const value of shared) {
|
|
644
933
|
if (!here.has(value)) {
|
|
645
934
|
shared.delete(value);
|
|
646
935
|
}
|
|
647
936
|
}
|
|
648
937
|
}
|
|
649
|
-
return first.filter((condition) => shared.has(
|
|
938
|
+
return first.filter((condition) => shared.has(keyOf2(condition))).map((condition) => ({
|
|
650
939
|
sourceText: condition.sourceText,
|
|
651
940
|
structured: readPredicate === void 0 || condition.expression === null ? null : readPredicate(condition.expression),
|
|
652
941
|
polarity: condition.polarity,
|
|
@@ -700,6 +989,21 @@ var IdMap = class {
|
|
|
700
989
|
}
|
|
701
990
|
};
|
|
702
991
|
|
|
992
|
+
// src/paths/nodeWalk.ts
|
|
993
|
+
var SKIP_CHILDREN = /* @__PURE__ */ Symbol("skipChildren");
|
|
994
|
+
function walkDescendants(root, carried, visitor) {
|
|
995
|
+
for (const child of root.namedChildren) {
|
|
996
|
+
if (child === null) {
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
visitor.at(child, carried);
|
|
1000
|
+
const inner = visitor.into(child, carried);
|
|
1001
|
+
if (inner !== SKIP_CHILDREN) {
|
|
1002
|
+
walkDescendants(child, inner, visitor);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
703
1007
|
// src/reading.ts
|
|
704
1008
|
function writtenReading(value, range) {
|
|
705
1009
|
return { kind: "written", value, range };
|
|
@@ -889,19 +1193,22 @@ function buildMetadata(raw) {
|
|
|
889
1193
|
let metadata = {};
|
|
890
1194
|
const http = buildHttpMetadataValue(raw);
|
|
891
1195
|
if (http !== null) {
|
|
892
|
-
metadata = (0,
|
|
1196
|
+
metadata = (0, import_behavioral_ir3.withHttpMetadata)(metadata, http);
|
|
893
1197
|
}
|
|
894
1198
|
const graphql = buildGraphqlMetadataValue(raw);
|
|
895
1199
|
if (graphql !== null) {
|
|
896
|
-
metadata = (0,
|
|
1200
|
+
metadata = (0, import_behavioral_ir3.withGraphqlMetadata)(metadata, graphql);
|
|
897
1201
|
}
|
|
898
1202
|
if (raw.sourceDocumentLabel !== void 0) {
|
|
899
|
-
metadata = (0,
|
|
1203
|
+
metadata = (0, import_behavioral_ir3.withSourceDocumentMetadata)(metadata, {
|
|
900
1204
|
label: raw.sourceDocumentLabel
|
|
901
1205
|
});
|
|
902
1206
|
}
|
|
903
1207
|
if (raw.mount !== void 0) {
|
|
904
|
-
metadata = (0,
|
|
1208
|
+
metadata = (0, import_behavioral_ir3.withMountMetadata)(metadata, raw.mount);
|
|
1209
|
+
}
|
|
1210
|
+
if (raw.wrappers !== void 0 && raw.wrappers.length > 0) {
|
|
1211
|
+
metadata = (0, import_behavioral_ir3.withWrapperMetadata)(metadata, { applied: raw.wrappers });
|
|
905
1212
|
}
|
|
906
1213
|
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
907
1214
|
}
|
|
@@ -951,7 +1258,7 @@ function answersWithHttpResponses(raw) {
|
|
|
951
1258
|
if (raw.boundaryBinding === null || raw.boundaryBinding === void 0) {
|
|
952
1259
|
return true;
|
|
953
1260
|
}
|
|
954
|
-
return (0,
|
|
1261
|
+
return (0, import_behavioral_ir3.exchangesHttpResponses)(raw.boundaryBinding);
|
|
955
1262
|
}
|
|
956
1263
|
function detectGaps(raw, transitions, options) {
|
|
957
1264
|
if (options.gapHandling === "silent") {
|
|
@@ -1166,27 +1473,39 @@ function paramToInput(param) {
|
|
|
1166
1473
|
0 && (module.exports = {
|
|
1167
1474
|
IdMap,
|
|
1168
1475
|
IdSet,
|
|
1476
|
+
MAX_PATHS,
|
|
1169
1477
|
PathBudgetExceeded,
|
|
1478
|
+
SKIP_CHILDREN,
|
|
1170
1479
|
UnmodeledFlow,
|
|
1171
1480
|
absentReading,
|
|
1172
1481
|
ambiguousReading,
|
|
1173
1482
|
andThenReading,
|
|
1174
1483
|
assembleSummary,
|
|
1175
1484
|
assessConfidence,
|
|
1485
|
+
composeWrappers,
|
|
1486
|
+
configuredCallOption,
|
|
1176
1487
|
detectGaps,
|
|
1177
1488
|
effectToIR,
|
|
1178
1489
|
enumerateOrDegrade,
|
|
1179
1490
|
enumerateStructuredPaths,
|
|
1180
1491
|
firstWrittenReading,
|
|
1492
|
+
guardsHoldOn,
|
|
1181
1493
|
httpRouteDiscovery,
|
|
1182
1494
|
makeTransitionId,
|
|
1183
1495
|
mapReading,
|
|
1184
1496
|
paramToInput,
|
|
1497
|
+
rawConditionToPredicate,
|
|
1185
1498
|
registrationHelperDiscovery,
|
|
1499
|
+
routeHelperIndex,
|
|
1500
|
+
runsBefore,
|
|
1501
|
+
scopeOption,
|
|
1186
1502
|
sharedGatingConditions,
|
|
1503
|
+
storageSystemOption,
|
|
1187
1504
|
terminalToOutput,
|
|
1188
1505
|
unreadableReading,
|
|
1189
1506
|
unwrapJsonStringify,
|
|
1190
1507
|
valueToReadFurtherFrom,
|
|
1508
|
+
walkDescendants,
|
|
1509
|
+
wrapperDiscovery,
|
|
1191
1510
|
writtenReading
|
|
1192
1511
|
});
|