@suss/adapter-typescript 0.22.2 → 0.22.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ADAPTER_VERSION: () => ADAPTER_VERSION,
34
+ GENERIC_TERMINALS: () => GENERIC_TERMINALS,
35
+ NO_BARRIERS: () => NO_BARRIERS,
34
36
  ResolutionStore: () => ResolutionStore,
35
37
  TypeScriptWhySession: () => TypeScriptWhySession,
36
38
  accessContextFor: () => accessContextFor,
@@ -51,6 +53,7 @@ __export(index_exports, {
51
53
  findNearestTsconfig: () => findNearestTsconfig,
52
54
  findTerminals: () => findTerminals,
53
55
  functionTargetOf: () => functionTargetOf,
56
+ hasBody: () => hasBody,
54
57
  importDeclarationsOf: () => importDeclarationsOf,
55
58
  importedNamesOf: () => importedNamesOf,
56
59
  importedRootsOf: () => importedRootsOf,
@@ -66,6 +69,7 @@ __export(index_exports, {
66
69
  resolveAliasedSymbol: () => resolveAliasedSymbol,
67
70
  resolveSubject: () => resolveSubject,
68
71
  rootIdentifier: () => rootIdentifier,
72
+ startsItsOwnScope: () => startsItsOwnScope,
69
73
  stubEvidence: () => stubEvidence,
70
74
  stubEvidenceIn: () => stubEvidenceIn,
71
75
  workspaceRootFor: () => workspaceRootFor
@@ -3732,6 +3736,18 @@ var ResolutionStore = class {
3732
3736
  const target = factKeyOf(value);
3733
3737
  return this.askAbout(target, "wanted", () => this.lookupObject(target));
3734
3738
  }
3739
+ /**
3740
+ * The function a call returns: `app.use(requireCaller(config))`
3741
+ * registers what the factory gives back. A separate question from
3742
+ * `resolveCallable`, which keeps a call unresolved on purpose so an
3743
+ * unwrapping answer wins; this one is asked only after that has
3744
+ * declined. Two returned functions give null, like every other
3745
+ * single-answer question here.
3746
+ */
3747
+ resolveReturnedCallable(call) {
3748
+ const target = factKeyOf(call);
3749
+ return this.askAbout(target, "wanted", () => this.lookupReturned(target));
3750
+ }
3735
3751
  /**
3736
3752
  * For a value that is neither a function nor an object, such as a
3737
3753
  * GraphQL document kept in a constant in another file.
@@ -4175,6 +4191,21 @@ var ResolutionStore = class {
4175
4191
  }
4176
4192
  return [...candidates][0];
4177
4193
  }
4194
+ lookupReturned(call) {
4195
+ this.derive();
4196
+ const candidates = /* @__PURE__ */ new Set();
4197
+ for (const target of this.answersFor("wantedGivesBack", nodeId(call))) {
4198
+ const node = this.table.byId.get(target);
4199
+ if (node === void 0 || !isFunctionRoot(node)) {
4200
+ continue;
4201
+ }
4202
+ candidates.add(node);
4203
+ }
4204
+ if (candidates.size !== 1) {
4205
+ return null;
4206
+ }
4207
+ return [...candidates][0];
4208
+ }
4178
4209
  /**
4179
4210
  * Two candidates give null, since ambiguity is nothing. The value
4180
4211
  * itself is never a result either, or an identifier whose chain went
@@ -4658,6 +4689,26 @@ function resolveImportedLocalName(sourceFile, importModule, importName) {
4658
4689
  }
4659
4690
  return null;
4660
4691
  }
4692
+ function importedReferenceSpellings(sourceFile, importModule, importName) {
4693
+ const spellings = [];
4694
+ const localName = resolveImportedLocalName(
4695
+ sourceFile,
4696
+ importModule,
4697
+ importName
4698
+ );
4699
+ if (localName !== null) {
4700
+ spellings.push(localName);
4701
+ }
4702
+ if (!sourceFile.getFullText().includes(`.${importName}`)) {
4703
+ return spellings;
4704
+ }
4705
+ for (const root of importedRootsOf(sourceFile, [importModule])) {
4706
+ if (root !== importName) {
4707
+ spellings.push(`${root}.${importName}`);
4708
+ }
4709
+ }
4710
+ return spellings;
4711
+ }
4661
4712
 
4662
4713
  // src/discovery/resolveValue.ts
4663
4714
  var import_ts_morph19 = require("ts-morph");
@@ -5262,7 +5313,27 @@ function functionValueOf(value, resolution) {
5262
5313
  return null;
5263
5314
  }
5264
5315
  const resolved = resolution.resolveCallable(written);
5265
- return resolved === null ? null : toFunctionRoot(resolved);
5316
+ if (resolved !== null) {
5317
+ return toFunctionRoot(resolved);
5318
+ }
5319
+ if (!import_ts_morph19.Node.isCallExpression(written)) {
5320
+ return null;
5321
+ }
5322
+ const returned = resolution.resolveReturnedCallable(written);
5323
+ return returned === null ? null : toFunctionRoot(returned);
5324
+ }
5325
+ function propertyNameOf(property) {
5326
+ if (import_ts_morph19.Node.isPropertyAssignment(property) || import_ts_morph19.Node.isShorthandPropertyAssignment(property) || import_ts_morph19.Node.isMethodDeclaration(property)) {
5327
+ return property.getName();
5328
+ }
5329
+ return null;
5330
+ }
5331
+ function propertyFunctionOf(property, resolution) {
5332
+ if (import_ts_morph19.Node.isMethodDeclaration(property)) {
5333
+ return property;
5334
+ }
5335
+ const held = propertyValueOf(property);
5336
+ return held === null ? null : functionValueOf(held, resolution);
5266
5337
  }
5267
5338
  function objectLiteralOf(value, resolution) {
5268
5339
  return literalValueOf(
@@ -8081,13 +8152,13 @@ function collectMethodsCalledOnCallResults(sourceFile) {
8081
8152
  return called;
8082
8153
  }
8083
8154
  function registrationSubjectsOf(sourceFile, importModule, importName, resolution) {
8084
- const importedLocalName = resolveImportedLocalName(
8155
+ const spellings = importedReferenceSpellings(
8085
8156
  sourceFile,
8086
8157
  importModule,
8087
8158
  importName
8088
8159
  );
8089
8160
  const subjects = /* @__PURE__ */ new Map();
8090
- if (importedLocalName === null) {
8161
+ if (spellings.length === 0) {
8091
8162
  return subjects;
8092
8163
  }
8093
8164
  sourceFile.forEachDescendant((node) => {
@@ -8097,7 +8168,7 @@ function registrationSubjectsOf(sourceFile, importModule, importName, resolution
8097
8168
  return;
8098
8169
  }
8099
8170
  const calleeText = import_ts_morph34.Node.isCallExpression(init) || import_ts_morph34.Node.isNewExpression(init) ? init.getExpression().getText() : null;
8100
- if (calleeText === importedLocalName) {
8171
+ if (calleeText !== null && spellings.includes(calleeText)) {
8101
8172
  subjects.set(node.getName(), init);
8102
8173
  }
8103
8174
  return;
@@ -8110,7 +8181,9 @@ function registrationSubjectsOf(sourceFile, importModule, importName, resolution
8110
8181
  return;
8111
8182
  }
8112
8183
  const typeText = typeNode.getText();
8113
- if (typeText === importedLocalName || typeText.startsWith(`${importedLocalName}<`)) {
8184
+ if (spellings.some(
8185
+ (spelling) => typeText === spelling || typeText.startsWith(`${spelling}<`)
8186
+ )) {
8114
8187
  subjects.set(node.getName(), creationSiteBehind(node, resolution));
8115
8188
  }
8116
8189
  });
@@ -8154,17 +8227,17 @@ function registrationSubjectIdsOf(sourceFile, matches, resolution) {
8154
8227
  return ids;
8155
8228
  }
8156
8229
  function unboundConstructionsOf(sourceFile, importModule, importName) {
8157
- const importedLocalName = resolveImportedLocalName(
8230
+ const spellings = importedReferenceSpellings(
8158
8231
  sourceFile,
8159
8232
  importModule,
8160
8233
  importName
8161
8234
  );
8162
- if (importedLocalName === null) {
8235
+ if (spellings.length === 0) {
8163
8236
  return [];
8164
8237
  }
8165
8238
  const constructions = [];
8166
8239
  sourceFile.forEachDescendant((node) => {
8167
- if ((import_ts_morph34.Node.isCallExpression(node) || import_ts_morph34.Node.isNewExpression(node)) && node.getExpression().getText() === importedLocalName && !import_ts_morph34.Node.isVariableDeclaration(node.getParent())) {
8240
+ if ((import_ts_morph34.Node.isCallExpression(node) || import_ts_morph34.Node.isNewExpression(node)) && spellings.includes(node.getExpression().getText()) && !import_ts_morph34.Node.isVariableDeclaration(node.getParent())) {
8168
8241
  constructions.push(node);
8169
8242
  }
8170
8243
  });
@@ -8720,7 +8793,7 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
8720
8793
  }
8721
8794
  const schemaSdl = extractTypeDefsSdl(config, store);
8722
8795
  for (const typeProp of propertiesOf(resolversObj, store)) {
8723
- const typeName = resolverPropertyName(typeProp);
8796
+ const typeName = propertyNameOf(typeProp);
8724
8797
  if (typeName === null || excludeTypes.has(typeName)) {
8725
8798
  continue;
8726
8799
  }
@@ -8729,11 +8802,11 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
8729
8802
  continue;
8730
8803
  }
8731
8804
  for (const fieldProp of propertiesOf(typeObj, store)) {
8732
- const fieldName = resolverPropertyName(fieldProp);
8805
+ const fieldName = propertyNameOf(fieldProp);
8733
8806
  if (fieldName === null) {
8734
8807
  continue;
8735
8808
  }
8736
- const fn = resolverPropertyFunction(fieldProp, store);
8809
+ const fn = propertyFunctionOf(fieldProp, store);
8737
8810
  if (fn === null) {
8738
8811
  continue;
8739
8812
  }
@@ -8797,22 +8870,6 @@ function writtenSdl(expr) {
8797
8870
  }
8798
8871
  return null;
8799
8872
  }
8800
- function resolverPropertyName(prop) {
8801
- if (import_ts_morph38.Node.isPropertyAssignment(prop) || import_ts_morph38.Node.isMethodDeclaration(prop)) {
8802
- return prop.getName();
8803
- }
8804
- if (import_ts_morph38.Node.isShorthandPropertyAssignment(prop)) {
8805
- return prop.getName();
8806
- }
8807
- return null;
8808
- }
8809
- function resolverPropertyFunction(prop, resolution) {
8810
- if (import_ts_morph38.Node.isMethodDeclaration(prop)) {
8811
- return prop;
8812
- }
8813
- const held = propertyValueOf(prop);
8814
- return held === null ? null : functionValueOf(held, resolution);
8815
- }
8816
8873
 
8817
8874
  // src/discovery/index.ts
8818
8875
  function runPattern(sourceFile, pattern, resolution, mountPrefixes, expandedElsewhere) {
@@ -10195,199 +10252,12 @@ function tryMatchParameterCall(node, func, pattern, match) {
10195
10252
  return { node, terminal };
10196
10253
  }
10197
10254
 
10198
- // src/terminals/jsx.ts
10199
- var import_ts_morph46 = require("ts-morph");
10200
- function tryMatchJsxReturn(node, pattern) {
10201
- let expr;
10202
- let source;
10203
- if (import_ts_morph46.Node.isReturnStatement(node)) {
10204
- expr = node.getExpression();
10205
- source = node;
10206
- } else if (import_ts_morph46.Node.isArrowFunction(node)) {
10207
- const body = node.getBody();
10208
- if (body !== void 0 && !import_ts_morph46.Node.isBlock(body)) {
10209
- expr = body;
10210
- source = body;
10211
- }
10212
- }
10213
- if (expr === void 0 || source === void 0) {
10214
- return null;
10215
- }
10216
- const tree = jsxToRenderNode(expr);
10217
- if (tree === null) {
10218
- return null;
10219
- }
10220
- const component = tree.type === "element" ? tree.tag : "Fragment";
10221
- const terminal = {
10222
- kind: pattern.kind,
10223
- statusCode: null,
10224
- body: null,
10225
- exceptionType: null,
10226
- message: null,
10227
- component,
10228
- delegateTarget: null,
10229
- emitEvent: null,
10230
- renderTree: tree,
10231
- location: {
10232
- start: startLineOf(node),
10233
- end: endLineOf(node)
10234
- }
10235
- };
10236
- return { node, source, terminal };
10237
- }
10238
- function componentTargetOf(tagNode) {
10239
- if (!import_ts_morph46.Node.isIdentifier(tagNode) || !/^[A-Z]/.test(tagNode.getText())) {
10240
- return null;
10241
- }
10242
- const target = functionTargetOf(tagNode);
10243
- return target === null ? null : { file: target.file, name: target.name };
10244
- }
10245
- function jsxToRenderNode(node) {
10246
- if (import_ts_morph46.Node.isJsxElement(node)) {
10247
- const opening = node.getOpeningElement();
10248
- const tag = opening.getTagNameNode().getText();
10249
- const attrs = collectJsxAttributes(opening);
10250
- const target = componentTargetOf(opening.getTagNameNode());
10251
- const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
10252
- return {
10253
- type: "element",
10254
- tag,
10255
- children,
10256
- ...attrs !== null ? { attrs } : {},
10257
- ...target !== null ? { target } : {}
10258
- };
10259
- }
10260
- if (import_ts_morph46.Node.isJsxSelfClosingElement(node)) {
10261
- const tag = node.getTagNameNode().getText();
10262
- const attrs = collectJsxAttributes(node);
10263
- const target = componentTargetOf(node.getTagNameNode());
10264
- return {
10265
- type: "element",
10266
- tag,
10267
- children: [],
10268
- ...attrs !== null ? { attrs } : {},
10269
- ...target !== null ? { target } : {}
10270
- };
10271
- }
10272
- if (import_ts_morph46.Node.isJsxFragment(node)) {
10273
- const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
10274
- return { type: "element", tag: "Fragment", children };
10275
- }
10276
- if (import_ts_morph46.Node.isParenthesizedExpression(node)) {
10277
- return jsxToRenderNode(node.getExpression());
10278
- }
10279
- return null;
10280
- }
10281
- function collectJsxAttributes(opening) {
10282
- const entries = {};
10283
- for (const attr of opening.getAttributes()) {
10284
- if (import_ts_morph46.Node.isJsxSpreadAttribute(attr)) {
10285
- const exprText = attr.getExpression().getText();
10286
- entries[`...${exprText}`] = exprText;
10287
- continue;
10288
- }
10289
- if (!import_ts_morph46.Node.isJsxAttribute(attr)) {
10290
- continue;
10291
- }
10292
- const name = attr.getNameNode().getText();
10293
- const initializer = attr.getInitializer();
10294
- if (initializer === void 0) {
10295
- entries[name] = "";
10296
- continue;
10297
- }
10298
- if (import_ts_morph46.Node.isJsxExpression(initializer)) {
10299
- const inner = initializer.getExpression();
10300
- entries[name] = inner !== void 0 ? inner.getText() : "";
10301
- continue;
10302
- }
10303
- entries[name] = initializer.getText();
10304
- }
10305
- return Object.keys(entries).length > 0 ? entries : null;
10306
- }
10307
- function jsxChildToRenderNode(child) {
10308
- if (import_ts_morph46.Node.isJsxText(child)) {
10309
- const text = child.getLiteralText().trim();
10310
- if (text.length === 0) {
10311
- return null;
10312
- }
10313
- return { type: "text", value: text };
10314
- }
10315
- if (import_ts_morph46.Node.isJsxExpression(child)) {
10316
- const inner = child.getExpression();
10317
- if (inner === void 0) {
10318
- return null;
10319
- }
10320
- return jsxExpressionToRenderNode(inner);
10321
- }
10322
- if (import_ts_morph46.Node.isJsxElement(child) || import_ts_morph46.Node.isJsxSelfClosingElement(child) || import_ts_morph46.Node.isJsxFragment(child)) {
10323
- return jsxToRenderNode(child);
10324
- }
10325
- return null;
10326
- }
10327
- function jsxExpressionToRenderNode(expr) {
10328
- if (import_ts_morph46.Node.isParenthesizedExpression(expr)) {
10329
- return jsxExpressionToRenderNode(expr.getExpression());
10330
- }
10331
- if (import_ts_morph46.Node.isBinaryExpression(expr)) {
10332
- const op = expr.getOperatorToken().getText();
10333
- if (op === "&&") {
10334
- const right = renderAlternative(peelParens(expr.getRight()));
10335
- if (right.kind === "jsx") {
10336
- return {
10337
- type: "conditional",
10338
- condition: expr.getLeft().getText(),
10339
- whenTrue: right.node,
10340
- whenFalse: null
10341
- };
10342
- }
10343
- }
10344
- }
10345
- if (import_ts_morph46.Node.isConditionalExpression(expr)) {
10346
- const truthy = renderAlternative(peelParens(expr.getWhenTrue()));
10347
- const falsy = renderAlternative(peelParens(expr.getWhenFalse()));
10348
- if (truthy.kind === "jsx") {
10349
- return {
10350
- type: "conditional",
10351
- condition: expr.getCondition().getText(),
10352
- whenTrue: truthy.node,
10353
- whenFalse: falsy.kind === "jsx" ? falsy.node : null
10354
- };
10355
- }
10356
- if (falsy.kind === "jsx") {
10357
- return {
10358
- type: "conditional",
10359
- condition: `!(${expr.getCondition().getText()})`,
10360
- whenTrue: falsy.node,
10361
- whenFalse: null
10362
- };
10363
- }
10364
- }
10365
- return { type: "expression", sourceText: expr.getText() };
10366
- }
10367
- function renderAlternative(node) {
10368
- if (import_ts_morph46.Node.isNullLiteral(node)) {
10369
- return { kind: "noRender" };
10370
- }
10371
- if (import_ts_morph46.Node.isFalseLiteral(node)) {
10372
- return { kind: "noRender" };
10373
- }
10374
- if (import_ts_morph46.Node.isIdentifier(node) && node.getText() === "undefined") {
10375
- return { kind: "noRender" };
10376
- }
10377
- if (import_ts_morph46.Node.isJsxElement(node) || import_ts_morph46.Node.isJsxSelfClosingElement(node) || import_ts_morph46.Node.isJsxFragment(node)) {
10378
- const tree = jsxToRenderNode(node);
10379
- if (tree !== null) {
10380
- return { kind: "jsx", node: tree };
10381
- }
10382
- }
10383
- return { kind: "notStatic" };
10384
- }
10385
-
10386
- // src/terminals/returns.ts
10387
- var import_ts_morph51 = require("ts-morph");
10255
+ // src/terminals/extract.ts
10256
+ var import_ts_morph47 = require("ts-morph");
10257
+ var import_values5 = require("@suss/values");
10388
10258
 
10389
10259
  // src/shapes/shapes.ts
10390
- var import_ts_morph47 = require("ts-morph");
10260
+ var import_ts_morph46 = require("ts-morph");
10391
10261
 
10392
10262
  // src/shapes/typeShapes.ts
10393
10263
  var import_node_crypto = require("crypto");
@@ -10846,60 +10716,60 @@ function extractShapeWithArguments(node, substitutions) {
10846
10716
  }
10847
10717
  function extractShapeInner(node) {
10848
10718
  const unwrapped2 = peelSyntax(node);
10849
- if (boundArguments !== null && import_ts_morph47.Node.isIdentifier(unwrapped2)) {
10719
+ if (boundArguments !== null && import_ts_morph46.Node.isIdentifier(unwrapped2)) {
10850
10720
  const argument = boundArguments.get(unwrapped2.getText());
10851
10721
  if (argument !== void 0 && argument !== unwrapped2) {
10852
10722
  return extractShape(argument);
10853
10723
  }
10854
10724
  }
10855
- if (import_ts_morph47.Node.isObjectLiteralExpression(unwrapped2)) {
10725
+ if (import_ts_morph46.Node.isObjectLiteralExpression(unwrapped2)) {
10856
10726
  return shapeFromObjectLiteral(unwrapped2);
10857
10727
  }
10858
- if (import_ts_morph47.Node.isArrayLiteralExpression(unwrapped2)) {
10728
+ if (import_ts_morph46.Node.isArrayLiteralExpression(unwrapped2)) {
10859
10729
  return shapeFromArrayLiteral(unwrapped2);
10860
10730
  }
10861
- if (import_ts_morph47.Node.isStringLiteral(unwrapped2) || import_ts_morph47.Node.isNoSubstitutionTemplateLiteral(unwrapped2)) {
10731
+ if (import_ts_morph46.Node.isStringLiteral(unwrapped2) || import_ts_morph46.Node.isNoSubstitutionTemplateLiteral(unwrapped2)) {
10862
10732
  return { type: "literal", value: unwrapped2.getLiteralValue() };
10863
10733
  }
10864
- if (import_ts_morph47.Node.isTemplateExpression(unwrapped2)) {
10734
+ if (import_ts_morph46.Node.isTemplateExpression(unwrapped2)) {
10865
10735
  return { type: "text" };
10866
10736
  }
10867
- if (import_ts_morph47.Node.isNumericLiteral(unwrapped2)) {
10737
+ if (import_ts_morph46.Node.isNumericLiteral(unwrapped2)) {
10868
10738
  return numericLiteralShape(
10869
10739
  unwrapped2.getLiteralValue(),
10870
10740
  unwrapped2.getText()
10871
10741
  );
10872
10742
  }
10873
- if (import_ts_morph47.Node.isPrefixUnaryExpression(unwrapped2)) {
10743
+ if (import_ts_morph46.Node.isPrefixUnaryExpression(unwrapped2)) {
10874
10744
  const op = unwrapped2.getOperatorToken();
10875
- if (op === import_ts_morph47.SyntaxKind.MinusToken || op === import_ts_morph47.SyntaxKind.PlusToken) {
10745
+ if (op === import_ts_morph46.SyntaxKind.MinusToken || op === import_ts_morph46.SyntaxKind.PlusToken) {
10876
10746
  const inner = unwrapped2.getOperand();
10877
- if (import_ts_morph47.Node.isNumericLiteral(inner)) {
10747
+ if (import_ts_morph46.Node.isNumericLiteral(inner)) {
10878
10748
  const v3 = inner.getLiteralValue();
10879
- const signed = op === import_ts_morph47.SyntaxKind.MinusToken ? -v3 : v3;
10749
+ const signed = op === import_ts_morph46.SyntaxKind.MinusToken ? -v3 : v3;
10880
10750
  const rawBody = inner.getText();
10881
- const raw = op === import_ts_morph47.SyntaxKind.MinusToken ? `-${rawBody}` : rawBody;
10751
+ const raw = op === import_ts_morph46.SyntaxKind.MinusToken ? `-${rawBody}` : rawBody;
10882
10752
  return numericLiteralShape(signed, raw);
10883
10753
  }
10884
10754
  }
10885
10755
  }
10886
- if (import_ts_morph47.Node.isBigIntLiteral(unwrapped2)) {
10756
+ if (import_ts_morph46.Node.isBigIntLiteral(unwrapped2)) {
10887
10757
  return { type: "ref", name: "bigint" };
10888
10758
  }
10889
10759
  const kind = unwrapped2.getKind();
10890
- if (kind === import_ts_morph47.SyntaxKind.TrueKeyword) {
10760
+ if (kind === import_ts_morph46.SyntaxKind.TrueKeyword) {
10891
10761
  return { type: "literal", value: true };
10892
10762
  }
10893
- if (kind === import_ts_morph47.SyntaxKind.FalseKeyword) {
10763
+ if (kind === import_ts_morph46.SyntaxKind.FalseKeyword) {
10894
10764
  return { type: "literal", value: false };
10895
10765
  }
10896
- if (kind === import_ts_morph47.SyntaxKind.NullKeyword) {
10766
+ if (kind === import_ts_morph46.SyntaxKind.NullKeyword) {
10897
10767
  return { type: "null" };
10898
10768
  }
10899
- if (import_ts_morph47.Node.isIdentifier(unwrapped2) && unwrapped2.getText() === "undefined") {
10769
+ if (import_ts_morph46.Node.isIdentifier(unwrapped2) && unwrapped2.getText() === "undefined") {
10900
10770
  return { type: "undefined" };
10901
10771
  }
10902
- if (import_ts_morph47.Node.isConditionalExpression(unwrapped2)) {
10772
+ if (import_ts_morph46.Node.isConditionalExpression(unwrapped2)) {
10903
10773
  const whenTrue = extractShape(unwrapped2.getWhenTrue());
10904
10774
  const whenFalse = extractShape(unwrapped2.getWhenFalse());
10905
10775
  const variants = [];
@@ -10930,7 +10800,7 @@ function shapeFromArrayLiteral(arr) {
10930
10800
  }
10931
10801
  const shapes = [];
10932
10802
  for (const el of elements) {
10933
- if (import_ts_morph47.Node.isSpreadElement(el)) {
10803
+ if (import_ts_morph46.Node.isSpreadElement(el)) {
10934
10804
  const inner = el.getExpression();
10935
10805
  const innerShape = extractShape(inner);
10936
10806
  if (innerShape?.type === "array") {
@@ -10949,7 +10819,7 @@ function shapeFromObjectLiteral(obj) {
10949
10819
  const properties = {};
10950
10820
  const unresolvedSpreads = [];
10951
10821
  for (const prop of obj.getProperties()) {
10952
- if (import_ts_morph47.Node.isPropertyAssignment(prop)) {
10822
+ if (import_ts_morph46.Node.isPropertyAssignment(prop)) {
10953
10823
  const name = propertyName2(prop);
10954
10824
  if (name === null) {
10955
10825
  continue;
@@ -10965,7 +10835,7 @@ function shapeFromObjectLiteral(obj) {
10965
10835
  };
10966
10836
  continue;
10967
10837
  }
10968
- if (import_ts_morph47.Node.isShorthandPropertyAssignment(prop)) {
10838
+ if (import_ts_morph46.Node.isShorthandPropertyAssignment(prop)) {
10969
10839
  const name = prop.getName();
10970
10840
  const valueExpr = prop.getNameNode();
10971
10841
  properties[name] = extractShape(valueExpr) ?? {
@@ -10974,7 +10844,7 @@ function shapeFromObjectLiteral(obj) {
10974
10844
  };
10975
10845
  continue;
10976
10846
  }
10977
- if (import_ts_morph47.Node.isSpreadAssignment(prop)) {
10847
+ if (import_ts_morph46.Node.isSpreadAssignment(prop)) {
10978
10848
  const expr = prop.getExpression();
10979
10849
  const spreadShape = extractShape(expr);
10980
10850
  if (spreadShape?.type === "record") {
@@ -10993,7 +10863,7 @@ function shapeFromObjectLiteral(obj) {
10993
10863
  }
10994
10864
  function propertyName2(prop) {
10995
10865
  const nameNode = prop.getNameNode();
10996
- if (import_ts_morph47.Node.isIdentifier(nameNode) || import_ts_morph47.Node.isStringLiteral(nameNode) || import_ts_morph47.Node.isNoSubstitutionTemplateLiteral(nameNode) || import_ts_morph47.Node.isNumericLiteral(nameNode)) {
10866
+ if (import_ts_morph46.Node.isIdentifier(nameNode) || import_ts_morph46.Node.isStringLiteral(nameNode) || import_ts_morph46.Node.isNoSubstitutionTemplateLiteral(nameNode) || import_ts_morph46.Node.isNumericLiteral(nameNode)) {
10997
10867
  return nameNode.getText().replace(/^["']|["']$/g, "");
10998
10868
  }
10999
10869
  return null;
@@ -11019,22 +10889,20 @@ function collapseVariants2(shapes) {
11019
10889
  }
11020
10890
 
11021
10891
  // src/terminals/extract.ts
11022
- var import_ts_morph48 = require("ts-morph");
11023
- var import_values5 = require("@suss/values");
11024
10892
  function unwrapAs(node) {
11025
10893
  return peelSyntax(node);
11026
10894
  }
11027
10895
  function unwrapJsonStringify(node) {
11028
10896
  const outer = unwrapAs(node);
11029
- if (!import_ts_morph48.Node.isCallExpression(outer)) {
10897
+ if (!import_ts_morph47.Node.isCallExpression(outer)) {
11030
10898
  return node;
11031
10899
  }
11032
10900
  const callee = outer.getExpression();
11033
- if (!import_ts_morph48.Node.isPropertyAccessExpression(callee) || callee.getName() !== "stringify") {
10901
+ if (!import_ts_morph47.Node.isPropertyAccessExpression(callee) || callee.getName() !== "stringify") {
11034
10902
  return node;
11035
10903
  }
11036
10904
  const receiver = callee.getExpression();
11037
- if (!import_ts_morph48.Node.isIdentifier(receiver) || receiver.getText() !== "JSON") {
10905
+ if (!import_ts_morph47.Node.isIdentifier(receiver) || receiver.getText() !== "JSON") {
11038
10906
  return node;
11039
10907
  }
11040
10908
  const args = outer.getArguments();
@@ -11044,7 +10912,7 @@ function unwrapJsonStringify(node) {
11044
10912
  return unwrapAs(args[0]);
11045
10913
  }
11046
10914
  function substituted(value, ctx) {
11047
- if (ctx.substitutions === void 0 || !import_ts_morph48.Node.isIdentifier(value)) {
10915
+ if (ctx.substitutions === void 0 || !import_ts_morph47.Node.isIdentifier(value)) {
11048
10916
  return value;
11049
10917
  }
11050
10918
  return ctx.substitutions.get(value.getText()) ?? value;
@@ -11057,10 +10925,10 @@ function extractStatusCode(ctx) {
11057
10925
  if (result !== null) {
11058
10926
  return result;
11059
10927
  }
11060
- if (ctx.extraction.defaultStatusCode !== void 0) {
11061
- return { type: "literal", value: ctx.extraction.defaultStatusCode };
11062
- }
11063
- return null;
10928
+ return declaredStatusCode(ctx.extraction);
10929
+ }
10930
+ function declaredStatusCode(extraction) {
10931
+ return extraction.defaultStatusCode !== void 0 ? { type: "literal", value: extraction.defaultStatusCode } : null;
11064
10932
  }
11065
10933
  function matchConstructorCode(ctorText, codes) {
11066
10934
  const fullMatch = codes[ctorText];
@@ -11088,10 +10956,10 @@ function statusFromProperty(obj, name, ctx) {
11088
10956
  if (prop === void 0) {
11089
10957
  return null;
11090
10958
  }
11091
- if (import_ts_morph48.Node.isShorthandPropertyAssignment(prop)) {
10959
+ if (import_ts_morph47.Node.isShorthandPropertyAssignment(prop)) {
11092
10960
  return { type: "dynamic", sourceText: prop.getName() };
11093
10961
  }
11094
- if (!import_ts_morph48.Node.isPropertyAssignment(prop)) {
10962
+ if (!import_ts_morph47.Node.isPropertyAssignment(prop)) {
11095
10963
  return null;
11096
10964
  }
11097
10965
  const value = prop.getInitializer();
@@ -11114,7 +10982,7 @@ function extractStatusCodeFromRule(ctx) {
11114
10982
  return null;
11115
10983
  }
11116
10984
  const first = ctx.throwCallArgs?.[0];
11117
- if (first !== void 0 && import_ts_morph48.Node.isNumericLiteral(first)) {
10985
+ if (first !== void 0 && import_ts_morph47.Node.isNumericLiteral(first)) {
11118
10986
  const value = first.getLiteralValue();
11119
10987
  if (value >= 100 && value <= 599) {
11120
10988
  return { type: "literal", value };
@@ -11127,7 +10995,7 @@ function extractStatusCodeFromRule(ctx) {
11127
10995
  return null;
11128
10996
  }
11129
10997
  const arg = ctx.throwCallArgs[sc.position];
11130
- if (arg === void 0 || !import_ts_morph48.Node.isNewExpression(arg)) {
10998
+ if (arg === void 0 || !import_ts_morph47.Node.isNewExpression(arg)) {
11131
10999
  return null;
11132
11000
  }
11133
11001
  const ctorText = arg.getExpression().getText();
@@ -11136,7 +11004,7 @@ function extractStatusCodeFromRule(ctx) {
11136
11004
  if (sc.from === "argumentProperty") {
11137
11005
  const args = ctx.throwCallArgs ?? ctx.calls?.[0]?.getArguments();
11138
11006
  const arg = args?.[sc.position];
11139
- if (arg === void 0 || !import_ts_morph48.Node.isObjectLiteralExpression(arg)) {
11007
+ if (arg === void 0 || !import_ts_morph47.Node.isObjectLiteralExpression(arg)) {
11140
11008
  return null;
11141
11009
  }
11142
11010
  return statusFromProperty(arg, sc.name, ctx);
@@ -11146,8 +11014,8 @@ function extractStatusCodeFromRule(ctx) {
11146
11014
  return null;
11147
11015
  }
11148
11016
  for (const prop of ctx.returnedObj.getProperties()) {
11149
- if (!import_ts_morph48.Node.isPropertyAssignment(prop)) {
11150
- if (import_ts_morph48.Node.isShorthandPropertyAssignment(prop)) {
11017
+ if (!import_ts_morph47.Node.isPropertyAssignment(prop)) {
11018
+ if (import_ts_morph47.Node.isShorthandPropertyAssignment(prop)) {
11151
11019
  if (prop.getName() === sc.name) {
11152
11020
  const resolved = ctx.substitutions?.get(prop.getName());
11153
11021
  if (resolved !== void 0) {
@@ -11205,8 +11073,8 @@ function extractBody(ctx) {
11205
11073
  return null;
11206
11074
  }
11207
11075
  for (const prop of ctx.returnedObj.getProperties()) {
11208
- if (!import_ts_morph48.Node.isPropertyAssignment(prop)) {
11209
- if (import_ts_morph48.Node.isShorthandPropertyAssignment(prop)) {
11076
+ if (!import_ts_morph47.Node.isPropertyAssignment(prop)) {
11077
+ if (import_ts_morph47.Node.isShorthandPropertyAssignment(prop)) {
11210
11078
  if (prop.getName() === b.name) {
11211
11079
  const resolved = ctx.substitutions?.get(prop.getName());
11212
11080
  if (resolved !== void 0) {
@@ -11260,6 +11128,197 @@ function extractBody(ctx) {
11260
11128
  return null;
11261
11129
  }
11262
11130
 
11131
+ // src/terminals/jsx.ts
11132
+ var import_ts_morph48 = require("ts-morph");
11133
+ function tryMatchJsxReturn(node, pattern) {
11134
+ let expr;
11135
+ let source;
11136
+ if (import_ts_morph48.Node.isReturnStatement(node)) {
11137
+ expr = node.getExpression();
11138
+ source = node;
11139
+ } else if (import_ts_morph48.Node.isArrowFunction(node)) {
11140
+ const body = node.getBody();
11141
+ if (body !== void 0 && !import_ts_morph48.Node.isBlock(body)) {
11142
+ expr = body;
11143
+ source = body;
11144
+ }
11145
+ }
11146
+ if (expr === void 0 || source === void 0) {
11147
+ return null;
11148
+ }
11149
+ const tree = jsxToRenderNode(expr);
11150
+ if (tree === null) {
11151
+ return null;
11152
+ }
11153
+ const component = tree.type === "element" ? tree.tag : "Fragment";
11154
+ const terminal = {
11155
+ kind: pattern.kind,
11156
+ statusCode: null,
11157
+ body: null,
11158
+ exceptionType: null,
11159
+ message: null,
11160
+ component,
11161
+ delegateTarget: null,
11162
+ emitEvent: null,
11163
+ renderTree: tree,
11164
+ location: {
11165
+ start: startLineOf(node),
11166
+ end: endLineOf(node)
11167
+ }
11168
+ };
11169
+ return { node, source, terminal };
11170
+ }
11171
+ function componentTargetOf(tagNode) {
11172
+ if (!import_ts_morph48.Node.isIdentifier(tagNode) || !/^[A-Z]/.test(tagNode.getText())) {
11173
+ return null;
11174
+ }
11175
+ const target = functionTargetOf(tagNode);
11176
+ return target === null ? null : { file: target.file, name: target.name };
11177
+ }
11178
+ function jsxToRenderNode(node) {
11179
+ if (import_ts_morph48.Node.isJsxElement(node)) {
11180
+ const opening = node.getOpeningElement();
11181
+ const tag = opening.getTagNameNode().getText();
11182
+ const attrs = collectJsxAttributes(opening);
11183
+ const target = componentTargetOf(opening.getTagNameNode());
11184
+ const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
11185
+ return {
11186
+ type: "element",
11187
+ tag,
11188
+ children,
11189
+ ...attrs !== null ? { attrs } : {},
11190
+ ...target !== null ? { target } : {}
11191
+ };
11192
+ }
11193
+ if (import_ts_morph48.Node.isJsxSelfClosingElement(node)) {
11194
+ const tag = node.getTagNameNode().getText();
11195
+ const attrs = collectJsxAttributes(node);
11196
+ const target = componentTargetOf(node.getTagNameNode());
11197
+ return {
11198
+ type: "element",
11199
+ tag,
11200
+ children: [],
11201
+ ...attrs !== null ? { attrs } : {},
11202
+ ...target !== null ? { target } : {}
11203
+ };
11204
+ }
11205
+ if (import_ts_morph48.Node.isJsxFragment(node)) {
11206
+ const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
11207
+ return { type: "element", tag: "Fragment", children };
11208
+ }
11209
+ if (import_ts_morph48.Node.isParenthesizedExpression(node)) {
11210
+ return jsxToRenderNode(node.getExpression());
11211
+ }
11212
+ return null;
11213
+ }
11214
+ function collectJsxAttributes(opening) {
11215
+ const entries = {};
11216
+ for (const attr of opening.getAttributes()) {
11217
+ if (import_ts_morph48.Node.isJsxSpreadAttribute(attr)) {
11218
+ const exprText = attr.getExpression().getText();
11219
+ entries[`...${exprText}`] = exprText;
11220
+ continue;
11221
+ }
11222
+ if (!import_ts_morph48.Node.isJsxAttribute(attr)) {
11223
+ continue;
11224
+ }
11225
+ const name = attr.getNameNode().getText();
11226
+ const initializer = attr.getInitializer();
11227
+ if (initializer === void 0) {
11228
+ entries[name] = "";
11229
+ continue;
11230
+ }
11231
+ if (import_ts_morph48.Node.isJsxExpression(initializer)) {
11232
+ const inner = initializer.getExpression();
11233
+ entries[name] = inner !== void 0 ? inner.getText() : "";
11234
+ continue;
11235
+ }
11236
+ entries[name] = initializer.getText();
11237
+ }
11238
+ return Object.keys(entries).length > 0 ? entries : null;
11239
+ }
11240
+ function jsxChildToRenderNode(child) {
11241
+ if (import_ts_morph48.Node.isJsxText(child)) {
11242
+ const text = child.getLiteralText().trim();
11243
+ if (text.length === 0) {
11244
+ return null;
11245
+ }
11246
+ return { type: "text", value: text };
11247
+ }
11248
+ if (import_ts_morph48.Node.isJsxExpression(child)) {
11249
+ const inner = child.getExpression();
11250
+ if (inner === void 0) {
11251
+ return null;
11252
+ }
11253
+ return jsxExpressionToRenderNode(inner);
11254
+ }
11255
+ if (import_ts_morph48.Node.isJsxElement(child) || import_ts_morph48.Node.isJsxSelfClosingElement(child) || import_ts_morph48.Node.isJsxFragment(child)) {
11256
+ return jsxToRenderNode(child);
11257
+ }
11258
+ return null;
11259
+ }
11260
+ function jsxExpressionToRenderNode(expr) {
11261
+ if (import_ts_morph48.Node.isParenthesizedExpression(expr)) {
11262
+ return jsxExpressionToRenderNode(expr.getExpression());
11263
+ }
11264
+ if (import_ts_morph48.Node.isBinaryExpression(expr)) {
11265
+ const op = expr.getOperatorToken().getText();
11266
+ if (op === "&&") {
11267
+ const right = renderAlternative(peelParens(expr.getRight()));
11268
+ if (right.kind === "jsx") {
11269
+ return {
11270
+ type: "conditional",
11271
+ condition: expr.getLeft().getText(),
11272
+ whenTrue: right.node,
11273
+ whenFalse: null
11274
+ };
11275
+ }
11276
+ }
11277
+ }
11278
+ if (import_ts_morph48.Node.isConditionalExpression(expr)) {
11279
+ const truthy = renderAlternative(peelParens(expr.getWhenTrue()));
11280
+ const falsy = renderAlternative(peelParens(expr.getWhenFalse()));
11281
+ if (truthy.kind === "jsx") {
11282
+ return {
11283
+ type: "conditional",
11284
+ condition: expr.getCondition().getText(),
11285
+ whenTrue: truthy.node,
11286
+ whenFalse: falsy.kind === "jsx" ? falsy.node : null
11287
+ };
11288
+ }
11289
+ if (falsy.kind === "jsx") {
11290
+ return {
11291
+ type: "conditional",
11292
+ condition: `!(${expr.getCondition().getText()})`,
11293
+ whenTrue: falsy.node,
11294
+ whenFalse: null
11295
+ };
11296
+ }
11297
+ }
11298
+ return { type: "expression", sourceText: expr.getText() };
11299
+ }
11300
+ function renderAlternative(node) {
11301
+ if (import_ts_morph48.Node.isNullLiteral(node)) {
11302
+ return { kind: "noRender" };
11303
+ }
11304
+ if (import_ts_morph48.Node.isFalseLiteral(node)) {
11305
+ return { kind: "noRender" };
11306
+ }
11307
+ if (import_ts_morph48.Node.isIdentifier(node) && node.getText() === "undefined") {
11308
+ return { kind: "noRender" };
11309
+ }
11310
+ if (import_ts_morph48.Node.isJsxElement(node) || import_ts_morph48.Node.isJsxSelfClosingElement(node) || import_ts_morph48.Node.isJsxFragment(node)) {
11311
+ const tree = jsxToRenderNode(node);
11312
+ if (tree !== null) {
11313
+ return { kind: "jsx", node: tree };
11314
+ }
11315
+ }
11316
+ return { kind: "notStatic" };
11317
+ }
11318
+
11319
+ // src/terminals/returns.ts
11320
+ var import_ts_morph51 = require("ts-morph");
11321
+
11263
11322
  // src/terminals/helperResolution.ts
11264
11323
  var import_ts_morph49 = require("ts-morph");
11265
11324
  function resolveHelperReturn(call, resolveCallee2) {
@@ -11831,10 +11890,9 @@ function tryMatchReturnStatement(node, pattern, func, allPatterns) {
11831
11890
  return null;
11832
11891
  }
11833
11892
  function buildReturnTerminal(locationNode, pattern, body) {
11834
- const statusCode = pattern.extraction.defaultStatusCode !== void 0 ? { type: "literal", value: pattern.extraction.defaultStatusCode } : null;
11835
11893
  const terminal = {
11836
11894
  kind: pattern.kind,
11837
- statusCode,
11895
+ statusCode: declaredStatusCode(pattern.extraction),
11838
11896
  body,
11839
11897
  exceptionType: null,
11840
11898
  message: null,
@@ -12123,15 +12181,15 @@ function functionMayFallThrough(func) {
12123
12181
  }
12124
12182
  return true;
12125
12183
  }
12126
- function makeFallthroughTerminal(func) {
12184
+ function makeFallthroughTerminal(func, pattern) {
12127
12185
  const body = func.getBody();
12128
12186
  const anchor = body ?? func;
12129
12187
  const line = endLineOf(anchor);
12130
12188
  return {
12131
12189
  node: anchor,
12132
12190
  terminal: {
12133
- kind: "return",
12134
- statusCode: null,
12191
+ kind: pattern.kind,
12192
+ statusCode: declaredStatusCode(pattern.extraction),
12135
12193
  body: null,
12136
12194
  exceptionType: null,
12137
12195
  message: null,
@@ -12217,7 +12275,7 @@ function countUnmatchedReturns(func, terminals, barriers = NO_BARRIERS) {
12217
12275
  });
12218
12276
  return unmatched;
12219
12277
  }
12220
- function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2) {
12278
+ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2, collectCallAccounting = false) {
12221
12279
  const terminals = findTerminals(
12222
12280
  func,
12223
12281
  terminalPatterns,
@@ -12251,15 +12309,15 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12251
12309
  terminals.map(({ node }) => node),
12252
12310
  barriers
12253
12311
  );
12254
- const wantsFallthrough = terminalPatterns.some(
12312
+ const fallthroughPattern = terminalPatterns.find(
12255
12313
  (p) => p.match.type === "functionFallthrough"
12256
12314
  );
12257
- if (wantsFallthrough && functionMayFallThrough(func)) {
12315
+ if (fallthroughPattern !== void 0 && functionMayFallThrough(func)) {
12258
12316
  const hasDefaultTerminal = terminals.some(
12259
12317
  ({ node }) => (byTerminal.get(node) ?? []).some(isDefaultConditionList)
12260
12318
  );
12261
12319
  if (!hasDefaultTerminal) {
12262
- const synthetic = makeFallthroughTerminal(func);
12320
+ const synthetic = makeFallthroughTerminal(func, fallthroughPattern);
12263
12321
  terminals.push(synthetic);
12264
12322
  const pathResult = computePathConditions(
12265
12323
  func,
@@ -12305,8 +12363,8 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12305
12363
  seenBranches.add(key);
12306
12364
  return true;
12307
12365
  });
12366
+ const terminalNodes = new Set(terminals.map(({ node }) => node));
12308
12367
  if (invocations.length > 0) {
12309
- const terminalNodes = new Set(terminals.map(({ node }) => node));
12310
12368
  const sideEffects = invocations.filter(
12311
12369
  (i) => !writesTerminal(i.node, terminalNodes)
12312
12370
  );
@@ -12322,7 +12380,39 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12322
12380
  branch.extraEffects = [...branch.extraEffects ?? [], ...extra];
12323
12381
  }
12324
12382
  }
12325
- return { branches: distinctBranches, terminals };
12383
+ return {
12384
+ branches: distinctBranches,
12385
+ terminals,
12386
+ ...collectCallAccounting ? {
12387
+ callAccounting: invocations.map(
12388
+ (site) => accountFor(site, terminalNodes, distinctBranches)
12389
+ )
12390
+ } : {}
12391
+ };
12392
+ }
12393
+ function accountFor(site, terminalNodes, branches) {
12394
+ const { node, line } = site;
12395
+ const callee = node.getExpression().getText();
12396
+ if (writesTerminal(node, terminalNodes)) {
12397
+ return { node, callee, line, outcome: "terminal" };
12398
+ }
12399
+ const call = {
12400
+ line,
12401
+ alwaysRuns: site.alwaysRuns,
12402
+ preconditions: preconditionsOf(site.effect)
12403
+ };
12404
+ if (branches.some((branch) => firesOn(call, branch))) {
12405
+ return { node, callee, line, outcome: "invocation" };
12406
+ }
12407
+ const reachable = branches.some(
12408
+ (branch) => site.alwaysRuns || (0, import_extractor5.runsBefore)(line, branch.terminal.location.end)
12409
+ );
12410
+ return {
12411
+ node,
12412
+ callee,
12413
+ line,
12414
+ outcome: reachable ? "unrecorded" : "unreachable"
12415
+ };
12326
12416
  }
12327
12417
 
12328
12418
  // src/bootstrap/sourceFileLookup.ts
@@ -12537,7 +12627,7 @@ function unwrapContractInit(init) {
12537
12627
  }
12538
12628
  return null;
12539
12629
  }
12540
- function extractEndpointContract(endpointNode, pattern, framework) {
12630
+ function extractEndpointContract(endpointNode, pattern, framework, resolution) {
12541
12631
  if (!import_ts_morph56.Node.isObjectLiteralExpression(endpointNode)) {
12542
12632
  return null;
12543
12633
  }
@@ -12562,9 +12652,10 @@ function extractEndpointContract(endpointNode, pattern, framework) {
12562
12652
  }
12563
12653
  }
12564
12654
  if (propName === pattern.responseExtraction.property) {
12565
- const val = prop.getInitializer();
12566
- if (val !== void 0 && import_ts_morph56.Node.isObjectLiteralExpression(val)) {
12567
- for (const respProp of val.getProperties()) {
12655
+ const written = prop.getInitializer();
12656
+ const val = written === void 0 ? null : objectLiteralOf(written, resolution);
12657
+ if (val !== null) {
12658
+ for (const respProp of propertiesOf(val, resolution)) {
12568
12659
  if (!import_ts_morph56.Node.isPropertyAssignment(respProp)) {
12569
12660
  continue;
12570
12661
  }
@@ -12603,7 +12694,12 @@ function readContract(unit, pattern, framework, resolution) {
12603
12694
  if (endpointNode === null) {
12604
12695
  return null;
12605
12696
  }
12606
- return extractEndpointContract(endpointNode, pattern, framework);
12697
+ return extractEndpointContract(
12698
+ endpointNode,
12699
+ pattern,
12700
+ framework,
12701
+ resolution
12702
+ );
12607
12703
  }
12608
12704
  const routerInfo = findRouterCall(unit);
12609
12705
  if (routerInfo === null) {
@@ -12624,7 +12720,12 @@ function readContract(unit, pattern, framework, resolution) {
12624
12720
  if (endpointInit === void 0) {
12625
12721
  continue;
12626
12722
  }
12627
- return extractEndpointContract(endpointInit, pattern, framework);
12723
+ return extractEndpointContract(
12724
+ endpointInit,
12725
+ pattern,
12726
+ framework,
12727
+ resolution
12728
+ );
12628
12729
  }
12629
12730
  return null;
12630
12731
  }
@@ -12701,7 +12802,12 @@ function readContractForClientCall(callExpression, methodName, pattern, framewor
12701
12802
  if (endpointInit === void 0) {
12702
12803
  continue;
12703
12804
  }
12704
- return extractEndpointContract(endpointInit, pattern, framework);
12805
+ return extractEndpointContract(
12806
+ endpointInit,
12807
+ pattern,
12808
+ framework,
12809
+ resolution
12810
+ );
12705
12811
  }
12706
12812
  return null;
12707
12813
  }
@@ -13453,7 +13559,11 @@ function recordEdge(edgesByChild, candidate) {
13453
13559
  // src/discovery/wrapperIndex.ts
13454
13560
  var import_ts_morph60 = require("ts-morph");
13455
13561
  var import_resolution6 = require("@suss/resolution");
13456
- var NO_WRAPPERS = { wrappersFor: () => [], unitsIn: () => [] };
13562
+ var NO_WRAPPERS = {
13563
+ wrappersFor: () => [],
13564
+ unfollowedFor: () => [],
13565
+ unitsIn: () => []
13566
+ };
13457
13567
  var ANONYMOUS = "<anon>";
13458
13568
  function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13459
13569
  const workByPack = /* @__PURE__ */ new Map();
@@ -13479,21 +13589,32 @@ function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13479
13589
  }
13480
13590
  }
13481
13591
  const bySubject = /* @__PURE__ */ new Map();
13592
+ const unfollowedBySubject = /* @__PURE__ */ new Map();
13482
13593
  const unitsByFile = /* @__PURE__ */ new Map();
13594
+ const stopsSeen = /* @__PURE__ */ new Set();
13483
13595
  let found = false;
13484
13596
  for (const [packName, work] of workByPack) {
13485
13597
  const candidates = [];
13486
13598
  for (const { sourceFile, pattern, match, wraps } of work) {
13487
- candidates.push(
13488
- ...discoverWrapperRegistrations(
13489
- sourceFile,
13490
- pattern,
13491
- match,
13492
- wraps,
13493
- resolution,
13494
- mountPrefixes
13495
- )
13599
+ const scan = discoverWrapperRegistrations(
13600
+ sourceFile,
13601
+ pattern,
13602
+ match,
13603
+ wraps,
13604
+ resolution,
13605
+ mountPrefixes
13496
13606
  );
13607
+ candidates.push(...scan.candidates);
13608
+ for (const { subjectId, callId, stop } of scan.unresolved) {
13609
+ if (stopsSeen.has(callId)) {
13610
+ continue;
13611
+ }
13612
+ stopsSeen.add(callId);
13613
+ found = true;
13614
+ const stops = unfollowedBySubject.get(subjectId) ?? [];
13615
+ stops.push(stop);
13616
+ unfollowedBySubject.set(subjectId, stops);
13617
+ }
13497
13618
  }
13498
13619
  for (const candidate of mostSpecific(candidates)) {
13499
13620
  found = true;
@@ -13511,6 +13632,7 @@ function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13511
13632
  }
13512
13633
  return {
13513
13634
  wrappersFor: (subjectId) => bySubject.get(subjectId) ?? [],
13635
+ unfollowedFor: (subjectId) => unfollowedBySubject.get(subjectId) ?? [],
13514
13636
  unitsIn: (file, pack) => unitsByFile.get(unitKey(file, pack)) ?? []
13515
13637
  };
13516
13638
  }
@@ -13544,10 +13666,20 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13544
13666
  match.importName,
13545
13667
  resolution
13546
13668
  );
13669
+ if ("constructorOption" in wraps) {
13670
+ return discoverConstructorOptions(
13671
+ sourceFile,
13672
+ pattern,
13673
+ wraps,
13674
+ subjects,
13675
+ resolution
13676
+ );
13677
+ }
13547
13678
  if (subjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution)) {
13548
- return [];
13679
+ return { candidates: [], unresolved: [] };
13549
13680
  }
13550
13681
  const candidates = [];
13682
+ const unresolved = [];
13551
13683
  sourceFile.forEachDescendant((node) => {
13552
13684
  if (!import_ts_morph60.Node.isCallExpression(node)) {
13553
13685
  return;
@@ -13570,8 +13702,13 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13570
13702
  if (targetArg === void 0) {
13571
13703
  return;
13572
13704
  }
13705
+ const subjectId = nodeId(subjectNode);
13573
13706
  const target = functionValueOf(targetArg, resolution);
13574
13707
  if (target === null) {
13708
+ const stop = factoryStopOf(targetArg);
13709
+ if (stop !== null) {
13710
+ unresolved.push({ subjectId, callId: nodeId(node), stop });
13711
+ }
13575
13712
  return;
13576
13713
  }
13577
13714
  if (wraps.arity !== void 0 && target.getParameters().length !== wraps.arity) {
@@ -13581,29 +13718,136 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13581
13718
  if (scope === null) {
13582
13719
  return;
13583
13720
  }
13584
- const subjectId = nodeId(subjectNode);
13585
13721
  const mountedScope = scope === void 0 ? void 0 : mountedAt(subjectId, mountPrefixes, scope);
13586
- const named2 = functionNameOrAnon(target) !== ANONYMOUS;
13587
- candidates.push({
13588
- subjectId,
13589
- callId: nodeId(node),
13590
- targetId: nodeId(target),
13591
- byArity: wraps.arity !== void 0,
13592
- target,
13593
- pattern,
13594
- registeredIn: sourceFile.getFilePath(),
13595
- named: named2,
13596
- reference: {
13597
- file: target.getSourceFile().getFilePath(),
13598
- // A function written out at the registration call has no name
13599
- // of its own, so it goes by the method that registered it.
13600
- name: named2 ? functionNameOrAnon(target) : wraps.method,
13601
- ...wraps.throwParam === void 0 ? {} : { onThrow: true },
13602
- ...mountedScope === void 0 ? {} : { scope: mountedScope }
13603
- }
13604
- });
13722
+ candidates.push(
13723
+ candidateOf(
13724
+ {
13725
+ subjectId,
13726
+ callId: nodeId(node),
13727
+ target,
13728
+ label: labelFor(targetArg, wraps),
13729
+ byArity: wraps.arity !== void 0,
13730
+ onThrow: wraps.throwParam !== void 0,
13731
+ scope: mountedScope
13732
+ },
13733
+ pattern,
13734
+ sourceFile
13735
+ )
13736
+ );
13605
13737
  });
13606
- return candidates;
13738
+ return { candidates, unresolved };
13739
+ }
13740
+ function discoverConstructorOptions(sourceFile, pattern, wraps, subjects, resolution) {
13741
+ const candidates = [];
13742
+ const unresolved = [];
13743
+ const seen = /* @__PURE__ */ new Set();
13744
+ for (const construction of subjects.values()) {
13745
+ const subjectId = nodeId(construction);
13746
+ if (seen.has(subjectId) || construction.getSourceFile() !== sourceFile) {
13747
+ continue;
13748
+ }
13749
+ seen.add(subjectId);
13750
+ const option = constructorOptionOf(construction, wraps, resolution);
13751
+ if (option === void 0) {
13752
+ continue;
13753
+ }
13754
+ const callId = nodeId(option);
13755
+ const written = propertyValueOf(option);
13756
+ const target = propertyFunctionOf(option, resolution);
13757
+ if (target === null) {
13758
+ const stop = written === null ? null : factoryStopOf(written);
13759
+ if (stop !== null) {
13760
+ unresolved.push({ subjectId, callId, stop });
13761
+ }
13762
+ continue;
13763
+ }
13764
+ candidates.push(
13765
+ candidateOf(
13766
+ {
13767
+ subjectId,
13768
+ callId,
13769
+ target,
13770
+ label: (written === null ? void 0 : factoryNameOf(written)) ?? wraps.constructorOption,
13771
+ byArity: false,
13772
+ onThrow: wraps.throwParam !== void 0,
13773
+ scope: void 0
13774
+ },
13775
+ pattern,
13776
+ sourceFile
13777
+ )
13778
+ );
13779
+ }
13780
+ return { candidates, unresolved };
13781
+ }
13782
+ function constructorOptionOf(construction, wraps, resolution) {
13783
+ if (!(import_ts_morph60.Node.isNewExpression(construction) || import_ts_morph60.Node.isCallExpression(construction))) {
13784
+ return void 0;
13785
+ }
13786
+ const optionsArg = construction.getArguments()[wraps.targetPosition];
13787
+ if (optionsArg === void 0) {
13788
+ return void 0;
13789
+ }
13790
+ const options = objectLiteralOf(optionsArg, resolution);
13791
+ if (options === null) {
13792
+ return void 0;
13793
+ }
13794
+ return propertiesOf(options, resolution).find(
13795
+ (property) => propertyNameOf(property) === wraps.constructorOption
13796
+ );
13797
+ }
13798
+ function candidateOf(registration, pattern, sourceFile) {
13799
+ const { target, label, onThrow, scope } = registration;
13800
+ const named2 = functionNameOrAnon(target) !== ANONYMOUS;
13801
+ return {
13802
+ subjectId: registration.subjectId,
13803
+ callId: registration.callId,
13804
+ targetId: nodeId(target),
13805
+ byArity: registration.byArity,
13806
+ target,
13807
+ pattern,
13808
+ registeredIn: sourceFile.getFilePath(),
13809
+ named: named2,
13810
+ reference: {
13811
+ file: target.getSourceFile().getFilePath(),
13812
+ name: named2 ? functionNameOrAnon(target) : label,
13813
+ ...onThrow ? { onThrow: true } : {},
13814
+ ...scope === void 0 ? {} : { scope }
13815
+ }
13816
+ };
13817
+ }
13818
+ function labelFor(targetArg, wraps) {
13819
+ return factoryNameOf(targetArg) ?? wraps.method;
13820
+ }
13821
+ function factoryNameOf(targetArg) {
13822
+ const written = factKeyOf(targetArg);
13823
+ if (!import_ts_morph60.Node.isCallExpression(written)) {
13824
+ return void 0;
13825
+ }
13826
+ const callee = written.getExpression();
13827
+ if (import_ts_morph60.Node.isIdentifier(callee)) {
13828
+ return callee.getText();
13829
+ }
13830
+ if (import_ts_morph60.Node.isPropertyAccessExpression(callee)) {
13831
+ return callee.getName();
13832
+ }
13833
+ return void 0;
13834
+ }
13835
+ function factoryStopOf(targetArg) {
13836
+ const written = factKeyOf(targetArg);
13837
+ if (!import_ts_morph60.Node.isCallExpression(written)) {
13838
+ return null;
13839
+ }
13840
+ const callee = factoryNameOf(targetArg);
13841
+ if (callee === void 0) {
13842
+ return null;
13843
+ }
13844
+ const reason = classifyStop(
13845
+ declarationsBehind(written.getExpression().getSymbol())
13846
+ );
13847
+ if (!(0, import_behavioral_ir2.worthRecording)(reason)) {
13848
+ return null;
13849
+ }
13850
+ return { callee, reason: "unresolvedWrapper" };
13607
13851
  }
13608
13852
  function mountedAt(subjectId, mountPrefixes, scope) {
13609
13853
  const prefix = mountPrefixes?.prefixForId?.(subjectId) ?? "";
@@ -13975,22 +14219,23 @@ var import_ts_morph62 = require("ts-morph");
13975
14219
  var import_behavioral_ir6 = require("@suss/behavioral-ir");
13976
14220
  var import_datalog4 = require("@suss/datalog");
13977
14221
  var import_extractor7 = require("@suss/extractor");
14222
+ var GENERIC_TERMINALS = [
14223
+ { kind: "return", match: { type: "returnStatement" }, extraction: {} },
14224
+ { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
14225
+ {
14226
+ // Implicit fall-through at the end of a function body: covers
14227
+ // void-returning orchestrators that never write `return`.
14228
+ kind: "return",
14229
+ match: { type: "functionFallthrough" },
14230
+ extraction: {}
14231
+ }
14232
+ ];
13978
14233
  var reachablePack = {
13979
14234
  name: "reachable",
13980
14235
  languages: ["typescript"],
13981
14236
  protocol: "in-process",
13982
14237
  discovery: [],
13983
- terminals: [
13984
- { kind: "return", match: { type: "returnStatement" }, extraction: {} },
13985
- { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
13986
- {
13987
- // Implicit fall-through at the end of a function body: covers
13988
- // void-returning orchestrators that never write `return`.
13989
- kind: "return",
13990
- match: { type: "functionFallthrough" },
13991
- extraction: {}
13992
- }
13993
- ],
14238
+ terminals: GENERIC_TERMINALS,
13994
14239
  inputMapping: { type: "allPositional" }
13995
14240
  };
13996
14241
  function nodeKey(func) {
@@ -15784,10 +16029,10 @@ var FUNCTION_FALLTHROUGH_TERMINAL = {
15784
16029
  match: { type: "functionFallthrough" },
15785
16030
  extraction: {}
15786
16031
  };
15787
- function thrownValueAt(unit) {
15788
- return unit.pattern?.wraps?.throwParam;
16032
+ function frameworkValueAt(unit) {
16033
+ return unit.pattern?.wraps?.throwParam ?? unit.pattern?.wraps?.resultParam;
15789
16034
  }
15790
- function pastThrownValue(terminals, at) {
16035
+ function pastFrameworkValue(terminals, at) {
15791
16036
  if (at === void 0) {
15792
16037
  return terminals;
15793
16038
  }
@@ -15801,7 +16046,7 @@ function pastThrownValue(terminals, at) {
15801
16046
  } : pattern
15802
16047
  );
15803
16048
  }
15804
- function rolesPastThrownValue(mapping, at) {
16049
+ function rolesPastFrameworkValue(mapping, at) {
15805
16050
  if (at === void 0 || mapping.type !== "positionalParams") {
15806
16051
  return mapping;
15807
16052
  }
@@ -15827,7 +16072,7 @@ function continuationTerminal(unit) {
15827
16072
  }
15828
16073
  function terminalsFor(unit, pack) {
15829
16074
  const declared = [
15830
- ...unit.terminals ?? pastThrownValue(pack.terminals, thrownValueAt(unit)),
16075
+ ...unit.terminals ?? pastFrameworkValue(pack.terminals, frameworkValueAt(unit)),
15831
16076
  ...continuationTerminal(unit)
15832
16077
  ];
15833
16078
  if (unit.callSite === void 0) {
@@ -15845,7 +16090,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
15845
16090
  }
15846
16091
  const params = extractParameters(
15847
16092
  func,
15848
- unit.inputMapping ?? rolesPastThrownValue(pack.inputMapping, thrownValueAt(unit))
16093
+ unit.inputMapping ?? rolesPastFrameworkValue(pack.inputMapping, frameworkValueAt(unit))
15849
16094
  );
15850
16095
  const extracted = extractRawBranches(
15851
16096
  func,
@@ -16347,8 +16592,10 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
16347
16592
  }
16348
16593
  }
16349
16594
  const summary = (0, import_extractor11.assembleSummary)(raw, options);
16350
- if (unit.unfollowed !== void 0 && options?.gapHandling !== "silent") {
16351
- summary.gaps.push((0, import_behavioral_ir2.unfollowedCallGap)(unit.unfollowed));
16595
+ if (options?.gapHandling !== "silent") {
16596
+ for (const stop of unfollowedStopsOf(unit, wrappers)) {
16597
+ summary.gaps.push((0, import_behavioral_ir2.unfollowedCallGap)(stop));
16598
+ }
16352
16599
  }
16353
16600
  if (unit.metadata !== void 0 && Object.keys(unit.metadata).length > 0) {
16354
16601
  summary.metadata = {
@@ -16401,6 +16648,13 @@ function stampWrappers(raw, unit, wrappers) {
16401
16648
  recordFileDependency(wrapper.file);
16402
16649
  }
16403
16650
  }
16651
+ function unfollowedStopsOf(unit, wrappers) {
16652
+ const stops = unit.unfollowed === void 0 ? [] : [unit.unfollowed];
16653
+ if (wrappers === void 0 || unit.registrationSubjectId === void 0) {
16654
+ return stops;
16655
+ }
16656
+ return [...stops, ...wrappers.unfollowedFor(unit.registrationSubjectId)];
16657
+ }
16404
16658
  function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
16405
16659
  for (const pack of frameworks) {
16406
16660
  for (const declared of pack.libraryEnvVars ?? []) {
@@ -17115,7 +17369,7 @@ function createTypeScriptAdapter(suppliedConfig) {
17115
17369
  });
17116
17370
  const composed = timer.time(
17117
17371
  "composeWrappers",
17118
- () => (0, import_extractor11.composeWrappers)(enriched)
17372
+ () => (0, import_extractor11.composeWrappers)(enriched, config.extractorOptions ?? {})
17119
17373
  );
17120
17374
  if (config.onTiming !== void 0) {
17121
17375
  config.onTiming(timer.report());
@@ -17735,6 +17989,8 @@ function stubEvidence(options) {
17735
17989
  // Annotate the CommonJS export names for ESM import in node:
17736
17990
  0 && (module.exports = {
17737
17991
  ADAPTER_VERSION,
17992
+ GENERIC_TERMINALS,
17993
+ NO_BARRIERS,
17738
17994
  ResolutionStore,
17739
17995
  TypeScriptWhySession,
17740
17996
  accessContextFor,
@@ -17755,6 +18011,7 @@ function stubEvidence(options) {
17755
18011
  findNearestTsconfig,
17756
18012
  findTerminals,
17757
18013
  functionTargetOf,
18014
+ hasBody,
17758
18015
  importDeclarationsOf,
17759
18016
  importedNamesOf,
17760
18017
  importedRootsOf,
@@ -17770,6 +18027,7 @@ function stubEvidence(options) {
17770
18027
  resolveAliasedSymbol,
17771
18028
  resolveSubject,
17772
18029
  rootIdentifier,
18030
+ startsItsOwnScope,
17773
18031
  stubEvidence,
17774
18032
  stubEvidenceIn,
17775
18033
  workspaceRootFor