carrick 0.3.134 → 0.3.135
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/plugin/.claude-plugin/plugin.json +1 -1
- package/sidecar/dist/src/capture/index.js +18 -0
- package/sidecar/dist/src/capture/machine-paths.d.ts +44 -0
- package/sidecar/dist/src/capture/machine-paths.js +97 -0
- package/sidecar/dist/src/capture/node-builder.d.ts +2 -0
- package/sidecar/dist/src/capture/node-builder.js +1 -1
- package/sidecar/dist/src/index.js +1 -1
- package/sidecar/dist/src/library-claims.d.ts +35 -3
- package/sidecar/dist/src/library-claims.js +380 -62
- package/sidecar/dist/src/types.d.ts +27 -3
- package/sidecar/dist/src/validators.d.ts +12 -0
- package/sidecar/dist/src/validators.js +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.135",
|
|
4
4
|
"carrickCache": {
|
|
5
5
|
"cache_version": 22,
|
|
6
6
|
"intent_cache_version": 2
|
|
@@ -114,11 +114,11 @@
|
|
|
114
114
|
"zod": "^3.23.0"
|
|
115
115
|
},
|
|
116
116
|
"optionalDependencies": {
|
|
117
|
-
"@carrick-tools/cli-darwin-arm64": "0.3.
|
|
118
|
-
"@carrick-tools/cli-darwin-x64": "0.3.
|
|
119
|
-
"@carrick-tools/cli-linux-arm64": "0.3.
|
|
120
|
-
"@carrick-tools/cli-linux-x64": "0.3.
|
|
121
|
-
"@carrick-tools/cli-win32-x64": "0.3.
|
|
117
|
+
"@carrick-tools/cli-darwin-arm64": "0.3.135",
|
|
118
|
+
"@carrick-tools/cli-darwin-x64": "0.3.135",
|
|
119
|
+
"@carrick-tools/cli-linux-arm64": "0.3.135",
|
|
120
|
+
"@carrick-tools/cli-linux-x64": "0.3.135",
|
|
121
|
+
"@carrick-tools/cli-win32-x64": "0.3.135"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@types/node": "^24.13.3"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
3
|
"description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.135",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Carrick",
|
|
7
7
|
"email": "hello@carrick.tools"
|
|
@@ -35,6 +35,7 @@ import { findAugmentationFiles } from './augmentations.js';
|
|
|
35
35
|
import { installedVersions, lockfileVersions } from './lockfile.js';
|
|
36
36
|
import { installedResolutionEdges, RESOLUTION_FILE } from './resolution-edges.js';
|
|
37
37
|
import { rewriteEmittedSpecifiers } from './paths-rewrite.js';
|
|
38
|
+
import { substituteMachinePathImports } from './machine-paths.js';
|
|
38
39
|
import { installedPackageSpecifier, typesPackageOf, withInstalledPackages } from './installed-package.js';
|
|
39
40
|
import { selfCheckStub } from './self-check.js';
|
|
40
41
|
import { collectSpecifiers, isRelative, packageNameOf } from './specifiers.js';
|
|
@@ -448,6 +449,23 @@ export function captureStub(opts) {
|
|
|
448
449
|
entryDir,
|
|
449
450
|
});
|
|
450
451
|
const specifierRewrites = denoRewrites + placed.rewrites + rewritten.rewrites;
|
|
452
|
+
// What the rewrite could not map still names a machine path (carrick#2943).
|
|
453
|
+
const machinePaths = substituteMachinePathImports({
|
|
454
|
+
files: emittedFiles.map((rel) => path.join(typesDir, rel)),
|
|
455
|
+
surfaceAbsPath,
|
|
456
|
+
tree: stubGuard.narrow(typesDir),
|
|
457
|
+
labelRoot: opts.scanRoot ?? repoRoot,
|
|
458
|
+
});
|
|
459
|
+
if (machinePaths.size > 0) {
|
|
460
|
+
resolved = resolved.map((anchor) => {
|
|
461
|
+
const found = machinePaths.get(anchor.request.alias);
|
|
462
|
+
if (!found)
|
|
463
|
+
return anchor;
|
|
464
|
+
const paths = [...new Set([...(anchor.unresolved?.paths ?? []), ...found.paths])];
|
|
465
|
+
const specifiers = [...new Set([...found.modules, ...(anchor.unresolved?.specifiers ?? [])])];
|
|
466
|
+
return { ...anchor, unresolved: { paths, specifiers } };
|
|
467
|
+
});
|
|
468
|
+
}
|
|
451
469
|
// ---- Pin external deps: installed node_modules first, lockfile fallback ----
|
|
452
470
|
// Externals are collected AFTER the rewrite pass: a rewritten paths
|
|
453
471
|
// specifier is internal, not a dependency.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No stub names a module by a machine path (carrick#2943).
|
|
3
|
+
*
|
|
4
|
+
* The specifier rewrite maps an absolute specifier onto a file of the stub's
|
|
5
|
+
* tree or onto an installed package. What it cannot map stays as printed: a
|
|
6
|
+
* type that a printer could name only by where its module sits on this
|
|
7
|
+
* machine, such as a workspace sibling's source reached through a
|
|
8
|
+
* `node_modules` link, which the declaration emit does not carry. That path
|
|
9
|
+
* resolves on the machine that ran the capture, to a source file outside the
|
|
10
|
+
* stub, and nowhere else: the upload rewrites it to `<checkout>/...`, so the
|
|
11
|
+
* stored stub and a fresh one disagree about the same alias.
|
|
12
|
+
*
|
|
13
|
+
* Each such reference becomes `unknown` at its own position. In the surface
|
|
14
|
+
* the position is reported per alias, so the self-check labels the finding
|
|
15
|
+
* there as a reference that did not resolve rather than as declared; the rest
|
|
16
|
+
* of the alias's type survives. Any other tree file gets the dangling-import
|
|
17
|
+
* repair (carrick#1397), which writes `unknown` where the import's names were
|
|
18
|
+
* used. The stub then reads the same on every machine.
|
|
19
|
+
*
|
|
20
|
+
* Seam: node builtins + `typescript`, like the rest of this directory.
|
|
21
|
+
*/
|
|
22
|
+
import type { WriteGuard } from './guarded-fs.js';
|
|
23
|
+
/** A specifier that names a module by where it sits on one machine. */
|
|
24
|
+
export declare function isMachinePath(specifier: string): boolean;
|
|
25
|
+
/** What the surface substitution replaced in one alias. */
|
|
26
|
+
export interface MachinePathPositions {
|
|
27
|
+
/** Member paths now `unknown`, in the deep walk's notation (`''` = root). */
|
|
28
|
+
paths: string[];
|
|
29
|
+
/** The modules they named, relative to `labelRoot` (never absolute). */
|
|
30
|
+
modules: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Replace every machine-path import in the stub's tree with `unknown`.
|
|
34
|
+
* `files` are absolute tree paths; `surfaceAbsPath` is one of them. Returns
|
|
35
|
+
* the positions replaced in the surface, by alias.
|
|
36
|
+
*/
|
|
37
|
+
export declare function substituteMachinePathImports(args: {
|
|
38
|
+
files: readonly string[];
|
|
39
|
+
surfaceAbsPath: string;
|
|
40
|
+
/** A guard over the stub's types tree. */
|
|
41
|
+
tree: WriteGuard;
|
|
42
|
+
/** The root a module is named from in a label (the scanned repo's). */
|
|
43
|
+
labelRoot: string;
|
|
44
|
+
}): Map<string, MachinePathPositions>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No stub names a module by a machine path (carrick#2943).
|
|
3
|
+
*
|
|
4
|
+
* The specifier rewrite maps an absolute specifier onto a file of the stub's
|
|
5
|
+
* tree or onto an installed package. What it cannot map stays as printed: a
|
|
6
|
+
* type that a printer could name only by where its module sits on this
|
|
7
|
+
* machine, such as a workspace sibling's source reached through a
|
|
8
|
+
* `node_modules` link, which the declaration emit does not carry. That path
|
|
9
|
+
* resolves on the machine that ran the capture, to a source file outside the
|
|
10
|
+
* stub, and nowhere else: the upload rewrites it to `<checkout>/...`, so the
|
|
11
|
+
* stored stub and a fresh one disagree about the same alias.
|
|
12
|
+
*
|
|
13
|
+
* Each such reference becomes `unknown` at its own position. In the surface
|
|
14
|
+
* the position is reported per alias, so the self-check labels the finding
|
|
15
|
+
* there as a reference that did not resolve rather than as declared; the rest
|
|
16
|
+
* of the alias's type survives. Any other tree file gets the dangling-import
|
|
17
|
+
* repair (carrick#1397), which writes `unknown` where the import's names were
|
|
18
|
+
* used. The stub then reads the same on every machine.
|
|
19
|
+
*
|
|
20
|
+
* Seam: node builtins + `typescript`, like the rest of this directory.
|
|
21
|
+
*/
|
|
22
|
+
import ts from 'typescript';
|
|
23
|
+
import * as fs from 'node:fs';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
import { childPath } from './node-builder.js';
|
|
26
|
+
import { repairDanglingImports } from './repair-dangling.js';
|
|
27
|
+
import { collectSpecifiers } from './specifiers.js';
|
|
28
|
+
/** A specifier that names a module by where it sits on one machine. */
|
|
29
|
+
export function isMachinePath(specifier) {
|
|
30
|
+
return path.posix.isAbsolute(specifier) || /^[A-Za-z]:[\\/]/.test(specifier) || specifier.startsWith('\\\\');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Replace every machine-path import in the stub's tree with `unknown`.
|
|
34
|
+
* `files` are absolute tree paths; `surfaceAbsPath` is one of them. Returns
|
|
35
|
+
* the positions replaced in the surface, by alias.
|
|
36
|
+
*/
|
|
37
|
+
export function substituteMachinePathImports(args) {
|
|
38
|
+
const failing = new Map();
|
|
39
|
+
for (const file of args.files) {
|
|
40
|
+
if (path.resolve(file) === path.resolve(args.surfaceAbsPath))
|
|
41
|
+
continue;
|
|
42
|
+
const machine = [...collectSpecifiers(fs.readFileSync(file, 'utf8'))].filter(isMachinePath);
|
|
43
|
+
if (machine.length > 0)
|
|
44
|
+
failing.set(file, new Set(machine));
|
|
45
|
+
}
|
|
46
|
+
if (failing.size > 0)
|
|
47
|
+
repairDanglingImports(failing, args.tree);
|
|
48
|
+
return substituteInSurface(args.surfaceAbsPath, args.tree, args.labelRoot);
|
|
49
|
+
}
|
|
50
|
+
function substituteInSurface(surfaceAbsPath, tree, labelRoot) {
|
|
51
|
+
const byAlias = new Map();
|
|
52
|
+
const text = fs.readFileSync(surfaceAbsPath, 'utf8');
|
|
53
|
+
if (!/import\s*\(/.test(text))
|
|
54
|
+
return byAlias;
|
|
55
|
+
const source = ts.createSourceFile(surfaceAbsPath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
56
|
+
const edits = [];
|
|
57
|
+
for (const statement of source.statements) {
|
|
58
|
+
if (!ts.isTypeAliasDeclaration(statement))
|
|
59
|
+
continue;
|
|
60
|
+
const found = { paths: [], modules: [] };
|
|
61
|
+
const visit = (node, at) => {
|
|
62
|
+
const specifier = importTypeSpecifier(node);
|
|
63
|
+
if (specifier !== undefined && isMachinePath(specifier)) {
|
|
64
|
+
edits.push({ start: node.getStart(source), end: node.getEnd() });
|
|
65
|
+
if (!found.paths.includes(at))
|
|
66
|
+
found.paths.push(at);
|
|
67
|
+
const module = moduleLabel(specifier, labelRoot);
|
|
68
|
+
if (!found.modules.includes(module))
|
|
69
|
+
found.modules.push(module);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
node.forEachChild((child) => visit(child, childPath(node, child, at)));
|
|
73
|
+
};
|
|
74
|
+
visit(statement.type, '');
|
|
75
|
+
if (found.paths.length > 0)
|
|
76
|
+
byAlias.set(statement.name.text, found);
|
|
77
|
+
}
|
|
78
|
+
if (edits.length === 0)
|
|
79
|
+
return byAlias;
|
|
80
|
+
edits.sort((a, b) => b.start - a.start);
|
|
81
|
+
let out = text;
|
|
82
|
+
for (const edit of edits)
|
|
83
|
+
out = `${out.slice(0, edit.start)}unknown${out.slice(edit.end)}`;
|
|
84
|
+
tree.writeFile(surfaceAbsPath, out);
|
|
85
|
+
return byAlias;
|
|
86
|
+
}
|
|
87
|
+
/** The string an `import("...")` type names, when `node` is one. */
|
|
88
|
+
function importTypeSpecifier(node) {
|
|
89
|
+
if (!ts.isImportTypeNode(node) || !ts.isLiteralTypeNode(node.argument))
|
|
90
|
+
return undefined;
|
|
91
|
+
const literal = node.argument.literal;
|
|
92
|
+
return ts.isStringLiteral(literal) ? literal.text : undefined;
|
|
93
|
+
}
|
|
94
|
+
/** `specifier` relative to `root`, with forward slashes. */
|
|
95
|
+
function moduleLabel(specifier, root) {
|
|
96
|
+
return path.relative(path.resolve(root), path.resolve(specifier)).split(path.sep).join('/');
|
|
97
|
+
}
|
|
@@ -67,6 +67,8 @@ export declare function substituteUndeclaredNames(node: ts.TypeNode, program: ts
|
|
|
67
67
|
node: ts.TypeNode;
|
|
68
68
|
substitutions: UndeclaredSubstitution[];
|
|
69
69
|
};
|
|
70
|
+
/** The deep walk's path for `child` inside `parent`, extending `path`. */
|
|
71
|
+
export declare function childPath(parent: ts.Node, child: ts.Node, path: string): string;
|
|
70
72
|
/**
|
|
71
73
|
* Bare names in a printed type node that nothing in the producer's program
|
|
72
74
|
* declares (carrick#1165).
|
|
@@ -174,7 +174,7 @@ export function substituteUndeclaredNames(node, program, destination) {
|
|
|
174
174
|
return { node: rewritten, substitutions };
|
|
175
175
|
}
|
|
176
176
|
/** The deep walk's path for `child` inside `parent`, extending `path`. */
|
|
177
|
-
function childPath(parent, child, path) {
|
|
177
|
+
export function childPath(parent, child, path) {
|
|
178
178
|
if (ts.isPropertySignature(parent) && parent.type === child) {
|
|
179
179
|
const name = ts.isIdentifier(parent.name) || ts.isStringLiteral(parent.name)
|
|
180
180
|
? parent.name.text
|
|
@@ -637,7 +637,7 @@ function handleListLibrarySurface(request) {
|
|
|
637
637
|
const { claimsVerifier } = projectComponents();
|
|
638
638
|
const fromDir = path.resolve(projectLoader.getRepoRoot(), request.from_dir);
|
|
639
639
|
log(`Listing the declared surface of ${request.packages.length} specifier(s) from ${fromDir}`);
|
|
640
|
-
const { surfaces, surface_sha256 } = claimsVerifier.listSurface(fromDir, request.packages, request.max_entries ?? SURFACE_MAX_ENTRIES, request.exports ?? {});
|
|
640
|
+
const { surfaces, surface_sha256 } = claimsVerifier.listSurface(fromDir, request.packages, request.max_entries ?? SURFACE_MAX_ENTRIES, request.exports ?? {}, { handlers: request.handlers, resultCandidates: request.result_candidates });
|
|
641
641
|
return { request_id: request.request_id, status: 'success', surfaces, surface_sha256 };
|
|
642
642
|
}
|
|
643
643
|
catch (err) {
|
|
@@ -50,6 +50,11 @@ type Outcome = {
|
|
|
50
50
|
*/
|
|
51
51
|
members?: readonly ts.Declaration[];
|
|
52
52
|
bodyMembers?: readonly ts.Declaration[];
|
|
53
|
+
/**
|
|
54
|
+
* Stamped: why a receive op's `request` was dropped while the op
|
|
55
|
+
* itself verified (carrick#2968). Only `request_member_missing`.
|
|
56
|
+
*/
|
|
57
|
+
requestDropped?: string;
|
|
53
58
|
} | {
|
|
54
59
|
verdict: 'failed' | 'unchecked';
|
|
55
60
|
reason: string;
|
|
@@ -86,6 +91,15 @@ export interface ModuleGraph {
|
|
|
86
91
|
/** How the program resolves `name` from `from`; `undefined` where there is no graph, and Node resolution answers. */
|
|
87
92
|
resolveModule(name: string, from: string, options: ts.CompilerOptions): ts.ResolvedModuleFull | undefined;
|
|
88
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Test instrumentation, never on the wire: how many times the lister built a
|
|
96
|
+
* handler parameter's members and printed a type, since the caller last
|
|
97
|
+
* reset them (carrick#2977).
|
|
98
|
+
*/
|
|
99
|
+
export declare const surfaceReadCounts: {
|
|
100
|
+
handlerMemberBuilds: number;
|
|
101
|
+
typePrints: number;
|
|
102
|
+
};
|
|
89
103
|
export declare class LibraryClaimsVerifier {
|
|
90
104
|
private readonly project;
|
|
91
105
|
private readonly graph;
|
|
@@ -124,9 +138,20 @@ export declare class LibraryClaimsVerifier {
|
|
|
124
138
|
* (`receiverExpression` on the export's `importLine`, read through
|
|
125
139
|
* `DeclarationReader.returnOf`) and lists from it.
|
|
126
140
|
*
|
|
141
|
+
* A member of an `instance:` receiver whose call returns another object
|
|
142
|
+
* that routes are registered on lists that object as its `scope_receiver`
|
|
143
|
+
* (carrick#2774), built as the verifier builds a scope step, under a cap of
|
|
144
|
+
* its own and left out of the hash.
|
|
145
|
+
*
|
|
146
|
+
* Each member's handler-parameter members (carrick#2674 S3) are listed
|
|
147
|
+
* only when `options.handlers` is set, and each signature's result
|
|
148
|
+
* candidates (carrick#2817) only when `options.resultCandidates` is
|
|
149
|
+
* (carrick#2966): on a large package either costs more than the rest of the
|
|
150
|
+
* listing.
|
|
151
|
+
*
|
|
127
152
|
* `surface_sha256` is the full-surface hash (see `fullSurfaceSha256`).
|
|
128
153
|
*/
|
|
129
|
-
listSurface(fromDir: string, packages: string[], maxEntries: number, only?: Readonly<Record<string, string[]
|
|
154
|
+
listSurface(fromDir: string, packages: string[], maxEntries: number, only?: Readonly<Record<string, string[]>>, options?: SurfaceOptions): ListedSurface;
|
|
130
155
|
/** `read` over a probe file holding `lines` in `fromDir`, removed again afterwards. */
|
|
131
156
|
private readProbe;
|
|
132
157
|
}
|
|
@@ -140,8 +165,8 @@ export interface ListedSurface {
|
|
|
140
165
|
* array of `[package, exports]` for every surface that listed at least one
|
|
141
166
|
* export, sorted by `package` (code unit order). A specifier that lists
|
|
142
167
|
* nothing (unresolved, local, no value exports) is left out, so asking for a
|
|
143
|
-
* subpath a version does not have changes nothing. Handler members
|
|
144
|
-
* hashed. Type text already names
|
|
168
|
+
* subpath a version does not have changes nothing. Handler members, result
|
|
169
|
+
* candidates and scope receivers are not hashed. Type text already names
|
|
145
170
|
* the request's directory as `<root>`.
|
|
146
171
|
*/
|
|
147
172
|
export declare function fullSurfaceSha256(surfaces: readonly LibrarySurface[]): string;
|
|
@@ -177,4 +202,11 @@ export interface LabelledMembers {
|
|
|
177
202
|
/** How each label read came out (`verified`, or the reason it was not), for the log. */
|
|
178
203
|
outcomes?: Readonly<Record<string, number>>;
|
|
179
204
|
}
|
|
205
|
+
/** What a surface listing adds beyond the hashed surface, each only when asked (carrick#2966). */
|
|
206
|
+
export interface SurfaceOptions {
|
|
207
|
+
/** Each member's `handlers` (carrick#2674 S3). */
|
|
208
|
+
handlers?: boolean;
|
|
209
|
+
/** Each signature's `result_candidates` (carrick#2817). */
|
|
210
|
+
resultCandidates?: boolean;
|
|
211
|
+
}
|
|
180
212
|
export {};
|
|
@@ -259,6 +259,12 @@ function sortedDeclarations(declarations) {
|
|
|
259
259
|
}
|
|
260
260
|
let probeSequence = 0;
|
|
261
261
|
const NO_MODULE_GRAPH = { packageDirectoryOf: () => undefined, resolveModule: () => undefined };
|
|
262
|
+
/**
|
|
263
|
+
* Test instrumentation, never on the wire: how many times the lister built a
|
|
264
|
+
* handler parameter's members and printed a type, since the caller last
|
|
265
|
+
* reset them (carrick#2977).
|
|
266
|
+
*/
|
|
267
|
+
export const surfaceReadCounts = { handlerMemberBuilds: 0, typePrints: 0 };
|
|
262
268
|
export class LibraryClaimsVerifier {
|
|
263
269
|
project;
|
|
264
270
|
graph;
|
|
@@ -281,6 +287,7 @@ export class LibraryClaimsVerifier {
|
|
|
281
287
|
receiver: check.receiver,
|
|
282
288
|
verdict: 'verified',
|
|
283
289
|
...(outcome.declarations?.length ? { declarations: outcome.declarations } : {}),
|
|
290
|
+
...(outcome.requestDropped === undefined ? {} : { request_dropped: outcome.requestDropped }),
|
|
284
291
|
}
|
|
285
292
|
: {
|
|
286
293
|
claim_id: check.claim_id,
|
|
@@ -587,6 +594,8 @@ export class LibraryClaimsVerifier {
|
|
|
587
594
|
const declarations = [];
|
|
588
595
|
const members = [];
|
|
589
596
|
const bodyMembers = [];
|
|
597
|
+
// A request one receiver does not hold is dropped for the op (carrick#2968).
|
|
598
|
+
let requestDropped;
|
|
590
599
|
for (const receiver of receivers.value) {
|
|
591
600
|
const result = reader.judgeMessage(receiver, check.claim, rules, builtAtDefaults, collectionsOf);
|
|
592
601
|
if (result.outcome.verdict !== 'verified')
|
|
@@ -595,11 +604,14 @@ export class LibraryClaimsVerifier {
|
|
|
595
604
|
declarations.push(...(result.outcome.declarations ?? []));
|
|
596
605
|
members.push(...(result.outcome.members ?? []));
|
|
597
606
|
bodyMembers.push(...(result.outcome.bodyMembers ?? []));
|
|
607
|
+
requestDropped ??= result.outcome.requestDropped;
|
|
598
608
|
}
|
|
599
609
|
const verified = declarations.length > 0 ? { verdict: 'verified', declarations: sortedDeclarations(declarations) } : { ...VERIFIED };
|
|
600
610
|
if (verified.verdict === 'verified' && members.length > 0)
|
|
601
611
|
verified.members = members;
|
|
602
|
-
if (verified.verdict === 'verified' &&
|
|
612
|
+
if (verified.verdict === 'verified' && requestDropped !== undefined)
|
|
613
|
+
verified.requestDropped = requestDropped;
|
|
614
|
+
else if (verified.verdict === 'verified' && bodyMembers.length > 0)
|
|
603
615
|
verified.bodyMembers = bodyMembers;
|
|
604
616
|
return { outcome: verified, holding };
|
|
605
617
|
};
|
|
@@ -645,7 +657,11 @@ export class LibraryClaimsVerifier {
|
|
|
645
657
|
const observe = (check, outcome) => {
|
|
646
658
|
if (check.claim.kind !== 'op')
|
|
647
659
|
return;
|
|
648
|
-
const read = outcome.verdict
|
|
660
|
+
const read = outcome.verdict !== 'verified'
|
|
661
|
+
? `${outcome.verdict} ${outcome.reason}`
|
|
662
|
+
: outcome.requestDropped === undefined
|
|
663
|
+
? 'verified'
|
|
664
|
+
: `verified request_dropped ${outcome.requestDropped}`;
|
|
649
665
|
outcomes[read] = (outcomes[read] ?? 0) + 1;
|
|
650
666
|
if (outcome.verdict !== 'verified')
|
|
651
667
|
return;
|
|
@@ -678,9 +694,20 @@ export class LibraryClaimsVerifier {
|
|
|
678
694
|
* (`receiverExpression` on the export's `importLine`, read through
|
|
679
695
|
* `DeclarationReader.returnOf`) and lists from it.
|
|
680
696
|
*
|
|
697
|
+
* A member of an `instance:` receiver whose call returns another object
|
|
698
|
+
* that routes are registered on lists that object as its `scope_receiver`
|
|
699
|
+
* (carrick#2774), built as the verifier builds a scope step, under a cap of
|
|
700
|
+
* its own and left out of the hash.
|
|
701
|
+
*
|
|
702
|
+
* Each member's handler-parameter members (carrick#2674 S3) are listed
|
|
703
|
+
* only when `options.handlers` is set, and each signature's result
|
|
704
|
+
* candidates (carrick#2817) only when `options.resultCandidates` is
|
|
705
|
+
* (carrick#2966): on a large package either costs more than the rest of the
|
|
706
|
+
* listing.
|
|
707
|
+
*
|
|
681
708
|
* `surface_sha256` is the full-surface hash (see `fullSurfaceSha256`).
|
|
682
709
|
*/
|
|
683
|
-
listSurface(fromDir, packages, maxEntries, only = {}) {
|
|
710
|
+
listSurface(fromDir, packages, maxEntries, only = {}, options = {}) {
|
|
684
711
|
if (packages.length === 0)
|
|
685
712
|
return { surfaces: [], surface_sha256: fullSurfaceSha256([]) };
|
|
686
713
|
// Each specifier twice: as a namespace, which holds its named exports, and
|
|
@@ -709,10 +736,12 @@ export class LibraryClaimsVerifier {
|
|
|
709
736
|
const buildLines = [];
|
|
710
737
|
const builtIndex = new Map();
|
|
711
738
|
packages.forEach((pkg, i) => {
|
|
712
|
-
for (const { export: name, receivers } of makers[i]) {
|
|
739
|
+
for (const { export: name, receivers, scopes } of makers[i]) {
|
|
713
740
|
const local = `__carrick_e${importLines.length}`;
|
|
714
741
|
importLines.push(probeLine(pkg, name, local));
|
|
715
|
-
|
|
742
|
+
// The scope receivers a maker's members may return (carrick#2774) are
|
|
743
|
+
// built beside the makers, so `listPackage` reads them as it reads those.
|
|
744
|
+
for (const receiver of [...receivers, ...scopes]) {
|
|
716
745
|
const expression = receiverExpression(local, receiver);
|
|
717
746
|
if (expression === undefined)
|
|
718
747
|
continue;
|
|
@@ -722,7 +751,7 @@ export class LibraryClaimsVerifier {
|
|
|
722
751
|
}
|
|
723
752
|
});
|
|
724
753
|
const firstBuild = imports.length + importLines.length;
|
|
725
|
-
const surfaces = this.readProbe(fromDir, [...imports, ...importLines, ...buildLines], (checker, reader, file) => packages.map((pkg, i) => reader.listPackage(pkg, probeOf(file, i), maxEntries, only[pkg], (name, receiver) => {
|
|
754
|
+
const surfaces = this.readProbe(fromDir, [...imports, ...importLines, ...buildLines], (checker, reader, file) => packages.map((pkg, i) => reader.listPackage(pkg, probeOf(file, i), maxEntries, only[pkg], options, (name, receiver) => {
|
|
726
755
|
const index = builtIndex.get(builtKey({ package: pkg, export: name }, receiver));
|
|
727
756
|
return index === undefined ? undefined : resolvedBuild(checker, file.statements[firstBuild + index]);
|
|
728
757
|
})));
|
|
@@ -763,8 +792,8 @@ function resolvedBuild(checker, statement) {
|
|
|
763
792
|
* array of `[package, exports]` for every surface that listed at least one
|
|
764
793
|
* export, sorted by `package` (code unit order). A specifier that lists
|
|
765
794
|
* nothing (unresolved, local, no value exports) is left out, so asking for a
|
|
766
|
-
* subpath a version does not have changes nothing. Handler members
|
|
767
|
-
* hashed. Type text already names
|
|
795
|
+
* subpath a version does not have changes nothing. Handler members, result
|
|
796
|
+
* candidates and scope receivers are not hashed. Type text already names
|
|
768
797
|
* the request's directory as `<root>`.
|
|
769
798
|
*/
|
|
770
799
|
export function fullSurfaceSha256(surfaces) {
|
|
@@ -772,10 +801,11 @@ export function fullSurfaceSha256(surfaces) {
|
|
|
772
801
|
.filter(surface => surface.reason === undefined && surface.exports.length > 0)
|
|
773
802
|
.map(surface => [surface.package, surface.exports])
|
|
774
803
|
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
775
|
-
// A member's `handlers` (carrick#2674)
|
|
776
|
-
// (carrick#2817)
|
|
777
|
-
//
|
|
778
|
-
|
|
804
|
+
// A member's `handlers` (carrick#2674), a signature's `result_candidates`
|
|
805
|
+
// (carrick#2817) and a member's `scope_receiver` (carrick#2774) are out of
|
|
806
|
+
// the hash at every depth, so a package's key does not change when the
|
|
807
|
+
// lister learns to list them.
|
|
808
|
+
const text = JSON.stringify(listed, (key, value) => key === 'handlers' || key === 'result_candidates' || key === 'scope_receiver' ? undefined : value);
|
|
779
809
|
return crypto.createHash('sha256').update(text).digest('hex');
|
|
780
810
|
}
|
|
781
811
|
function exportKey(check) {
|
|
@@ -928,6 +958,18 @@ function receiverExpression(local, receiver) {
|
|
|
928
958
|
text = `${text}${parsed.scope.split('.').map(access).join('')}()`;
|
|
929
959
|
return text;
|
|
930
960
|
}
|
|
961
|
+
/**
|
|
962
|
+
* The id of the receiver `member` of the receiver `receiver` returns when
|
|
963
|
+
* called with a path (carrick#2774): `receiver>scope:member`. Undefined for a
|
|
964
|
+
* receiver that is not an instance, or a member name the verifier's grammar
|
|
965
|
+
* does not read back as one scope step.
|
|
966
|
+
*/
|
|
967
|
+
function scopeReceiverId(receiver, member) {
|
|
968
|
+
if (!receiver.startsWith('instance:') || receiver.includes('>') || /[>.\s]/.test(member))
|
|
969
|
+
return undefined;
|
|
970
|
+
const id = `${receiver}>scope:${member}`;
|
|
971
|
+
return parseReceiver(id)?.scope === member ? id : undefined;
|
|
972
|
+
}
|
|
931
973
|
/** The symbols that say who declared a type: its own, its alias's, its parts'. */
|
|
932
974
|
function typeSymbols(type) {
|
|
933
975
|
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
@@ -1020,8 +1062,24 @@ class DeclarationReader {
|
|
|
1020
1062
|
currentPackage = '';
|
|
1021
1063
|
/** `declaredProperties` per type, for `currentPackage` (its augmentations decide them). */
|
|
1022
1064
|
declared = new Map();
|
|
1065
|
+
/**
|
|
1066
|
+
* A handler parameter's listed members per type (`handlerMembersOf`), kept
|
|
1067
|
+
* across every specifier of one package and cleared with `declared` when
|
|
1068
|
+
* the package changes: a member's listing reads that package's
|
|
1069
|
+
* augmentations (carrick#2977).
|
|
1070
|
+
*/
|
|
1071
|
+
handlerMembers = new Map();
|
|
1072
|
+
/** `takesString` per signature, and `scopeMembers` per type and home: the same types recur across receivers. */
|
|
1073
|
+
takesStringRead = new Map();
|
|
1074
|
+
scopeMembersRead = new Map();
|
|
1075
|
+
/** Handler-parameter members `HANDLER_MEMBER_CAP` dropped in the package being listed. */
|
|
1076
|
+
handlersDropped = 0;
|
|
1077
|
+
/** Whether the package being listed lists each signature's `result_candidates`. */
|
|
1078
|
+
listResultCandidates = false;
|
|
1023
1079
|
/** The service root as given and as a realpath, longest first: what a listing prints as `<root>`. */
|
|
1024
1080
|
printedRoots;
|
|
1081
|
+
/** `printed` per type: its text reads nothing but the type and `printedRoots` (carrick#2977). */
|
|
1082
|
+
printedTexts = new Map();
|
|
1025
1083
|
constructor(program, probe, host, serviceRoot, scanRoot, graph) {
|
|
1026
1084
|
this.program = program;
|
|
1027
1085
|
this.probe = probe;
|
|
@@ -2045,6 +2103,10 @@ class DeclarationReader {
|
|
|
2045
2103
|
const labels = claim.key_labels ?? {};
|
|
2046
2104
|
let best;
|
|
2047
2105
|
let held = false;
|
|
2106
|
+
// An overload that holds but for a request member the declarations lack
|
|
2107
|
+
// (carrick#2968): the op stands without its request, unless another
|
|
2108
|
+
// overload holds the request too. Every other request fault fails the op.
|
|
2109
|
+
let requestDropped;
|
|
2048
2110
|
const bodyMembers = [];
|
|
2049
2111
|
for (const signature of callee.signatures) {
|
|
2050
2112
|
let result = this.messageSignatureOutcome(signature, layout.value, labels);
|
|
@@ -2055,19 +2117,26 @@ class DeclarationReader {
|
|
|
2055
2117
|
}
|
|
2056
2118
|
if (result.rank === Infinity && claim.request !== undefined) {
|
|
2057
2119
|
const read = this.requestOutcome(signature, claim.handler, claim.request, bodyMembers);
|
|
2120
|
+
if (read?.verdict === 'failed' && read.reason === 'request_member_missing') {
|
|
2121
|
+
requestDropped = read.reason;
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2058
2124
|
if (read)
|
|
2059
2125
|
result = { rank: 5, outcome: read };
|
|
2060
2126
|
}
|
|
2061
2127
|
if (result.rank === Infinity) {
|
|
2062
2128
|
held = true;
|
|
2129
|
+
requestDropped = undefined;
|
|
2063
2130
|
break;
|
|
2064
2131
|
}
|
|
2065
2132
|
best = furthest(best, result.rank, result.outcome);
|
|
2066
2133
|
}
|
|
2067
|
-
if (!held)
|
|
2134
|
+
if (!held && requestDropped === undefined)
|
|
2068
2135
|
return best.outcome;
|
|
2069
|
-
if (boundRuns)
|
|
2070
|
-
|
|
2136
|
+
if (boundRuns) {
|
|
2137
|
+
const ran = this.boundRunsOutcome(receiver.makerHolding, boundRuns);
|
|
2138
|
+
return ran.verdict === 'verified' && requestDropped !== undefined ? { ...ran, requestDropped } : ran;
|
|
2139
|
+
}
|
|
2071
2140
|
const declarations = this.declarationsOf(callee);
|
|
2072
2141
|
const members = callee.named
|
|
2073
2142
|
? (callee.named.property.declarations ?? [])
|
|
@@ -2077,6 +2146,7 @@ class DeclarationReader {
|
|
|
2077
2146
|
...(declarations.length > 0 ? { declarations } : {}),
|
|
2078
2147
|
...(members.length > 0 ? { members } : {}),
|
|
2079
2148
|
...(bodyMembers.length > 0 ? { bodyMembers } : {}),
|
|
2149
|
+
...(requestDropped === undefined ? {} : { requestDropped }),
|
|
2080
2150
|
};
|
|
2081
2151
|
}
|
|
2082
2152
|
/**
|
|
@@ -3011,7 +3081,7 @@ class DeclarationReader {
|
|
|
3011
3081
|
* `built` answers the probe's no-argument build of a receiver an export
|
|
3012
3082
|
* makes, resolved, when the probe holds one.
|
|
3013
3083
|
*/
|
|
3014
|
-
listPackage(pkg, probe, maxEntries, only, built) {
|
|
3084
|
+
listPackage(pkg, probe, maxEntries, only, options, built) {
|
|
3015
3085
|
this.setPackage(pkg);
|
|
3016
3086
|
// Runtime modules on: a `node:` specifier is listed from the runtime's
|
|
3017
3087
|
// types package, as the message roles read it.
|
|
@@ -3036,24 +3106,24 @@ class DeclarationReader {
|
|
|
3036
3106
|
dropped += 1;
|
|
3037
3107
|
return false;
|
|
3038
3108
|
};
|
|
3039
|
-
// Handler-parameter members (carrick#2674)
|
|
3040
|
-
//
|
|
3041
|
-
//
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
handlerBudget -= 1;
|
|
3047
|
-
return true;
|
|
3048
|
-
};
|
|
3109
|
+
// Handler-parameter members (carrick#2674) take nothing from the main cap,
|
|
3110
|
+
// so the listing, `truncated` and the hash are the ones the lister gave
|
|
3111
|
+
// before handlers were listed; each parameter has a cap of its own
|
|
3112
|
+
// (`HANDLER_MEMBER_CAP`, carrick#2966).
|
|
3113
|
+
this.handlersDropped = 0;
|
|
3114
|
+
this.listResultCandidates = options.resultCandidates === true;
|
|
3115
|
+
const withHandlers = options.handlers === true;
|
|
3049
3116
|
// Four passes: every export and receiver, then every member name, then the
|
|
3050
|
-
// members of each handler's parameters (carrick#2674
|
|
3117
|
+
// members of each handler's parameters (carrick#2674; only when asked,
|
|
3118
|
+
// carrick#2966), then signatures.
|
|
3051
3119
|
const names = [];
|
|
3052
3120
|
const handlers = [];
|
|
3053
3121
|
const fills = [];
|
|
3054
3122
|
// What the package's default export names: a maker also exported by name
|
|
3055
3123
|
// under this declaration is imported the same way, so it is read the same.
|
|
3056
3124
|
const defaultTarget = exported.find(each => each.name === 'default')?.target;
|
|
3125
|
+
// The instance receivers listed, for the scope pass (carrick#2774).
|
|
3126
|
+
const instances = [];
|
|
3057
3127
|
for (const { name, target, type } of exported) {
|
|
3058
3128
|
if (!take())
|
|
3059
3129
|
continue;
|
|
@@ -3065,7 +3135,11 @@ class DeclarationReader {
|
|
|
3065
3135
|
if (!take())
|
|
3066
3136
|
return;
|
|
3067
3137
|
const home = this.homeOf(pkg, [target, ...typeSymbols(receiverType)]);
|
|
3068
|
-
|
|
3138
|
+
const receiver = this.outlineReceiver(pkg, receiverName, receiverType, home, take, names, handlers, fills);
|
|
3139
|
+
entry.receivers.push(receiver);
|
|
3140
|
+
if (receiverName.startsWith('instance:')) {
|
|
3141
|
+
instances.push({ export: name, target, type: receiverType, home, receiver });
|
|
3142
|
+
}
|
|
3069
3143
|
};
|
|
3070
3144
|
receiverOf('export', type);
|
|
3071
3145
|
for (const maker of this.makers(pkg, target, type)) {
|
|
@@ -3082,13 +3156,57 @@ class DeclarationReader {
|
|
|
3082
3156
|
}
|
|
3083
3157
|
for (const run of names)
|
|
3084
3158
|
run();
|
|
3085
|
-
|
|
3086
|
-
run
|
|
3159
|
+
if (withHandlers)
|
|
3160
|
+
for (const run of handlers)
|
|
3161
|
+
run();
|
|
3087
3162
|
for (const fill of fills)
|
|
3088
3163
|
fill();
|
|
3164
|
+
this.listScopeReceivers(pkg, instances, maxEntries, withHandlers, built);
|
|
3089
3165
|
surface.truncated = dropped;
|
|
3166
|
+
if (withHandlers)
|
|
3167
|
+
surface.handlers_truncated = this.handlersDropped;
|
|
3090
3168
|
return surface;
|
|
3091
3169
|
}
|
|
3170
|
+
/**
|
|
3171
|
+
* The fifth pass (carrick#2774): each own member of an instance receiver
|
|
3172
|
+
* whose call returns the object routes are registered on lists it as
|
|
3173
|
+
* `scope_receiver`, outlined as any receiver is. Everything it lists draws
|
|
3174
|
+
* on one budget of its own, spent after every other pass, so the listing
|
|
3175
|
+
* without it, `truncated` and the hash are the ones the lister gave before.
|
|
3176
|
+
* One level: a scope receiver lists no scope receiver of its own.
|
|
3177
|
+
*/
|
|
3178
|
+
listScopeReceivers(pkg, sources, maxEntries, withHandlers, built) {
|
|
3179
|
+
let budget = maxEntries;
|
|
3180
|
+
const take = () => budget > 0 && (budget--, true);
|
|
3181
|
+
const names = [];
|
|
3182
|
+
const handlers = [];
|
|
3183
|
+
const fills = [];
|
|
3184
|
+
for (const source of sources) {
|
|
3185
|
+
const holder = this.surfaceHolder(pkg, source.type, source.home);
|
|
3186
|
+
for (const member of source.receiver.members) {
|
|
3187
|
+
const id = member.own ? scopeReceiverId(source.receiver.receiver, member.name) : undefined;
|
|
3188
|
+
if (id === undefined)
|
|
3189
|
+
continue;
|
|
3190
|
+
const callee = this.ownCallee(holder, [], member.name, 'call');
|
|
3191
|
+
if (!('signatures' in callee))
|
|
3192
|
+
continue;
|
|
3193
|
+
if (!callee.signatures.some(signature => this.takesString(signature)))
|
|
3194
|
+
continue;
|
|
3195
|
+
const type = this.scopeTypeOf(callee.signatures, built(source.export, id), source.type, source.home);
|
|
3196
|
+
if (type === undefined || !take())
|
|
3197
|
+
continue;
|
|
3198
|
+
const home = this.homeOf(pkg, [source.target, ...typeSymbols(type)]);
|
|
3199
|
+
member.scope_receiver = this.outlineReceiver(pkg, id, type, home, take, names, handlers, fills);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
for (const run of names)
|
|
3203
|
+
run();
|
|
3204
|
+
if (withHandlers)
|
|
3205
|
+
for (const run of handlers)
|
|
3206
|
+
run();
|
|
3207
|
+
for (const fill of fills)
|
|
3208
|
+
fill();
|
|
3209
|
+
}
|
|
3092
3210
|
/**
|
|
3093
3211
|
* The receivers each export of a specifier can make, by receiver id, for
|
|
3094
3212
|
* the probe statements that build them (see `LibraryClaimsVerifier.listSurface`).
|
|
@@ -3100,12 +3218,157 @@ class DeclarationReader {
|
|
|
3100
3218
|
return [];
|
|
3101
3219
|
return (this.listedExports(probe, only, moduleRead?.globalFile) ?? [])
|
|
3102
3220
|
.filter(({ type }) => !this.isOpenTop(type))
|
|
3103
|
-
.map(({ name, target, type }) =>
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3221
|
+
.map(({ name, target, type }) => {
|
|
3222
|
+
const makers = this.makers(pkg, target, type);
|
|
3223
|
+
return {
|
|
3224
|
+
export: name,
|
|
3225
|
+
receivers: makers.map(maker => maker.receiver),
|
|
3226
|
+
scopes: this.scopeCandidates(pkg, target, makers),
|
|
3227
|
+
};
|
|
3228
|
+
})
|
|
3107
3229
|
.filter(({ receivers }) => receivers.length > 0);
|
|
3108
3230
|
}
|
|
3231
|
+
/**
|
|
3232
|
+
* The `R>scope:m` receivers an export's `instance:` makers may have
|
|
3233
|
+
* (carrick#2774): for each object type a maker's overloads declare as their
|
|
3234
|
+
* return, each own member that `scopeTypeOf` lists at its declared return.
|
|
3235
|
+
* A superset of the listing, which reads each at the type the probe's build
|
|
3236
|
+
* resolves; the second program builds these so `listPackage` can.
|
|
3237
|
+
*/
|
|
3238
|
+
scopeCandidates(pkg, target, makers) {
|
|
3239
|
+
const found = new Set();
|
|
3240
|
+
for (const maker of makers) {
|
|
3241
|
+
if (!maker.receiver.startsWith('instance:'))
|
|
3242
|
+
continue;
|
|
3243
|
+
const types = new Set(maker.signatures.map(sig => this.checker.getReturnTypeOfSignature(sig)));
|
|
3244
|
+
for (const type of types) {
|
|
3245
|
+
if (this.returnSaysNothing(type) || !this.isObjectType(type))
|
|
3246
|
+
continue;
|
|
3247
|
+
const home = this.homeOf(pkg, [target, ...typeSymbols(type)]);
|
|
3248
|
+
for (const name of this.scopeMembers(pkg, type, home)) {
|
|
3249
|
+
const id = scopeReceiverId(maker.receiver, name);
|
|
3250
|
+
if (id !== undefined)
|
|
3251
|
+
found.add(id);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return [...found];
|
|
3256
|
+
}
|
|
3257
|
+
/** The members of `type` that list a scope receiver at their declared return, read once per type and home. */
|
|
3258
|
+
scopeMembers(pkg, type, home) {
|
|
3259
|
+
const key = [...home].sort().join('\u0000');
|
|
3260
|
+
let byHome = this.scopeMembersRead.get(type);
|
|
3261
|
+
if (byHome === undefined)
|
|
3262
|
+
this.scopeMembersRead.set(type, (byHome = new Map()));
|
|
3263
|
+
const read = byHome.get(key);
|
|
3264
|
+
if (read !== undefined)
|
|
3265
|
+
return read;
|
|
3266
|
+
const names = [];
|
|
3267
|
+
const holder = this.surfaceHolder(pkg, type, home);
|
|
3268
|
+
for (const property of sortedByName(this.namedProperties(type))) {
|
|
3269
|
+
if (!this.mayScope(property))
|
|
3270
|
+
continue;
|
|
3271
|
+
const callee = this.ownCallee(holder, [], property.getName(), 'call');
|
|
3272
|
+
if ('signatures' in callee && this.scopeTypeOf(callee.signatures, undefined, type, home) !== undefined) {
|
|
3273
|
+
names.push(property.getName());
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
byHome.set(key, names);
|
|
3277
|
+
return names;
|
|
3278
|
+
}
|
|
3279
|
+
/** Some library signature of the member takes a string: the cheap superset of what `scopeTypeOf` asks first. */
|
|
3280
|
+
mayScope(property) {
|
|
3281
|
+
return this.librarySignatures(this.checker.getTypeOfSymbol(property), 'call').some(signature => this.takesString(signature));
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* The object a scope member returns, as the verifier reads a scope step
|
|
3285
|
+
* (carrick#2774): what a route is registered on when the member is called
|
|
3286
|
+
* with a path. Undefined unless:
|
|
3287
|
+
* - some own signature takes a string, positionally or at a key of an
|
|
3288
|
+
* object parameter;
|
|
3289
|
+
* - the returns (at `built`'s defaults, less those that say nothing) are
|
|
3290
|
+
* all object types, none thenable, sharing one declared symbol that is
|
|
3291
|
+
* declared in the receiver's home and has a callable member of its own;
|
|
3292
|
+
* - that symbol and the receiver's share no declaration in the home
|
|
3293
|
+
* (`family`): the receiver itself (`basePath(): this`), a sibling or a
|
|
3294
|
+
* wrapper of one base is a builder's next link, not an object routes
|
|
3295
|
+
* are registered on.
|
|
3296
|
+
* The type is the return of the overload `built` resolves, when that is
|
|
3297
|
+
* one of the signatures that take a string, else the first of them.
|
|
3298
|
+
*/
|
|
3299
|
+
scopeTypeOf(signatures, built, receiver, home) {
|
|
3300
|
+
const takers = signatures.filter(signature => this.takesString(signature));
|
|
3301
|
+
const returns = takers
|
|
3302
|
+
.map(signature => ({ signature, type: this.returnOf(signature, built) }))
|
|
3303
|
+
.filter(({ type }) => !this.returnSaysNothing(type));
|
|
3304
|
+
if (returns.length === 0)
|
|
3305
|
+
return undefined;
|
|
3306
|
+
const symbolOf = (type) => type.getSymbol() ?? type.aliasSymbol;
|
|
3307
|
+
const symbol = symbolOf(returns[0].type);
|
|
3308
|
+
if (symbol === undefined)
|
|
3309
|
+
return undefined;
|
|
3310
|
+
// The receiver itself, a sibling of it, or a wrapper built from the same
|
|
3311
|
+
// base (a fluent builder's `.default(x)`) is the same kind of thing, not
|
|
3312
|
+
// an object routes are registered on: abstain.
|
|
3313
|
+
const mine = this.family(receiver, home);
|
|
3314
|
+
if ([...this.family(returns[0].type, home)].some(each => mine.has(each)))
|
|
3315
|
+
return undefined;
|
|
3316
|
+
for (const { type } of returns) {
|
|
3317
|
+
if (!this.isObjectType(type) || symbolOf(type) !== symbol || this.isThenable(type))
|
|
3318
|
+
return undefined;
|
|
3319
|
+
}
|
|
3320
|
+
if (!(symbol.declarations ?? []).some(declaration => this.isOwnFile(declaration.getSourceFile(), home)))
|
|
3321
|
+
return undefined;
|
|
3322
|
+
const chosen = returns.find(({ signature }) => built !== undefined && this.instantiates(built, signature)) ?? returns[0];
|
|
3323
|
+
const callable = this.namedProperties(chosen.type).some(member => this.librarySignatures(this.checker.getTypeOfSymbol(member), 'call').length > 0);
|
|
3324
|
+
return callable ? chosen.type : undefined;
|
|
3325
|
+
}
|
|
3326
|
+
/** The type's own declaration and every base it extends or implements that its home declares. */
|
|
3327
|
+
family(type, home) {
|
|
3328
|
+
const found = new Set();
|
|
3329
|
+
const visit = (each, depth) => {
|
|
3330
|
+
const target = (each.objectFlags ?? 0) & ts.ObjectFlags.Reference ? each.target : each;
|
|
3331
|
+
const symbol = target.getSymbol();
|
|
3332
|
+
if (symbol === undefined || found.has(symbol) || depth > 12)
|
|
3333
|
+
return;
|
|
3334
|
+
if (!(symbol.declarations ?? []).some(declaration => this.isOwnFile(declaration.getSourceFile(), home)))
|
|
3335
|
+
return;
|
|
3336
|
+
found.add(symbol);
|
|
3337
|
+
if ((target.objectFlags ?? 0) & ts.ObjectFlags.ClassOrInterface) {
|
|
3338
|
+
for (const base of this.checker.getBaseTypes(target))
|
|
3339
|
+
visit(base, depth + 1);
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
visit(type, 0);
|
|
3343
|
+
return found;
|
|
3344
|
+
}
|
|
3345
|
+
/** A parameter of the signature accepts a string, positionally or at a key of its object. */
|
|
3346
|
+
takesString(signature) {
|
|
3347
|
+
let read = this.takesStringRead.get(signature);
|
|
3348
|
+
if (read === undefined)
|
|
3349
|
+
this.takesStringRead.set(signature, (read = this.readTakesString(signature)));
|
|
3350
|
+
return read;
|
|
3351
|
+
}
|
|
3352
|
+
readTakesString(signature) {
|
|
3353
|
+
const count = signature.getParameters().length;
|
|
3354
|
+
for (let index = 0; index < count; index++) {
|
|
3355
|
+
const slot = this.keyParameterAt(signature, index);
|
|
3356
|
+
if (slot === undefined || slot === VARIADIC)
|
|
3357
|
+
continue;
|
|
3358
|
+
if (this.acceptsString(this.throughConditional(slot)))
|
|
3359
|
+
return true;
|
|
3360
|
+
if (this.isObjectType(slot) && this.handlerFailure(slot) !== undefined) {
|
|
3361
|
+
if (this.listKeys(slot, () => true).some(key => key.accepts_string))
|
|
3362
|
+
return true;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
return false;
|
|
3366
|
+
}
|
|
3367
|
+
/** The type has a callable `then`: awaiting it gives something else. */
|
|
3368
|
+
isThenable(type) {
|
|
3369
|
+
const then = this.checker.getPropertyOfType(type, 'then');
|
|
3370
|
+
return then !== undefined && this.checker.getNonNullableType(this.checker.getTypeOfSymbol(then)).getCallSignatures().length > 0;
|
|
3371
|
+
}
|
|
3109
3372
|
/**
|
|
3110
3373
|
* The value exports a specifier lists (every one, or only those `only`
|
|
3111
3374
|
* names), sorted by name: what its namespace holds, and `default` for a
|
|
@@ -3232,7 +3495,7 @@ class DeclarationReader {
|
|
|
3232
3495
|
* parameters next (`handlers`), once every name is; its call, construct and
|
|
3233
3496
|
* member signatures last (`fills`), once every handler is.
|
|
3234
3497
|
*/
|
|
3235
|
-
outlineReceiver(pkg, receiverName, type, home, take,
|
|
3498
|
+
outlineReceiver(pkg, receiverName, type, home, take, names, handlers, fills) {
|
|
3236
3499
|
const receiver = { receiver: receiverName, members: [] };
|
|
3237
3500
|
const holder = this.surfaceHolder(pkg, type, home);
|
|
3238
3501
|
// The receiver's own call and construct signatures, as a claim with a
|
|
@@ -3260,7 +3523,7 @@ class DeclarationReader {
|
|
|
3260
3523
|
const member = { name: property.getName(), own, signatures: [] };
|
|
3261
3524
|
receiver.members.push(member);
|
|
3262
3525
|
handlers.push(() => {
|
|
3263
|
-
const listed = this.listHandlers(own ? callee.signatures : library
|
|
3526
|
+
const listed = this.listHandlers(own ? callee.signatures : library);
|
|
3264
3527
|
if (listed.length > 0)
|
|
3265
3528
|
member.handlers = listed;
|
|
3266
3529
|
});
|
|
@@ -3276,14 +3539,14 @@ class DeclarationReader {
|
|
|
3276
3539
|
* A parameter that declares no member lists nothing, and so does a handler
|
|
3277
3540
|
* none of whose parameters do.
|
|
3278
3541
|
*/
|
|
3279
|
-
listHandlers(signatures
|
|
3542
|
+
listHandlers(signatures) {
|
|
3280
3543
|
const listed = [];
|
|
3281
3544
|
const seen = new Set();
|
|
3282
3545
|
const add = (arg, key, slot) => {
|
|
3283
3546
|
const place = JSON.stringify([arg, key ?? null]);
|
|
3284
3547
|
if (seen.has(place))
|
|
3285
3548
|
return;
|
|
3286
|
-
const params = this.listHandlerParams(slot
|
|
3549
|
+
const params = this.listHandlerParams(slot);
|
|
3287
3550
|
if (params.length === 0)
|
|
3288
3551
|
return;
|
|
3289
3552
|
seen.add(place);
|
|
@@ -3315,8 +3578,8 @@ class DeclarationReader {
|
|
|
3315
3578
|
}
|
|
3316
3579
|
return listed;
|
|
3317
3580
|
}
|
|
3318
|
-
/** The parameters of a function type's first signature that declare members, each
|
|
3319
|
-
listHandlerParams(handler
|
|
3581
|
+
/** The parameters of a function type's first signature that declare members, each with its members (`handlerMembersOf`). */
|
|
3582
|
+
listHandlerParams(handler) {
|
|
3320
3583
|
const slot = this.throughConditional(this.throughConstraint(handler));
|
|
3321
3584
|
if (slot === VARIADIC)
|
|
3322
3585
|
return [];
|
|
@@ -3328,32 +3591,73 @@ class DeclarationReader {
|
|
|
3328
3591
|
const listed = [];
|
|
3329
3592
|
signature.getParameters().forEach((parameter, index) => {
|
|
3330
3593
|
const type = this.checker.getTypeOfSymbol(parameter);
|
|
3331
|
-
if (this.namedProperties(type).length === 0
|
|
3594
|
+
if (this.namedProperties(type).length === 0)
|
|
3332
3595
|
return;
|
|
3333
|
-
const members =
|
|
3334
|
-
this.
|
|
3596
|
+
const { members, dropped } = this.handlerMembersOf(type);
|
|
3597
|
+
this.handlersDropped += dropped;
|
|
3335
3598
|
listed.push({ param: index, name: parameter.getName(), type: this.printed(type), members });
|
|
3336
3599
|
});
|
|
3337
3600
|
return listed;
|
|
3338
3601
|
}
|
|
3339
|
-
/**
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3602
|
+
/**
|
|
3603
|
+
* A handler parameter's declared members by path, down to
|
|
3604
|
+
* `HANDLER_MEMBER_DEPTH`, at most `HANDLER_MEMBER_CAP` of them (carrick#2966).
|
|
3605
|
+
* The cap keeps every top-level member before any nested one, so a wide
|
|
3606
|
+
* parameter loses its deepest members, never a top-level one a later member
|
|
3607
|
+
* name sorts after. Kept members come back parents before children, each
|
|
3608
|
+
* level sorted by name. Read once per type in a package, across its
|
|
3609
|
+
* specifiers: a type many handlers share is listed once.
|
|
3610
|
+
*/
|
|
3611
|
+
handlerMembersOf(type) {
|
|
3612
|
+
const known = this.handlerMembers.get(type);
|
|
3613
|
+
if (known)
|
|
3614
|
+
return known;
|
|
3615
|
+
surfaceReadCounts.handlerMemberBuilds += 1;
|
|
3616
|
+
const roots = [];
|
|
3617
|
+
let kept = 0;
|
|
3618
|
+
let dropped = 0;
|
|
3619
|
+
// Breadth first: a level is kept whole, in name order, before the next.
|
|
3620
|
+
let level = [{ type, prefix: [], into: roots }];
|
|
3621
|
+
while (level.length > 0) {
|
|
3622
|
+
const next = [];
|
|
3623
|
+
for (const { type: holder, prefix, into } of level) {
|
|
3624
|
+
for (const property of sortedByName(this.namedProperties(holder))) {
|
|
3625
|
+
if (kept >= HANDLER_MEMBER_CAP) {
|
|
3626
|
+
dropped += 1;
|
|
3627
|
+
continue;
|
|
3628
|
+
}
|
|
3629
|
+
kept += 1;
|
|
3630
|
+
const memberType = this.checker.getTypeOfSymbol(property);
|
|
3631
|
+
const path = [...prefix, property.getName()];
|
|
3632
|
+
const callable = this.librarySignatures(memberType, 'call').length > 0;
|
|
3633
|
+
const node = {
|
|
3634
|
+
member: {
|
|
3635
|
+
path,
|
|
3636
|
+
type: this.printed(memberType),
|
|
3637
|
+
optional: (property.flags & ts.SymbolFlags.Optional) !== 0,
|
|
3638
|
+
function: callable,
|
|
3639
|
+
},
|
|
3640
|
+
children: [],
|
|
3641
|
+
};
|
|
3642
|
+
into.push(node);
|
|
3643
|
+
if (!callable && path.length < HANDLER_MEMBER_DEPTH && this.isObjectType(memberType)) {
|
|
3644
|
+
next.push({ type: memberType, prefix: path, into: node.children });
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3355
3647
|
}
|
|
3648
|
+
level = next;
|
|
3356
3649
|
}
|
|
3650
|
+
const members = [];
|
|
3651
|
+
const emit = (nodes) => {
|
|
3652
|
+
for (const node of nodes) {
|
|
3653
|
+
members.push(node.member);
|
|
3654
|
+
emit(node.children);
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
emit(roots);
|
|
3658
|
+
const listed = { members, dropped };
|
|
3659
|
+
this.handlerMembers.set(type, listed);
|
|
3660
|
+
return listed;
|
|
3357
3661
|
}
|
|
3358
3662
|
listSignatures(signatures, holder, take) {
|
|
3359
3663
|
const listed = [];
|
|
@@ -3391,7 +3695,7 @@ class DeclarationReader {
|
|
|
3391
3695
|
params.push(param);
|
|
3392
3696
|
}
|
|
3393
3697
|
const entry = { params, returns: this.printed(this.checker.getReturnTypeOfSignature(signature)) };
|
|
3394
|
-
const candidates = resultCandidatesOf(this.checker, signature);
|
|
3698
|
+
const candidates = this.listResultCandidates ? resultCandidatesOf(this.checker, signature) : undefined;
|
|
3395
3699
|
if (candidates !== undefined)
|
|
3396
3700
|
entry.result_candidates = candidates;
|
|
3397
3701
|
listed.push(entry);
|
|
@@ -3441,9 +3745,15 @@ class DeclarationReader {
|
|
|
3441
3745
|
* so where the cut falls does not depend on where the package is installed.
|
|
3442
3746
|
*/
|
|
3443
3747
|
printed(type) {
|
|
3748
|
+
const known = this.printedTexts.get(type);
|
|
3749
|
+
if (known !== undefined)
|
|
3750
|
+
return known;
|
|
3751
|
+
surfaceReadCounts.typePrints += 1;
|
|
3444
3752
|
const node = this.checker.typeToTypeNode(type, undefined, PRINT_FLAGS);
|
|
3445
3753
|
const text = node === undefined ? this.checker.typeToString(type) : printTypeNode(this.orderUnions(node));
|
|
3446
|
-
|
|
3754
|
+
const listed = truncate(this.scrubbed(text));
|
|
3755
|
+
this.printedTexts.set(type, listed);
|
|
3756
|
+
return listed;
|
|
3447
3757
|
}
|
|
3448
3758
|
/** `text` with the service root written as `<root>`. */
|
|
3449
3759
|
scrubbed(text) {
|
|
@@ -3617,8 +3927,10 @@ class DeclarationReader {
|
|
|
3617
3927
|
/** Judge the next check as a claim about `pkg`. */
|
|
3618
3928
|
setPackage(pkg) {
|
|
3619
3929
|
const named = packageNameOf(pkg);
|
|
3620
|
-
if (named !== this.currentPackage)
|
|
3930
|
+
if (named !== this.currentPackage) {
|
|
3621
3931
|
this.declared.clear();
|
|
3932
|
+
this.handlerMembers.clear();
|
|
3933
|
+
}
|
|
3622
3934
|
this.currentPackage = named;
|
|
3623
3935
|
}
|
|
3624
3936
|
/**
|
|
@@ -3911,6 +4223,12 @@ class DeclarationReader {
|
|
|
3911
4223
|
*/
|
|
3912
4224
|
/** The longest member path a handler parameter's members are listed to (`['raw', 'url']`). */
|
|
3913
4225
|
const HANDLER_MEMBER_DEPTH = 2;
|
|
4226
|
+
/**
|
|
4227
|
+
* The most members one handler parameter lists (carrick#2966), top-level
|
|
4228
|
+
* first. The store's server question shows at most 300 of them in the same
|
|
4229
|
+
* order, so this must stay at least that.
|
|
4230
|
+
*/
|
|
4231
|
+
const HANDLER_MEMBER_CAP = 500;
|
|
3914
4232
|
const PRINT_FLAGS = ts.NodeBuilderFlags.AllowUniqueESSymbolType |
|
|
3915
4233
|
ts.NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope |
|
|
3916
4234
|
ts.NodeBuilderFlags.IgnoreErrors |
|
|
@@ -679,6 +679,10 @@ export interface ListLibrarySurfaceRequest extends BaseRequest {
|
|
|
679
679
|
max_entries?: number;
|
|
680
680
|
/** Per package, the only exports to list (the ones the service imports); absent lists every value export. */
|
|
681
681
|
exports?: Record<string, string[]>;
|
|
682
|
+
/** List each member's handler-parameter members (`handlers`, carrick#2674 S3); default false (carrick#2966). */
|
|
683
|
+
handlers?: boolean;
|
|
684
|
+
/** List each signature's `result_candidates` (carrick#2817); default false (carrick#2966). */
|
|
685
|
+
result_candidates?: boolean;
|
|
682
686
|
}
|
|
683
687
|
/**
|
|
684
688
|
* Union type for all possible sidecar requests
|
|
@@ -982,6 +986,12 @@ export interface SemanticsResult {
|
|
|
982
986
|
reason?: string;
|
|
983
987
|
/** Verified op verdicts only: the declarations its member resolves to on the receiver. */
|
|
984
988
|
declarations?: SemanticsDeclaration[];
|
|
989
|
+
/**
|
|
990
|
+
* Verified receive op verdicts only: the op verified but its `request`
|
|
991
|
+
* names a member the declarations lack, so the request is dropped and the
|
|
992
|
+
* route kept (carrick#2968). The reason, `request_member_missing`.
|
|
993
|
+
*/
|
|
994
|
+
request_dropped?: string;
|
|
985
995
|
}
|
|
986
996
|
/** How one package of the request resolved, for logs. */
|
|
987
997
|
export interface SemanticsModule {
|
|
@@ -1032,8 +1042,9 @@ export interface SurfaceSignature {
|
|
|
1032
1042
|
returns: string;
|
|
1033
1043
|
/**
|
|
1034
1044
|
* Which parts of the awaited return could hold the response body
|
|
1035
|
-
* (carrick#2817). Left out of the surface hash.
|
|
1036
|
-
*
|
|
1045
|
+
* (carrick#2817). Left out of the surface hash. Listed only on a
|
|
1046
|
+
* `result_candidates: true` request (carrick#2966); absent when the
|
|
1047
|
+
* signature returns nothing.
|
|
1037
1048
|
*/
|
|
1038
1049
|
result_candidates?: SurfaceResultCandidates;
|
|
1039
1050
|
}
|
|
@@ -1065,9 +1076,17 @@ export interface SurfaceMember {
|
|
|
1065
1076
|
/**
|
|
1066
1077
|
* The members the parameters of a function this member takes declare
|
|
1067
1078
|
* (carrick#2674): what a receive op's `request` field names its member
|
|
1068
|
-
* paths in.
|
|
1079
|
+
* paths in. Listed only on a `handlers: true` request, under a cap of
|
|
1080
|
+
* their own: 500 members per parameter, top-level first (carrick#2966).
|
|
1069
1081
|
*/
|
|
1070
1082
|
handlers?: SurfaceHandler[];
|
|
1083
|
+
/**
|
|
1084
|
+
* The object this member returns when called with a path, listed as a
|
|
1085
|
+
* receiver (id `R>scope:m`) when routes are registered on it (carrick#2774).
|
|
1086
|
+
* Instance receivers only, one level, under a cap of its own, and left out
|
|
1087
|
+
* of the hash.
|
|
1088
|
+
*/
|
|
1089
|
+
scope_receiver?: SurfaceReceiver;
|
|
1071
1090
|
signatures: SurfaceSignature[];
|
|
1072
1091
|
}
|
|
1073
1092
|
/**
|
|
@@ -1116,6 +1135,11 @@ export interface LibrarySurface {
|
|
|
1116
1135
|
reason?: string;
|
|
1117
1136
|
/** Entries dropped by `max_entries`. */
|
|
1118
1137
|
truncated: number;
|
|
1138
|
+
/**
|
|
1139
|
+
* With `handlers: true`, the handler-parameter members dropped by the cap of
|
|
1140
|
+
* 500 per parameter (carrick#2966), summed over every listed parameter.
|
|
1141
|
+
*/
|
|
1142
|
+
handlers_truncated?: number;
|
|
1119
1143
|
exports: SurfaceExport[];
|
|
1120
1144
|
}
|
|
1121
1145
|
export interface ListLibrarySurfaceResponse extends BaseResponse {
|
|
@@ -2810,11 +2810,15 @@ export declare const ListLibrarySurfaceRequestSchema: z.ZodObject<{
|
|
|
2810
2810
|
packages: z.ZodArray<z.ZodString, "many">;
|
|
2811
2811
|
max_entries: z.ZodOptional<z.ZodNumber>;
|
|
2812
2812
|
exports: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
2813
|
+
handlers: z.ZodOptional<z.ZodBoolean>;
|
|
2814
|
+
result_candidates: z.ZodOptional<z.ZodBoolean>;
|
|
2813
2815
|
}, "strip", z.ZodTypeAny, {
|
|
2814
2816
|
packages: string[];
|
|
2815
2817
|
request_id: string;
|
|
2816
2818
|
action: "list_library_surface";
|
|
2817
2819
|
from_dir: string;
|
|
2820
|
+
handlers?: boolean | undefined;
|
|
2821
|
+
result_candidates?: boolean | undefined;
|
|
2818
2822
|
max_entries?: number | undefined;
|
|
2819
2823
|
exports?: Record<string, string[]> | undefined;
|
|
2820
2824
|
}, {
|
|
@@ -2822,6 +2826,8 @@ export declare const ListLibrarySurfaceRequestSchema: z.ZodObject<{
|
|
|
2822
2826
|
request_id: string;
|
|
2823
2827
|
action: "list_library_surface";
|
|
2824
2828
|
from_dir: string;
|
|
2829
|
+
handlers?: boolean | undefined;
|
|
2830
|
+
result_candidates?: boolean | undefined;
|
|
2825
2831
|
max_entries?: number | undefined;
|
|
2826
2832
|
exports?: Record<string, string[]> | undefined;
|
|
2827
2833
|
}>;
|
|
@@ -5388,11 +5394,15 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
|
|
|
5388
5394
|
packages: z.ZodArray<z.ZodString, "many">;
|
|
5389
5395
|
max_entries: z.ZodOptional<z.ZodNumber>;
|
|
5390
5396
|
exports: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
5397
|
+
handlers: z.ZodOptional<z.ZodBoolean>;
|
|
5398
|
+
result_candidates: z.ZodOptional<z.ZodBoolean>;
|
|
5391
5399
|
}, "strip", z.ZodTypeAny, {
|
|
5392
5400
|
packages: string[];
|
|
5393
5401
|
request_id: string;
|
|
5394
5402
|
action: "list_library_surface";
|
|
5395
5403
|
from_dir: string;
|
|
5404
|
+
handlers?: boolean | undefined;
|
|
5405
|
+
result_candidates?: boolean | undefined;
|
|
5396
5406
|
max_entries?: number | undefined;
|
|
5397
5407
|
exports?: Record<string, string[]> | undefined;
|
|
5398
5408
|
}, {
|
|
@@ -5400,6 +5410,8 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
|
|
|
5400
5410
|
request_id: string;
|
|
5401
5411
|
action: "list_library_surface";
|
|
5402
5412
|
from_dir: string;
|
|
5413
|
+
handlers?: boolean | undefined;
|
|
5414
|
+
result_candidates?: boolean | undefined;
|
|
5403
5415
|
max_entries?: number | undefined;
|
|
5404
5416
|
exports?: Record<string, string[]> | undefined;
|
|
5405
5417
|
}>, z.ZodObject<{
|
|
@@ -588,6 +588,8 @@ export const ListLibrarySurfaceRequestSchema = BaseRequestSchema.extend({
|
|
|
588
588
|
packages: z.array(z.string().min(1)),
|
|
589
589
|
max_entries: z.number().int().positive().optional(),
|
|
590
590
|
exports: z.record(z.string(), z.array(z.string().min(1))).optional(),
|
|
591
|
+
handlers: z.boolean().optional(),
|
|
592
|
+
result_candidates: z.boolean().optional(),
|
|
591
593
|
});
|
|
592
594
|
// ============================================================================
|
|
593
595
|
// Discriminated Union Schema
|