carrick 0.3.133 → 0.3.134
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/installed-package.d.ts +5 -0
- package/sidecar/dist/src/capture/installed-package.js +64 -6
- 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 +116 -4
- 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.js +42 -4
- 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 +52 -0
- package/sidecar/dist/src/unwidened.js +10 -1
- package/sidecar/dist/src/validators.d.ts +919 -197
- package/sidecar/dist/src/validators.js +32 -0
- package/templates/skills/carrick-reuse.md +11 -0
|
@@ -30,6 +30,7 @@ import { readResponseModes } from './response-modes.js';
|
|
|
30
30
|
import { functionAtLine } from './function-line-index.js';
|
|
31
31
|
import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
|
|
32
32
|
import { endLineOf, startLineOf } from './line-index.js';
|
|
33
|
+
import { callsOf, descendantsOf, descendantsOnLine, descendantsOnLines, wrap, } from './node-index.js';
|
|
33
34
|
import { elapsedMs, inferTiming, phaseClock, timedPhase } from './infer-timing.js';
|
|
34
35
|
import { StatedParams } from './stated-params.js';
|
|
35
36
|
import { bindingToleratesAbsence, handlerToleratesAbsentBody } from './absent-body.js';
|
|
@@ -933,7 +934,49 @@ export class TypeInferrer {
|
|
|
933
934
|
// ===========================================================================
|
|
934
935
|
// Inference Methods by Kind
|
|
935
936
|
// ===========================================================================
|
|
937
|
+
/**
|
|
938
|
+
* carrick#2824: a located response expression that sits in ANOTHER route's
|
|
939
|
+
* handler is not this row's. A model can locate `response` at a line that
|
|
940
|
+
* belongs to the next registration in the same module; reading that node
|
|
941
|
+
* publishes the other route's reply. Returns the request with its locator
|
|
942
|
+
* dropped, so it is answered by the registration on its own line, when:
|
|
943
|
+
* 1. a registration R sits on the request's line;
|
|
944
|
+
* 2. the located node is outside R and outside R's handler;
|
|
945
|
+
* 3. some ancestor of the node is a registration whose handler contains it.
|
|
946
|
+
* Anything else is returned unchanged. Matches on node shape, no names.
|
|
947
|
+
*/
|
|
948
|
+
withoutForeignLocator(sourceFile, request) {
|
|
949
|
+
const node = this.resolveTargetNode(sourceFile, request);
|
|
950
|
+
if (!node)
|
|
951
|
+
return request;
|
|
952
|
+
const atLine = this.registrationAtLine(sourceFile, request.line_number);
|
|
953
|
+
if (!atLine)
|
|
954
|
+
return request;
|
|
955
|
+
const within = (inner, outer) => inner.getPos() >= outer.getPos() && inner.getEnd() <= outer.getEnd();
|
|
956
|
+
if (within(node, atLine.registration) || within(node, atLine.handler)) {
|
|
957
|
+
return request;
|
|
958
|
+
}
|
|
959
|
+
const inOtherHandler = node.getAncestors().some((ancestor) => {
|
|
960
|
+
if (!Node.isCallExpression(ancestor) && !Node.isObjectLiteralExpression(ancestor)) {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
const handler = this.resolveRegisteredHandler(ancestor);
|
|
964
|
+
return handler !== undefined && within(node, handler);
|
|
965
|
+
});
|
|
966
|
+
if (!inOtherHandler)
|
|
967
|
+
return request;
|
|
968
|
+
this.log(`Located expression at ${request.file_path}:${request.expression_line ?? request.line_number} ` +
|
|
969
|
+
`sits in another registration's handler; answering by the registration on line ${request.line_number}`);
|
|
970
|
+
return {
|
|
971
|
+
...request,
|
|
972
|
+
span_start: undefined,
|
|
973
|
+
span_end: undefined,
|
|
974
|
+
expression_text: undefined,
|
|
975
|
+
expression_line: undefined,
|
|
976
|
+
};
|
|
977
|
+
}
|
|
936
978
|
inferFunctionReturn(sourceFile, request, extractionConfig) {
|
|
979
|
+
request = this.withoutForeignLocator(sourceFile, request);
|
|
937
980
|
// A `function_return` request can point at a route REGISTRATION: that is
|
|
938
981
|
// the shape the scanner sends for a route whose handler RETURNS its
|
|
939
982
|
// payload. The containing-function walk below then resolves the function
|
|
@@ -948,6 +991,16 @@ export class TypeInferrer {
|
|
|
948
991
|
}
|
|
949
992
|
const func = this.resolveContainingFunction(sourceFile, request);
|
|
950
993
|
if (!func) {
|
|
994
|
+
// carrick#807: a file route exported as a call to a route builder
|
|
995
|
+
// (`export const x = builder(options, async () => ...)`) starts no
|
|
996
|
+
// function at its line; the handler follows the options object. Where a
|
|
997
|
+
// registration sits on the line, read the handler it hands in, as the
|
|
998
|
+
// line-only `response_body` path does.
|
|
999
|
+
const atLine = this.registrationAtLine(sourceFile, request.line_number);
|
|
1000
|
+
if (atLine) {
|
|
1001
|
+
this.log(`Line ${request.line_number} is a route registration; following handler return`);
|
|
1002
|
+
return this.buildFunctionReturnInferredType(request, atLine.handler, extractionConfig, true);
|
|
1003
|
+
}
|
|
951
1004
|
this.log(`No function found for request at ${request.file_path}:${request.line_number}`);
|
|
952
1005
|
return null;
|
|
953
1006
|
}
|
|
@@ -1263,6 +1316,7 @@ export class TypeInferrer {
|
|
|
1263
1316
|
return undefined;
|
|
1264
1317
|
}
|
|
1265
1318
|
inferResponseBody(sourceFile, request, extractionConfig) {
|
|
1319
|
+
request = this.withoutForeignLocator(sourceFile, request);
|
|
1266
1320
|
const node = this.resolveTargetNode(sourceFile, request);
|
|
1267
1321
|
if (!node) {
|
|
1268
1322
|
// Line-only anchor (no span, no expression text) — the shape the scanner
|
|
@@ -1998,6 +2052,78 @@ export class TypeInferrer {
|
|
|
1998
2052
|
return undefined;
|
|
1999
2053
|
return accept(parent) ? parent : undefined;
|
|
2000
2054
|
}
|
|
2055
|
+
/**
|
|
2056
|
+
* A call's result read through the library store's label (carrick#2818):
|
|
2057
|
+
* `whole` is the awaited type, `member` is that member of every non-nullish
|
|
2058
|
+
* constituent of it, and `parse` is the awaited return of that method as the
|
|
2059
|
+
* call returns it (a thenable's own `json()` is not on what it resolves to).
|
|
2060
|
+
* A part the type lacks is a decided abstain, so the capture's own re-read
|
|
2061
|
+
* of the raw call does not publish the envelope in its place.
|
|
2062
|
+
*/
|
|
2063
|
+
labelledCallResult(request, callExpr, label) {
|
|
2064
|
+
const location = this.getNodeLocation(callExpr);
|
|
2065
|
+
const raw = callExpr.getType();
|
|
2066
|
+
const awaited = this.awaitedBeyondPromise(raw) ?? this.unwrapPromiseType(raw);
|
|
2067
|
+
const missing = (what) => this.decidedAbstain(request, location, {
|
|
2068
|
+
path: '',
|
|
2069
|
+
kind: 'unknown',
|
|
2070
|
+
reason: 'result_member_missing',
|
|
2071
|
+
detail: `the library's label names ${what}, which this call's type does not have`,
|
|
2072
|
+
});
|
|
2073
|
+
let read;
|
|
2074
|
+
if ('whole' in label) {
|
|
2075
|
+
read = [awaited];
|
|
2076
|
+
}
|
|
2077
|
+
else if ('member' in label) {
|
|
2078
|
+
const name = label.member[0];
|
|
2079
|
+
read = [];
|
|
2080
|
+
const constituents = awaited.isUnion() ? awaited.getUnionTypes() : [awaited];
|
|
2081
|
+
for (const constituent of constituents) {
|
|
2082
|
+
if (isNullishType(constituent))
|
|
2083
|
+
continue;
|
|
2084
|
+
const held = this.readMemberType(constituent, name, callExpr);
|
|
2085
|
+
if (!held)
|
|
2086
|
+
return missing(`the member '${name}'`);
|
|
2087
|
+
for (const part of held.isUnion() ? held.getUnionTypes() : [held]) {
|
|
2088
|
+
if (!isNullishType(part))
|
|
2089
|
+
read.push(part);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (read.length === 0)
|
|
2093
|
+
return missing(`the member '${name}'`);
|
|
2094
|
+
}
|
|
2095
|
+
else {
|
|
2096
|
+
const method = (this.readMemberType(raw, label.parse, callExpr) ??
|
|
2097
|
+
this.readMemberType(awaited, label.parse, callExpr));
|
|
2098
|
+
const signature = method
|
|
2099
|
+
?.getCallSignatures()
|
|
2100
|
+
.find((candidate) => candidate.getParameters().every((parameter) => {
|
|
2101
|
+
const declaration = parameter.getDeclarations()[0];
|
|
2102
|
+
return (!!declaration &&
|
|
2103
|
+
Node.isParameterDeclaration(declaration) &&
|
|
2104
|
+
(declaration.hasQuestionToken() ||
|
|
2105
|
+
declaration.hasInitializer() ||
|
|
2106
|
+
declaration.isRestParameter()));
|
|
2107
|
+
}));
|
|
2108
|
+
if (!signature)
|
|
2109
|
+
return missing(`the method '${label.parse}'`);
|
|
2110
|
+
const returned = signature.getReturnType();
|
|
2111
|
+
const parsed = this.awaitedBeyondPromise(returned) ?? this.unwrapPromiseType(returned);
|
|
2112
|
+
// A type parameter of the method that nothing at the call instantiates
|
|
2113
|
+
// (`json<J>()`) states no type: the caller supplies it.
|
|
2114
|
+
if (parsed.isTypeParameter())
|
|
2115
|
+
return missing(`a concrete type from '${label.parse}'`);
|
|
2116
|
+
read = [parsed];
|
|
2117
|
+
}
|
|
2118
|
+
const texts = [];
|
|
2119
|
+
for (const part of read) {
|
|
2120
|
+
const text = this.structuralTextFromType(part, callExpr) ?? typeText(part, callExpr);
|
|
2121
|
+
if (!texts.includes(text))
|
|
2122
|
+
texts.push(text);
|
|
2123
|
+
}
|
|
2124
|
+
const anchor = read.length === 1 ? this.unwrapArrayLevels(this.unwrapPromiseType(read[0])) : undefined;
|
|
2125
|
+
return this.createInferredType(request, texts.join(' | '), false, location, undefined, anchor ? this.primaryTypeSymbol(anchor.element) : undefined, anchor?.depth, anchor ? this.primaryTypeSymbolSource(anchor.element) : undefined);
|
|
2126
|
+
}
|
|
2001
2127
|
inferCallResult(sourceFile, request, extractionConfig) {
|
|
2002
2128
|
const callExpr = this.resolveTargetCallExpression(sourceFile, request);
|
|
2003
2129
|
// A call's result is read off the call. A locator that finds no call names
|
|
@@ -2007,6 +2133,12 @@ export class TypeInferrer {
|
|
|
2007
2133
|
this.log(`Call-result locator at ${request.file_path}:${request.line_number} finds no call; leaving unresolved`);
|
|
2008
2134
|
return null;
|
|
2009
2135
|
}
|
|
2136
|
+
// carrick#2818: the library store labels which part of this call's result
|
|
2137
|
+
// holds the response body. The label is the library's own statement, so it
|
|
2138
|
+
// is read before any wrapper rule of the service's extraction config.
|
|
2139
|
+
if (request.result) {
|
|
2140
|
+
return this.labelledCallResult(request, callExpr, request.result);
|
|
2141
|
+
}
|
|
2010
2142
|
// Walk up from the already-found call expression instead of re-searching
|
|
2011
2143
|
const func = this.findContainingFunctionForNode(callExpr);
|
|
2012
2144
|
const use = this.resolveCallResultTerminalNode(callExpr, func);
|
|
@@ -4624,6 +4756,14 @@ export class TypeInferrer {
|
|
|
4624
4756
|
}
|
|
4625
4757
|
return readings;
|
|
4626
4758
|
}
|
|
4759
|
+
/**
|
|
4760
|
+
* The type of `type`'s member `name` at `node`, or `undefined` when the type
|
|
4761
|
+
* has no such member. The one property read the payload-property rules and
|
|
4762
|
+
* the library store's result label (carrick#2818) share.
|
|
4763
|
+
*/
|
|
4764
|
+
readMemberType(type, name, node) {
|
|
4765
|
+
return type.getProperty(name)?.getTypeAtLocation(node);
|
|
4766
|
+
}
|
|
4627
4767
|
/**
|
|
4628
4768
|
* Extract the payload type from a matched wrapper. Returns null when the
|
|
4629
4769
|
* rule matched the wrapper but no payload is recoverable from generics or
|
|
@@ -4696,11 +4836,10 @@ export class TypeInferrer {
|
|
|
4696
4836
|
if (rule.payloadPropertyPath && rule.payloadPropertyPath.length > 0) {
|
|
4697
4837
|
let currentType = type;
|
|
4698
4838
|
for (const propName of rule.payloadPropertyPath) {
|
|
4699
|
-
const
|
|
4700
|
-
if (!
|
|
4839
|
+
const propType = this.readMemberType(currentType, propName, node);
|
|
4840
|
+
if (!propType) {
|
|
4701
4841
|
break;
|
|
4702
4842
|
}
|
|
4703
|
-
const propType = prop.getTypeAtLocation(node);
|
|
4704
4843
|
currentType = propType;
|
|
4705
4844
|
}
|
|
4706
4845
|
if (currentType !== type) {
|
|
@@ -6730,10 +6869,10 @@ export class TypeInferrer {
|
|
|
6730
6869
|
* registration node itself, so the schema anchors need both halves.
|
|
6731
6870
|
*/
|
|
6732
6871
|
registrationAtLine(sourceFile, line) {
|
|
6733
|
-
for (const
|
|
6734
|
-
if (
|
|
6735
|
-
|
|
6736
|
-
|
|
6872
|
+
for (const entry of descendantsOnLine(sourceFile, line)) {
|
|
6873
|
+
if (entry.kind === SyntaxKind.CallExpression ||
|
|
6874
|
+
entry.kind === SyntaxKind.ObjectLiteralExpression) {
|
|
6875
|
+
const node = wrap(sourceFile, entry);
|
|
6737
6876
|
const handler = this.resolveRegisteredHandler(node);
|
|
6738
6877
|
if (handler) {
|
|
6739
6878
|
return { registration: node, handler };
|
|
@@ -9307,20 +9446,23 @@ export class TypeInferrer {
|
|
|
9307
9446
|
* 5. Return smallest matching node closest to target line
|
|
9308
9447
|
*/
|
|
9309
9448
|
findNodeByText(sourceFile, expressionText, lineNumber, searchRadius = 5) {
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
return this.matchByText(
|
|
9449
|
+
// Only the window's nodes are wrapped: `matchByText` keeps no other.
|
|
9450
|
+
const inWindow = lineNumber
|
|
9451
|
+
? descendantsOnLines(sourceFile, lineNumber - searchRadius, lineNumber + searchRadius)
|
|
9452
|
+
: descendantsOf(sourceFile);
|
|
9453
|
+
const nodes = inWindow
|
|
9454
|
+
.filter((entry) => entry.kind !== SyntaxKind.SyntaxList)
|
|
9455
|
+
.map((entry) => wrap(sourceFile, entry));
|
|
9456
|
+
return this.matchByText(nodes, expressionText, lineNumber, searchRadius);
|
|
9318
9457
|
}
|
|
9319
9458
|
/**
|
|
9320
9459
|
* Find a CallExpression by matching expression text near a target line.
|
|
9321
9460
|
*/
|
|
9322
9461
|
findCallExpressionByText(sourceFile, expressionText, lineNumber, searchRadius = 5) {
|
|
9323
|
-
const
|
|
9462
|
+
const inWindow = lineNumber
|
|
9463
|
+
? callsOf(sourceFile).filter((entry) => entry.line >= lineNumber - searchRadius && entry.line <= lineNumber + searchRadius)
|
|
9464
|
+
: callsOf(sourceFile);
|
|
9465
|
+
const callExpressions = inWindow.map((entry) => wrap(sourceFile, entry));
|
|
9324
9466
|
return this.matchByText(callExpressions, expressionText, lineNumber, searchRadius);
|
|
9325
9467
|
}
|
|
9326
9468
|
/**
|
|
@@ -9606,23 +9748,15 @@ export class TypeInferrer {
|
|
|
9606
9748
|
});
|
|
9607
9749
|
}
|
|
9608
9750
|
findNodeAtSpan(sourceFile, spanStart, spanEnd) {
|
|
9609
|
-
const
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
return false;
|
|
9613
|
-
// Skip SyntaxList nodes as they have unreliable types
|
|
9614
|
-
if (node.getKind() === SyntaxKind.SyntaxList)
|
|
9615
|
-
return false;
|
|
9616
|
-
const start = node.getStart();
|
|
9617
|
-
const end = node.getEnd();
|
|
9618
|
-
return start <= spanStart && spanEnd <= end;
|
|
9619
|
-
});
|
|
9751
|
+
const containing = descendantsOf(sourceFile).filter(
|
|
9752
|
+
// Skip SyntaxList nodes as they have unreliable types
|
|
9753
|
+
(entry) => entry.kind !== SyntaxKind.SyntaxList && entry.start <= spanStart && spanEnd <= entry.end);
|
|
9620
9754
|
if (containing.length === 0) {
|
|
9621
9755
|
return undefined;
|
|
9622
9756
|
}
|
|
9623
|
-
|
|
9624
|
-
const bestRange = best.
|
|
9625
|
-
const currentRange = current.
|
|
9757
|
+
const best = containing.reduce((best, current) => {
|
|
9758
|
+
const bestRange = best.end - best.start;
|
|
9759
|
+
const currentRange = current.end - current.start;
|
|
9626
9760
|
// Prefer exact matches, then smallest containing
|
|
9627
9761
|
const bestDelta = Math.abs(bestRange - (spanEnd - spanStart));
|
|
9628
9762
|
const currentDelta = Math.abs(currentRange - (spanEnd - spanStart));
|
|
@@ -9635,10 +9769,9 @@ export class TypeInferrer {
|
|
|
9635
9769
|
// span published `any` as its request body. Descendants follow their
|
|
9636
9770
|
// ancestors in `getDescendants()` order, so a tied later node inside the
|
|
9637
9771
|
// current best is the deeper one.
|
|
9638
|
-
return current.
|
|
9639
|
-
? current
|
|
9640
|
-
: best;
|
|
9772
|
+
return current.start >= best.start && current.end <= best.end ? current : best;
|
|
9641
9773
|
});
|
|
9774
|
+
return wrap(sourceFile, best);
|
|
9642
9775
|
}
|
|
9643
9776
|
findCallExpressionAtSpan(sourceFile, spanStart, spanEnd) {
|
|
9644
9777
|
// Tolerate the SWC span convention (#336): the scanner sends raw SWC
|
|
@@ -9658,23 +9791,18 @@ export class TypeInferrer {
|
|
|
9658
9791
|
// more and the slack only widens the candidate set. Removing it is
|
|
9659
9792
|
// carrick#823, which wants its own before/after.
|
|
9660
9793
|
const SPAN_SLACK = 2;
|
|
9661
|
-
const
|
|
9662
|
-
.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
9663
|
-
const candidates = callExpressions.filter((expr) => {
|
|
9664
|
-
const start = expr.getStart();
|
|
9665
|
-
const end = expr.getEnd();
|
|
9666
|
-
return start - SPAN_SLACK <= spanStart && spanEnd <= end + SPAN_SLACK;
|
|
9667
|
-
});
|
|
9794
|
+
const candidates = callsOf(sourceFile).filter((entry) => entry.start - SPAN_SLACK <= spanStart && spanEnd <= entry.end + SPAN_SLACK);
|
|
9668
9795
|
if (candidates.length === 0) {
|
|
9669
9796
|
return undefined;
|
|
9670
9797
|
}
|
|
9671
|
-
|
|
9672
|
-
const bestRange = best.
|
|
9673
|
-
const currentRange = current.
|
|
9798
|
+
const best = candidates.reduce((best, current) => {
|
|
9799
|
+
const bestRange = best.end - best.start;
|
|
9800
|
+
const currentRange = current.end - current.start;
|
|
9674
9801
|
const bestDelta = Math.abs(bestRange - (spanEnd - spanStart));
|
|
9675
9802
|
const currentDelta = Math.abs(currentRange - (spanEnd - spanStart));
|
|
9676
9803
|
return currentDelta < bestDelta ? current : best;
|
|
9677
9804
|
});
|
|
9805
|
+
return wrap(sourceFile, best);
|
|
9678
9806
|
}
|
|
9679
9807
|
// ===========================================================================
|
|
9680
9808
|
// Type Utilities
|
|
@@ -9845,3 +9973,7 @@ function nodeAtSpan(span) {
|
|
|
9845
9973
|
}
|
|
9846
9974
|
return node;
|
|
9847
9975
|
}
|
|
9976
|
+
/** `undefined`, `null` and `void`: what a labelled read removes (carrick#2818). */
|
|
9977
|
+
function isNullishType(type) {
|
|
9978
|
+
return type.isUndefined() || type.isNull() || type.getText() === 'void';
|
|
9979
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* These types define the JSON messages exchanged between Rust and the Node.js sidecar
|
|
4
4
|
*/
|
|
5
5
|
import type { ResponseModes } from './response-modes.js';
|
|
6
|
+
import type { ResultLabel } from './capture/api.js';
|
|
6
7
|
/**
|
|
7
8
|
* The kind of type inference to perform
|
|
8
9
|
*/
|
|
@@ -543,6 +544,8 @@ export type LibraryClaim = {
|
|
|
543
544
|
key_labels?: Record<string, KeyLabel>;
|
|
544
545
|
name_scope?: NameScope;
|
|
545
546
|
picker?: string;
|
|
547
|
+
/** Which part of what the maker returns holds the value its function resolves to; read by a `result` claim, never by this one. */
|
|
548
|
+
result?: ResultShape;
|
|
546
549
|
} | {
|
|
547
550
|
/** A member that returns a receiver bound to a name (a channel, room or queue). */
|
|
548
551
|
kind: 'scope';
|
|
@@ -603,6 +606,23 @@ export type LibraryClaim = {
|
|
|
603
606
|
yields?: Yields;
|
|
604
607
|
/** Route fields (#2474 R0), `receive` only: where the handler's request holds its path and method. */
|
|
605
608
|
request?: RequestAt;
|
|
609
|
+
/** Which part of what the call returns holds the response; read by a `result` claim, never by this one. */
|
|
610
|
+
result?: ResultShape;
|
|
611
|
+
} | {
|
|
612
|
+
/**
|
|
613
|
+
* Which part of what a call returns holds the response body, judged over
|
|
614
|
+
* every overload of `member` (after `path`) on the receiver; `member`
|
|
615
|
+
* null is the receiver itself called (carrick#2817). `form` says whether
|
|
616
|
+
* the call is a maker's or an op's.
|
|
617
|
+
*/
|
|
618
|
+
kind: 'result';
|
|
619
|
+
form: 'make' | 'op';
|
|
620
|
+
member: string | null;
|
|
621
|
+
path?: string[];
|
|
622
|
+
on?: ClaimOn;
|
|
623
|
+
of?: string;
|
|
624
|
+
picker?: string;
|
|
625
|
+
result: ResultShape;
|
|
606
626
|
} | {
|
|
607
627
|
/** A name the library emits itself, spelled in one of `member`'s parameters. */
|
|
608
628
|
kind: 'reserved';
|
|
@@ -713,6 +733,11 @@ export interface InferRequestItem {
|
|
|
713
733
|
param_name?: string;
|
|
714
734
|
/** The member of a request options literal that carries the body (`request_config`). */
|
|
715
735
|
member?: string;
|
|
736
|
+
/**
|
|
737
|
+
* carrick#2818: the library store's label for the call's result, naming the
|
|
738
|
+
* part that holds the response body. Read for `call_result`.
|
|
739
|
+
*/
|
|
740
|
+
result?: ResultLabel;
|
|
716
741
|
}
|
|
717
742
|
/**
|
|
718
743
|
* Response status
|
|
@@ -1005,7 +1030,34 @@ export interface SurfaceKey {
|
|
|
1005
1030
|
export interface SurfaceSignature {
|
|
1006
1031
|
params: SurfaceParam[];
|
|
1007
1032
|
returns: string;
|
|
1033
|
+
/**
|
|
1034
|
+
* Which parts of the awaited return could hold the response body
|
|
1035
|
+
* (carrick#2817). Left out of the surface hash. Absent when the signature
|
|
1036
|
+
* returns nothing.
|
|
1037
|
+
*/
|
|
1038
|
+
result_candidates?: SurfaceResultCandidates;
|
|
1008
1039
|
}
|
|
1040
|
+
/** What a signature's return offers as its result, by rule R (`result-candidates.ts`). */
|
|
1041
|
+
export interface SurfaceResultCandidates {
|
|
1042
|
+
/** The caller supplies the whole return as a type argument: `whole` is the only candidate. */
|
|
1043
|
+
whole_decided: boolean;
|
|
1044
|
+
/** Properties that carry a type parameter of the signature, sorted. */
|
|
1045
|
+
members: string[];
|
|
1046
|
+
/** Methods with no required argument that return a bare type parameter of their own, sorted. */
|
|
1047
|
+
parse: string[];
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Which part of what a call returns holds the response the server sent:
|
|
1051
|
+
* exactly one of the whole awaited return, one member of it, or the awaited
|
|
1052
|
+
* return of one of its parse methods (carrick#2817).
|
|
1053
|
+
*/
|
|
1054
|
+
export type ResultShape = {
|
|
1055
|
+
whole: true;
|
|
1056
|
+
} | {
|
|
1057
|
+
member: [string];
|
|
1058
|
+
} | {
|
|
1059
|
+
parse: string;
|
|
1060
|
+
};
|
|
1009
1061
|
export interface SurfaceMember {
|
|
1010
1062
|
name: string;
|
|
1011
1063
|
/** Declared by the receiver's home packages (not only inherited from another package). */
|
|
@@ -303,12 +303,21 @@ export function mapBack(pos, insertions) {
|
|
|
303
303
|
}
|
|
304
304
|
return pos - shift;
|
|
305
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Diagnostics about how the program was assembled (a file outside `rootDir`,
|
|
308
|
+
* a file missing from a composite project's list, an output that would
|
|
309
|
+
* overwrite its input), not about the text of the file they are reported on.
|
|
310
|
+
* The compiler attaches them to an import or a root file, and whether one is
|
|
311
|
+
* present depends on which program build ran last, so a before/after
|
|
312
|
+
* comparison would read it as added by a rewrite (carrick#2898).
|
|
313
|
+
*/
|
|
314
|
+
const PROGRAM_ASSEMBLY_CODES = new Set([6059, 6305, 6307]);
|
|
306
315
|
/** Syntactic and semantic diagnostics reported in this file. */
|
|
307
316
|
export function fileDiagnostics(sourceFile) {
|
|
308
317
|
const program = sourceFile.getProject().getProgram().compilerObject;
|
|
309
318
|
const node = sourceFile.compilerNode;
|
|
310
319
|
return [...program.getSyntacticDiagnostics(node), ...program.getSemanticDiagnostics(node)]
|
|
311
|
-
.filter((d) => d.file === node && d.start !== undefined)
|
|
320
|
+
.filter((d) => d.file === node && d.start !== undefined && !PROGRAM_ASSEMBLY_CODES.has(d.code))
|
|
312
321
|
.map((d) => ({
|
|
313
322
|
start: d.start,
|
|
314
323
|
code: d.code,
|