carrick 0.3.104 → 0.3.106

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 (33) hide show
  1. package/package.json +6 -6
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/sidecar/dist/src/capture/anchors.d.ts +13 -0
  4. package/sidecar/dist/src/capture/anchors.js +229 -37
  5. package/sidecar/dist/src/capture/api.d.ts +64 -4
  6. package/sidecar/dist/src/capture/check-classify.d.ts +10 -1
  7. package/sidecar/dist/src/capture/check-classify.js +23 -12
  8. package/sidecar/dist/src/capture/check-fields.js +4 -6
  9. package/sidecar/dist/src/capture/check-probe.js +16 -6
  10. package/sidecar/dist/src/capture/check-scrub.d.ts +3 -0
  11. package/sidecar/dist/src/capture/check-scrub.js +8 -4
  12. package/sidecar/dist/src/capture/check.js +75 -49
  13. package/sidecar/dist/src/capture/deep-walk.d.ts +20 -0
  14. package/sidecar/dist/src/capture/deep-walk.js +51 -11
  15. package/sidecar/dist/src/capture/guarded-fs.d.ts +5 -1
  16. package/sidecar/dist/src/capture/guarded-fs.js +23 -1
  17. package/sidecar/dist/src/capture/index.js +14 -2
  18. package/sidecar/dist/src/capture/member-name.d.ts +20 -0
  19. package/sidecar/dist/src/capture/member-name.js +24 -0
  20. package/sidecar/dist/src/capture/self-check.js +50 -9
  21. package/sidecar/dist/src/capture/service-config.d.ts +2 -0
  22. package/sidecar/dist/src/capture/service-config.js +1 -1
  23. package/sidecar/dist/src/failure-path.d.ts +67 -0
  24. package/sidecar/dist/src/failure-path.js +236 -0
  25. package/sidecar/dist/src/printed-names.d.ts +43 -0
  26. package/sidecar/dist/src/printed-names.js +186 -0
  27. package/sidecar/dist/src/retype.js +60 -99
  28. package/sidecar/dist/src/type-inferrer.d.ts +35 -3
  29. package/sidecar/dist/src/type-inferrer.js +208 -13
  30. package/sidecar/dist/src/type-structural-expander.js +10 -1
  31. package/sidecar/dist/src/types.d.ts +24 -0
  32. package/sidecar/dist/src/validators.d.ts +76 -0
  33. package/sidecar/dist/src/validators.js +8 -0
@@ -20,7 +20,9 @@
20
20
  import * as path from 'node:path';
21
21
  import { Node, SyntaxKind, ts, } from 'ts-morph';
22
22
  import { validateInferRequestItem } from './validators.js';
23
+ import { notePrintedType, PrintedTypes } from './printed-names.js';
23
24
  import { externalImportsOf, isExternalOrigin } from './origin.js';
25
+ import { reachedOnlyOnFailure } from './failure-path.js';
24
26
  import { addedDiagnostics, applyInsertions, fileDiagnostics, literalInsertions, mapBack, mapForward, normalise, pathOf, } from './unwidened.js';
25
27
  import { expandTypeStructural, } from './type-structural-expander.js';
26
28
  /**
@@ -219,6 +221,14 @@ const REQUEST_BODY_PARTS = new Set(['json', 'form', 'body']);
219
221
  * these names; a type parameter alone also types response modes and fallbacks.
220
222
  */
221
223
  const BODY_MEMBER_NAMES = new Set(['data', 'body', 'json']);
224
+ /**
225
+ * The zero-argument reads that take the WHOLE body out of a transport response
226
+ * (the platform `Response` and the clients that copy its shape): parsed as
227
+ * JSON, or as raw text (carrick#1842).
228
+ */
229
+ const WHOLE_BODY_READS = new Set(['json', 'text']);
230
+ /** The body format a caller names to read a response as raw text (carrick#1842). */
231
+ const RAW_TEXT_FORMAT = 'text';
222
232
  /**
223
233
  * Print a `Type` to its string form WITHOUT the compiler's default truncation.
224
234
  *
@@ -229,7 +239,18 @@ const BODY_MEMBER_NAMES = new Set(['data', 'body', 'json']);
229
239
  */
