carrick 0.3.133 → 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/dist/contract.d.ts +6 -0
- package/dist/contract.js.map +1 -1
- package/dist/hook/post-edit.d.ts +10 -5
- package/dist/hook/post-edit.js +80 -23
- package/dist/hook/post-edit.js.map +1 -1
- package/dist/hook/refresh.d.ts +23 -3
- package/dist/hook/refresh.js +48 -6
- package/dist/hook/refresh.js.map +1 -1
- package/dist/hook/reuse.d.ts +7 -21
- package/dist/hook/reuse.js +23 -36
- package/dist/hook/reuse.js.map +1 -1
- package/dist/hook/session-start.js +15 -2
- package/dist/hook/session-start.js.map +1 -1
- package/dist/hook/stop.js.map +1 -1
- package/dist/init/agents-section.js +1 -1
- package/dist/init/agents-section.js.map +1 -1
- package/dist/init/doctor.js +4 -0
- package/dist/init/doctor.js.map +1 -1
- package/dist/init/hosted.d.ts +1 -0
- package/dist/init/hosted.js +4 -1
- package/dist/init/hosted.js.map +1 -1
- package/dist/init/remove.js +7 -1
- package/dist/init/remove.js.map +1 -1
- package/dist/init/repo-copies.js +3 -1
- package/dist/init/repo-copies.js.map +1 -1
- package/dist/init/run.d.ts +1 -1
- package/dist/init/run.js +10 -2
- package/dist/init/run.js.map +1 -1
- package/dist/init/task-skills.d.ts +22 -1
- package/dist/init/task-skills.js +65 -1
- package/dist/init/task-skills.js.map +1 -1
- package/dist/render.d.ts +41 -16
- package/dist/render.js +91 -102
- package/dist/render.js.map +1 -1
- package/package.json +10 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/sidecar/dist/src/capture/anchors.js +18 -1
- package/sidecar/dist/src/capture/api.d.ts +17 -4
- package/sidecar/dist/src/capture/check-workspace.js +1 -1
- package/sidecar/dist/src/capture/check.d.ts +44 -0
- package/sidecar/dist/src/capture/check.js +112 -10
- package/sidecar/dist/src/capture/index.js +18 -0
- package/sidecar/dist/src/capture/installed-package.d.ts +5 -0
- package/sidecar/dist/src/capture/installed-package.js +64 -6
- 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/capture/paths-rewrite.js +3 -3
- package/sidecar/dist/src/capture/result-label.d.ts +42 -0
- package/sidecar/dist/src/capture/result-label.js +91 -0
- package/sidecar/dist/src/capture/self-check.js +13 -2
- package/sidecar/dist/src/index.js +117 -5
- package/sidecar/dist/src/infer-cache.d.ts +125 -0
- package/sidecar/dist/src/infer-cache.js +244 -0
- package/sidecar/dist/src/library-claims.d.ts +35 -3
- package/sidecar/dist/src/library-claims.js +417 -61
- package/sidecar/dist/src/node-index.d.ts +53 -0
- package/sidecar/dist/src/node-index.js +100 -0
- package/sidecar/dist/src/project-loader.d.ts +11 -0
- package/sidecar/dist/src/project-loader.js +15 -0
- package/sidecar/dist/src/result-candidates.d.ts +28 -0
- package/sidecar/dist/src/result-candidates.js +209 -0
- package/sidecar/dist/src/type-inferrer.d.ts +27 -0
- package/sidecar/dist/src/type-inferrer.js +175 -43
- package/sidecar/dist/src/types.d.ts +77 -1
- package/sidecar/dist/src/unwidened.js +10 -1
- package/sidecar/dist/src/validators.d.ts +931 -197
- package/sidecar/dist/src/validators.js +34 -0
- package/templates/skills/carrick-reuse.md +11 -0
|
@@ -39,6 +39,7 @@ import * as crypto from 'node:crypto';
|
|
|
39
39
|
import * as fs from 'node:fs';
|
|
40
40
|
import * as path from 'node:path';
|
|
41
41
|
import { ts } from 'ts-morph';
|
|
42
|
+
import { holdsResult, resultCandidatesOf } from './result-candidates.js';
|
|
42
43
|
import { PathNames, declaringFileName } from './position-rows.js';
|
|
43
44
|
import { memberDeclaration } from './declaration-attribution.js';
|
|
44
45
|
/** The methods a `verb` claim may name, and a method key may accept. */
|
|
@@ -258,6 +259,12 @@ function sortedDeclarations(declarations) {
|
|
|
258
259
|
}
|
|
259
260
|
let probeSequence = 0;
|
|
260
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 };
|
|
261
268
|
export class LibraryClaimsVerifier {
|
|
262
269
|
project;
|
|
263
270
|
graph;
|
|
@@ -280,6 +287,7 @@ export class LibraryClaimsVerifier {
|
|
|
280
287
|
receiver: check.receiver,
|
|
281
288
|
verdict: 'verified',
|
|
282
289
|
...(outcome.declarations?.length ? { declarations: outcome.declarations } : {}),
|
|
290
|
+
...(outcome.requestDropped === undefined ? {} : { request_dropped: outcome.requestDropped }),
|
|
283
291
|
}
|
|
284
292
|
: {
|
|
285
293
|
claim_id: check.claim_id,
|
|
@@ -586,6 +594,8 @@ export class LibraryClaimsVerifier {
|
|
|
586
594
|
const declarations = [];
|
|
587
595
|
const members = [];
|
|
588
596
|
const bodyMembers = [];
|
|
597
|
+
// A request one receiver does not hold is dropped for the op (carrick#2968).
|
|
598
|
+
let requestDropped;
|
|
589
599
|
for (const receiver of receivers.value) {
|
|
590
600
|
const result = reader.judgeMessage(receiver, check.claim, rules, builtAtDefaults, collectionsOf);
|
|
591
601
|
if (result.outcome.verdict !== 'verified')
|
|
@@ -594,11 +604,14 @@ export class LibraryClaimsVerifier {
|
|
|
594
604
|
declarations.push(...(result.outcome.declarations ?? []));
|
|
595
605
|
members.push(...(result.outcome.members ?? []));
|
|
596
606
|
bodyMembers.push(...(result.outcome.bodyMembers ?? []));
|
|
607
|
+
requestDropped ??= result.outcome.requestDropped;
|
|
597
608
|
}
|
|
598
609
|
const verified = declarations.length > 0 ? { verdict: 'verified', declarations: sortedDeclarations(declarations) } : { ...VERIFIED };
|
|
599
610
|
if (verified.verdict === 'verified' && members.length > 0)
|
|
600
611
|
verified.members = members;
|
|
601
|
-
if (verified.verdict === 'verified' &&
|
|
612
|
+
if (verified.verdict === 'verified' && requestDropped !== undefined)
|
|
613
|
+
verified.requestDropped = requestDropped;
|
|
614
|
+
else if (verified.verdict === 'verified' && bodyMembers.length > 0)
|
|
602
615
|
verified.bodyMembers = bodyMembers;
|
|
603
616
|
return { outcome: verified, holding };
|
|
604
617
|
};
|
|
@@ -644,7 +657,11 @@ export class LibraryClaimsVerifier {
|
|
|
644
657
|
const observe = (check, outcome) => {
|
|
645
658
|
if (check.claim.kind !== 'op')
|
|
646
659
|
return;
|
|
647
|
-
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}`;
|
|
648
665
|
outcomes[read] = (outcomes[read] ?? 0) + 1;
|
|
649
666
|
if (outcome.verdict !== 'verified')
|
|
650
667
|
return;
|
|
@@ -677,9 +694,20 @@ export class LibraryClaimsVerifier {
|
|
|
677
694
|
* (`receiverExpression` on the export's `importLine`, read through
|
|
678
695
|
* `DeclarationReader.returnOf`) and lists from it.
|
|
679
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
|
+
*
|
|
680
708
|
* `surface_sha256` is the full-surface hash (see `fullSurfaceSha256`).
|
|
681
709
|
*/
|
|
682
|
-
listSurface(fromDir, packages, maxEntries, only = {}) {
|
|
710
|
+
listSurface(fromDir, packages, maxEntries, only = {}, options = {}) {
|
|
683
711
|
if (packages.length === 0)
|
|
684
712
|
return { surfaces: [], surface_sha256: fullSurfaceSha256([]) };
|
|
685
713
|
// Each specifier twice: as a namespace, which holds its named exports, and
|
|
@@ -708,10 +736,12 @@ export class LibraryClaimsVerifier {
|
|
|
708
736
|
const buildLines = [];
|
|
709
737
|
const builtIndex = new Map();
|
|
710
738
|
packages.forEach((pkg, i) => {
|
|
711
|
-
for (const { export: name, receivers } of makers[i]) {
|
|
739
|
+
for (const { export: name, receivers, scopes } of makers[i]) {
|
|
712
740
|
const local = `__carrick_e${importLines.length}`;
|
|
713
741
|
importLines.push(probeLine(pkg, name, local));
|
|
714
|
-
|
|
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]) {
|
|
715
745
|
const expression = receiverExpression(local, receiver);
|
|
716
746
|
if (expression === undefined)
|
|
717
747
|
continue;
|
|
@@ -721,7 +751,7 @@ export class LibraryClaimsVerifier {
|
|
|
721
751
|
}
|
|
722
752
|
});
|
|
723
753
|
const firstBuild = imports.length + importLines.length;
|
|
724
|
-
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) => {
|
|
725
755
|
const index = builtIndex.get(builtKey({ package: pkg, export: name }, receiver));
|
|
726
756
|
return index === undefined ? undefined : resolvedBuild(checker, file.statements[firstBuild + index]);
|
|
727
757
|
})));
|
|
@@ -762,8 +792,8 @@ function resolvedBuild(checker, statement) {
|
|
|
762
792
|
* array of `[package, exports]` for every surface that listed at least one
|
|
763
793
|
* export, sorted by `package` (code unit order). A specifier that lists
|
|
764
794
|
* nothing (unresolved, local, no value exports) is left out, so asking for a
|
|
765
|
-
* subpath a version does not have changes nothing. Handler members
|
|
766
|
-
* 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
|
|
767
797
|
* the request's directory as `<root>`.
|
|
768
798
|
*/
|
|
769
799
|
export function fullSurfaceSha256(surfaces) {
|
|
@@ -771,9 +801,11 @@ export function fullSurfaceSha256(surfaces) {
|
|
|
771
801
|
.filter(surface => surface.reason === undefined && surface.exports.length > 0)
|
|
772
802
|
.map(surface => [surface.package, surface.exports])
|
|
773
803
|
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
774
|
-
// A member's `handlers` (carrick#2674)
|
|
775
|
-
// a
|
|
776
|
-
|
|
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);
|
|
777
809
|
return crypto.createHash('sha256').update(text).digest('hex');
|
|
778
810
|
}
|
|
779
811
|
function exportKey(check) {
|
|
@@ -926,6 +958,18 @@ function receiverExpression(local, receiver) {
|
|
|
926
958
|
text = `${text}${parsed.scope.split('.').map(access).join('')}()`;
|
|
927
959
|
return text;
|
|
928
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
|
+
}
|
|
929
973
|
/** The symbols that say who declared a type: its own, its alias's, its parts'. */
|
|
930
974
|
function typeSymbols(type) {
|
|
931
975
|
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
@@ -1018,8 +1062,24 @@ class DeclarationReader {
|
|
|
1018
1062
|
currentPackage = '';
|
|
1019
1063
|
/** `declaredProperties` per type, for `currentPackage` (its augmentations decide them). */
|
|
1020
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;
|
|
1021
1079
|
/** The service root as given and as a realpath, longest first: what a listing prints as `<root>`. */
|
|
1022
1080
|
printedRoots;
|
|
1081
|
+
/** `printed` per type: its text reads nothing but the type and `printedRoots` (carrick#2977). */
|
|
1082
|
+
printedTexts = new Map();
|
|
1023
1083
|
constructor(program, probe, host, serviceRoot, scanRoot, graph) {
|
|
1024
1084
|
this.program = program;
|
|
1025
1085
|
this.probe = probe;
|
|
@@ -1365,6 +1425,8 @@ class DeclarationReader {
|
|
|
1365
1425
|
* `claim_invalid`.
|
|
1366
1426
|
*/
|
|
1367
1427
|
judgeHttp(receiver, claim) {
|
|
1428
|
+
if (claim.kind === 'result')
|
|
1429
|
+
return this.judgeHttpResult(receiver, claim);
|
|
1368
1430
|
if (claim.kind === 'make') {
|
|
1369
1431
|
const base = claim.base;
|
|
1370
1432
|
if (claim.form !== 'call' ||
|
|
@@ -1624,7 +1686,37 @@ class DeclarationReader {
|
|
|
1624
1686
|
return { outcome: this.judgeOp(receiver, claim, rules) };
|
|
1625
1687
|
case 'reserved':
|
|
1626
1688
|
return { outcome: this.judgeReserved(receiver, claim) };
|
|
1689
|
+
case 'result':
|
|
1690
|
+
return { outcome: this.resultOutcome(this.ownCallee(receiver, claim.path ?? [], claim.member, 'call'), claim.result) };
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* A `result` claim (carrick#2817): the claimed result is a candidate of at
|
|
1695
|
+
* least one signature the callee declares, by rule R (`result-candidates.ts`).
|
|
1696
|
+
* Anything else, including a call that returns nothing, is
|
|
1697
|
+
* `result_not_candidate`.
|
|
1698
|
+
*/
|
|
1699
|
+
resultOutcome(callee, shape) {
|
|
1700
|
+
if ('failure' in callee)
|
|
1701
|
+
return callee.failure;
|
|
1702
|
+
for (const signature of callee.signatures) {
|
|
1703
|
+
const candidates = resultCandidatesOf(this.checker, signature);
|
|
1704
|
+
if (candidates !== undefined && holdsResult(candidates, shape))
|
|
1705
|
+
return VERIFIED;
|
|
1627
1706
|
}
|
|
1707
|
+
return failed('result_not_candidate');
|
|
1708
|
+
}
|
|
1709
|
+
/** A `result` claim on an HTTP receiver: `path` walked from it, then `member` of what that reaches. */
|
|
1710
|
+
judgeHttpResult(receiver, claim) {
|
|
1711
|
+
let holder = receiver;
|
|
1712
|
+
for (const hop of claim.path ?? []) {
|
|
1713
|
+
const property = this.declaredProperty(holder, hop);
|
|
1714
|
+
if (!property)
|
|
1715
|
+
return failed('member_missing');
|
|
1716
|
+
holder = this.checker.getTypeOfSymbol(property);
|
|
1717
|
+
}
|
|
1718
|
+
const callee = claim.member === null ? this.callSignatures(holder) : this.callableProperty(holder, claim.member);
|
|
1719
|
+
return this.resultOutcome(callee, claim.result);
|
|
1628
1720
|
}
|
|
1629
1721
|
/**
|
|
1630
1722
|
* `member` (or the export itself, when null) is callable (`call`) or
|
|
@@ -2011,6 +2103,10 @@ class DeclarationReader {
|
|
|
2011
2103
|
const labels = claim.key_labels ?? {};
|
|
2012
2104
|
let best;
|
|
2013
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;
|
|
2014
2110
|
const bodyMembers = [];
|
|
2015
2111
|
for (const signature of callee.signatures) {
|
|
2016
2112
|
let result = this.messageSignatureOutcome(signature, layout.value, labels);
|
|
@@ -2021,19 +2117,26 @@ class DeclarationReader {
|
|
|
2021
2117
|
}
|
|
2022
2118
|
if (result.rank === Infinity && claim.request !== undefined) {
|
|
2023
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
|
+
}
|
|
2024
2124
|
if (read)
|
|
2025
2125
|
result = { rank: 5, outcome: read };
|
|
2026
2126
|
}
|
|
2027
2127
|
if (result.rank === Infinity) {
|
|
2028
2128
|
held = true;
|
|
2129
|
+
requestDropped = undefined;
|
|
2029
2130
|
break;
|
|
2030
2131
|
}
|
|
2031
2132
|
best = furthest(best, result.rank, result.outcome);
|
|
2032
2133
|
}
|
|
2033
|
-
if (!held)
|
|
2134
|
+
if (!held && requestDropped === undefined)
|
|
2034
2135
|
return best.outcome;
|
|
2035
|
-
if (boundRuns)
|
|
2036
|
-
|
|
2136
|
+
if (boundRuns) {
|
|
2137
|
+
const ran = this.boundRunsOutcome(receiver.makerHolding, boundRuns);
|
|
2138
|
+
return ran.verdict === 'verified' && requestDropped !== undefined ? { ...ran, requestDropped } : ran;
|
|
2139
|
+
}
|
|
2037
2140
|
const declarations = this.declarationsOf(callee);
|
|
2038
2141
|
const members = callee.named
|
|
2039
2142
|
? (callee.named.property.declarations ?? [])
|
|
@@ -2043,6 +2146,7 @@ class DeclarationReader {
|
|
|
2043
2146
|
...(declarations.length > 0 ? { declarations } : {}),
|
|
2044
2147
|
...(members.length > 0 ? { members } : {}),
|
|
2045
2148
|
...(bodyMembers.length > 0 ? { bodyMembers } : {}),
|
|
2149
|
+
...(requestDropped === undefined ? {} : { requestDropped }),
|
|
2046
2150
|
};
|
|
2047
2151
|
}
|
|
2048
2152
|
/**
|
|
@@ -2977,7 +3081,7 @@ class DeclarationReader {
|
|
|
2977
3081
|
* `built` answers the probe's no-argument build of a receiver an export
|
|
2978
3082
|
* makes, resolved, when the probe holds one.
|
|
2979
3083
|
*/
|
|
2980
|
-
listPackage(pkg, probe, maxEntries, only, built) {
|
|
3084
|
+
listPackage(pkg, probe, maxEntries, only, options, built) {
|
|
2981
3085
|
this.setPackage(pkg);
|
|
2982
3086
|
// Runtime modules on: a `node:` specifier is listed from the runtime's
|
|
2983
3087
|
// types package, as the message roles read it.
|
|
@@ -3002,24 +3106,24 @@ class DeclarationReader {
|
|
|
3002
3106
|
dropped += 1;
|
|
3003
3107
|
return false;
|
|
3004
3108
|
};
|
|
3005
|
-
// Handler-parameter members (carrick#2674)
|
|
3006
|
-
//
|
|
3007
|
-
//
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
handlerBudget -= 1;
|
|
3013
|
-
return true;
|
|
3014
|
-
};
|
|
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;
|
|
3015
3116
|
// Four passes: every export and receiver, then every member name, then the
|
|
3016
|
-
// 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.
|
|
3017
3119
|
const names = [];
|
|
3018
3120
|
const handlers = [];
|
|
3019
3121
|
const fills = [];
|
|
3020
3122
|
// What the package's default export names: a maker also exported by name
|
|
3021
3123
|
// under this declaration is imported the same way, so it is read the same.
|
|
3022
3124
|
const defaultTarget = exported.find(each => each.name === 'default')?.target;
|
|
3125
|
+
// The instance receivers listed, for the scope pass (carrick#2774).
|
|
3126
|
+
const instances = [];
|
|
3023
3127
|
for (const { name, target, type } of exported) {
|
|
3024
3128
|
if (!take())
|
|
3025
3129
|
continue;
|
|
@@ -3031,7 +3135,11 @@ class DeclarationReader {
|
|
|
3031
3135
|
if (!take())
|
|
3032
3136
|
return;
|
|
3033
3137
|
const home = this.homeOf(pkg, [target, ...typeSymbols(receiverType)]);
|
|
3034
|
-
|
|
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
|
+
}
|
|
3035
3143
|
};
|
|
3036
3144
|
receiverOf('export', type);
|
|
3037
3145
|
for (const maker of this.makers(pkg, target, type)) {
|
|
@@ -3048,13 +3156,57 @@ class DeclarationReader {
|
|
|
3048
3156
|
}
|
|
3049
3157
|
for (const run of names)
|
|
3050
3158
|
run();
|
|
3051
|
-
|
|
3052
|
-
run
|
|
3159
|
+
if (withHandlers)
|
|
3160
|
+
for (const run of handlers)
|
|
3161
|
+
run();
|
|
3053
3162
|
for (const fill of fills)
|
|
3054
3163
|
fill();
|
|
3164
|
+
this.listScopeReceivers(pkg, instances, maxEntries, withHandlers, built);
|
|
3055
3165
|
surface.truncated = dropped;
|
|
3166
|
+
if (withHandlers)
|
|
3167
|
+
surface.handlers_truncated = this.handlersDropped;
|
|
3056
3168
|
return surface;
|
|
3057
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
|
+
}
|
|
3058
3210
|
/**
|
|
3059
3211
|
* The receivers each export of a specifier can make, by receiver id, for
|
|
3060
3212
|
* the probe statements that build them (see `LibraryClaimsVerifier.listSurface`).
|
|
@@ -3066,12 +3218,157 @@ class DeclarationReader {
|
|
|
3066
3218
|
return [];
|
|
3067
3219
|
return (this.listedExports(probe, only, moduleRead?.globalFile) ?? [])
|
|
3068
3220
|
.filter(({ type }) => !this.isOpenTop(type))
|
|
3069
|
-
.map(({ name, target, type }) =>
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
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
|
+
})
|
|
3073
3229
|
.filter(({ receivers }) => receivers.length > 0);
|
|
3074
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
|
+
}
|
|
3075
3372
|
/**
|
|
3076
3373
|
* The value exports a specifier lists (every one, or only those `only`
|
|
3077
3374
|
* names), sorted by name: what its namespace holds, and `default` for a
|
|
@@ -3198,7 +3495,7 @@ class DeclarationReader {
|
|
|
3198
3495
|
* parameters next (`handlers`), once every name is; its call, construct and
|
|
3199
3496
|
* member signatures last (`fills`), once every handler is.
|
|
3200
3497
|
*/
|
|
3201
|
-
outlineReceiver(pkg, receiverName, type, home, take,
|
|
3498
|
+
outlineReceiver(pkg, receiverName, type, home, take, names, handlers, fills) {
|
|
3202
3499
|
const receiver = { receiver: receiverName, members: [] };
|
|
3203
3500
|
const holder = this.surfaceHolder(pkg, type, home);
|
|
3204
3501
|
// The receiver's own call and construct signatures, as a claim with a
|
|
@@ -3226,7 +3523,7 @@ class DeclarationReader {
|
|
|
3226
3523
|
const member = { name: property.getName(), own, signatures: [] };
|
|
3227
3524
|
receiver.members.push(member);
|
|
3228
3525
|
handlers.push(() => {
|
|
3229
|
-
const listed = this.listHandlers(own ? callee.signatures : library
|
|
3526
|
+
const listed = this.listHandlers(own ? callee.signatures : library);
|
|
3230
3527
|
if (listed.length > 0)
|
|
3231
3528
|
member.handlers = listed;
|
|
3232
3529
|
});
|
|
@@ -3242,14 +3539,14 @@ class DeclarationReader {
|
|
|
3242
3539
|
* A parameter that declares no member lists nothing, and so does a handler
|
|
3243
3540
|
* none of whose parameters do.
|
|
3244
3541
|
*/
|
|
3245
|
-
listHandlers(signatures
|
|
3542
|
+
listHandlers(signatures) {
|
|
3246
3543
|
const listed = [];
|
|
3247
3544
|
const seen = new Set();
|
|
3248
3545
|
const add = (arg, key, slot) => {
|
|
3249
3546
|
const place = JSON.stringify([arg, key ?? null]);
|
|
3250
3547
|
if (seen.has(place))
|
|
3251
3548
|
return;
|
|
3252
|
-
const params = this.listHandlerParams(slot
|
|
3549
|
+
const params = this.listHandlerParams(slot);
|
|
3253
3550
|
if (params.length === 0)
|
|
3254
3551
|
return;
|
|
3255
3552
|
seen.add(place);
|
|
@@ -3281,8 +3578,8 @@ class DeclarationReader {
|
|
|
3281
3578
|
}
|
|
3282
3579
|
return listed;
|
|
3283
3580
|
}
|
|
3284
|
-
/** The parameters of a function type's first signature that declare members, each
|
|
3285
|
-
listHandlerParams(handler
|
|
3581
|
+
/** The parameters of a function type's first signature that declare members, each with its members (`handlerMembersOf`). */
|
|
3582
|
+
listHandlerParams(handler) {
|
|
3286
3583
|
const slot = this.throughConditional(this.throughConstraint(handler));
|
|
3287
3584
|
if (slot === VARIADIC)
|
|
3288
3585
|
return [];
|
|
@@ -3294,32 +3591,73 @@ class DeclarationReader {
|
|
|
3294
3591
|
const listed = [];
|
|
3295
3592
|
signature.getParameters().forEach((parameter, index) => {
|
|
3296
3593
|
const type = this.checker.getTypeOfSymbol(parameter);
|
|
3297
|
-
if (this.namedProperties(type).length === 0
|
|
3594
|
+
if (this.namedProperties(type).length === 0)
|
|
3298
3595
|
return;
|
|
3299
|
-
const members =
|
|
3300
|
-
this.
|
|
3596
|
+
const { members, dropped } = this.handlerMembersOf(type);
|
|
3597
|
+
this.handlersDropped += dropped;
|
|
3301
3598
|
listed.push({ param: index, name: parameter.getName(), type: this.printed(type), members });
|
|
3302
3599
|
});
|
|
3303
3600
|
return listed;
|
|
3304
3601
|
}
|
|
3305
|
-
/**
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
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
|
+
}
|
|
3321
3647
|
}
|
|
3648
|
+
level = next;
|
|
3322
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;
|
|
3323
3661
|
}
|
|
3324
3662
|
listSignatures(signatures, holder, take) {
|
|
3325
3663
|
const listed = [];
|
|
@@ -3356,7 +3694,11 @@ class DeclarationReader {
|
|
|
3356
3694
|
param.literals = [...literals].sort();
|
|
3357
3695
|
params.push(param);
|
|
3358
3696
|
}
|
|
3359
|
-
|
|
3697
|
+
const entry = { params, returns: this.printed(this.checker.getReturnTypeOfSignature(signature)) };
|
|
3698
|
+
const candidates = this.listResultCandidates ? resultCandidatesOf(this.checker, signature) : undefined;
|
|
3699
|
+
if (candidates !== undefined)
|
|
3700
|
+
entry.result_candidates = candidates;
|
|
3701
|
+
listed.push(entry);
|
|
3360
3702
|
}
|
|
3361
3703
|
return listed;
|
|
3362
3704
|
}
|
|
@@ -3403,9 +3745,15 @@ class DeclarationReader {
|
|
|
3403
3745
|
* so where the cut falls does not depend on where the package is installed.
|
|
3404
3746
|
*/
|
|
3405
3747
|
printed(type) {
|
|
3748
|
+
const known = this.printedTexts.get(type);
|
|
3749
|
+
if (known !== undefined)
|
|
3750
|
+
return known;
|
|
3751
|
+
surfaceReadCounts.typePrints += 1;
|
|
3406
3752
|
const node = this.checker.typeToTypeNode(type, undefined, PRINT_FLAGS);
|
|
3407
3753
|
const text = node === undefined ? this.checker.typeToString(type) : printTypeNode(this.orderUnions(node));
|
|
3408
|
-
|
|
3754
|
+
const listed = truncate(this.scrubbed(text));
|
|
3755
|
+
this.printedTexts.set(type, listed);
|
|
3756
|
+
return listed;
|
|
3409
3757
|
}
|
|
3410
3758
|
/** `text` with the service root written as `<root>`. */
|
|
3411
3759
|
scrubbed(text) {
|
|
@@ -3579,8 +3927,10 @@ class DeclarationReader {
|
|
|
3579
3927
|
/** Judge the next check as a claim about `pkg`. */
|
|
3580
3928
|
setPackage(pkg) {
|
|
3581
3929
|
const named = packageNameOf(pkg);
|
|
3582
|
-
if (named !== this.currentPackage)
|
|
3930
|
+
if (named !== this.currentPackage) {
|
|
3583
3931
|
this.declared.clear();
|
|
3932
|
+
this.handlerMembers.clear();
|
|
3933
|
+
}
|
|
3584
3934
|
this.currentPackage = named;
|
|
3585
3935
|
}
|
|
3586
3936
|
/**
|
|
@@ -3873,6 +4223,12 @@ class DeclarationReader {
|
|
|
3873
4223
|
*/
|
|
3874
4224
|
/** The longest member path a handler parameter's members are listed to (`['raw', 'url']`). */
|
|
3875
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;
|
|
3876
4232
|
const PRINT_FLAGS = ts.NodeBuilderFlags.AllowUniqueESSymbolType |
|
|
3877
4233
|
ts.NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope |
|
|
3878
4234
|
ts.NodeBuilderFlags.IgnoreErrors |
|