carrick 0.3.116 → 0.3.118

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 (42) hide show
  1. package/dist/contract.d.ts +2 -0
  2. package/dist/contract.js.map +1 -1
  3. package/dist/render.js +2 -1
  4. package/dist/render.js.map +1 -1
  5. package/package.json +6 -6
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/sidecar/dist/src/capture/anchors.d.ts +1 -0
  8. package/sidecar/dist/src/capture/anchors.js +1 -1
  9. package/sidecar/dist/src/capture/api.d.ts +6 -0
  10. package/sidecar/dist/src/capture/check-classify.js +25 -0
  11. package/sidecar/dist/src/capture/check-probe.d.ts +1 -1
  12. package/sidecar/dist/src/capture/check-probe.js +20 -0
  13. package/sidecar/dist/src/capture/checkout.d.ts +3 -0
  14. package/sidecar/dist/src/capture/checkout.js +1 -1
  15. package/sidecar/dist/src/capture/deep-walk.d.ts +10 -0
  16. package/sidecar/dist/src/capture/deep-walk.js +15 -0
  17. package/sidecar/dist/src/capture/guarded-fs.d.ts +6 -0
  18. package/sidecar/dist/src/capture/guarded-fs.js +10 -0
  19. package/sidecar/dist/src/capture/index.d.ts +5 -0
  20. package/sidecar/dist/src/capture/index.js +62 -7
  21. package/sidecar/dist/src/capture/project-references.d.ts +24 -13
  22. package/sidecar/dist/src/capture/project-references.js +53 -16
  23. package/sidecar/dist/src/capture/reference-redirect.d.ts +84 -0
  24. package/sidecar/dist/src/capture/reference-redirect.js +209 -0
  25. package/sidecar/dist/src/capture/unresolved.d.ts +8 -0
  26. package/sidecar/dist/src/capture/unresolved.js +18 -2
  27. package/sidecar/dist/src/declaration-attribution.d.ts +40 -0
  28. package/sidecar/dist/src/declaration-attribution.js +66 -0
  29. package/sidecar/dist/src/index.js +37 -0
  30. package/sidecar/dist/src/library-claims.js +234 -23
  31. package/sidecar/dist/src/module-format.d.ts +19 -1
  32. package/sidecar/dist/src/module-format.js +9 -2
  33. package/sidecar/dist/src/position-rows.d.ts +95 -0
  34. package/sidecar/dist/src/position-rows.js +844 -0
  35. package/sidecar/dist/src/project-loader.d.ts +36 -7
  36. package/sidecar/dist/src/project-loader.js +70 -28
  37. package/sidecar/dist/src/retype.js +13 -0
  38. package/sidecar/dist/src/type-inferrer.d.ts +25 -29
  39. package/sidecar/dist/src/type-inferrer.js +119 -70
  40. package/sidecar/dist/src/types.d.ts +78 -2
  41. package/sidecar/dist/src/validators.d.ts +431 -30
  42. package/sidecar/dist/src/validators.js +45 -1
@@ -49,6 +49,8 @@ const HTTP_METHODS = new Set([
49
49
  'HEAD',
50
50
  'OPTIONS',
51
51
  ]);
