@suss/extractor 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 +47 -0
- package/dist/index.cjs +406 -83
- package/dist/index.d.cts +429 -15
- package/dist/index.d.ts +429 -15
- package/dist/index.js +396 -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 };
|
|
@@ -866,6 +1170,7 @@ function assembleSummary(raw, options = DEFAULT_OPTIONS) {
|
|
|
866
1170
|
location: {
|
|
867
1171
|
file: raw.identity.file,
|
|
868
1172
|
range: raw.identity.range,
|
|
1173
|
+
...raw.identity.span !== void 0 ? { span: raw.identity.span } : {},
|
|
869
1174
|
exportName: raw.identity.exportName
|
|
870
1175
|
},
|
|
871
1176
|
identity: {
|
|
@@ -888,17 +1193,23 @@ function buildMetadata(raw) {
|
|
|
888
1193
|
let metadata = {};
|
|
889
1194
|
const http = buildHttpMetadataValue(raw);
|
|
890
1195
|
if (http !== null) {
|
|
891
|
-
metadata = (0,
|
|
1196
|
+
metadata = (0, import_behavioral_ir3.withHttpMetadata)(metadata, http);
|
|
892
1197
|
}
|
|
893
1198
|
const graphql = buildGraphqlMetadataValue(raw);
|
|
894
1199
|
if (graphql !== null) {
|
|
895
|
-
metadata = (0,
|
|
1200
|
+
metadata = (0, import_behavioral_ir3.withGraphqlMetadata)(metadata, graphql);
|
|
896
1201
|
}
|
|
897
1202
|
if (raw.sourceDocumentLabel !== void 0) {
|
|
898
|
-
metadata = (0,
|
|
1203
|
+
metadata = (0, import_behavioral_ir3.withSourceDocumentMetadata)(metadata, {
|
|
899
1204
|
label: raw.sourceDocumentLabel
|
|
900
1205
|
});
|
|
901
1206
|
}
|
|
1207
|
+
if (raw.mount !== void 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 });
|
|
1212
|
+
}
|
|
902
1213
|
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
903
1214
|
}
|
|
904
1215
|
function buildHttpMetadataValue(raw) {
|
|
@@ -947,7 +1258,7 @@ function answersWithHttpResponses(raw) {
|
|
|
947
1258
|
if (raw.boundaryBinding === null || raw.boundaryBinding === void 0) {
|
|
948
1259
|
return true;
|
|
949
1260
|
}
|
|
950
|
-
return (0,
|
|
1261
|
+
return (0, import_behavioral_ir3.exchangesHttpResponses)(raw.boundaryBinding);
|
|
951
1262
|
}
|
|
952
1263
|
function detectGaps(raw, transitions, options) {
|
|
953
1264
|
if (options.gapHandling === "silent") {
|
|
@@ -1162,27 +1473,39 @@ function paramToInput(param) {
|
|
|
1162
1473
|
0 && (module.exports = {
|
|
1163
1474
|
IdMap,
|
|
1164
1475
|
IdSet,
|
|
1476
|
+
MAX_PATHS,
|
|
1165
1477
|
PathBudgetExceeded,
|
|
1478
|
+
SKIP_CHILDREN,
|
|
1166
1479
|
UnmodeledFlow,
|
|
1167
1480
|
absentReading,
|
|
1168
1481
|
ambiguousReading,
|
|
1169
1482
|
andThenReading,
|
|
1170
1483
|
assembleSummary,
|
|
1171
1484
|
assessConfidence,
|
|
1485
|
+
composeWrappers,
|
|
1486
|
+
configuredCallOption,
|
|
1172
1487
|
detectGaps,
|
|
1173
1488
|
effectToIR,
|
|
1174
1489
|
enumerateOrDegrade,
|
|
1175
1490
|
enumerateStructuredPaths,
|
|
1176
1491
|
firstWrittenReading,
|
|
1492
|
+
guardsHoldOn,
|
|
1177
1493
|
httpRouteDiscovery,
|
|
1178
1494
|
makeTransitionId,
|
|
1179
1495
|
mapReading,
|
|
1180
1496
|
paramToInput,
|
|
1497
|
+
rawConditionToPredicate,
|
|
1181
1498
|
registrationHelperDiscovery,
|
|
1499
|
+
routeHelperIndex,
|
|
1500
|
+
runsBefore,
|
|
1501
|
+
scopeOption,
|
|
1182
1502
|
sharedGatingConditions,
|
|
1503
|
+
storageSystemOption,
|
|
1183
1504
|
terminalToOutput,
|
|
1184
1505
|
unreadableReading,
|
|
1185
1506
|
unwrapJsonStringify,
|
|
1186
1507
|
valueToReadFurtherFrom,
|
|
1508
|
+
walkDescendants,
|
|
1509
|
+
wrapperDiscovery,
|
|
1187
1510
|
writtenReading
|
|
1188
1511
|
});
|