carrick 0.3.72 → 0.3.73
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/package.json +6 -6
- package/sidecar/dist/src/capture/anchors.d.ts +15 -0
- package/sidecar/dist/src/capture/anchors.js +70 -5
- package/sidecar/dist/src/capture/api.d.ts +39 -4
- package/sidecar/dist/src/capture/check-classify.d.ts +2 -0
- package/sidecar/dist/src/capture/check-classify.js +28 -0
- package/sidecar/dist/src/capture/check-deep.js +1 -1
- package/sidecar/dist/src/capture/check-probe.d.ts +1 -1
- package/sidecar/dist/src/capture/check-probe.js +16 -0
- package/sidecar/dist/src/capture/deep-walk.d.ts +33 -6
- package/sidecar/dist/src/capture/deep-walk.js +81 -28
- package/sidecar/dist/src/capture/index.js +16 -6
- package/sidecar/dist/src/capture/installed-package.d.ts +58 -0
- package/sidecar/dist/src/capture/installed-package.js +311 -0
- package/sidecar/dist/src/capture/lockfile.d.ts +8 -0
- package/sidecar/dist/src/capture/lockfile.js +1 -1
- package/sidecar/dist/src/capture/node-builder.d.ts +27 -0
- package/sidecar/dist/src/capture/node-builder.js +107 -1
- package/sidecar/dist/src/capture/paths-rewrite.d.ts +23 -6
- package/sidecar/dist/src/capture/paths-rewrite.js +43 -11
- package/sidecar/dist/src/capture/self-check.js +12 -3
- package/sidecar/dist/src/capture/unresolved.d.ts +28 -0
- package/sidecar/dist/src/capture/unresolved.js +107 -0
- package/sidecar/dist/src/type-inferrer.d.ts +108 -11
- package/sidecar/dist/src/type-inferrer.js +513 -90
- package/sidecar/dist/src/type-structural-expander.d.ts +23 -2
- package/sidecar/dist/src/type-structural-expander.js +55 -17
- package/sidecar/dist/src/type-text-canonicalizer.d.ts +14 -0
- package/sidecar/dist/src/type-text-canonicalizer.js +23 -2
- package/sidecar/dist/src/validators.d.ts +10 -0
- package/sidecar/dist/src/validators.js +1 -0
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* producer's own resolution context -- `paths` cannot be fixed at check time
|
|
8
8
|
* (program-global, and namespaces collide across stubs).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* An absolute path into an installed package becomes that package's bare
|
|
11
|
+
* specifier (installed-package.ts). Other specifiers that map outside the
|
|
12
|
+
* emitted tree are left untouched: the per-alias self-check classifies them
|
|
13
|
+
* as dangling internals with a recorded reason, which is the honest outcome.
|
|
13
14
|
*/
|
|
14
15
|
import * as fs from 'node:fs';
|
|
15
16
|
import * as path from 'node:path';
|
|
17
|
+
import { installedPackageSpecifier } from './installed-package.js';
|
|
16
18
|
import { isRelative, matchPathsPattern, rewriteSpecifiers, } from './specifiers.js';
|
|
17
19
|
export function parsePathsPatterns(options, configPath) {
|
|
18
20
|
const paths = options.paths;
|
|
@@ -55,21 +57,51 @@ function relativeSpecifier(fromFile, toFile) {
|
|
|
55
57
|
return rel;
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* `import("<absolute>").Name` occurrences: the absolute specifier and the
|
|
61
|
+
* first name read off it, so an installed-package rewrite can find the entry
|
|
62
|
+
* that exports that name.
|
|
63
|
+
*/
|
|
64
|
+
const ABSOLUTE_IMPORT_TYPE = /(import\s*\(\s*)(["'])(\/[^"']+)\2(\s*\))(\s*\.\s*)([A-Za-z_$][\w$]*)/g;
|
|
65
|
+
/**
|
|
66
|
+
* Rewrite paths-mapped, absolute-internal and absolute installed-package
|
|
67
|
+
* specifiers in every emitted file (carrick#1174 for the last: a path into
|
|
68
|
+
* `node_modules` or a runtime npm cache becomes the package's bare specifier).
|
|
60
69
|
*/
|
|
61
70
|
export function rewriteEmittedSpecifiers(args) {
|
|
62
71
|
const patterns = parsePathsPatterns(args.options, args.configPath);
|
|
63
72
|
const emitted = new Set(args.files);
|
|
73
|
+
const pins = {};
|
|
74
|
+
const installs = {};
|
|
64
75
|
let total = 0;
|
|
76
|
+
const installed = (spec, importedName) => {
|
|
77
|
+
const mapped = installedPackageSpecifier(spec, importedName);
|
|
78
|
+
if (!mapped)
|
|
79
|
+
return undefined;
|
|
80
|
+
installs[mapped.install.name] = mapped.install.root;
|
|
81
|
+
if (mapped.pin)
|
|
82
|
+
pins[mapped.pin.name] = mapped.pin.version;
|
|
83
|
+
return mapped.specifier;
|
|
84
|
+
};
|
|
65
85
|
for (const file of args.files) {
|
|
66
86
|
const absFile = path.join(args.typesDir, file);
|
|
67
|
-
const
|
|
87
|
+
const original = fs.readFileSync(absFile, 'utf8');
|
|
88
|
+
let importTypeRewrites = 0;
|
|
89
|
+
// Import types first, while the member name is still attached to its
|
|
90
|
+
// specifier; an in-tree target is left for the general pass below.
|
|
91
|
+
const text = original.replace(ABSOLUTE_IMPORT_TYPE, (whole, open, quote, spec, close, dot, name) => {
|
|
92
|
+
if (treeFileFor(spec, args.entryDir, emitted))
|
|
93
|
+
return whole;
|
|
94
|
+
const replacement = installed(spec, name);
|
|
95
|
+
if (replacement === undefined)
|
|
96
|
+
return whole;
|
|
97
|
+
importTypeRewrites++;
|
|
98
|
+
return `${open}${quote}${replacement}${quote}${close}${dot}${name}`;
|
|
99
|
+
});
|
|
68
100
|
const { text: rewritten, rewrites } = rewriteSpecifiers(text, (spec) => {
|
|
69
|
-
// Absolute
|
|
101
|
+
// Absolute paths: an emitted tree file, else an installed package.
|
|
70
102
|
if (spec.startsWith('/')) {
|
|
71
103
|
const target = treeFileFor(spec, args.entryDir, emitted);
|
|
72
|
-
return target ? relativeSpecifier(file, target) :
|
|
104
|
+
return target ? relativeSpecifier(file, target) : installed(spec);
|
|
73
105
|
}
|
|
74
106
|
if (isRelative(spec))
|
|
75
107
|
return undefined;
|
|
@@ -91,10 +123,10 @@ export function rewriteEmittedSpecifiers(args) {
|
|
|
91
123
|
}
|
|
92
124
|
return undefined;
|
|
93
125
|
});
|
|
94
|
-
if (rewrites > 0) {
|
|
126
|
+
if (rewrites + importTypeRewrites > 0) {
|
|
95
127
|
fs.writeFileSync(absFile, rewritten);
|
|
96
|
-
total += rewrites;
|
|
128
|
+
total += rewrites + importTypeRewrites;
|
|
97
129
|
}
|
|
98
130
|
}
|
|
99
|
-
return total;
|
|
131
|
+
return { rewrites: total, pins, installs };
|
|
100
132
|
}
|
|
@@ -142,7 +142,7 @@ function demotedRecord(anchor) {
|
|
|
142
142
|
alias: anchor.request.alias,
|
|
143
143
|
anchor_kind: anchor.request.kind,
|
|
144
144
|
symbol_name: 'symbol_name' in anchor.request ? anchor.request.symbol_name : undefined,
|
|
145
|
-
source_file:
|
|
145
|
+
source_file: anchor.request.kind === 'literal' ? '<inline>' : anchor.request.source_file,
|
|
146
146
|
anchor_origin: anchor.request.anchor_origin,
|
|
147
147
|
serialization: 'structural_fallback',
|
|
148
148
|
self_check: 'decayed_internal',
|
|
@@ -194,6 +194,7 @@ function checkedRecord(anchor, ctx) {
|
|
|
194
194
|
}
|
|
195
195
|
let blamedExternal;
|
|
196
196
|
let internalFailure;
|
|
197
|
+
const danglingSpecifiers = new Set();
|
|
197
198
|
for (const file of closure) {
|
|
198
199
|
const failures = ctx.failuresByFile.get(file);
|
|
199
200
|
if (!failures)
|
|
@@ -202,6 +203,8 @@ function checkedRecord(anchor, ctx) {
|
|
|
202
203
|
blamedExternal = [...failures.externalPinned][0];
|
|
203
204
|
if (!internalFailure)
|
|
204
205
|
internalFailure = [...failures.internal][0];
|
|
206
|
+
for (const specifier of failures.internal)
|
|
207
|
+
danglingSpecifiers.add(specifier);
|
|
205
208
|
}
|
|
206
209
|
// Classification consults the closure failures REGARDLESS of the root
|
|
207
210
|
// type: a dangling internal specifier means part of this alias's closure
|
|
@@ -283,7 +286,7 @@ function checkedRecord(anchor, ctx) {
|
|
|
283
286
|
alias,
|
|
284
287
|
anchor_kind: anchor.request.kind,
|
|
285
288
|
symbol_name: 'symbol_name' in anchor.request ? anchor.request.symbol_name : undefined,
|
|
286
|
-
source_file:
|
|
289
|
+
source_file: anchor.request.kind === 'literal' ? '<inline>' : anchor.request.source_file,
|
|
287
290
|
anchor_origin: anchor.request.anchor_origin,
|
|
288
291
|
serialization: anchor.serialization,
|
|
289
292
|
self_check: outcome,
|
|
@@ -295,8 +298,14 @@ function checkedRecord(anchor, ctx) {
|
|
|
295
298
|
self_check_detail: anchor.abstainReason ?? detail ?? anchor.reaimNote,
|
|
296
299
|
top_type_at_self_check: topType,
|
|
297
300
|
...(unexplainedDeep.length > 0
|
|
298
|
-
? {
|
|
301
|
+
? {
|
|
302
|
+
any_provenance: unexplainedDeep.map((finding) => provenanceOf(finding, anchor.unresolved)),
|
|
303
|
+
}
|
|
304
|
+
: {}),
|
|
305
|
+
...(danglingSpecifiers.size > 0
|
|
306
|
+
? { dangling_specifiers: [...danglingSpecifiers].sort() }
|
|
299
307
|
: {}),
|
|
308
|
+
...(anchor.undeclaredNames ? { undeclared_names: anchor.undeclaredNames } : {}),
|
|
300
309
|
};
|
|
301
310
|
}
|
|
302
311
|
/** Resolve a relative specifier from `fromAbs` to a tree file, if present. */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an anchor's source program could not resolve (carrick#1164).
|
|
3
|
+
*
|
|
4
|
+
* The node builder and declaration emit both print TypeScript's
|
|
5
|
+
* unresolved-reference placeholder as the keyword `any`. After that the stub
|
|
6
|
+
* text cannot say whether an `any` member was written by the author or left
|
|
7
|
+
* behind by an import that did not resolve on the scanned checkout — a
|
|
8
|
+
* dependency that was not installed, a generated module that was never
|
|
9
|
+
* generated. The self-check reads only that text, so it used to label both
|
|
10
|
+
* `declared`, and a reader told "declared that way in the source" stops
|
|
11
|
+
* looking where the fix is to install or generate the missing module.
|
|
12
|
+
*
|
|
13
|
+
* The source program still holds the placeholder, so the capture records the
|
|
14
|
+
* placeholder's member paths at anchor time, together with the module
|
|
15
|
+
* specifiers the anchor's file can reach that did not resolve, and the
|
|
16
|
+
* self-check labels the matching findings. Seam: node builtins + `typescript`.
|
|
17
|
+
*/
|
|
18
|
+
import ts from 'typescript';
|
|
19
|
+
import { type UnresolvedAtAnchor } from './deep-walk.js';
|
|
20
|
+
/**
|
|
21
|
+
* The unresolved placeholders inside `type` (read at `location` in `sourceFile`),
|
|
22
|
+
* or `undefined` when it has none — the common case, which costs one walk.
|
|
23
|
+
*
|
|
24
|
+
* `pathPrefix` maps the anchor type's member paths onto the paths of the alias
|
|
25
|
+
* the surface declares when the two differ: a symbol anchor restoring array
|
|
26
|
+
* depth prints `import('./m').Row[]`, whose members sit under `<0>`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function unresolvedAtAnchor(program: ts.Program, sourceFile: ts.SourceFile, type: ts.Type, location: ts.Node, pathPrefix?: string): UnresolvedAtAnchor | undefined;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an anchor's source program could not resolve (carrick#1164).
|
|
3
|
+
*
|
|
4
|
+
* The node builder and declaration emit both print TypeScript's
|
|
5
|
+
* unresolved-reference placeholder as the keyword `any`. After that the stub
|
|
6
|
+
* text cannot say whether an `any` member was written by the author or left
|
|
7
|
+
* behind by an import that did not resolve on the scanned checkout — a
|
|
8
|
+
* dependency that was not installed, a generated module that was never
|
|
9
|
+
* generated. The self-check reads only that text, so it used to label both
|
|
10
|
+
* `declared`, and a reader told "declared that way in the source" stops
|
|
11
|
+
* looking where the fix is to install or generate the missing module.
|
|
12
|
+
*
|
|
13
|
+
* The source program still holds the placeholder, so the capture records the
|
|
14
|
+
* placeholder's member paths at anchor time, together with the module
|
|
15
|
+
* specifiers the anchor's file can reach that did not resolve, and the
|
|
16
|
+
* self-check labels the matching findings. Seam: node builtins + `typescript`.
|
|
17
|
+
*/
|
|
18
|
+
import ts from 'typescript';
|
|
19
|
+
import { findUnresolvedPlaceholders } from './deep-walk.js';
|
|
20
|
+
/** Files the specifier walk visits from one anchor before it stops. */
|
|
21
|
+
const MAX_REACHABLE_FILES = 256;
|
|
22
|
+
/** Per-program memo: source file name -> unresolved specifiers it reaches. */
|
|
23
|
+
const reachableCache = new WeakMap();
|
|
24
|
+
/**
|
|
25
|
+
* The unresolved placeholders inside `type` (read at `location` in `sourceFile`),
|
|
26
|
+
* or `undefined` when it has none — the common case, which costs one walk.
|
|
27
|
+
*
|
|
28
|
+
* `pathPrefix` maps the anchor type's member paths onto the paths of the alias
|
|
29
|
+
* the surface declares when the two differ: a symbol anchor restoring array
|
|
30
|
+
* depth prints `import('./m').Row[]`, whose members sit under `<0>`.
|
|
31
|
+
*/
|
|
32
|
+
export function unresolvedAtAnchor(program, sourceFile, type, location, pathPrefix = '') {
|
|
33
|
+
const checker = program.getTypeChecker();
|
|
34
|
+
const paths = findUnresolvedPlaceholders(type, program, checker, location).map((path) => prefixPath(pathPrefix, path));
|
|
35
|
+
if (paths.length === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
return { paths, specifiers: unresolvedSpecifiersReachableFrom(program, sourceFile) };
|
|
38
|
+
}
|
|
39
|
+
/** Join a walk path under a prefix in the walk's own notation. */
|
|
40
|
+
function prefixPath(prefix, path) {
|
|
41
|
+
if (prefix === '')
|
|
42
|
+
return path;
|
|
43
|
+
if (path === '')
|
|
44
|
+
return prefix;
|
|
45
|
+
return /^[<[(]/.test(path) ? `${prefix}${path}` : `${prefix}.${path}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Module specifiers, as written, that do not resolve from `sourceFile` or from
|
|
49
|
+
* any source module it imports, breadth-first so the nearest come first, with
|
|
50
|
+
* relative specifiers ahead of package names. Installed packages and the
|
|
51
|
+
* default library are not descended into.
|
|
52
|
+
*/
|
|
53
|
+
function unresolvedSpecifiersReachableFrom(program, sourceFile) {
|
|
54
|
+
let cache = reachableCache.get(program);
|
|
55
|
+
if (!cache) {
|
|
56
|
+
cache = new Map();
|
|
57
|
+
reachableCache.set(program, cache);
|
|
58
|
+
}
|
|
59
|
+
const cached = cache.get(sourceFile.fileName);
|
|
60
|
+
if (cached)
|
|
61
|
+
return cached;
|
|
62
|
+
const checker = program.getTypeChecker();
|
|
63
|
+
const unresolved = new Set();
|
|
64
|
+
const visited = new Set([sourceFile.fileName]);
|
|
65
|
+
const queue = [sourceFile];
|
|
66
|
+
while (queue.length > 0 && visited.size <= MAX_REACHABLE_FILES) {
|
|
67
|
+
const file = queue.shift();
|
|
68
|
+
for (const literal of moduleSpecifiersOf(file)) {
|
|
69
|
+
const module = checker.getSymbolAtLocation(literal);
|
|
70
|
+
if (!module) {
|
|
71
|
+
unresolved.add(literal.text);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// An ambient `declare module 'x'` resolves to a module declaration, not
|
|
75
|
+
// a file: resolved, with nothing further to walk.
|
|
76
|
+
const target = module.declarations?.find(ts.isSourceFile);
|
|
77
|
+
if (!target ||
|
|
78
|
+
visited.has(target.fileName) ||
|
|
79
|
+
program.isSourceFileDefaultLibrary(target) ||
|
|
80
|
+
program.isSourceFileFromExternalLibrary(target)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
visited.add(target.fileName);
|
|
84
|
+
queue.push(target);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const ordered = [...unresolved].sort((a, b) => Number(!a.startsWith('.')) - Number(!b.startsWith('.')));
|
|
88
|
+
cache.set(sourceFile.fileName, ordered);
|
|
89
|
+
return ordered;
|
|
90
|
+
}
|
|
91
|
+
/** The string-literal module specifiers of a file's imports and re-exports. */
|
|
92
|
+
function moduleSpecifiersOf(file) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const statement of file.statements) {
|
|
95
|
+
let specifier;
|
|
96
|
+
if (ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) {
|
|
97
|
+
specifier = statement.moduleSpecifier;
|
|
98
|
+
}
|
|
99
|
+
else if (ts.isImportEqualsDeclaration(statement) &&
|
|
100
|
+
ts.isExternalModuleReference(statement.moduleReference)) {
|
|
101
|
+
specifier = statement.moduleReference.expression;
|
|
102
|
+
}
|
|
103
|
+
if (specifier && ts.isStringLiteral(specifier))
|
|
104
|
+
out.push(specifier);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
@@ -135,6 +135,57 @@ export declare class TypeInferrer {
|
|
|
135
135
|
*/
|
|
136
136
|
private resolveParamTarget;
|
|
137
137
|
private inferResponseBody;
|
|
138
|
+
/**
|
|
139
|
+
* The wire representation a request's printed type takes. A route response
|
|
140
|
+
* is serialised as JSON by every sender this layer reads a payload out of,
|
|
141
|
+
* so it prints `toJSON()` results rather than the objects that declare them
|
|
142
|
+
* (carrick#1163). Everything else prints the declared type.
|
|
143
|
+
*/
|
|
144
|
+
private wireFormatFor;
|
|
145
|
+
/** An inferred type for a payload read out of response sends. */
|
|
146
|
+
private inferredFromRecoveredPayload;
|
|
147
|
+
/**
|
|
148
|
+
* carrick#1161: type a route response from every send in the handler the
|
|
149
|
+
* locator anchors, rather than from the one send it names.
|
|
150
|
+
*
|
|
151
|
+
* The analyzer reports one response expression per row, and on a handler
|
|
152
|
+
* that guards before it succeeds that is the guard's body. So the located
|
|
153
|
+
* expression selects the HANDLER: the function that contains it, on a row
|
|
154
|
+
* whose line is a route registration. Its returned sends are enumerated
|
|
155
|
+
* (conditional branches split), each is classified by the status it states
|
|
156
|
+
* (`responseSiteStatus`), the error and redirect branches are dropped, and
|
|
157
|
+
* the success bodies are joined exactly as the payload walk joins return
|
|
158
|
+
* statements.
|
|
159
|
+
*
|
|
160
|
+
* Returns `undefined` when this does not apply, which leaves the caller's
|
|
161
|
+
* own reading of the located expression untouched:
|
|
162
|
+
* - the row's line is not a route registration;
|
|
163
|
+
* - the located expression is neither a send nor the argument of one;
|
|
164
|
+
* - that send is not one the handler returns;
|
|
165
|
+
* - it is a success send and the only one that survives, so the join would
|
|
166
|
+
* be the located body anyway;
|
|
167
|
+
* - no joined body is the located one (a text body beside JSON ones).
|
|
168
|
+
*
|
|
169
|
+
* When the located send is an error or redirect branch and no success body
|
|
170
|
+
* survives, the route sends no body a contract can describe: an explicit
|
|
171
|
+
* `unknown` with its reason, so no later locator re-run reads the error body
|
|
172
|
+
* or the redirect location back in.
|
|
173
|
+
*/
|
|
174
|
+
private inferFromResponseSites;
|
|
175
|
+
/**
|
|
176
|
+
* True when `candidate` is a response send: a call or `new` whose result is
|
|
177
|
+
* transport machinery (by the extraction config's verified rule or the
|
|
178
|
+
* structural check), or whose callee this handler's own returns prove is a
|
|
179
|
+
* serialiser (`calleesProvenSerialiser`, which holds on a bare checkout).
|
|
180
|
+
*/
|
|
181
|
+
private isResponseSend;
|
|
182
|
+
/**
|
|
183
|
+
* The send `node` is an argument of, looking through the wrappers that do
|
|
184
|
+
* not change a payload (parentheses, `as`, `satisfies`, `!`, `await`) and a
|
|
185
|
+
* `JSON.stringify` around the body. `undefined` when the parent call is not
|
|
186
|
+
* a send or `node` is its callee.
|
|
187
|
+
*/
|
|
188
|
+
private sendReceivingArgument;
|
|
138
189
|
private inferCallResult;
|
|
139
190
|
private inferVariable;
|
|
140
191
|
private inferExpression;
|
|
@@ -524,24 +575,45 @@ export declare class TypeInferrer {
|
|
|
524
575
|
*/
|
|
525
576
|
private statesResponseInit;
|
|
526
577
|
/**
|
|
527
|
-
*
|
|
528
|
-
* `status`/`statusCode`, or a bare status code (`send(body, 404)`).
|
|
578
|
+
* What a response send states about its HTTP status (carrick#1161).
|
|
529
579
|
*
|
|
530
|
-
* Read
|
|
531
|
-
*
|
|
532
|
-
*
|
|
580
|
+
* Read in order, and the first that states anything decides:
|
|
581
|
+
*
|
|
582
|
+
* 1. an argument past the body that carries a status: a numeric literal
|
|
583
|
+
* (`send(body, 404)`), an options object (`{ status: 401 }`), or any
|
|
584
|
+
* expression whose TYPE is a status literal or a union of them
|
|
585
|
+
* (`let code: 400 | 500`). A numeric argument whose type is a plain
|
|
586
|
+
* `number` states a status the source does not fix, which is
|
|
587
|
+
* `variable`: the branch fails open into the union and is logged.
|
|
588
|
+
* 2. the send call's own RESULT type, when a member of it is typed as a
|
|
589
|
+
* status literal. A framework that types its sends records the status
|
|
590
|
+
* there, and a redirect helper's default `302` is only visible there.
|
|
591
|
+
* A result whose status member spans success and error codes (the
|
|
592
|
+
* default of a typed `json(body, status?)`) says nothing either way.
|
|
593
|
+
*
|
|
594
|
+
* A status in the FIRST argument is a field of the body and is never read.
|
|
595
|
+
* No method or framework name is consulted anywhere.
|
|
533
596
|
*/
|
|
534
|
-
private
|
|
597
|
+
private responseSiteStatus;
|
|
535
598
|
/**
|
|
536
|
-
* The HTTP status an argument states,
|
|
599
|
+
* The HTTP status codes an argument states, `'variable'` when it is a
|
|
600
|
+
* status-shaped value the source does not fix, or `undefined` when it says
|
|
601
|
+
* nothing about a status.
|
|
537
602
|
*
|
|
538
603
|
* Read from the AST first: `{ status: 400 }` in an argument position widens
|
|
539
604
|
* to `{ status: number }`, so the literal only survives syntactically. The
|
|
540
|
-
* type
|
|
541
|
-
* values in the HTTP range count
|
|
542
|
-
* domain object (`{ status: 2 }`)
|
|
605
|
+
* type read behind it catches `as const`, hoisted option objects and
|
|
606
|
+
* literal-typed variables. Only values in the HTTP range count: an
|
|
607
|
+
* arbitrary number named `status` on a domain object (`{ status: 2 }`)
|
|
608
|
+
* states nothing about transport.
|
|
609
|
+
*/
|
|
610
|
+
private statedStatusCodes;
|
|
611
|
+
/**
|
|
612
|
+
* Status codes a send call's result type records: the members (of the
|
|
613
|
+
* result, or of each part of an intersection result) typed as a status
|
|
614
|
+
* literal or a union of them. `undefined` when none is.
|
|
543
615
|
*/
|
|
544
|
-
private
|
|
616
|
+
private sendResultStatusCodes;
|
|
545
617
|
/**
|
|
546
618
|
* Peel the wrappers that do not change an expression's payload: parentheses
|
|
547
619
|
* and `await`. Unlike `unwrapExpressionNode` this KEEPS `as`/`satisfies`,
|
|
@@ -937,6 +1009,31 @@ export declare class TypeInferrer {
|
|
|
937
1009
|
* question (`schemaContract`), not this walk's.
|
|
938
1010
|
*/
|
|
939
1011
|
private middlewareBodySchemaValues;
|
|
1012
|
+
/**
|
|
1013
|
+
* The contract of the schema a handler validates an untyped body read with
|
|
1014
|
+
* (carrick#1166), or null.
|
|
1015
|
+
*
|
|
1016
|
+
* `read` is the located expression: the read call, or the binding that holds
|
|
1017
|
+
* it. Every call in the enclosing function that takes that value as an
|
|
1018
|
+
* argument is a candidate, and the schema is the call's receiver
|
|
1019
|
+
* (`Schema.safeParse(body)`) or one of its other arguments
|
|
1020
|
+
* (`parse(Schema, body)`). A candidate counts only when it declares schema
|
|
1021
|
+
* types (`schemaOutputType` / `schemaInputType`), so a logger or a service
|
|
1022
|
+
* call the body is also handed to is never read as a contract. No method or
|
|
1023
|
+
* library name is matched.
|
|
1024
|
+
*/
|
|
1025
|
+
private schemaConsumingRead;
|
|
1026
|
+
/**
|
|
1027
|
+
* The part a validated read names when that part is not a body, or
|
|
1028
|
+
* `undefined` (carrick#1166).
|
|
1029
|
+
*
|
|
1030
|
+
* The read is a call whose only argument is a string literal, and the
|
|
1031
|
+
* registration carries a validator middleware binding that same literal.
|
|
1032
|
+
* The part names match through the source's own literal, so no framework
|
|
1033
|
+
* vocabulary is assumed beyond `REQUEST_BODY_PARTS`, which already decides
|
|
1034
|
+
* what a body is for the middleware anchor.
|
|
1035
|
+
*/
|
|
1036
|
+
private nonBodyValidatedPart;
|
|
940
1037
|
/**
|
|
941
1038
|
* The request contract behind a VALIDATED READ: a call inside a registered
|
|
942
1039
|
* handler whose type is exactly the parsed output of a body schema the
|