52
+ /** The methods a route may register: every request method, and `ALL` for a member that registers each. */
53
+ const ROUTE_METHODS = new Set([...HTTP_METHODS, 'ALL']);
52
54
  /**
53
55
  * Interfaces whose members every value of that kind inherits. A key found only
54
56
  * on one of these (`constructor`, `toString`, a string's `length`) is not a
@@ -100,6 +102,18 @@ const MESSAGE_RULES = {
100
102
  message: true,
101
103
  ops: new Set(['send', 'receive', 'request']),
102
104
  };
105
+ /**
106
+ * An `http_client` export whose claims in the request use a label v2 field
107
+ * (`hasV2Field`) is read as the store's other roles are: its makers, `of`
108
+ * and `on`, paths and the name-slot rules, plus the method fields. An export
109
+ * whose claims use none keeps the #1564 checks exactly (`HTTP_RULES`).
110
+ */
111
+ const HTTP_V2_RULES = { http: false, message: true, ops: new Set(['request']), methods: HTTP_METHODS };
112
+ /**
113
+ * A server framework's route members: a `receive` op whose name is the path,
114
+ * the method from the member's own name (`get` registers GET) or from a slot.
115
+ */
116
+ const SERVER_RULES = { http: false, message: true, ops: new Set(['receive']), methods: ROUTE_METHODS };
103
117
  /**
104
118
  * The role table: the role alone picks the checks. A role this build does
105
119
  * not read yet (GraphQL executors, server mounts) answers `role_unsupported`,
@@ -111,9 +125,21 @@ const ROLE_TABLE = {
111
125
  in_process_bus: MESSAGE_RULES,
112
126
  socket: MESSAGE_RULES,
113
127
  graphql_client: undefined,
114
- server_framework: undefined,
128
+ server_framework: SERVER_RULES,
115
129
  none: undefined,
116
130
  };
131
+ /**
132
+ * Whether a claim uses a field label structure v2 added (carrick#2434). An
133
+ * `http_client` export with one such claim in the request is read under
134
+ * `HTTP_V2_RULES`.
135
+ */
136
+ function hasV2Field(claim) {
137
+ if (claim.kind === 'make')
138
+ return claim.runs !== undefined;
139
+ if (claim.kind !== 'op')
140
+ return false;
141
+ return claim.method_default !== undefined || claim.base !== undefined || claim.runs !== undefined || claim.yields !== undefined;
142
+ }
117
143
  const VERIFIED = { verdict: 'verified' };
118
144
  const failed = (reason) => ({ verdict: 'failed', reason });
119
145
  const unchecked = (reason) => ({ verdict: 'unchecked', reason });