230
240
  const TYPE_TEXT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.InTypeAlias;
231
241
  function typeText(type, enclosingNode) {
232
- return type.getText(enclosingNode, TYPE_TEXT_FLAGS);
242
+ const text = type.getText(enclosingNode, TYPE_TEXT_FLAGS);
243
+ notePrintedType(type, enclosingNode, text);
244
+ return text;
245
+ }
246
+ /**
247
+ * `text` is `string`, alone or beside `null` and `undefined` in either order:
248
+ * the only payload a raw-text read publishes (carrick#1842).
249
+ */
250
+ function isBareStringText(text) {
251
+ const members = text.split('|').map((member) => member.trim());
252
+ return (members.includes('string') &&
253
+ members.every((member) => member === 'string' || member === 'null' || member === 'undefined'));
233
254
  }
234
255
  /**
235
256
  * How long the unwidened reading of one batch may take by default. The
@@ -309,8 +330,14 @@ export class TypeInferrer {
309
330
  errors.push(`Invalid infer item at ${request.file_path}:${loc} (${request.infer_kind}): ${itemError}`);
310
331
  continue;
311
332
  }
312
- const result = this.inferSingle(request, extractionConfig);
333
+ // carrick#1836: what the names the published text prints meant, read
334
+ // while the program the prints were made in is still the project's.
335
+ // The unwidened re-read below records nothing: it prints the same
336
+ // node, and its reading has a budget.
337
+ const prints = new PrintedTypes();
338
+ const result = prints.during(() => this.inferSingle(request, extractionConfig));
313
339
  if (result) {
340
+ this.recordPrintedNames(result, request, prints);
314
341
  inferredTypes.push(result);
315
342
  if (request.infer_kind === 'response_body' || request.infer_kind === 'function_return') {
316
343
  responses.push({ request, result });
@@ -340,6 +367,18 @@ export class TypeInferrer {
340
367
  errors: errors.length > 0 ? errors : undefined,
341
368
  };
342
369
  }
370
+ /**
371
+ * carrick#1836: list on `result` the declarations behind the names its text
372
+ * prints that the request's file cannot resolve (`PrintedTypes.namesIn`).
373
+ */
374
+ recordPrintedNames(result, request, prints) {
375
+ const sourceFile = this.getSourceFile(request.file_path);
376
+ if (!sourceFile)
377
+ return;
378
+ const printedNames = prints.namesIn([result.type_string], sourceFile.compilerNode, this.project.getTypeChecker().compilerObject);
379
+ if (printedNames.length > 0)
380
+ result.printed_names = printedNames;
381
+ }
343
382
  /**
344
383
  * carrick#1516: read each response inference again with the literals on its
345
384
  * handler's path marked `as const`, and record the narrower type the handler
@@ -1208,16 +1247,55 @@ export class TypeInferrer {
1208
1247
  const carrierCandidate = unwrapResult.wasUnwrapped || explicitType
1209
1248
  ? undefined
1210
1249
  : this.resultCarrierPayload(returnType, terminalNode, use.projections, `${request.file_path}:${request.line_number}`);
1250
+ // carrick#1841: what the carrier holds goes through the service's wrapper
1251
+ // rules before it is published, as the call's own result did. The carrier
1252
+ // is found by its shape, so what it holds can still be a library's
1253
+ // envelope: a request library answers `Task<Outcome<Reply<T>, E>>`, and
1254
+ // `Reply<T>` is that library's response object, a status, a url and
1255
+ // headers around the body. Where a rule reads a payload out of it, that
1256
+ // payload is the body. Where a rule verifies it as the library's transport
1257
+ // and reads nothing out of it, this site states no contract, and the
1258
+ // transport object must not stand in for one: judged against a
1259
+ // producer's body, every member it adds reads as a field the producer
1260
+ // does not send.
1261
+ //
1262
+ // The abstain is decided, so it rides the row (`machinery_envelope` at the
1263
+ // root, no anchor). A plain `null` is re-read by the capture's own
1264
+ // locator, which would resolve the raw call and publish the carrier.
1265
+ const carrierUnwrap = carrierCandidate
1266
+ ? this.unwrapTypeWithConfig(carrierCandidate, terminalNode, extractionConfig)
1267
+ : undefined;
1268
+ if (carrierCandidate &&
1269
+ carrierUnwrap?.wasUnwrapped &&
1270
+ carrierUnwrap.typeString.trim() === 'unknown') {
1271
+ const carried = typeText(carrierCandidate, terminalNode);
1272
+ this.log(`Call result at ${request.file_path}:${request.line_number} carries ${carried}, which ` +
1273
+ "the service's wrapper rules verify as transport and read no payload out of; this " +
1274
+ 'site states no response contract');
1275
+ const abstain = this.createInferredType(request, 'unknown', false, this.getNodeLocation(callExpr));
1276
+ abstain.any_provenance = [
1277
+ {
1278
+ path: '',
1279
+ kind: 'unknown',
1280
+ reason: 'machinery_envelope',
1281
+ detail: "what this call's result carries is transport that the service's wrapper rules " +
1282
+ 'verify and read no payload out of (a library response object around the body), ' +
1283
+ 'so this site states no response contract',
1284
+ },
1285
+ ];
1286
+ return abstain;
1287
+ }
1288
+ const carried = carrierUnwrap?.wasUnwrapped && carrierUnwrap.payloadType
1289
+ ? carrierUnwrap.payloadType
1290
+ : carrierCandidate;
1211
1291
  // The payload rides the row as its own MEMBERS, never as its bare name
1212
1292
  // (#257): `derive_capture_anchors` turns a usable inference into a literal
1213
1293
  // capture anchor, and a bare name is out of scope where the surface
1214
1294
  // declares the alias, so it would decay to a top type and publish nothing
1215
1295
  // — trading a wrong answer for no answer. Where the payload carries no
1216
1296
  // member shape to print, the carrier keeps its own answer.
1217
- const carrierText = carrierCandidate
1218
- ? this.structuralTextFromType(carrierCandidate, terminalNode)
1219
- : null;
1220
- const carrierPayload = carrierText ? carrierCandidate : undefined;
1297
+ const carrierText = carried ? this.structuralTextFromType(carried, terminalNode) : null;
1298
+ const carrierPayload = carrierText ? carried : undefined;
1221
1299
  if (carrierPayload && carrierText) {
1222
1300
  typeString = carrierText;
1223
1301
  }
@@ -1345,8 +1423,101 @@ export class TypeInferrer {
1345
1423
  if (statedBody) {
1346
1424
  inferred.stated_body = statedBody;
1347
1425
  }
1426
+ // carrick#1842: `string` cannot tell a body read as raw text from a JSON
1427
+ // body that is a string, and raw text states no structural contract. The
1428
+ // row says which it is, so the check phase can read the pair unverifiable
1429
+ // instead of comparing `string` with the other side's body.
1430
+ if (isBareStringText(typeString) &&
1431
+ (this.textReadAtTerminal(callExpr, terminalNode) || this.callChoosesTextBody(callExpr))) {
1432
+ inferred.raw_text_read = true;
1433
+ }
1348
1434
  return inferred;
1349
1435
  }
1436
+ /**
1437
+ * The terminal of a call's def-use walk is a zero-argument `.text()` read of
1438
+ * the call's own result (carrick#1842): on its binding (`res.text()`,
1439
+ * through a declaration, `await` or a cast), or in the callback a `then` on
1440
+ * the call hands the response to (`fetch(u).then((res) => res.text())`).
1441
+ */
1442
+ textReadAtTerminal(callExpr, terminal) {
1443
+ const value = Node.isVariableDeclaration(terminal) ? terminal.getInitializer() : terminal;
1444
+ const read = value ? this.unwrapExpressionNode(value) : undefined;
1445
+ if (!read || !Node.isCallExpression(read))
1446
+ return false;
1447
+ const callResult = this.unwrapPromiseType(callExpr.getType()).compilerType;
1448
+ if (this.isTextReadOf(read, (receiver) => receiver === callExpr ||
1449
+ this.unwrapPromiseType(receiver.getType()).compilerType === callResult)) {
1450
+ return true;
1451
+ }
1452
+ const then = read.getExpression();
1453
+ if (!Node.isPropertyAccessExpression(then) ||
1454
+ then.getName() !== 'then' ||
1455
+ this.unwrapExpressionNode(then.getExpression()) !== callExpr) {
1456
+ return false;
1457
+ }
1458
+ const callback = read.getArguments()[0];
1459
+ if (!callback || (!Node.isArrowFunction(callback) && !Node.isFunctionExpression(callback))) {
1460
+ return false;
1461
+ }
1462
+ const param = callback.getParameters()[0]?.getNameNode();
1463
+ if (!param || !Node.isIdentifier(param))
1464
+ return false;
1465
+ const returned = this.returnedExpressions(callback);
1466
+ const inner = returned.length === 1 ? this.unwrapExpressionNode(returned[0]) : undefined;
1467
+ return (!!inner &&
1468
+ Node.isCallExpression(inner) &&
1469
+ this.isTextReadOf(inner, (receiver) => Node.isIdentifier(receiver) && receiver.getSymbol() === param.getSymbol()));
1470
+ }
1471
+ /** `call` is `<receiver>.text()` with no arguments, and `accept` takes its receiver. */
1472
+ isTextReadOf(call, accept) {
1473
+ const access = call.getExpression();
1474
+ return (Node.isPropertyAccessExpression(access) &&
1475
+ access.getName() === RAW_TEXT_FORMAT &&
1476
+ call.getArguments().length === 0 &&
1477
+ accept(this.unwrapExpressionNode(access.getExpression())));
1478
+ }
1479
+ /**
1480
+ * The call's resolved signature, at this site, types a member of an object
1481
+ * argument as exactly the literal `'text'`, and the source passes that
1482
+ * literal there (carrick#1842). That is how a request library lets a caller
1483
+ * choose a text body from the formats it offers (`{ type: 'text' }`): a
1484
+ * generic config instantiated by the literal, or an overload taken by it.
1485
+ * A member typed as a wider union (`kind: 'text' | 'image'`) chooses no
1486
+ * format, and no member name is read.
1487
+ */
1488
+ callChoosesTextBody(callExpr) {
1489
+ const checker = this.project.getTypeChecker().compilerObject;
1490
+ let signature;
1491
+ try {
1492
+ signature = checker.getResolvedSignature(callExpr.compilerNode);
1493
+ }
1494
+ catch {
1495
+ return false;
1496
+ }
1497
+ if (!signature || signature.parameters.length === 0)
1498
+ return false;
1499
+ return callExpr.getArguments().some((argument, index) => {
1500
+ if (!Node.isObjectLiteralExpression(argument))
1501
+ return false;
1502
+ const parameter = signature.parameters[Math.min(index, signature.parameters.length - 1)];
1503
+ const parameterType = checker.getTypeOfSymbolAtLocation(parameter, callExpr.compilerNode);
1504
+ return argument.getProperties().some((property) => {
1505
+ if (!Node.isPropertyAssignment(property))
1506
+ return false;
1507
+ const value = property.getInitializer();
1508
+ if (!value ||
1509
+ !(Node.isStringLiteral(value) || Node.isNoSubstitutionTemplateLiteral(value)) ||
1510
+ value.getLiteralValue() !== RAW_TEXT_FORMAT) {
1511
+ return false;
1512
+ }
1513
+ const member = checker.getPropertyOfType(parameterType, property.getName());
1514
+ if (!member)
1515
+ return false;
1516
+ const declared = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(member, callExpr.compilerNode));
1517
+ return declared.isStringLiteral() && declared.value === RAW_TEXT_FORMAT;
1518
+ });
1519
+ });
1520
+ }
1350
1521
  /**
1351
1522
  * What the source states the body read at `terminal` to be, when the type
1352
1523
  * `extractExplicitTypeFromAncestor` printed for it is stated AT the read
@@ -1458,10 +1629,12 @@ export class TypeInferrer {
1458
1629
  symbol = symbol.getAliasedSymbol() ?? symbol;
1459
1630
  }
1460
1631
  const source = symbol?.getDeclarations()[0]?.getSourceFile().getFilePath();
1632
+ const typeArguments = node.getTypeArguments().length;
1461
1633
  return {
1462
1634
  root: nameNode.getText(),
1463
1635
  ...(source ? { root_source: source } : {}),
1464
1636
  ...depthField,
1637
+ ...(typeArguments > 0 ? { root_type_arguments: typeArguments } : {}),
1465
1638
  };
1466
1639
  }
1467
1640
  inferVariable(sourceFile, request, extractionConfig) {
@@ -1813,6 +1986,16 @@ export class TypeInferrer {
1813
1986
  for (const expr of candidates) {
1814
1987
  if (expr.getStart() <= startPos)
1815
1988
  continue;
1989
+ // carrick#1796: what the source does with the response once it has
1990
+ // FAILED describes the failure, not the payload. `if (res.ok) {
1991
+ // return ... } const errorText = await res.text()` published the error
1992
+ // text as the response contract because the walk keeps the LAST whole
1993
+ // read, and the failure path's read comes after the success path's
1994
+ // return. A read there is neither the payload nor a part of it.
1995
+ if (this.usesTrackedNames(expr, currentNames) &&
1996
+ reachedOnlyOnFailure(expr, func, (node) => this.isIdentifierUsage(node, currentNames))) {
1997
+ continue;
1998
+ }
1816
1999
  // carrick#1017: a comparison is `boolean` by construction, so it is
1817
2000
  // never the payload a call yields. `const res = await fetch(url); if
1818
2001
  // (res.status === 404) return null; return await res.json() as Entry`
@@ -1823,9 +2006,9 @@ export class TypeInferrer {
1823
2006
  continue;
1824
2007
  }
1825
2008
  // The body a caller reads OUT of a transport response IS the payload
1826
- // of the call that produced it: `res.json()` on the tracked binding,
1827
- // with whatever `as T` the source states around it read by
1828
- // `extractExplicitTypeFromAncestor` below. A bare identifier never
2009
+ // of the call that produced it: `res.json()` or `res.text()` on the
2010
+ // tracked binding, with whatever `as T` the source states around it
2011
+ // read by `extractExplicitTypeFromAncestor` below. A bare identifier never
1829
2012
  // matches `expressionUsesNames` (it tests DESCENDANTS), so without
1830
2013
  // this the body read is invisible to the def-use walk.
1831
2014
  if (Node.isIdentifier(expr) && this.isIdentifierUsage(expr, currentNames)) {
@@ -2083,10 +2266,10 @@ export class TypeInferrer {
2083
2266
  * `query.data`, `envelope` in `envelope.list[0]` — or `undefined` when the
2084
2267
  * identifier names the value itself.
2085
2268
  *
2086
- * A member CALL is not a projection: `res.text()` yields a body rather than
2269
+ * A member CALL is not a projection: `res.blob()` yields a body rather than
2087
2270
  * a part of one, and what it returns stays the walk's business. The
2088
- * zero-argument json body read has its own branch and is taken before this
2089
- * is asked.
2271
+ * zero-argument json and text body reads have their own branch and are
2272
+ * taken before this is asked.
2090
2273
  */
