carrick 0.3.123 → 0.3.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/sidecar/dist/src/capture/machinery.js +6 -0
- package/sidecar/dist/src/index.js +1 -1
- package/sidecar/dist/src/library-claims.d.ts +1 -1
- package/sidecar/dist/src/library-claims.js +170 -11
- package/sidecar/dist/src/position-rows.d.ts +44 -0
- package/sidecar/dist/src/position-rows.js +6 -4
- package/sidecar/dist/src/type-inferrer.js +21 -0
- package/sidecar/dist/src/types.d.ts +32 -0
- package/sidecar/dist/src/validators.d.ts +392 -60
- package/sidecar/dist/src/validators.js +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.124",
|
|
4
4
|
"carrickCache": {
|
|
5
5
|
"cache_version": 22,
|
|
6
6
|
"intent_cache_version": 2
|
|
@@ -113,11 +113,11 @@
|
|
|
113
113
|
"zod": "^3.23.0"
|
|
114
114
|
},
|
|
115
115
|
"optionalDependencies": {
|
|
116
|
-
"@carrick-tools/cli-darwin-arm64": "0.3.
|
|
117
|
-
"@carrick-tools/cli-darwin-x64": "0.3.
|
|
118
|
-
"@carrick-tools/cli-linux-arm64": "0.3.
|
|
119
|
-
"@carrick-tools/cli-linux-x64": "0.3.
|
|
120
|
-
"@carrick-tools/cli-win32-x64": "0.3.
|
|
116
|
+
"@carrick-tools/cli-darwin-arm64": "0.3.124",
|
|
117
|
+
"@carrick-tools/cli-darwin-x64": "0.3.124",
|
|
118
|
+
"@carrick-tools/cli-linux-arm64": "0.3.124",
|
|
119
|
+
"@carrick-tools/cli-linux-x64": "0.3.124",
|
|
120
|
+
"@carrick-tools/cli-win32-x64": "0.3.124"
|
|
121
121
|
},
|
|
122
122
|
"devDependencies": {
|
|
123
123
|
"@types/node": "^24.13.3"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
3
|
"description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.124",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Carrick",
|
|
7
7
|
"email": "hello@carrick.tools"
|
|
@@ -39,6 +39,12 @@ export const MACHINERY_MEMBER_INDICATORS = new Set([
|
|
|
39
39
|
'clone',
|
|
40
40
|
'json',
|
|
41
41
|
'statusText',
|
|
42
|
+
// FROZEN (old path, 2026-10-21): the reply-object half below only (the
|
|
43
|
+
// fetch half above and the threshold stay). Deletions and safety fixes
|
|
44
|
+
// only. Fixes for wrong or missing types go in labelled reply objects
|
|
45
|
+
// (carrick#2472 C4) and the language library rows (carrick#2445). Delete
|
|
46
|
+
// when a delete-and-replay on the four repos moves no row wrongly, after
|
|
47
|
+
// carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
|
|
42
48
|
// Node http ServerResponse / reply-object surface
|
|
43
49
|
'statusCode',
|
|
44
50
|
'statusMessage',
|
|
@@ -442,7 +442,7 @@ function handleVerifyLibraryClaims(request) {
|
|
|
442
442
|
writeProgress(request.request_id, 'verify_library_claims', 'program ready');
|
|
443
443
|
const fromDir = path.resolve(projectLoader.getRepoRoot(), request.from_dir);
|
|
444
444
|
log(`Verifying ${request.checks.length} library claim(s) from ${fromDir}`);
|
|
445
|
-
const { semantics, modules } = claimsVerifier.run(fromDir, request.checks, request.budget_ms ?? SEMANTICS_BUDGET_MS);
|
|
445
|
+
const { semantics, modules } = claimsVerifier.run(fromDir, request.checks, request.budget_ms ?? SEMANTICS_BUDGET_MS, request.scan_root);
|
|
446
446
|
return {
|
|
447
447
|
request_id: request.request_id,
|
|
448
448
|
status: 'success',
|
|
@@ -63,7 +63,7 @@ export declare class LibraryClaimsVerifier {
|
|
|
63
63
|
* A check on an instance or a scope reads the maker or scope claims of the
|
|
64
64
|
* same request first, whatever their place in it.
|
|
65
65
|
*/
|
|
66
|
-
run(fromDir: string, checks: LibraryCheck[], budgetMs: number): VerifyResult;
|
|
66
|
+
run(fromDir: string, checks: LibraryCheck[], budgetMs: number, scanRoot?: string): VerifyResult;
|
|
67
67
|
/**
|
|
68
68
|
* Each specifier's declared surface, read with the verifier's predicates
|
|
69
69
|
* (carrick#1660): every value export (or only those `only` names for the
|
|
@@ -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 { PathNames, declaringFileName } from './position-rows.js';
|
|
42
43
|
/** The methods a `verb` claim may name, and a method key may accept. */
|
|
43
44
|
const HTTP_METHODS = new Set([
|
|
44
45
|
'GET',
|
|
@@ -130,8 +131,9 @@ const HTTP_V2_RULES = { http: false, message: true, ops: new Set(['request']), m
|
|
|
130
131
|
const SERVER_RULES = { http: false, message: true, ops: new Set(['receive']), methods: ROUTE_METHODS };
|
|
131
132
|
/**
|
|
132
133
|
* The role table: the role alone picks the checks. A role this build does
|
|
133
|
-
* not read yet (GraphQL executors
|
|
134
|
-
*
|
|
134
|
+
* not read yet (GraphQL executors) answers `role_unsupported`, and `none`
|
|
135
|
+
* has nothing on the wire to check. A server's mounts are scope claims
|
|
136
|
+
* (`mounts`, #2474 R0).
|
|
135
137
|
*/
|
|
136
138
|
const ROLE_TABLE = {
|
|
137
139
|
http_client: HTTP_RULES,
|
|
@@ -238,6 +240,13 @@ export function httpCheck(check) {
|
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
}
|
|
243
|
+
/** Declarations in a fixed order, each once. */
|
|
244
|
+
function sortedDeclarations(declarations) {
|
|
245
|
+
const seen = new Map();
|
|
246
|
+
for (const each of declarations)
|
|
247
|
+
seen.set(`${each.decl_file}:${each.decl_line}`, each);
|
|
248
|
+
return [...seen.values()].sort((a, b) => (a.decl_file < b.decl_file ? -1 : a.decl_file > b.decl_file ? 1 : a.decl_line - b.decl_line));
|
|
249
|
+
}
|
|
241
250
|
let probeSequence = 0;
|
|
242
251
|
export class LibraryClaimsVerifier {
|
|
243
252
|
project;
|
|
@@ -250,10 +259,15 @@ export class LibraryClaimsVerifier {
|
|
|
250
259
|
* A check on an instance or a scope reads the maker or scope claims of the
|
|
251
260
|
* same request first, whatever their place in it.
|
|
252
261
|
*/
|
|
253
|
-
run(fromDir, checks, budgetMs) {
|
|
262
|
+
run(fromDir, checks, budgetMs, scanRoot) {
|
|
254
263
|
const deadline = performance.now() + budgetMs;
|
|
255
264
|
const stamp = (check, outcome) => outcome.verdict === 'verified'
|
|
256
|
-
? {
|
|
265
|
+
? {
|
|
266
|
+
claim_id: check.claim_id,
|
|
267
|
+
receiver: check.receiver,
|
|
268
|
+
verdict: 'verified',
|
|
269
|
+
...(outcome.declarations?.length ? { declarations: outcome.declarations } : {}),
|
|
270
|
+
}
|
|
257
271
|
: {
|
|
258
272
|
claim_id: check.claim_id,
|
|
259
273
|
receiver: check.receiver,
|
|
@@ -330,7 +344,7 @@ export class LibraryClaimsVerifier {
|
|
|
330
344
|
const file = program.getSourceFile(probe.getFilePath());
|
|
331
345
|
if (!file)
|
|
332
346
|
throw new Error(`probe file ${probePath} is not in the program`);
|
|
333
|
-
const reader = new DeclarationReader(program, file, this.project.getModuleResolutionHost(), fromDir);
|
|
347
|
+
const reader = new DeclarationReader(program, file, this.project.getModuleResolutionHost(), fromDir, scanRoot);
|
|
334
348
|
const localRanges = readLocalRanges(fromDir);
|
|
335
349
|
const moduleReads = new Map();
|
|
336
350
|
const exportReads = new Map();
|
|
@@ -501,13 +515,15 @@ export class LibraryClaimsVerifier {
|
|
|
501
515
|
const built = builtReceiver(check);
|
|
502
516
|
const builtAtDefaults = built === undefined ? undefined : atDefaults(check, built);
|
|
503
517
|
const holding = [];
|
|
518
|
+
const declarations = [];
|
|
504
519
|
for (const receiver of receivers.value) {
|
|
505
520
|
const result = reader.judgeMessage(receiver, check.claim, rules, builtAtDefaults);
|
|
506
521
|
if (result.outcome.verdict !== 'verified')
|
|
507
522
|
return result;
|
|
508
523
|
holding.push(...(result.holding ?? []));
|
|
524
|
+
declarations.push(...(result.outcome.declarations ?? []));
|
|
509
525
|
}
|
|
510
|
-
return { outcome: VERIFIED, holding };
|
|
526
|
+
return { outcome: declarations.length > 0 ? { verdict: 'verified', declarations: sortedDeclarations(declarations) } : VERIFIED, holding };
|
|
511
527
|
};
|
|
512
528
|
const semantics = [];
|
|
513
529
|
for (let index = 0; index < checks.length; index++) {
|
|
@@ -695,6 +711,19 @@ function placementInvalid(claim) {
|
|
|
695
711
|
const of = parseReceiver(claim.of);
|
|
696
712
|
return of === undefined || (of.maker === undefined && of.scope === undefined);
|
|
697
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Where a handler's request holds its path and method (#2474 R0) is read on
|
|
716
|
+
* a receive op with a handler, and names `path` or `url`; every member path
|
|
717
|
+
* it gives is non-empty.
|
|
718
|
+
*/
|
|
719
|
+
function requestAtValid(claim) {
|
|
720
|
+
const request = claim.request;
|
|
721
|
+
if (claim.op !== 'receive' || claim.handler === undefined)
|
|
722
|
+
return false;
|
|
723
|
+
if (request.path === undefined && request.url === undefined)
|
|
724
|
+
return false;
|
|
725
|
+
return [request.path, request.url, request.method].every(members => members === undefined || members.length > 0);
|
|
726
|
+
}
|
|
698
727
|
/**
|
|
699
728
|
* A message check names a receiver its claim can be read on (contract
|
|
700
729
|
* amendment 2, B3). A maker is read on the export itself. An element with
|
|
@@ -841,6 +870,8 @@ class DeclarationReader {
|
|
|
841
870
|
probe;
|
|
842
871
|
host;
|
|
843
872
|
checker;
|
|
873
|
+
/** Names declaring files as position rows do: from the scan root, else the service root. */
|
|
874
|
+
pathNames;
|
|
844
875
|
/** The service root, as a realpath. */
|
|
845
876
|
root;
|
|
846
877
|
packageDirectories = new Map();
|
|
@@ -853,11 +884,12 @@ class DeclarationReader {
|
|
|
853
884
|
declared = new Map();
|
|
854
885
|
/** The service root as given and as a realpath, longest first: what a listing prints as `<root>`. */
|
|
855
886
|
printedRoots;
|
|
856
|
-
constructor(program, probe, host, serviceRoot) {
|
|
887
|
+
constructor(program, probe, host, serviceRoot, scanRoot) {
|
|
857
888
|
this.program = program;
|
|
858
889
|
this.probe = probe;
|
|
859
890
|
this.host = host;
|
|
860
891
|
this.checker = program.getTypeChecker();
|
|
892
|
+
this.pathNames = new PathNames(scanRoot ?? serviceRoot);
|
|
861
893
|
this.root = this.realpath(serviceRoot);
|
|
862
894
|
this.printedRoots = [...new Set([this.root, serviceRoot])]
|
|
863
895
|
.filter(root => root.length > 1)
|
|
@@ -1472,9 +1504,26 @@ class DeclarationReader {
|
|
|
1472
1504
|
* `member` (after `path`) is a home-declared callable member whose name
|
|
1473
1505
|
* slot accepts a string, under the name-slot rules, and returns a type that
|
|
1474
1506
|
* says something, read at its type-parameter defaults (`returnOf`).
|
|
1507
|
+
*
|
|
1508
|
+
* Route fields (#2474 R0, contract amendment of 2026-10-22 on carrick#2434):
|
|
1509
|
+
* - `runs` with `yields`: the member runs the function at `runs` (a
|
|
1510
|
+
* function with a declared signature) and hands it, at `yields.param`,
|
|
1511
|
+
* an instance of the package's export `yields.export` (`yieldsOutcome`);
|
|
1512
|
+
* - `mounts`: the receiver the claim is read on fits the parameter there
|
|
1513
|
+
* (`mountsOutcome`).
|
|
1514
|
+
* Either one binds its instance through a parameter or an argument, not
|
|
1515
|
+
* the member's return, so the return is not read and the claim holds no
|
|
1516
|
+
* overloads: a `>scope:` receiver read through it is not resolved.
|
|
1517
|
+
* `runs` without `yields` (or the reverse), or `mounts` beside either, is
|
|
1518
|
+
* `claim_invalid`.
|
|
1475
1519
|
*/
|
|
1476
1520
|
judgeScope(receiver, claim, built) {
|
|
1477
|
-
|
|
1521
|
+
if ((claim.runs === undefined) !== (claim.yields === undefined))
|
|
1522
|
+
return { outcome: unchecked('claim_invalid') };
|
|
1523
|
+
if (claim.mounts !== undefined && claim.runs !== undefined)
|
|
1524
|
+
return { outcome: unchecked('claim_invalid') };
|
|
1525
|
+
const binds = claim.runs !== undefined || claim.mounts !== undefined;
|
|
1526
|
+
const layout = this.layout({ name: claim.name, runs: claim.runs, mounts: claim.mounts });
|
|
1478
1527
|
if ('failure' in layout)
|
|
1479
1528
|
return { outcome: layout.failure };
|
|
1480
1529
|
const callee = this.ownCallee(receiver, claim.path ?? [], claim.member, 'call');
|
|
@@ -1485,7 +1534,17 @@ class DeclarationReader {
|
|
|
1485
1534
|
let best;
|
|
1486
1535
|
for (const signature of callee.signatures) {
|
|
1487
1536
|
let result = this.messageSignatureOutcome(signature, layout.value, labels);
|
|
1488
|
-
if (result.rank === Infinity &&
|
|
1537
|
+
if (result.rank === Infinity && claim.yields !== undefined) {
|
|
1538
|
+
const given = this.yieldsOutcome(signature, claim.runs, claim.yields, receiver);
|
|
1539
|
+
if (given)
|
|
1540
|
+
result = { rank: 5, outcome: given };
|
|
1541
|
+
}
|
|
1542
|
+
if (result.rank === Infinity && claim.mounts !== undefined) {
|
|
1543
|
+
const mounted = this.mountsOutcome(signature, claim.mounts, receiver);
|
|
1544
|
+
if (mounted)
|
|
1545
|
+
result = { rank: 5, outcome: mounted };
|
|
1546
|
+
}
|
|
1547
|
+
if (result.rank === Infinity && !binds && this.returnSaysNothing(this.returnOf(signature, built))) {
|
|
1489
1548
|
result = { rank: 6, outcome: unchecked('scope_unresolved') };
|
|
1490
1549
|
}
|
|
1491
1550
|
if (result.rank === Infinity)
|
|
@@ -1494,9 +1553,38 @@ class DeclarationReader {
|
|
|
1494
1553
|
best = furthest(best, result.rank, result.outcome);
|
|
1495
1554
|
}
|
|
1496
1555
|
if (holding.length > 0)
|
|
1497
|
-
return { outcome: VERIFIED, holding };
|
|
1556
|
+
return binds ? { outcome: VERIFIED } : { outcome: VERIFIED, holding };
|
|
1498
1557
|
return { outcome: best.outcome };
|
|
1499
1558
|
}
|
|
1559
|
+
/**
|
|
1560
|
+
* `mounts: { arg, key? }` on a scope (#2474 R0): the receiver the claim is
|
|
1561
|
+
* read on is assignable to the parameter there, so an instance like it can
|
|
1562
|
+
* be served under the scope's name. A parameter typed `any`, `unknown`,
|
|
1563
|
+
* `{}` or by an unconstrained type parameter says nothing
|
|
1564
|
+
* (`mounts_untyped`). There is no separate "not a function" rule: a router
|
|
1565
|
+
* type that is itself a handler is a common shape, and a parameter only a
|
|
1566
|
+
* function fits fails here because the instance does not fit it.
|
|
1567
|
+
* Undefined when it holds.
|
|
1568
|
+
*/
|
|
1569
|
+
mountsOutcome(signature, mounts, receiver) {
|
|
1570
|
+
let declared = this.keyParameterAt(signature, mounts.arg);
|
|
1571
|
+
if (declared !== undefined && mounts.key !== undefined) {
|
|
1572
|
+
const property = this.declaredProperty(declared, mounts.key);
|
|
1573
|
+
declared = property ? this.checker.getTypeOfSymbol(property) : undefined;
|
|
1574
|
+
}
|
|
1575
|
+
if (declared === undefined)
|
|
1576
|
+
return failed('param_missing');
|
|
1577
|
+
const slot = this.throughConstraint(declared);
|
|
1578
|
+
if (slot === VARIADIC)
|
|
1579
|
+
return unchecked('mounts_untyped');
|
|
1580
|
+
const parts = this.parts(slot);
|
|
1581
|
+
if (parts.length === 0)
|
|
1582
|
+
return failed('mounts_not_instance');
|
|
1583
|
+
if (parts.some(part => this.isOpenTop(part) || this.isUnconstrained(part) || this.isEmptyObject(part))) {
|
|
1584
|
+
return unchecked('mounts_untyped');
|
|
1585
|
+
}
|
|
1586
|
+
return this.checker.isTypeAssignableTo(receiver.type, slot) ? undefined : failed('mounts_not_instance');
|
|
1587
|
+
}
|
|
1500
1588
|
/**
|
|
1501
1589
|
* A send, receive or request op: the home-declared callable member (after
|
|
1502
1590
|
* `path`; or the receiver itself, when null) with some overload that takes
|
|
@@ -1513,6 +1601,9 @@ class DeclarationReader {
|
|
|
1513
1601
|
if (claim.yields !== undefined && (claim.op !== 'receive' || claim.handler === undefined)) {
|
|
1514
1602
|
return unchecked('claim_invalid');
|
|
1515
1603
|
}
|
|
1604
|
+
// So is where the handler's request holds its path and method (#2474 R0).
|
|
1605
|
+
if (claim.request !== undefined && !requestAtValid(claim))
|
|
1606
|
+
return unchecked('claim_invalid');
|
|
1516
1607
|
const name = claim.name;
|
|
1517
1608
|
// A send carries a payload: a name alone on `send(data)` is the data. An
|
|
1518
1609
|
// op that runs a function or client it is handed takes its name from
|
|
@@ -1562,6 +1653,11 @@ class DeclarationReader {
|
|
|
1562
1653
|
if (given)
|
|
1563
1654
|
result = { rank: 5, outcome: given };
|
|
1564
1655
|
}
|
|
1656
|
+
if (result.rank === Infinity && claim.request !== undefined) {
|
|
1657
|
+
const read = this.requestOutcome(signature, claim.handler, claim.request);
|
|
1658
|
+
if (read)
|
|
1659
|
+
result = { rank: 5, outcome: read };
|
|
1660
|
+
}
|
|
1565
1661
|
if (result.rank === Infinity) {
|
|
1566
1662
|
held = true;
|
|
1567
1663
|
break;
|
|
@@ -1572,7 +1668,28 @@ class DeclarationReader {
|
|
|
1572
1668
|
return best.outcome;
|
|
1573
1669
|
if (boundRuns)
|
|
1574
1670
|
return this.boundRunsOutcome(receiver.makerHolding, boundRuns);
|
|
1575
|
-
|
|
1671
|
+
const declarations = this.declarationsOf(callee.signatures);
|
|
1672
|
+
return declarations.length > 0 ? { verdict: 'verified', declarations } : VERIFIED;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Where the signatures a verified op resolved to are declared, as position
|
|
1676
|
+
* rows spell it (`decl_file`, `decl_line`): the member on this receiver, so
|
|
1677
|
+
* two members of one name on two types give two answers. A declaration no
|
|
1678
|
+
* row could name is left out.
|
|
1679
|
+
*/
|
|
1680
|
+
declarationsOf(signatures) {
|
|
1681
|
+
const found = [];
|
|
1682
|
+
for (const signature of signatures) {
|
|
1683
|
+
const declaration = signature.getDeclaration();
|
|
1684
|
+
if (!declaration)
|
|
1685
|
+
continue;
|
|
1686
|
+
const file = declaration.getSourceFile();
|
|
1687
|
+
const decl_file = declaringFileName(this.program, this.pathNames, file);
|
|
1688
|
+
if (decl_file === null)
|
|
1689
|
+
continue;
|
|
1690
|
+
found.push({ decl_file, decl_line: file.getLineAndCharacterOfPosition(declaration.getStart(file)).line + 1 });
|
|
1691
|
+
}
|
|
1692
|
+
return sortedDeclarations(found);
|
|
1576
1693
|
}
|
|
1577
1694
|
/**
|
|
1578
1695
|
* The method fields of an op (label structure v2, carrick#2434), under the
|
|
@@ -1680,6 +1797,48 @@ class DeclarationReader {
|
|
|
1680
1797
|
}
|
|
1681
1798
|
return failed('yields_not_export');
|
|
1682
1799
|
}
|
|
1800
|
+
/**
|
|
1801
|
+
* `request: { param, path?, url?, method? }` on a receive op (#2474 R0):
|
|
1802
|
+
* some signature of the handler this overload takes declares a parameter
|
|
1803
|
+
* at `param` on which every member path the claim gives resolves, step by
|
|
1804
|
+
* step, through properties the library declares (`declaredProperty`).
|
|
1805
|
+
* Undefined when it holds.
|
|
1806
|
+
*/
|
|
1807
|
+
requestOutcome(signature, handler, request) {
|
|
1808
|
+
let declared = this.keyParameterAt(signature, handler.arg);
|
|
1809
|
+
if (declared !== undefined && handler.key !== undefined) {
|
|
1810
|
+
const property = this.declaredProperty(declared, handler.key);
|
|
1811
|
+
declared = property ? this.checker.getTypeOfSymbol(property) : undefined;
|
|
1812
|
+
}
|
|
1813
|
+
if (declared === undefined)
|
|
1814
|
+
return failed('param_missing');
|
|
1815
|
+
const slot = this.throughConditional(this.throughConstraint(declared));
|
|
1816
|
+
if (slot === VARIADIC)
|
|
1817
|
+
return unchecked('handler_untyped');
|
|
1818
|
+
const paths = [request.path, request.url, request.method].filter((p) => p !== undefined);
|
|
1819
|
+
let best = failed('param_missing');
|
|
1820
|
+
for (const part of this.parts(slot)) {
|
|
1821
|
+
for (const handed of part.getCallSignatures()) {
|
|
1822
|
+
const given = this.parameterAt(handed, request.param);
|
|
1823
|
+
if (given === undefined || given === VARIADIC)
|
|
1824
|
+
continue;
|
|
1825
|
+
const resolves = paths.every(members => {
|
|
1826
|
+
let at = given;
|
|
1827
|
+
for (const member of members) {
|
|
1828
|
+
const property = this.declaredProperty(at, member);
|
|
1829
|
+
if (!property)
|
|
1830
|
+
return false;
|
|
1831
|
+
at = this.checker.getTypeOfSymbol(property);
|
|
1832
|
+
}
|
|
1833
|
+
return true;
|
|
1834
|
+
});
|
|
1835
|
+
if (resolves)
|
|
1836
|
+
return undefined;
|
|
1837
|
+
best = failed('request_member_missing');
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return best;
|
|
1841
|
+
}
|
|
1683
1842
|
/**
|
|
1684
1843
|
* The declarations spell `name` in one of `member`'s parameters: an
|
|
1685
1844
|
* overload whose parameter there is that literal, or a key of the map a
|
|
@@ -107,5 +107,49 @@ export interface PositionRowsOptions {
|
|
|
107
107
|
* a directory inside the scanned trees anywhere but beneath `.carrick`.
|
|
108
108
|
*/
|
|
109
109
|
export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
|
|
110
|
+
/** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
|
|
111
|
+
export declare function declaringFileName(program: ts.Program, paths: PathNames, file: ts.SourceFile): string | null;
|
|
112
|
+
/**
|
|
113
|
+
* Names for the files rows point at, never an absolute path: relative to the
|
|
114
|
+
* scanned repo's root when inside it (its real path too, so a root reached
|
|
115
|
+
* through a link still contains its files), `lib:<name>` for the compiler's
|
|
116
|
+
* default library, `pkg:<name>/<path>` for an installed package outside the
|
|
117
|
+
* root, and nothing otherwise.
|
|
118
|
+
*/
|
|
119
|
+
export declare class PathNames {
|
|
120
|
+
private readonly roots;
|
|
121
|
+
private readonly packageNames;
|
|
122
|
+
/**
|
|
123
|
+
* Answers by declaring file: a service's rows name a few thousand files
|
|
124
|
+
* hundreds of thousands of times, and the module graph's answer is a scan
|
|
125
|
+
* of its packages (on a Deno service it was 40% of the walk).
|
|
126
|
+
*/
|
|
127
|
+
private readonly declaringFiles;
|
|
128
|
+
private readonly declaringPackages;
|
|
129
|
+
constructor(scanRoot: string);
|
|
130
|
+
/** The root `file` lies under, and its path from there; `undefined` outside every root. */
|
|
131
|
+
private inside;
|
|
132
|
+
/** A request file's name: relative to the root, which every file the scanner sends is inside. */
|
|
133
|
+
relative(file: string): string;
|
|
134
|
+
/** What a declaring file is written as, or `null` when nothing names it. */
|
|
135
|
+
declaringFile(fileName: string): string | null;
|
|
136
|
+
private nameDeclaringFile;
|
|
137
|
+
/**
|
|
138
|
+
* The package a declaring file belongs to: the module graph's or the path's
|
|
139
|
+
* answer (`installedPackageOf`), else, for a file inside the root, the
|
|
140
|
+
* `name` of the nearest package.json at or above it, up to the root.
|
|
141
|
+
*/
|
|
142
|
+
declaringPackage(fileName: string, packageOf: (filePath: string) => string | undefined): string | null;
|
|
143
|
+
private nameDeclaringPackage;
|
|
144
|
+
private packageNameIn;
|
|
145
|
+
/**
|
|
146
|
+
* `import("/abs/path")` in a printed type, written with the declaring
|
|
147
|
+
* file's name instead. The print has no enclosing node, so it names every
|
|
148
|
+
* module by the file that declares it and one type reads the same from
|
|
149
|
+
* every file; it also skips the accessibility search an enclosing node
|
|
150
|
+
* costs (40% of the walk on a 600-file service).
|
|
151
|
+
*/
|
|
152
|
+
scrubType(text: string): string;
|
|
153
|
+
}
|
|
110
154
|
/** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
|
|
111
155
|
export declare function kindOf(node: ts.Node): NodeKind | undefined;
|
|
@@ -242,6 +242,10 @@ export function writePositionRows(options) {
|
|
|
242
242
|
counts.load_ms = Math.round(counts.load_ms);
|
|
243
243
|
return counts;
|
|
244
244
|
}
|
|
245
|
+
/** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
|
|
246
|
+
export function declaringFileName(program, paths, file) {
|
|
247
|
+
return program.isSourceFileDefaultLibrary(file) ? `lib:${path.basename(file.fileName)}` : paths.declaringFile(file.fileName);
|
|
248
|
+
}
|
|
245
249
|
/** A file's line, with its keys in the pinned order. */
|
|
246
250
|
function fileLine(line) {
|
|
247
251
|
return JSON.stringify(line);
|
|
@@ -253,7 +257,7 @@ function fileLine(line) {
|
|
|
253
257
|
* default library, `pkg:<name>/<path>` for an installed package outside the
|
|
254
258
|
* root, and nothing otherwise.
|
|
255
259
|
*/
|
|
256
|
-
class PathNames {
|
|
260
|
+
export class PathNames {
|
|
257
261
|
roots;
|
|
258
262
|
packageNames = new Map();
|
|
259
263
|
/**
|
|
@@ -466,9 +470,7 @@ class PositionWalker {
|
|
|
466
470
|
}
|
|
467
471
|
/** How rows name a position in a program file: its file, 1-based line and package. */
|
|
468
472
|
located(declaringFile, position) {
|
|
469
|
-
const declFile = this.program.
|
|
470
|
-
? `lib:${path.basename(declaringFile.fileName)}`
|
|
471
|
-
: this.paths.declaringFile(declaringFile.fileName);
|
|
473
|
+
const declFile = declaringFileName(this.program, this.paths, declaringFile);
|
|
472
474
|
const declLine = declaringFile.getLineAndCharacterOfPosition(position).line + 1;
|
|
473
475
|
const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
|
|
474
476
|
return { declFile, declLine, declPackage };
|
|
@@ -115,6 +115,12 @@ export const MACHINERY_MEMBER_INDICATORS = new Set([
|
|
|
115
115
|
'clone',
|
|
116
116
|
'json',
|
|
117
117
|
'statusText',
|
|
118
|
+
// FROZEN (old path, 2026-10-21): the reply-object half below only (the
|
|
119
|
+
// fetch half above and the threshold stay). Deletions and safety fixes
|
|
120
|
+
// only. Fixes for wrong or missing types go in labelled reply objects
|
|
121
|
+
// (carrick#2472 C4) and the language library rows (carrick#2445). Delete
|
|
122
|
+
// when a delete-and-replay on the four repos moves no row wrongly, after
|
|
123
|
+
// carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
|
|
118
124
|
// Node http ServerResponse / reply-object surface
|
|
119
125
|
'statusCode',
|
|
120
126
|
'statusMessage',
|
|
@@ -1628,6 +1634,8 @@ export class TypeInferrer {
|
|
|
1628
1634
|
branches = [
|
|
1629
1635
|
...returnedBranches,
|
|
1630
1636
|
...statementSends
|
|
1637
|
+
// FROZEN (old path, 2026-10-21): the `statementEndsFunctionPath` test
|
|
1638
|
+
// here; see that method.
|
|
1631
1639
|
.filter((s) => s.send === locatedBranch || this.statementEndsFunctionPath(s.statement, func))
|
|
1632
1640
|
.map((s) => s.send),
|
|
1633
1641
|
];
|
|
@@ -1702,6 +1710,10 @@ export class TypeInferrer {
|
|
|
1702
1710
|
}
|
|
1703
1711
|
return sends;
|
|
1704
1712
|
}
|
|
1713
|
+
// FROZEN (old path, 2026-10-21): this method and its one use. Deletions and
|
|
1714
|
+
// safety fixes only. Fixes for wrong or missing types go in send versus
|
|
1715
|
+
// setter, read from the framework label (carrick#2472 C4). Delete when C4 is
|
|
1716
|
+
// released (carrick#2472, C6).
|
|
1705
1717
|
/**
|
|
1706
1718
|
* True when nothing in `func` runs after `statement`: a bare `return;`
|
|
1707
1719
|
* follows it, or it is the last statement of its block and the statement
|
|
@@ -1748,6 +1760,11 @@ export class TypeInferrer {
|
|
|
1748
1760
|
return false;
|
|
1749
1761
|
}
|
|
1750
1762
|
}
|
|
1763
|
+
// FROZEN (old path, 2026-10-21): this method's structural test (the whole
|
|
1764
|
+
// method is the upper bound). Deletions and safety fixes only. Fixes for
|
|
1765
|
+
// wrong or missing types go in labelled sends (carrick#2472 C4). Delete when
|
|
1766
|
+
// a delete-and-replay on the four repos moves no row wrongly, after
|
|
1767
|
+
// carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
|
|
1751
1768
|
/**
|
|
1752
1769
|
* True when `candidate` is a response send: a call or `new` whose result is
|
|
1753
1770
|
* transport machinery (by the extraction config's verified rule or the
|
|
@@ -5288,6 +5305,10 @@ export class TypeInferrer {
|
|
|
5288
5305
|
}
|
|
5289
5306
|
return flattened;
|
|
5290
5307
|
}
|
|
5308
|
+
// FROZEN (old path, 2026-10-21): this method only. Deletions and safety fixes
|
|
5309
|
+
// only. Fixes for wrong or missing types go in labelled sends (carrick#2472
|
|
5310
|
+
// C4). Delete when a delete-and-replay on the four repos moves no row
|
|
5311
|
+
// wrongly, after carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
|
|
5291
5312
|
/**
|
|
5292
5313
|
* The callees these returned expressions PROVE are response serialisers.
|
|
5293
5314
|
*
|
|
@@ -411,6 +411,18 @@ export interface Yields {
|
|
|
411
411
|
export: string;
|
|
412
412
|
side: string;
|
|
413
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Where a route handler's request holds its path and method (#2474 R0): the
|
|
416
|
+
* handler's parameter `param` is the request, and each of the others is a
|
|
417
|
+
* member path read off it, relative to that parameter (`['path']`,
|
|
418
|
+
* `['raw', 'url']`). At least one of `path` or `url`.
|
|
419
|
+
*/
|
|
420
|
+
export interface RequestAt {
|
|
421
|
+
param: number;
|
|
422
|
+
path?: string[];
|
|
423
|
+
url?: string[];
|
|
424
|
+
method?: string[];
|
|
425
|
+
}
|
|
414
426
|
/** The closed role list; the role alone picks the checks a claim needs. */
|
|
415
427
|
export type LibraryRole = 'http_client' | 'graphql_client' | 'broker' | 'in_process_bus' | 'socket' | 'server_framework' | 'none';
|
|
416
428
|
/** What an `op` claim does on the wire. */
|
|
@@ -467,6 +479,15 @@ export type LibraryClaim = {
|
|
|
467
479
|
key_labels?: Record<string, KeyLabel>;
|
|
468
480
|
name_scope?: NameScope;
|
|
469
481
|
picker?: string;
|
|
482
|
+
/**
|
|
483
|
+
* Route fields (#2474 R0): the member runs the function here and hands
|
|
484
|
+
* it, as `yields` says, an instance bound under `name`. Comes with
|
|
485
|
+
* `yields`.
|
|
486
|
+
*/
|
|
487
|
+
runs?: ClaimSlot;
|
|
488
|
+
yields?: Yields;
|
|
489
|
+
/** Route fields (#2474 R0): the instance handed here is served under `name`. */
|
|
490
|
+
mounts?: ClaimSlot;
|
|
470
491
|
} | {
|
|
471
492
|
kind: 'op';
|
|
472
493
|
op: LibraryOp;
|
|
@@ -502,6 +523,8 @@ export type LibraryClaim = {
|
|
|
502
523
|
runs?: RunsSlot;
|
|
503
524
|
/** Label v2, `receive` only: what the handler is handed. */
|
|
504
525
|
yields?: Yields;
|
|
526
|
+
/** Route fields (#2474 R0), `receive` only: where the handler's request holds its path and method. */
|
|
527
|
+
request?: RequestAt;
|
|
505
528
|
} | {
|
|
506
529
|
/** A name the library emits itself, spelled in one of `member`'s parameters. */
|
|
507
530
|
kind: 'reserved';
|
|
@@ -538,6 +561,8 @@ export interface LibraryCheck {
|
|
|
538
561
|
export interface VerifyLibraryClaimsRequest extends BaseRequest {
|
|
539
562
|
action: 'verify_library_claims';
|
|
540
563
|
from_dir: string;
|
|
564
|
+
/** The scanned repo's root, as on `init`: a verdict's `declarations` are named from it, as position rows are. */
|
|
565
|
+
scan_root?: string;
|
|
541
566
|
checks: LibraryCheck[];
|
|
542
567
|
/** Checks not reached in time come back `unchecked` with reason `budget`; null or absent is the default. */
|
|
543
568
|
budget_ms?: number | null;
|
|
@@ -825,6 +850,11 @@ export interface RetypeCheckResponse extends BaseResponse {
|
|
|
825
850
|
outcomes?: RetypeOutcome[];
|
|
826
851
|
errors?: string[];
|
|
827
852
|
}
|
|
853
|
+
/** Where a verified op's member is declared on its receiver, as a position row's `decl_file` and `decl_line`. */
|
|
854
|
+
export interface SemanticsDeclaration {
|
|
855
|
+
decl_file: string;
|
|
856
|
+
decl_line: number;
|
|
857
|
+
}
|
|
828
858
|
/**
|
|
829
859
|
* The verdict on one `(claim_id, receiver)` pair.
|
|
830
860
|
*
|
|
@@ -838,6 +868,8 @@ export interface SemanticsResult {
|
|
|
838
868
|
verdict: 'verified' | 'failed' | 'unchecked';
|
|
839
869
|
/** Absent exactly when verified. */
|
|
840
870
|
reason?: string;
|
|
871
|
+
/** Verified op verdicts only: the declarations its member resolves to on the receiver. */
|
|
872
|
+
declarations?: SemanticsDeclaration[];
|
|
841
873
|
}
|
|
842
874
|
/** How one package of the request resolved, for logs. */
|
|
843
875
|
export interface SemanticsModule {
|