@@ -230,6 +256,13 @@ export class LibraryClaimsVerifier {
230
256
  const factoryKeys = new Map();
231
257
  // The roles each (package, export) is given across the request.
232
258
  const roles = new Map();
259
+ // The `http_client` exports read under the label v2 rules.
260
+ const v2Exports = new Set();
261
+ for (const check of checks) {
262
+ if (check.role === 'http_client' && hasV2Field(check.claim))
263
+ v2Exports.add(exportKey(check));
264
+ }
265
+ const rulesFor = (check) => check.role === 'http_client' && v2Exports.has(exportKey(check)) ? HTTP_V2_RULES : ROLE_TABLE[check.role];
233
266
  for (const check of checks) {
234
267
  const key = exportKey(check);
235
268
  if (!importIndex.has(key)) {
@@ -260,7 +293,7 @@ export class LibraryClaimsVerifier {
260
293
  const builtLines = [];
261
294
  const builtIndex = new Map();
262
295
  for (const check of checks) {
263
- if (!ROLE_TABLE[check.role]?.message)
296
+ if (!rulesFor(check)?.message)
264
297
  continue;
265
298
  const built = builtReceiver(check);
266
299
  if (built === undefined)
@@ -326,6 +359,7 @@ export class LibraryClaimsVerifier {
326
359
  const receiverPath = parseReceiver(check.receiver);
327
360
  let types = [exported.type];
328
361
  let makerBindsName = false;
362
+ let makerHolding;
329
363
  if (receiverPath.maker) {
330
364
  const maker = receiverPath.maker;
331
365
  const makers = checks
@@ -336,10 +370,12 @@ export class LibraryClaimsVerifier {
336
370
  other.claim.kind === 'make' &&
337
371
  other.claim.form === maker.form &&
338
372
  other.claim.member === maker.member);
339
- const made = holdingReturns(makers.map(({ i }) => judge(i)), atDefaults(check, receiverPath.base));
373
+ const makerJudgements = makers.map(({ i }) => judge(i));
374
+ const made = holdingReturns(makerJudgements, atDefaults(check, receiverPath.base));
340
375
  if ('reason' in made)
341
376
  return { reason: made.reason === 'unresolved' ? 'maker_unresolved' : 'maker_unverified' };
342
377
  types = made.value;
378
+ makerHolding = makerJudgements.reduce((common, judgement) => common.filter(sig => judgement.holding.includes(sig)), makerJudgements[0].holding);
343
379
  makerBindsName = makers.some(({ other }) => other.claim.kind === 'make' && other.claim.name !== undefined);
344
380
  }
345
381
  if (receiverPath.scope !== undefined) {
@@ -366,6 +402,7 @@ export class LibraryClaimsVerifier {
366
402
  home: reader.homeOf(check.package, [exported.target, ...typeSymbols(type)]),
367
403
  makerBindsName,
368
404
  scoped: receiverPath.scope !== undefined,
405
+ makerHolding,
369
406
  })),
370
407
  };
371
408
  };
@@ -401,7 +438,7 @@ export class LibraryClaimsVerifier {
401
438
  };
402
439
  const judgeCheck = (index) => {
403
440
  const check = checks[index];
404
- const rules = ROLE_TABLE[check.role];
441
+ const rules = rulesFor(check);
405
442
  if (!rules)
406
443
  return { outcome: unchecked('role_unsupported') };
407
444
  // An export given two roles is classified two ways; neither is a fact.
@@ -446,7 +483,7 @@ export class LibraryClaimsVerifier {
446
483
  const builtAtDefaults = built === undefined ? undefined : atDefaults(check, built);
447
484
  const holding = [];
448
485
  for (const receiver of receivers.value) {
449
- const result = reader.judgeMessage(receiver, check.claim, builtAtDefaults);
486
+ const result = reader.judgeMessage(receiver, check.claim, rules, builtAtDefaults);
450
487
  if (result.outcome.verdict !== 'verified')
451
488
  return result;
452
489
  holding.push(...(result.holding ?? []));
@@ -1285,14 +1322,14 @@ class DeclarationReader {
1285
1322
  * overloads it holds on. `built` is the probe's no-argument build of the
1286
1323
  * receiver a maker or scope claim makes (see `returnOf`).
1287
1324
  */
1288
- judgeMessage(receiver, claim, built) {
1325
+ judgeMessage(receiver, claim, rules, built) {
1289
1326
  switch (claim.kind) {
1290
1327
  case 'make':
1291
1328
  return this.judgeMake(receiver, claim, built);
1292
1329
  case 'scope':
1293
1330
  return this.judgeScope(receiver, claim, built);
1294
1331
  case 'op':
1295
- return { outcome: this.judgeOp(receiver, claim) };
1332
+ return { outcome: this.judgeOp(receiver, claim, rules) };
1296
1333
  case 'reserved':
1297
1334
  return { outcome: this.judgeReserved(receiver, claim) };
1298
1335
  }
@@ -1308,7 +1345,13 @@ class DeclarationReader {
1308
1345
  * `handler` slot; a queue (`new Queue("emails")`) one with a positional name.
1309
1346
  */
1310
1347
  judgeMake(receiver, claim, built) {
1311
- const layout = this.layout({ base: claim.base, prefix: claim.prefix, name: claim.name, handler: claim.handler });
1348
+ const layout = this.layout({
1349
+ base: claim.base,
1350
+ prefix: claim.prefix,
1351
+ name: claim.name,
1352
+ handler: claim.handler,
1353
+ runs: claim.runs,
1354
+ });
1312
1355
  if ('failure' in layout)
1313
1356
  return { outcome: layout.failure };
1314
1357
  const callee = this.ownCallee(receiver, [], claim.member, claim.form);
@@ -1368,23 +1411,48 @@ class DeclarationReader {
1368
1411
  * instance's maker claim has a `name` slot, or the receiver came through a
1369
1412
  * scope.
1370
1413
  */
1371
- judgeOp(receiver, claim) {
1372
- if (claim.method !== undefined || claim.method_key !== undefined || claim.options !== undefined) {
1414
+ judgeOp(receiver, claim, rules) {
1415
+ const method = this.methodOutcome(claim, rules);
1416
+ if (method)
1417
+ return method;
1418
+ // What a handler is handed is read on a receive op's handler only.
1419
+ if (claim.yields !== undefined && (claim.op !== 'receive' || claim.handler === undefined)) {
1373
1420
  return unchecked('claim_invalid');
1374
1421
  }
1375
1422
  const name = claim.name;
1376
- // A send carries a payload: a name alone on `send(data)` is the data.
1377
- if (name === undefined || (claim.op === 'send' && claim.payload === undefined))
1423
+ // A send carries a payload: a name alone on `send(data)` is the data. An
1424
+ // op that runs a function or client it is handed takes its name from
1425
+ // what it runs, and one that hands its handler a receiver (a server's
1426
+ // connection event) names nothing on the wire itself, so neither needs a
1427
+ // name of its own.
1428
+ const nameless = claim.runs !== undefined || claim.yields !== undefined;
1429
+ if ((name === undefined && !nameless) || (claim.op === 'send' && claim.payload === undefined)) {
1378
1430
  return unchecked('claim_invalid');
1431
+ }
1379
1432
  let nameSlot;
1380
- if ('bound' in name) {
1433
+ if (name === undefined) {
1434
+ nameSlot = undefined;
1435
+ }
1436
+ else if ('bound' in name) {
1381
1437
  if (name.bound === 'maker' ? !receiver.makerBindsName : !receiver.scoped)
1382
1438
  return failed('name_unbound');
1383
1439
  }
1384
1440
  else {
1385
1441
  nameSlot = name;
1386
1442
  }
1387
- const layout = this.layout({ name: nameSlot, payload: claim.payload, handler: claim.handler, ack: claim.ack });
1443
+ const runs = claim.runs;
1444
+ const boundRuns = runs?.bound === 'maker' ? runs : undefined;
1445
+ if (boundRuns && !receiver.makerHolding)
1446
+ return failed('runs_unbound');
1447
+ const layout = this.layout({
1448
+ name: nameSlot,
1449
+ payload: claim.payload,
1450
+ handler: claim.handler,
1451
+ ack: claim.ack,
1452
+ base: claim.base,
1453
+ method: claim.method_key,
1454
+ runs: runs && !boundRuns ? { arg: runs.arg, ...(runs.key === undefined ? {} : { key: runs.key }) } : undefined,
1455
+ });
1388
1456
  if ('failure' in layout)
1389
1457
  return layout.failure;
1390
1458
  const callee = this.ownCallee(receiver, claim.path ?? [], claim.member, 'call');
@@ -1392,13 +1460,131 @@ class DeclarationReader {
1392
1460
  return callee.failure;
1393
1461
  const labels = claim.key_labels ?? {};
1394
1462
  let best;
1463
+ let held = false;
1395
1464
  for (const signature of callee.signatures) {
1396
- const result = this.messageSignatureOutcome(signature, layout.value, labels);
1397
- if (result.rank === Infinity)
1398
- return VERIFIED;
1465
+ let result = this.messageSignatureOutcome(signature, layout.value, labels);
1466
+ if (result.rank === Infinity && claim.yields !== undefined) {
1467
+ const given = this.yieldsOutcome(signature, claim.handler, claim.yields, receiver);
1468
+ if (given)
1469
+ result = { rank: 5, outcome: given };
1470
+ }
1471
+ if (result.rank === Infinity) {
1472
+ held = true;
1473
+ break;
1474
+ }
1399
1475
  best = furthest(best, result.rank, result.outcome);
1400
1476
  }
1401
- return best.outcome;
1477
+ if (!held)
1478
+ return best.outcome;
1479
+ if (boundRuns)
1480
+ return this.boundRunsOutcome(receiver.makerHolding, boundRuns);
1481
+ return VERIFIED;
1482
+ }
1483
+ /**
1484
+ * The method fields of an op (label structure v2, carrick#2434), under the
1485
+ * role's method words. Without method words (the message roles), an op
1486
+ * carrying `method`, `method_key`, `options`, `method_default` or `base` is
1487
+ * `claim_invalid`, as before. With them:
1488
+ * - `method` is the member's own name, upper-cased (ASCII only), and a
1489
+ * method word: `get` registers or sends GET (`method_not_member_verb`);
1490
+ * - `method_key` is where the method sits, a slot that accepts a string or
1491
+ * a method literal (read with the call's other parts);
1492
+ * - `method_default` is a method word (`method_not_word`): beside
1493
+ * `method_key`, the method when that option is absent; alone, the one
1494
+ * method the call sends. Beside `method`, it says nothing new and is
1495
+ * `claim_invalid`, as is `method` beside `method_key`;
1496
+ * - `options` belongs to the #1564 checks and is `claim_invalid`.
1497
+ * Undefined when the fields hold so far; their slots are read with the
1498
+ * call's other parts.
1499
+ */
1500
+ methodOutcome(claim, rules) {
1501
+ const words = rules.methods;
1502
+ if (!words) {
1503
+ const any = claim.method !== undefined ||
1504
+ claim.method_key !== undefined ||
1505
+ claim.options !== undefined ||
1506
+ claim.method_default !== undefined ||
1507
+ claim.base !== undefined;
1508
+ return any ? unchecked('claim_invalid') : undefined;
1509
+ }
1510
+ if (claim.options !== undefined)
1511
+ return unchecked('claim_invalid');
1512
+ if (claim.method !== undefined && (claim.method_key !== undefined || claim.method_default !== undefined)) {
1513
+ return unchecked('claim_invalid');
1514
+ }
1515
+ if (claim.method !== undefined) {
1516
+ if (claim.member === null || claim.method !== asciiUpperCase(claim.member) || !words.has(claim.method)) {
1517
+ return failed('method_not_member_verb');
1518
+ }
1519
+ }
1520
+ if (claim.method_default !== undefined && !words.has(claim.method_default))
1521
+ return failed('method_not_word');
1522
+ return undefined;
1523
+ }
1524
+ /**
1525
+ * `runs: { bound: 'maker', arg, key }`: some overload the instance's maker
1526
+ * holds on takes a function with a declared signature there (the function
1527
+ * a mutation object's `mutate` runs is the one its maker was given).
1528
+ */
1529
+ boundRunsOutcome(holding, runs) {
1530
+ let best;
1531
+ for (const signature of holding) {
1532
+ let declared = this.keyParameterAt(signature, runs.arg);
1533
+ if (declared !== undefined && runs.key !== undefined) {
1534
+ const property = this.declaredProperty(declared, runs.key);
1535
+ if (!property) {
1536
+ best = furthest(best, 2, failed('key_missing'));
1537
+ continue;
1538
+ }
1539
+ declared = this.checker.getTypeOfSymbol(property);
1540
+ }
1541
+ const failure = this.runsFailure(declared);
1542
+ if (!failure)
1543
+ return VERIFIED;
1544
+ best = furthest(best, 3, failure);
1545
+ }
1546
+ return best?.outcome ?? failed('param_missing');
1547
+ }
1548
+ /** A `runs` slot: a function with a declared signature, as a handler is, under its own reasons. */
1549
+ runsFailure(declared) {
1550
+ const failure = this.handlerFailure(declared);
1551
+ if (!failure || failure.verdict === 'verified')
1552
+ return failure;
1553
+ const reason = failure.reason.replace(/^handler_/, 'runs_');
1554
+ return failure.verdict === 'failed' ? failed(reason) : unchecked(reason);
1555
+ }
1556
+ /**
1557
+ * `yields: { param, export, side }` on a receive op: some signature of the
1558
+ * handler this overload takes declares a parameter at `param` whose type
1559
+ * is the package's own export named `export` (a class, or the interface
1560
+ * an instance has), declared in the receiver's home packages. `side` is
1561
+ * `client` or `server` (`yields_side_invalid`). Undefined when it holds.
1562
+ */
1563
+ yieldsOutcome(signature, handler, yields, receiver) {
1564
+ if (yields.side !== 'client' && yields.side !== 'server')
1565
+ return failed('yields_side_invalid');
1566
+ let declared = this.keyParameterAt(signature, handler.arg);
1567
+ if (declared !== undefined && handler.key !== undefined) {
1568
+ const property = this.declaredProperty(declared, handler.key);
1569
+ declared = property ? this.checker.getTypeOfSymbol(property) : undefined;
1570
+ }
1571
+ if (declared === undefined)
1572
+ return failed('param_missing');
1573
+ const slot = this.throughConditional(this.throughConstraint(declared));
1574
+ if (slot === VARIADIC)
1575
+ return unchecked('handler_untyped');
1576
+ for (const part of this.parts(slot)) {
1577
+ for (const handed of part.getCallSignatures()) {
1578
+ const given = this.parameterAt(handed, yields.param);
1579
+ if (given === undefined || given === VARIADIC)
1580
+ continue;
1581
+ const named = typeSymbols(given).some(symbol => symbol?.getName() === yields.export &&
1582
+ (symbol.declarations ?? []).some(declaration => this.isOwnFile(declaration.getSourceFile(), receiver.home)));
1583
+ if (named)
1584
+ return undefined;
1585
+ }
1586
+ }
1587
+ return failed('yields_not_export');
1402
1588
  }
1403
1589
  /**
1404
1590
  * The declarations spell `name` in one of `member`'s parameters: an
@@ -1462,6 +1648,14 @@ class DeclarationReader {
1462
1648
  */
1463
1649
  messageSignatureOutcome(signature, layout, labels) {
1464
1650
  for (const [arg, part] of layout.positional) {
1651
+ if (part === 'method') {
1652
+ const declared = this.keyParameterAt(signature, arg);
1653
+ if (declared === undefined)
1654
+ return { rank: 1, outcome: failed('param_missing') };
1655
+ if (!this.acceptsMethod(declared))
1656
+ return { rank: 1, outcome: this.slotFailure(declared, 'method_not_string') };
1657
+ continue;
1658
+ }
1465
1659
  if (part !== 'name' && part !== 'base' && part !== 'prefix')
1466
1660
  continue;
1467
1661
  const declared = this.keyParameterAt(signature, arg);
@@ -1486,9 +1680,10 @@ class DeclarationReader {
1486
1680
  }
1487
1681
  }
1488
1682
  for (const [arg, part] of layout.positional) {
1489
- if (part !== 'handler' && part !== 'ack')
1683
+ if (part !== 'handler' && part !== 'ack' && part !== 'runs')
1490
1684
  continue;
1491
- const failure = this.handlerFailure(this.keyParameterAt(signature, arg));
1685
+ const declared = this.keyParameterAt(signature, arg);
1686
+ const failure = part === 'runs' ? this.runsFailure(declared) : this.handlerFailure(declared);
1492
1687
  if (failure)
1493
1688
  return { rank: 3, outcome: failure };
1494
1689
  }
@@ -1537,6 +1732,17 @@ class DeclarationReader {
1537
1732
  failure = { rank: 3, outcome: failed('payload_is_function') };
1538
1733
  break;
1539
1734
  }
1735
+ if (part === 'method' && !this.acceptsMethod(type)) {
1736
+ failure = { rank: 3, outcome: this.slotFailure(type, 'method_not_string') };
1737
+ break;
1738
+ }
1739
+ if (part === 'runs') {
1740
+ const bad = this.runsFailure(type);
1741
+ if (bad) {
1742
+ failure = { rank: 3, outcome: bad };
1743
+ break;
1744
+ }
1745
+ }
1540
1746
  if (part === 'handler' || part === 'ack') {
1541
1747
  const bad = this.handlerFailure(type);
1542
1748
  if (bad) {
@@ -2690,16 +2896,21 @@ class DeclarationReader {
2690
2896
  }
2691
2897
  /**
2692
2898
  * What a factory builds says nothing: the type itself, or any branch of a
2693
- * conditional return type, does.
2899
+ * conditional return type, does. A conditional whose branches lead back to
2900
+ * itself (a recursive alias) is read once: `seen` holds the conditionals
2901
+ * on the way down, and one met again says nothing new.
2694
2902
  */
2695
- returnSaysNothing(type) {
2903
+ returnSaysNothing(type, seen = new Set()) {
2696
2904
  if (this.saysNothing(type))
2697
2905
  return true;
2698
2906
  return this.parts(type).some(part => {
2699
2907
  if (!(part.flags & ts.TypeFlags.Conditional))
2700
2908
  return false;
2701
2909
  const node = part.root.node;
2702
- return [node.trueType, node.falseType].some(branch => this.returnSaysNothing(this.checker.getTypeFromTypeNode(branch)));
2910
+ if (seen.has(node))
2911
+ return false;
2912
+ seen.add(node);
2913
+ return [node.trueType, node.falseType].some(branch => this.returnSaysNothing(this.checker.getTypeFromTypeNode(branch), seen));
2703
2914
  });
2704
2915
  }
2705
2916
  /** `any` (an unresolved type included) or `unknown`. */
@@ -29,4 +29,22 @@
29
29
  * and is left unresolved rather than given another import's mode.
30
30
  */
31
31
  import { type ResolutionHostFactory } from 'ts-morph';
32
- export declare const moduleFormatResolutionHost: ResolutionHostFactory;
32
+ /**
33
+ * The editor's project-reference rule for the program's owner config
34
+ * (`ReferenceRedirect` in `capture/reference-redirect.ts`, carrick#2418),
35
+ * restated here because this module is outside the capture seam.
36
+ */
37
+ export interface ResolutionRedirect {
38
+ referenceFor(containingFile: string): unknown;
39
+ resolutionHost<H extends {
40
+ fileExists(fileName: string): boolean;
41
+ }>(host: H): H;
42
+ sourceAnswer<R extends {
43
+ resolvedFileName: string;
44
+ } | undefined>(answer: R): R;
45
+ }
46
+ /**
47
+ * The resolution host factory, resolving through `redirect` when the owner
48
+ * config references other projects.
49
+ */
50
+ export declare const moduleFormatResolutionHost: (redirect?: ResolutionRedirect) => ResolutionHostFactory;
@@ -55,7 +55,12 @@ function usagesOf(file) {
55
55
  }
56
56
  /** Why a module name cannot be given one mode: see the header. */
57
57
  const UNPLACED = Symbol('unplaced');
58
- export const moduleFormatResolutionHost = (host, getOptions) => {
58
+ /**
59
+ * The resolution host factory, resolving through `redirect` when the owner
60
+ * config references other projects.
61
+ */
62
+ export const moduleFormatResolutionHost = (redirect) => (host, getOptions) => {
63
+ const resolutionHost = redirect ? redirect.resolutionHost(host) : host;
59
64
  let cache;
60
65
  let cacheOptions;
61
66
  const cacheFor = (options) => {
@@ -95,7 +100,9 @@ export const moduleFormatResolutionHost = (host, getOptions) => {
95
100
  const mode = modeOf(name);
96
101
  if (mode === UNPLACED)
97
102
  return undefined;
98
- return ts.resolveModuleName(name, containingFile, compilerOptions, host, resolutionCache, redirectedReference, mode).resolvedModule;
103
+ const answer = ts.resolveModuleName(name, containingFile, compilerOptions, resolutionHost, resolutionCache, redirectedReference ??
104
+ redirect?.referenceFor(containingFile), mode).resolvedModule;
105
+ return redirect ? redirect.sourceAnswer(answer) : answer;
99
106
  });
100
107
  },
101
108
  };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Per-position compiler rows for a service (carrick#2405, move 2 of the fact
3
+ * layer). Record-only: nothing in a scan reads the file yet.
4
+ *
5
+ * For every source file the scanner sends, one row for each position of six
6
+ * kinds, each holding what the compiler knows there: the symbol, the
7
+ * declaration it is attributed to (file and line), the package that declares
8
+ * it and the type text.
9
+ *
10
+ * | kind | node | symbol of | type of |
11
+ * |-----------|---------------------------------------------------|---------------------------|----------------|
12
+ * | `call` | call, `new`, tagged template | the callee's name | the call |
13
+ * | `member` | property or element access | the name or the argument | the access |
14
+ * | `compare` | `=== !== == != < > <= >= instanceof in` | the left operand | the left operand |
15
+ * | `ident` | an identifier in expression position | itself | itself |
16
+ * | `impl` | a `call` or `member` whose declaration has no body | an implementation of it | nothing |
17
+ * | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
18
+ *
19
+ * An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
20
+ * it belongs to and has its span: one per declaration that go-to-implementation
21
+ * names for that row's declaration, when that declaration is an interface's
22
+ * or type literal's method, a function-typed property signature, an abstract
23
+ * member, or a function-typed class property or parameter with no
24
+ * initializer. Its `decl_*` name the implementing declaration. Past
25
+ * {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
26
+ *
27
+ * A row's key is `(start, end, kind)`, except that several `impl` rows share
28
+ * one: TypeScript `getStart()`/`getEnd()` of the node, in UTF-16 code units
29
+ * from the start of the file, which is what the scanner's `sidecar_position`
30
+ * gives for an SWC span. Every path written is relative to the scanned repo's
31
+ * root, with `/` separators.
32
+ *
33
+ * Each file is read in the program of the project that owns it
34
+ * (`ProjectLoader.projectKeyFor`, carrick#1604), one owner's program at a
35
+ * time: a group's program is released before the next one is built. A file
36
+ * the owner's program does not take (JavaScript under a config without
37
+ * `allowJs`) is read after every owner, in that owner's `scripts` program
38
+ * (`ProjectLoader.scriptProjectFor`, carrick#2413), with the owner's root
39
+ * declaration files beside it so that the globals they declare are declared.
40
+ *
41
+ * The file format is pinned on the scanner side by `src/position_rows.rs`.
42
+ */
43
+ import { ts } from 'ts-morph';
44
+ import type { ProjectLoader } from './project-loader.js';
45
+ import type { PositionRowsCounts } from './types.js';
46
+ /** The format's version, written in the header. */
47
+ export declare const POSITION_ROWS_VERSION = 2;
48
+ /** The type is `any`, declared or inferred. Never set with the error type. */
49
+ export declare const FLAG_ANY = 1;
50
+ /** The type is the compiler's error type: something did not resolve. */
51
+ export declare const FLAG_ERROR_TYPE = 2;
52
+ /** The checker threw at this node; the row holds nothing else. */
53
+ export declare const FLAG_CHECKER_THREW = 4;
54
+ /** The declaration is outside the checkout, not the default library and in no installed package. */
55
+ export declare const FLAG_DECLARATION_UNNAMED = 8;
56
+ /** An `impl` row standing for more implementations than {@link IMPL_CAP}; it names none. */
57
+ export declare const FLAG_TOO_MANY = 16;
58
+ /** The most implementations one declaration's `impl` rows list. */
59
+ export declare const IMPL_CAP = 16;
60
+ /** The kinds {@link kindOf} gives a node; `impl` and `jsx` rows are written beside them. */
61
+ export type NodeKind = 'call' | 'member' | 'compare' | 'ident';
62
+ export type PositionKind = NodeKind | 'impl' | 'jsx';
63
+ /** `[kind, start, end, symbol, decl_file, decl_line, decl_package, type_text, flags]` */
64
+ export type PositionRow = [
65
+ PositionKind,
66
+ number,
67
+ number,
68
+ string | null,
69
+ string | null,
70
+ number | null,
71
+ string | null,
72
+ string | null,
73
+ number
74
+ ];
75
+ export interface PositionRowsOptions {
76
+ loader: ProjectLoader;
77
+ /** Absolute paths, written in this order. */
78
+ files: string[];
79
+ scanRoot: string;
80
+ outPath: string;
81
+ service: string;
82
+ /** Called after each file is walked or skipped. */
83
+ onFile?: () => void;
84
+ }
85
+ /**
86
+ * Walk `files`, write their rows to `outPath` and say what was written.
87
+ *
88
+ * The file is written beside `outPath` as `<outPath>.partial` and renamed
89
+ * over it, so a walk that dies leaves no half-written rows file. Every write
90
+ * goes through a {@link WriteGuard} over the file's directory, which refuses
91
+ * a directory inside the scanned trees anywhere but beneath `.carrick`.
92
+ */
93
+ export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
94
+ /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
95
+ export declare function kindOf(node: ts.Node): NodeKind | undefined;