2091
2274
  projectionOnReceiver(identifier) {
2092
2275
  const access = identifier.getParent();
@@ -2329,12 +2512,18 @@ export class TypeInferrer {
2329
2512
  !this.leavesAPositionOpen(statedNode));
2330
2513
  });
2331
2514
  }
2515
+ /**
2516
+ * The zero-argument whole-body read that takes `identifier` as its receiver,
2517
+ * `res.json()` or `res.text()`, or `undefined`. A text read is a body read
2518
+ * like a json one (carrick#1842): without it, `return res.text()` left the
2519
+ * walk on the response binding and published the transport object.
2520
+ */
2332
2521
  bodyReadOnReceiver(identifier) {
2333
2522
  const access = identifier.getParent();
2334
2523
  if (!access ||
2335
2524
  !Node.isPropertyAccessExpression(access) ||
2336
2525
  access.getExpression() !== identifier ||
2337
- access.getName() !== 'json') {
2526
+ !WHOLE_BODY_READS.has(access.getName())) {
2338
2527
  return undefined;
2339
2528
  }
2340
2529
  const call = access.getParent();
@@ -2361,6 +2550,12 @@ export class TypeInferrer {
2361
2550
  const identifiers = expr.getDescendantsOfKind(SyntaxKind.Identifier);
2362
2551
  return identifiers.some((id) => this.isIdentifierUsage(id, names));
2363
2552
  }
2553
+ /** `expr` is a use of a tracked name, or contains one. */
2554
+ usesTrackedNames(expr, names) {
2555
+ return Node.isIdentifier(expr)
2556
+ ? this.isIdentifierUsage(expr, names)
2557
+ : this.expressionUsesNames(expr, names);
2558
+ }
2364
2559
  isIdentifierUsage(id, names) {
2365
2560
  if (!Node.isIdentifier(id)) {
2366
2561
  return false;
@@ -31,6 +31,7 @@
31
31
  import { ts } from 'ts-morph';
32
32
  import { canonicalizeUnionsInText, foldBooleanLiterals, } from './type-text-canonicalizer.js';
33
33
  import { isExternalOrigin } from './origin.js';
34
+ import { notePrintedType } from './printed-names.js';
34
35
  /**
35
36
  * Bound on the structural-expansion recursion. Deep enough for every realistic
36
37
  * request/response shape; a backstop against pathological/recursive types the
@@ -385,8 +386,16 @@ function isLibraryType(type, origin) {
385
386
  export function namedText(type) {
386
387
  return canonicalizeUnionsInText(compilerText(type));
387
388
  }
388
- /** The compiler's print, with the two fallbacks. */
389
+ /**
390
+ * The compiler's print, with the two fallbacks. The print writes names bare,
391
+ * whatever scope reads it, so it is noted for the inference (carrick#1836).
392
+ */
389
393
  function compilerText(type) {
394
+ const text = compilerPrint(type);
395
+ notePrintedType(type, undefined, text);
396
+ return text;
397
+ }
398
+ function compilerPrint(type) {
390
399
  try {
391
400
  return type.getText(undefined, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.InTypeAlias);
392
401
  }
@@ -823,6 +823,23 @@ export interface InferredType {
823
823
  * stated, or when the only annotation is further away than the read.
824
824
  */
825
825
  stated_body?: StatedBody;
826
+ /**
827
+ * carrick#1836: the declarations behind the names `type_string` prints that
828
+ * the request's file does not resolve. The printer writes names bare where
829
+ * no scope reads them, so this is the only record of what they meant. A
830
+ * name printed for two declarations is listed twice. Absent when there is
831
+ * nothing to list.
832
+ */
833
+ printed_names?: PrintedName[];
834
+ /**
835
+ * carrick#1842, `call_result` only: the published `string` is the body read
836
+ * as raw text, by a `.text()` read of the call's response or by a call whose
837
+ * signature takes the literal `'text'` the source passes as its body format.
838
+ * Raw text states no structural contract, so the check phase reads the pair
839
+ * unverifiable rather than comparing `string` with the other side's body.
840
+ * Absent when the body is not a raw-text read.
841
+ */
842
+ raw_text_read?: true;
826
843
  }
827
844
  /**
828
845
  * What the source states a body read to be (carrick#1749). The scanner
@@ -843,6 +860,11 @@ export interface StatedBody {
843
860
  root_source?: string;
844
861
  /** Array levels peeled on the way to `root`. Omitted when 0. */
845
862
  array_depth?: number;
863
+ /**
864
+ * Type arguments written at `root` (`Page<Order>` → 1). Omitted when 0.
865
+ * `root` alone does not name the body such a statement states.
866
+ */
867
+ root_type_arguments?: number;
846
868
  }
847
869
  /**
848
870
  * Why a type carries `any`/`unknown` at a position, and where.
@@ -853,6 +875,8 @@ export interface StatedBody {
853
875
  * across the bundle seam.
854
876
  */
855
877
  export type TypeProvenance = import('./capture/api.js').TypeProvenance;
878
+ /** carrick#1836: declared in the capture bundle, which reads it on a literal anchor. */
879
+ export type PrintedName = import('./capture/api.js').PrintedName;
856
880
  export type TypeProvenanceReason = import('./capture/api.js').TypeProvenanceReason;
857
881
  /**
858
882
  * Source location information for a type
@@ -332,18 +332,44 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
332
332
  type_text: z.ZodString;
333
333
  anchor_origin: z.ZodEnum<["llm-symbol", "deterministic-infer", "anchor-backfill", "manifest-placeholder"]>;
334
334
  source_file: z.ZodOptional<z.ZodString>;
335
+ printed_names: z.ZodOptional<z.ZodArray<z.ZodObject<{
336
+ name: z.ZodString;
337
+ file: z.ZodString;
338
+ export_path: z.ZodArray<z.ZodString, "many">;
339
+ }, "strip", z.ZodTypeAny, {
340
+ name: string;
341
+ file: string;
342
+ export_path: string[];
343
+ }, {
344
+ name: string;
345
+ file: string;
346
+ export_path: string[];
347
+ }>, "many">>;
348
+ raw_text_read: z.ZodOptional<z.ZodLiteral<true>>;
335
349
  }, "strip", z.ZodTypeAny, {
336
350
  kind: "literal";
337
351
  alias: string;
338
352
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
339
353
  type_text: string;
340
354
  source_file?: string | undefined;
355
+ printed_names?: {
356
+ name: string;
357
+ file: string;
358
+ export_path: string[];
359
+ }[] | undefined;
360
+ raw_text_read?: true | undefined;
341
361
  }, {
342
362
  kind: "literal";
343
363
  alias: string;
344
364
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
345
365
  type_text: string;
346
366
  source_file?: string | undefined;
367
+ printed_names?: {
368
+ name: string;
369
+ file: string;
370
+ export_path: string[];
371
+ }[] | undefined;
372
+ raw_text_read?: true | undefined;
347
373
  }>]>, "many">;
348
374
  out_dir: z.ZodString;
349
375
  tsconfig_path: z.ZodOptional<z.ZodString>;
@@ -382,6 +408,12 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
382
408
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
383
409
  type_text: string;
384
410
  source_file?: string | undefined;
411
+ printed_names?: {
412
+ name: string;
413
+ file: string;
414
+ export_path: string[];
415
+ }[] | undefined;
416
+ raw_text_read?: true | undefined;
385
417
  })[];
386
418
  out_dir: string;
387
419
  tsconfig_path?: string | undefined;
@@ -420,6 +452,12 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
420
452
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
421
453
  type_text: string;
422
454
  source_file?: string | undefined;
455
+ printed_names?: {
456
+ name: string;
457
+ file: string;
458
+ export_path: string[];
459
+ }[] | undefined;
460
+ raw_text_read?: true | undefined;
423
461
  })[];
424
462
  out_dir: string;
425
463
  tsconfig_path?: string | undefined;
@@ -2077,18 +2115,44 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2077
2115
  type_text: z.ZodString;
2078
2116
  anchor_origin: z.ZodEnum<["llm-symbol", "deterministic-infer", "anchor-backfill", "manifest-placeholder"]>;
2079
2117
  source_file: z.ZodOptional<z.ZodString>;
2118
+ printed_names: z.ZodOptional<z.ZodArray<z.ZodObject<{
2119
+ name: z.ZodString;
2120
+ file: z.ZodString;
2121
+ export_path: z.ZodArray<z.ZodString, "many">;
2122
+ }, "strip", z.ZodTypeAny, {
2123
+ name: string;
2124
+ file: string;
2125
+ export_path: string[];
2126
+ }, {
2127
+ name: string;
2128
+ file: string;
2129
+ export_path: string[];
2130
+ }>, "many">>;
2131
+ raw_text_read: z.ZodOptional<z.ZodLiteral<true>>;
2080
2132
  }, "strip", z.ZodTypeAny, {
2081
2133
  kind: "literal";
2082
2134
  alias: string;
2083
2135
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
2084
2136
  type_text: string;
2085
2137
  source_file?: string | undefined;
2138
+ printed_names?: {
2139
+ name: string;
2140
+ file: string;
2141
+ export_path: string[];
2142
+ }[] | undefined;
2143
+ raw_text_read?: true | undefined;
2086
2144
  }, {
2087
2145
  kind: "literal";
2088
2146
  alias: string;
2089
2147
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
2090
2148
  type_text: string;
2091
2149
  source_file?: string | undefined;
2150
+ printed_names?: {
2151
+ name: string;
2152
+ file: string;
2153
+ export_path: string[];
2154
+ }[] | undefined;
2155
+ raw_text_read?: true | undefined;
2092
2156
  }>]>, "many">;
2093
2157
  out_dir: z.ZodString;
2094
2158
  tsconfig_path: z.ZodOptional<z.ZodString>;
@@ -2127,6 +2191,12 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2127
2191
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
2128
2192
  type_text: string;
2129
2193
  source_file?: string | undefined;
2194
+ printed_names?: {
2195
+ name: string;
2196
+ file: string;
2197
+ export_path: string[];
2198
+ }[] | undefined;
2199
+ raw_text_read?: true | undefined;
2130
2200
  })[];
2131
2201
  out_dir: string;
2132
2202
  tsconfig_path?: string | undefined;
@@ -2165,6 +2235,12 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2165
2235
  anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill" | "manifest-placeholder";
2166
2236
  type_text: string;
2167
2237
  source_file?: string | undefined;
2238
+ printed_names?: {
2239
+ name: string;
2240
+ file: string;
2241
+ export_path: string[];
2242
+ }[] | undefined;
2243
+ raw_text_read?: true | undefined;
2168
2244
  })[];
2169
2245
  out_dir: string;
2170
2246
  tsconfig_path?: string | undefined;
@@ -184,6 +184,14 @@ const CaptureAnchorRequestSchema = z.discriminatedUnion('kind', [
184
184
  type_text: z.string().min(1),
185
185
  anchor_origin: AnchorOriginSchema,
186
186
  source_file: z.string().min(1).optional(),
187
+ printed_names: z
188
+ .array(z.object({
189
+ name: z.string().min(1),
190
+ file: z.string().min(1),
191
+ export_path: z.array(z.string().min(1)).min(1),
192
+ }))
193
+ .optional(),
194
+ raw_text_read: z.literal(true).optional(),
187
195
  }),
188
196
  ]);
189
197
  export const CaptureV2RequestSchema = BaseRequestSchema.extend({