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.
Files changed (70) hide show
  1. package/dist/contract.d.ts +6 -0
  2. package/dist/contract.js.map +1 -1
  3. package/dist/hook/post-edit.d.ts +10 -5
  4. package/dist/hook/post-edit.js +80 -23
  5. package/dist/hook/post-edit.js.map +1 -1
  6. package/dist/hook/refresh.d.ts +23 -3
  7. package/dist/hook/refresh.js +48 -6
  8. package/dist/hook/refresh.js.map +1 -1
  9. package/dist/hook/reuse.d.ts +7 -21
  10. package/dist/hook/reuse.js +23 -36
  11. package/dist/hook/reuse.js.map +1 -1
  12. package/dist/hook/session-start.js +15 -2
  13. package/dist/hook/session-start.js.map +1 -1
  14. package/dist/hook/stop.js.map +1 -1
  15. package/dist/init/agents-section.js +1 -1
  16. package/dist/init/agents-section.js.map +1 -1
  17. package/dist/init/doctor.js +4 -0
  18. package/dist/init/doctor.js.map +1 -1
  19. package/dist/init/hosted.d.ts +1 -0
  20. package/dist/init/hosted.js +4 -1
  21. package/dist/init/hosted.js.map +1 -1
  22. package/dist/init/remove.js +7 -1
  23. package/dist/init/remove.js.map +1 -1
  24. package/dist/init/repo-copies.js +3 -1
  25. package/dist/init/repo-copies.js.map +1 -1
  26. package/dist/init/run.d.ts +1 -1
  27. package/dist/init/run.js +10 -2
  28. package/dist/init/run.js.map +1 -1
  29. package/dist/init/task-skills.d.ts +22 -1
  30. package/dist/init/task-skills.js +65 -1
  31. package/dist/init/task-skills.js.map +1 -1
  32. package/dist/render.d.ts +41 -16
  33. package/dist/render.js +91 -102
  34. package/dist/render.js.map +1 -1
  35. package/package.json +10 -9
  36. package/plugin/.claude-plugin/plugin.json +1 -1
  37. package/sidecar/dist/src/capture/anchors.js +18 -1
  38. package/sidecar/dist/src/capture/api.d.ts +17 -4
  39. package/sidecar/dist/src/capture/check-workspace.js +1 -1
  40. package/sidecar/dist/src/capture/check.d.ts +44 -0
  41. package/sidecar/dist/src/capture/check.js +112 -10
  42. package/sidecar/dist/src/capture/index.js +18 -0
  43. package/sidecar/dist/src/capture/installed-package.d.ts +5 -0
  44. package/sidecar/dist/src/capture/installed-package.js +64 -6
  45. package/sidecar/dist/src/capture/machine-paths.d.ts +44 -0
  46. package/sidecar/dist/src/capture/machine-paths.js +97 -0
  47. package/sidecar/dist/src/capture/node-builder.d.ts +2 -0
  48. package/sidecar/dist/src/capture/node-builder.js +1 -1
  49. package/sidecar/dist/src/capture/paths-rewrite.js +3 -3
  50. package/sidecar/dist/src/capture/result-label.d.ts +42 -0
  51. package/sidecar/dist/src/capture/result-label.js +91 -0
  52. package/sidecar/dist/src/capture/self-check.js +13 -2
  53. package/sidecar/dist/src/index.js +117 -5
  54. package/sidecar/dist/src/infer-cache.d.ts +125 -0
  55. package/sidecar/dist/src/infer-cache.js +244 -0
  56. package/sidecar/dist/src/library-claims.d.ts +35 -3
  57. package/sidecar/dist/src/library-claims.js +417 -61
  58. package/sidecar/dist/src/node-index.d.ts +53 -0
  59. package/sidecar/dist/src/node-index.js +100 -0
  60. package/sidecar/dist/src/project-loader.d.ts +11 -0
  61. package/sidecar/dist/src/project-loader.js +15 -0
  62. package/sidecar/dist/src/result-candidates.d.ts +28 -0
  63. package/sidecar/dist/src/result-candidates.js +209 -0
  64. package/sidecar/dist/src/type-inferrer.d.ts +27 -0
  65. package/sidecar/dist/src/type-inferrer.js +175 -43
  66. package/sidecar/dist/src/types.d.ts +77 -1
  67. package/sidecar/dist/src/unwidened.js +10 -1
  68. package/sidecar/dist/src/validators.d.ts +931 -197
  69. package/sidecar/dist/src/validators.js +34 -0
  70. package/templates/skills/carrick-reuse.md +11 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Every node of a source file, walked once per file (carrick#1935).
3
+ *
4
+ * The inferrer locates each item by a line, a text near a line, or a span.
5
+ * Each of those lookups used to call `sourceFile.getDescendants()`, which
6
+ * walks and wraps every node of the file, once per item: on a generated
7
+ * client of 8,000+ lines that was 71% of an infer request's time. Here the
8
+ * file is walked once, the positions are read once, and a lookup wraps only
9
+ * the nodes it returns.
10
+ *
11
+ * The walk is ts-morph's own (`getDescendants()` iterates the same compiler
12
+ * nodes in the same order, tokens, syntax lists and its comment nodes
13
+ * included), and a node is wrapped through the project's own factory, so a
14
+ * lookup returns the very wrapper the walk returned.
15
+ *
16
+ * The index belongs to the compiler's source file node, not to the file's
17
+ * name: replacing a file's text gives it a new node, so a file rewritten for
18
+ * one reading and then restored is indexed again each time.
19
+ */
20
+ import { Node, SyntaxKind, ts, type SourceFile } from 'ts-morph';
21
+ /** One node of the file, with what the lookups read of it. */
22
+ export interface IndexedNode {
23
+ node: ts.Node;
24
+ kind: SyntaxKind;
25
+ /** `getStart()`: the first character of its own text. */
26
+ start: number;
27
+ /** `getEnd()`. */
28
+ end: number;
29
+ /** `getStartLineNumber()`. */
30
+ line: number;
31
+ /** Its place in `getDescendants()` order. */
32
+ order: number;
33
+ }
34
+ /** How many file indexes have been built: tests read it to prove reuse. */
35
+ export declare function nodeIndexBuilds(): number;
36
+ /** The ts-morph node for an indexed one. */
37
+ export declare function wrap<T extends Node = Node>(sourceFile: SourceFile, entry: IndexedNode): T;
38
+ /** Every descendant, in `getDescendants()` order. */
39
+ export declare function descendantsOf(sourceFile: SourceFile): readonly IndexedNode[];
40
+ /**
41
+ * The call expressions, in `getDescendantsOfKind(SyntaxKind.CallExpression)`
42
+ * order.
43
+ */
44
+ export declare function callsOf(sourceFile: SourceFile): readonly IndexedNode[];
45
+ /** The descendants that start on `line`, in walk order. */
46
+ export declare function descendantsOnLine(sourceFile: SourceFile, line: number): readonly IndexedNode[];
47
+ /**
48
+ * The descendants that start on a line from `first` to `last`, in walk
49
+ * order. Walk order is not line order (a doc comment starts before the node
50
+ * that owns it but follows it in the walk), so the lines' lists are merged
51
+ * back into walk order.
52
+ */
53
+ export declare function descendantsOnLines(sourceFile: SourceFile, first: number, last: number): IndexedNode[];
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Every node of a source file, walked once per file (carrick#1935).
3
+ *
4
+ * The inferrer locates each item by a line, a text near a line, or a span.
5
+ * Each of those lookups used to call `sourceFile.getDescendants()`, which
6
+ * walks and wraps every node of the file, once per item: on a generated
7
+ * client of 8,000+ lines that was 71% of an infer request's time. Here the
8
+ * file is walked once, the positions are read once, and a lookup wraps only
9
+ * the nodes it returns.
10
+ *
11
+ * The walk is ts-morph's own (`getDescendants()` iterates the same compiler
12
+ * nodes in the same order, tokens, syntax lists and its comment nodes
13
+ * included), and a node is wrapped through the project's own factory, so a
14
+ * lookup returns the very wrapper the walk returned.
15
+ *
16
+ * The index belongs to the compiler's source file node, not to the file's
17
+ * name: replacing a file's text gives it a new node, so a file rewritten for
18
+ * one reading and then restored is indexed again each time.
19
+ */
20
+ import { SyntaxKind } from 'ts-morph';
21
+ import { lineIndex } from './line-index.js';
22
+ const internals = (sourceFile) => sourceFile;
23
+ const indexes = new WeakMap();
24
+ let builds = 0;
25
+ /** How many file indexes have been built: tests read it to prove reuse. */
26
+ export function nodeIndexBuilds() {
27
+ return builds;
28
+ }
29
+ function indexOf(sourceFile) {
30
+ const key = sourceFile.compilerNode;
31
+ let index = indexes.get(key);
32
+ if (!index) {
33
+ index = buildIndex(sourceFile);
34
+ indexes.set(key, index);
35
+ }
36
+ return index;
37
+ }
38
+ function buildIndex(sourceFile) {
39
+ builds += 1;
40
+ const file = sourceFile.compilerNode;
41
+ const lineAt = lineIndex(file.text);
42
+ const all = [];
43
+ const calls = [];
44
+ const byLine = new Map();
45
+ for (const node of internals(sourceFile)._getCompilerDescendantsIterator()) {
46
+ const start = node.getStart(file);
47
+ const entry = {
48
+ node,
49
+ kind: node.kind,
50
+ start,
51
+ end: node.end,
52
+ line: lineAt(start),
53
+ order: all.length,
54
+ };
55
+ all.push(entry);
56
+ if (entry.kind === SyntaxKind.CallExpression)
57
+ calls.push(entry);
58
+ const onLine = byLine.get(entry.line);
59
+ if (onLine)
60
+ onLine.push(entry);
61
+ else
62
+ byLine.set(entry.line, [entry]);
63
+ }
64
+ return { all, calls, byLine };
65
+ }
66
+ /** The ts-morph node for an indexed one. */
67
+ export function wrap(sourceFile, entry) {
68
+ return internals(sourceFile)._getNodeFromCompilerNode(entry.node);
69
+ }
70
+ /** Every descendant, in `getDescendants()` order. */
71
+ export function descendantsOf(sourceFile) {
72
+ return indexOf(sourceFile).all;
73
+ }
74
+ /**
75
+ * The call expressions, in `getDescendantsOfKind(SyntaxKind.CallExpression)`
76
+ * order.
77
+ */
78
+ export function callsOf(sourceFile) {
79
+ return indexOf(sourceFile).calls;
80
+ }
81
+ /** The descendants that start on `line`, in walk order. */
82
+ export function descendantsOnLine(sourceFile, line) {
83
+ return indexOf(sourceFile).byLine.get(line) ?? [];
84
+ }
85
+ /**
86
+ * The descendants that start on a line from `first` to `last`, in walk
87
+ * order. Walk order is not line order (a doc comment starts before the node
88
+ * that owns it but follows it in the walk), so the lines' lists are merged
89
+ * back into walk order.
90
+ */
91
+ export function descendantsOnLines(sourceFile, first, last) {
92
+ const { byLine } = indexOf(sourceFile);
93
+ const found = [];
94
+ for (let line = Math.ceil(first); line <= last; line++) {
95
+ const onLine = byLine.get(line);
96
+ if (onLine)
97
+ found.push(...onLine);
98
+ }
99
+ return found.sort((a, b) => a.order - b.order);
100
+ }
@@ -171,6 +171,17 @@ export declare class ProjectLoader {
171
171
  * @throws Error if load() has not succeeded, or if the build fails
172
172
  */
173
173
  getProject(): Project;
174
+ /**
175
+ * Whether the project behind a key from `projectKeyFor` has been built,
176
+ * without building it (carrick#2843).
177
+ */
178
+ isBuilt(key: string): boolean;
179
+ /**
180
+ * Whether the project resolves through a module graph read outside the
181
+ * compiler (Deno, carrick#1260). What such a program reads is not all in
182
+ * its source files (carrick#2843).
183
+ */
184
+ resolvesThroughModuleGraph(): boolean;
174
185
  /**
175
186
  * The default project when a request has built it, and `undefined` before
176
187
  * that, without building it (carrick#2027).
@@ -487,6 +487,21 @@ export class ProjectLoader {
487
487
  }
488
488
  return this.project;
489
489
  }
490
+ /**
491
+ * Whether the project behind a key from `projectKeyFor` has been built,
492
+ * without building it (carrick#2843).
493
+ */
494
+ isBuilt(key) {
495
+ return key === '' ? this.project !== null : this.ownerProjects.has(key);
496
+ }
497
+ /**
498
+ * Whether the project resolves through a module graph read outside the
499
+ * compiler (Deno, carrick#1260). What such a program reads is not all in
500
+ * its source files (carrick#2843).
501
+ */
502
+ resolvesThroughModuleGraph() {
503
+ return this.denoProject !== undefined;
504
+ }
490
505
  /**
491
506
  * The default project when a request has built it, and `undefined` before
492
507
  * that, without building it (carrick#2027).
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Which parts of a call's return could hold the response body the call
3
+ * resolves to (carrick#2817). The compiler decides one case, that the
4
+ * caller supplies the whole return as a type argument (`whole_decided`); for
5
+ * every other return it only lists the candidates: the members that carry a
6
+ * type parameter of the signature, and the parse methods. Choosing among them
7
+ * is the store picker's job, and `holdsResult` judges the choice.
8
+ *
9
+ * Rule R, per signature `S` with type parameters `P`:
10
+ * 1. `A` is the awaited return of `S`. While `A` is a type parameter of `S`
11
+ * whose default is not `unknown` or `any`, take the default (awaited), at
12
+ * most four hops: a default of `unknown`/`any` is the caller-supplies-it
13
+ * case.
14
+ * 2. `whole_decided`: `A` is a type parameter of `S` with no constraint.
15
+ * 3. `members`: across the branches of `A` (a conditional's true and false
16
+ * types, a union's constituents without `undefined`/`null`/`void`; depth at
17
+ * most four), every property with no call signature whose declared type
18
+ * mentions a type parameter in `P`.
19
+ * 4. `parse`: properties of `S`'s raw return or of a branch of `A` that can
20
+ * be called with no required argument and whose awaited return is a bare,
21
+ * unconstrained type parameter of that method's own signature.
22
+ * A signature that returns nothing (void, undefined, never) has no candidates.
23
+ */
24
+ import { ts } from 'ts-morph';
25
+ import type { ResultShape, SurfaceResultCandidates } from './types.js';
26
+ export declare function resultCandidatesOf(checker: ts.TypeChecker, signature: ts.Signature): SurfaceResultCandidates | undefined;
27
+ /** Whether `shape` names one of `candidates`. `whole` is always one; a decided whole is the only one. */
28
+ export declare function holdsResult(candidates: SurfaceResultCandidates, shape: ResultShape): boolean;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Which parts of a call's return could hold the response body the call
3
+ * resolves to (carrick#2817). The compiler decides one case, that the
4
+ * caller supplies the whole return as a type argument (`whole_decided`); for
5
+ * every other return it only lists the candidates: the members that carry a
6
+ * type parameter of the signature, and the parse methods. Choosing among them
7
+ * is the store picker's job, and `holdsResult` judges the choice.
8
+ *
9
+ * Rule R, per signature `S` with type parameters `P`:
10
+ * 1. `A` is the awaited return of `S`. While `A` is a type parameter of `S`
11
+ * whose default is not `unknown` or `any`, take the default (awaited), at
12
+ * most four hops: a default of `unknown`/`any` is the caller-supplies-it
13
+ * case.
14
+ * 2. `whole_decided`: `A` is a type parameter of `S` with no constraint.
15
+ * 3. `members`: across the branches of `A` (a conditional's true and false
16
+ * types, a union's constituents without `undefined`/`null`/`void`; depth at
17
+ * most four), every property with no call signature whose declared type
18
+ * mentions a type parameter in `P`.
19
+ * 4. `parse`: properties of `S`'s raw return or of a branch of `A` that can
20
+ * be called with no required argument and whose awaited return is a bare,
21
+ * unconstrained type parameter of that method's own signature.
22
+ * A signature that returns nothing (void, undefined, never) has no candidates.
23
+ */
24
+ import { ts } from 'ts-morph';
25
+ const MAX_DEPTH = 4;
26
+ const NOTHING = ts.TypeFlags.Void | ts.TypeFlags.Undefined | ts.TypeFlags.Never;
27
+ const NULLISH = ts.TypeFlags.Undefined | ts.TypeFlags.Null | ts.TypeFlags.Void;
28
+ const OPEN = ts.TypeFlags.Any | ts.TypeFlags.Unknown;
29
+ const isTypeParameter = (type) => (type.flags & ts.TypeFlags.TypeParameter) !== 0;
30
+ const byCodeUnit = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
31
+ export function resultCandidatesOf(checker, signature) {
32
+ const reader = new ResultReader(checker);
33
+ return reader.candidates(signature);
34
+ }
35
+ /** Whether `shape` names one of `candidates`. `whole` is always one; a decided whole is the only one. */
36
+ export function holdsResult(candidates, shape) {
37
+ if ('whole' in shape)
38
+ return true;
39
+ if (candidates.whole_decided)
40
+ return false;
41
+ if ('member' in shape)
42
+ return candidates.members.includes(shape.member[0]);
43
+ return candidates.parse.includes(shape.parse);
44
+ }
45
+ class ResultReader {
46
+ checker;
47
+ constructor(checker) {
48
+ this.checker = checker;
49
+ }
50
+ candidates(signature) {
51
+ const raw = this.checker.getReturnTypeOfSignature(signature);
52
+ if (raw.flags & NOTHING)
53
+ return undefined;
54
+ const params = new Set(signature.getTypeParameters() ?? []);
55
+ let awaited = this.awaited(raw);
56
+ for (let hop = 0; hop < MAX_DEPTH && isTypeParameter(awaited) && params.has(awaited); hop++) {
57
+ const fallback = this.checker.getDefaultFromTypeParameter(awaited);
58
+ if (fallback === undefined || fallback.flags & OPEN)
59
+ break;
60
+ awaited = this.awaited(fallback);
61
+ }
62
+ if (awaited.flags & NOTHING)
63
+ return undefined;
64
+ if (isTypeParameter(awaited) && params.has(awaited) && this.unconstrained(awaited)) {
65
+ return { whole_decided: true, members: [], parse: [] };
66
+ }
67
+ const leaves = [];
68
+ this.leavesOf(awaited, params, 0, leaves);
69
+ const members = new Set();
70
+ for (const leaf of leaves) {
71
+ if (!(leaf.type.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection)))
72
+ continue;
73
+ for (const property of this.checker.getPropertiesOfType(leaf.type)) {
74
+ const type = this.checker.getTypeOfSymbol(property);
75
+ if (this.checker.getNonNullableType(type).getCallSignatures().length > 0)
76
+ continue;
77
+ if (this.mentions(type, leaf.params, 0, new Set()))
78
+ members.add(property.getName());
79
+ }
80
+ }
81
+ const parse = new Set();
82
+ for (const holder of [raw, ...leaves.map(leaf => leaf.type)]) {
83
+ if (!(holder.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection)))
84
+ continue;
85
+ for (const property of this.checker.getPropertiesOfType(holder)) {
86
+ const type = this.checker.getNonNullableType(this.checker.getTypeOfSymbol(property));
87
+ if (type.getCallSignatures().some(method => this.parses(method)))
88
+ parse.add(property.getName());
89
+ }
90
+ }
91
+ return { whole_decided: false, members: [...members].sort(byCodeUnit), parse: [...parse].sort(byCodeUnit) };
92
+ }
93
+ /**
94
+ * `type` with a promise unwrapped. The checker spells an open parameter's
95
+ * awaited type `Awaited<T>`; that is read as `T`. A union and a conditional
96
+ * are left to `leavesOf`, which awaits each arm.
97
+ */
98
+ awaited(type, depth = 0) {
99
+ if (depth > MAX_DEPTH)
100
+ return type;
101
+ if (type.aliasSymbol?.getName() === 'Awaited' && type.aliasTypeArguments?.length === 1) {
102
+ return this.awaited(type.aliasTypeArguments[0], depth + 1);
103
+ }
104
+ if (type.flags & (ts.TypeFlags.TypeParameter | ts.TypeFlags.Union | ts.TypeFlags.Conditional))
105
+ return type;
106
+ const unwrapped = this.checker.getAwaitedType(type);
107
+ return unwrapped === undefined || unwrapped === type ? type : this.awaited(unwrapped, depth + 1);
108
+ }
109
+ unconstrained(parameter) {
110
+ const constraint = this.checker.getBaseConstraintOfType(parameter);
111
+ return constraint === undefined || (constraint.flags & OPEN) !== 0;
112
+ }
113
+ /**
114
+ * The branches of `type`: a union's parts without nullish ones, a
115
+ * conditional's two arms, each awaited. `params` are the type parameters a
116
+ * branch counts as carrying a value of: an arm of a generic alias is written
117
+ * in the alias's own parameters, so the alias parameters applied to
118
+ * something that mentions `params` join `params` for that arm.
119
+ */
120
+ leavesOf(type, params, depth, into) {
121
+ if (depth > MAX_DEPTH)
122
+ return;
123
+ const current = this.awaited(type);
124
+ if (current.flags & ts.TypeFlags.Union) {
125
+ for (const part of current.types) {
126
+ if (!(part.flags & NULLISH))
127
+ this.leavesOf(part, params, depth + 1, into);
128
+ }
129
+ return;
130
+ }
131
+ if (current.flags & ts.TypeFlags.Conditional) {
132
+ const node = current.root.node;
133
+ const armParams = this.aliasParams(current, params);
134
+ for (const arm of [node.trueType, node.falseType]) {
135
+ this.leavesOf(this.checker.getTypeFromTypeNode(arm), armParams, depth + 1, into);
136
+ }
137
+ return;
138
+ }
139
+ if (current.flags & NOTHING)
140
+ return;
141
+ into.push({ type: current, params });
142
+ }
143
+ /**
144
+ * `params`, plus each type parameter of the alias `type` is an application
145
+ * of whose argument mentions one of `params`. The arms of a conditional
146
+ * written in an alias name those parameters, not the arguments.
147
+ */
148
+ aliasParams(type, params) {
149
+ const declaration = type.aliasSymbol?.declarations?.find(ts.isTypeAliasDeclaration);
150
+ const args = type.aliasTypeArguments;
151
+ if (declaration === undefined || args === undefined)
152
+ return params;
153
+ const joined = new Set(params);
154
+ (declaration.typeParameters ?? []).forEach((node, index) => {
155
+ if (index < args.length && this.mentions(args[index], params, 0, new Set())) {
156
+ joined.add(this.checker.getTypeAtLocation(node));
157
+ }
158
+ });
159
+ return joined;
160
+ }
161
+ /** A parse method: no required argument, and it returns a bare unconstrained parameter of its own. */
162
+ parses(method) {
163
+ const own = method.getTypeParameters() ?? [];
164
+ if (own.length === 0)
165
+ return false;
166
+ const required = method.getParameters().some(parameter => {
167
+ const declaration = parameter.valueDeclaration;
168
+ if (declaration === undefined || !ts.isParameter(declaration))
169
+ return true;
170
+ return declaration.questionToken === undefined && declaration.initializer === undefined && declaration.dotDotDotToken === undefined;
171
+ });
172
+ if (required)
173
+ return false;
174
+ const returned = this.awaited(this.checker.getReturnTypeOfSignature(method));
175
+ return isTypeParameter(returned) && own.includes(returned) && this.unconstrained(returned);
176
+ }
177
+ /** Whether `type` names one of `params` anywhere a value of it could be read through. */
178
+ mentions(type, params, depth, seen) {
179
+ if (params.has(type))
180
+ return true;
181
+ if (depth > MAX_DEPTH || seen.has(type))
182
+ return false;
183
+ seen.add(type);
184
+ const next = (part) => this.mentions(part, params, depth + 1, seen);
185
+ if (type.aliasTypeArguments?.some(next))
186
+ return true;
187
+ if (type.isUnionOrIntersection())
188
+ return type.types.some(next);
189
+ if (type.flags & ts.TypeFlags.IndexedAccess) {
190
+ const access = type;
191
+ return next(access.objectType) || next(access.indexType);
192
+ }
193
+ if (type.flags & ts.TypeFlags.Index)
194
+ return next(type.type);
195
+ if (type.flags & ts.TypeFlags.Conditional) {
196
+ const conditional = type;
197
+ return next(conditional.checkType) || next(conditional.extendsType);
198
+ }
199
+ if (!(type.flags & ts.TypeFlags.Object))
200
+ return false;
201
+ const object = type;
202
+ if (object.objectFlags & ts.ObjectFlags.Reference && this.checker.getTypeArguments(object).some(next)) {
203
+ return true;
204
+ }
205
+ if (this.checker.isArrayType(type) || this.checker.isTupleType(type))
206
+ return false;
207
+ return this.checker.getPropertiesOfType(type).some(property => next(this.checker.getTypeOfSymbol(property)));
208
+ }
209
+ }
@@ -208,6 +208,18 @@ export declare class TypeInferrer {
208
208
  * Get or add a source file to the project
209
209
  */
210
210
  private getSourceFile;
211
+ /**
212
+ * carrick#2824: a located response expression that sits in ANOTHER route's
213
+ * handler is not this row's. A model can locate `response` at a line that
214
+ * belongs to the next registration in the same module; reading that node
215
+ * publishes the other route's reply. Returns the request with its locator
216
+ * dropped, so it is answered by the registration on its own line, when:
217
+ * 1. a registration R sits on the request's line;
218
+ * 2. the located node is outside R and outside R's handler;
219
+ * 3. some ancestor of the node is a registration whose handler contains it.
220
+ * Anything else is returned unchanged. Matches on node shape, no names.
221
+ */
222
+ private withoutForeignLocator;
211
223
  private inferFunctionReturn;
212
224
  /**
213
225
  * Build a `function_return` inferred type from an already-resolved function
@@ -446,6 +458,15 @@ export declare class TypeInferrer {
446
458
  * returns is a better statement of that body than the read (carrick#1382).
447
459
  */
448
460
  private receivingCallOf;
461
+ /**
462
+ * A call's result read through the library store's label (carrick#2818):
463
+ * `whole` is the awaited type, `member` is that member of every non-nullish
464
+ * constituent of it, and `parse` is the awaited return of that method as the
465
+ * call returns it (a thenable's own `json()` is not on what it resolves to).
466
+ * A part the type lacks is a decided abstain, so the capture's own re-read
467
+ * of the raw call does not publish the envelope in its place.
468
+ */
469
+ private labelledCallResult;
449
470
  private inferCallResult;
450
471
  /**
451
472
  * The terminal of a call's def-use walk is a zero-argument `.text()` read of
@@ -970,6 +991,12 @@ export declare class TypeInferrer {
970
991
  * arguments are what a rule naming `Task`, `Reply` or `Outcome` describes.
971
992
  */
972
993
  private wrapperReadings;
994
+ /**
995
+ * The type of `type`'s member `name` at `node`, or `undefined` when the type
996
+ * has no such member. The one property read the payload-property rules and
997
+ * the library store's result label (carrick#2818) share.
998
+ */
999
+ private readMemberType;
973
1000
  /**
974
1001
  * Extract the payload type from a matched wrapper. Returns null when the
975
1002
  * rule matched the wrapper but no payload is recoverable from generics or