@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.js CHANGED
@@ -3716,6 +3716,18 @@ var ResolutionStore = class {
3716
3716
  const target = factKeyOf(value);
3717
3717
  return this.askAbout(target, "wanted", () => this.lookupObject(target));
3718
3718
  }
3719
+ /**
3720
+ * The function a call returns: `app.use(requireCaller(config))`
3721
+ * registers what the factory gives back. A separate question from
3722
+ * `resolveCallable`, which keeps a call unresolved on purpose so an
3723
+ * unwrapping answer wins; this one is asked only after that has
3724
+ * declined. Two returned functions give null, like every other
3725
+ * single-answer question here.
3726
+ */
3727
+ resolveReturnedCallable(call) {
3728
+ const target = factKeyOf(call);
3729
+ return this.askAbout(target, "wanted", () => this.lookupReturned(target));
3730
+ }
3719
3731
  /**
3720
3732
  * For a value that is neither a function nor an object, such as a
3721
3733
  * GraphQL document kept in a constant in another file.
@@ -4159,6 +4171,21 @@ var ResolutionStore = class {
4159
4171
  }
4160
4172
  return [...candidates][0];
4161
4173
  }
4174
+ lookupReturned(call) {
4175
+ this.derive();
4176
+ const candidates = /* @__PURE__ */ new Set();
4177
+ for (const target of this.answersFor("wantedGivesBack", nodeId(call))) {
4178
+ const node = this.table.byId.get(target);
4179
+ if (node === void 0 || !isFunctionRoot(node)) {
4180
+ continue;
4181
+ }
4182
+ candidates.add(node);
4183
+ }
4184
+ if (candidates.size !== 1) {
4185
+ return null;
4186
+ }
4187
+ return [...candidates][0];
4188
+ }
4162
4189
  /**
4163
4190
  * Two candidates give null, since ambiguity is nothing. The value
4164
4191
  * itself is never a result either, or an identifier whose chain went
@@ -4647,6 +4674,26 @@ function resolveImportedLocalName(sourceFile, importModule, importName) {
4647
4674
  }
4648
4675
  return null;
4649
4676
  }
4677
+ function importedReferenceSpellings(sourceFile, importModule, importName) {
4678
+ const spellings = [];
4679
+ const localName = resolveImportedLocalName(
4680
+ sourceFile,
4681
+ importModule,
4682
+ importName
4683
+ );
4684
+ if (localName !== null) {
4685
+ spellings.push(localName);
4686
+ }
4687
+ if (!sourceFile.getFullText().includes(`.${importName}`)) {
4688
+ return spellings;
4689
+ }
4690
+ for (const root of importedRootsOf(sourceFile, [importModule])) {
4691
+ if (root !== importName) {
4692
+ spellings.push(`${root}.${importName}`);
4693
+ }
4694
+ }
4695
+ return spellings;
4696
+ }
4650
4697
 
4651
4698
  // src/discovery/resolveValue.ts
4652
4699
  import { Node as Node17 } from "ts-morph";
@@ -5266,7 +5313,27 @@ function functionValueOf(value, resolution) {
5266
5313
  return null;
5267
5314
  }
5268
5315
  const resolved = resolution.resolveCallable(written);
5269
- return resolved === null ? null : toFunctionRoot(resolved);
5316
+ if (resolved !== null) {
5317
+ return toFunctionRoot(resolved);
5318
+ }
5319
+ if (!Node17.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 (Node17.isPropertyAssignment(property) || Node17.isShorthandPropertyAssignment(property) || Node17.isMethodDeclaration(property)) {
5327
+ return property.getName();
5328
+ }
5329
+ return null;
5330
+ }
5331
+ function propertyFunctionOf(property, resolution) {
5332
+ if (Node17.isMethodDeclaration(property)) {
5333
+ return property;
5334
+ }
5335
+ const held = propertyValueOf(property);
5336
+ return held === null ? null : functionValueOf(held, resolution);
5270
5337
  }
5271
5338
  function objectLiteralOf(value, resolution) {
5272
5339
  return literalValueOf(
@@ -8093,13 +8160,13 @@ function collectMethodsCalledOnCallResults(sourceFile) {
8093
8160
  return called;
8094
8161
  }
8095
8162
  function registrationSubjectsOf(sourceFile, importModule, importName, resolution) {
8096
- const importedLocalName = resolveImportedLocalName(
8163
+ const spellings = importedReferenceSpellings(
8097
8164
  sourceFile,
8098
8165
  importModule,
8099
8166
  importName
8100
8167
  );
8101
8168
  const subjects = /* @__PURE__ */ new Map();
8102
- if (importedLocalName === null) {
8169
+ if (spellings.length === 0) {
8103
8170
  return subjects;
8104
8171
  }
8105
8172
  sourceFile.forEachDescendant((node) => {
@@ -8109,7 +8176,7 @@ function registrationSubjectsOf(sourceFile, importModule, importName, resolution
8109
8176
  return;
8110
8177
  }
8111
8178
  const calleeText = Node32.isCallExpression(init) || Node32.isNewExpression(init) ? init.getExpression().getText() : null;
8112
- if (calleeText === importedLocalName) {
8179
+ if (calleeText !== null && spellings.includes(calleeText)) {
8113
8180
  subjects.set(node.getName(), init);
8114
8181
  }
8115
8182
  return;
@@ -8122,7 +8189,9 @@ function registrationSubjectsOf(sourceFile, importModule, importName, resolution
8122
8189
  return;
8123
8190
  }
8124
8191
  const typeText = typeNode.getText();
8125
- if (typeText === importedLocalName || typeText.startsWith(`${importedLocalName}<`)) {
8192
+ if (spellings.some(
8193
+ (spelling) => typeText === spelling || typeText.startsWith(`${spelling}<`)
8194
+ )) {
8126
8195
  subjects.set(node.getName(), creationSiteBehind(node, resolution));
8127
8196
  }
8128
8197
  });
@@ -8166,17 +8235,17 @@ function registrationSubjectIdsOf(sourceFile, matches, resolution) {
8166
8235
  return ids;
8167
8236
  }
8168
8237
  function unboundConstructionsOf(sourceFile, importModule, importName) {
8169
- const importedLocalName = resolveImportedLocalName(
8238
+ const spellings = importedReferenceSpellings(
8170
8239
  sourceFile,
8171
8240
  importModule,
8172
8241
  importName
8173
8242
  );
8174
- if (importedLocalName === null) {
8243
+ if (spellings.length === 0) {
8175
8244
  return [];
8176
8245
  }
8177
8246
  const constructions = [];
8178
8247
  sourceFile.forEachDescendant((node) => {
8179
- if ((Node32.isCallExpression(node) || Node32.isNewExpression(node)) && node.getExpression().getText() === importedLocalName && !Node32.isVariableDeclaration(node.getParent())) {
8248
+ if ((Node32.isCallExpression(node) || Node32.isNewExpression(node)) && spellings.includes(node.getExpression().getText()) && !Node32.isVariableDeclaration(node.getParent())) {
8180
8249
  constructions.push(node);
8181
8250
  }
8182
8251
  });
@@ -8738,7 +8807,7 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
8738
8807
  }
8739
8808
  const schemaSdl = extractTypeDefsSdl(config, store);
8740
8809
  for (const typeProp of propertiesOf(resolversObj, store)) {
8741
- const typeName = resolverPropertyName(typeProp);
8810
+ const typeName = propertyNameOf(typeProp);
8742
8811
  if (typeName === null || excludeTypes.has(typeName)) {
8743
8812
  continue;
8744
8813
  }
@@ -8747,11 +8816,11 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
8747
8816
  continue;
8748
8817
  }
8749
8818
  for (const fieldProp of propertiesOf(typeObj, store)) {
8750
- const fieldName = resolverPropertyName(fieldProp);
8819
+ const fieldName = propertyNameOf(fieldProp);
8751
8820
  if (fieldName === null) {
8752
8821
  continue;
8753
8822
  }
8754
- const fn = resolverPropertyFunction(fieldProp, store);
8823
+ const fn = propertyFunctionOf(fieldProp, store);
8755
8824
  if (fn === null) {
8756
8825
  continue;
8757
8826
  }
@@ -8815,22 +8884,6 @@ function writtenSdl(expr) {
8815
8884
  }
8816
8885
  return null;
8817
8886
  }
8818
- function resolverPropertyName(prop) {
8819
- if (Node36.isPropertyAssignment(prop) || Node36.isMethodDeclaration(prop)) {
8820
- return prop.getName();
8821
- }
8822
- if (Node36.isShorthandPropertyAssignment(prop)) {
8823
- return prop.getName();
8824
- }
8825
- return null;
8826
- }
8827
- function resolverPropertyFunction(prop, resolution) {
8828
- if (Node36.isMethodDeclaration(prop)) {
8829
- return prop;
8830
- }
8831
- const held = propertyValueOf(prop);
8832
- return held === null ? null : functionValueOf(held, resolution);
8833
- }
8834
8887
 
8835
8888
  // src/discovery/index.ts
8836
8889
  function runPattern(sourceFile, pattern, resolution, mountPrefixes, expandedElsewhere) {
@@ -10213,202 +10266,15 @@ function tryMatchParameterCall(node, func, pattern, match) {
10213
10266
  return { node, terminal };
10214
10267
  }
10215
10268
 
10216
- // src/terminals/jsx.ts
10217
- import { Node as Node44 } from "ts-morph";
10218
- function tryMatchJsxReturn(node, pattern) {
10219
- let expr;
10220
- let source;
10221
- if (Node44.isReturnStatement(node)) {
10222
- expr = node.getExpression();
10223
- source = node;
10224
- } else if (Node44.isArrowFunction(node)) {
10225
- const body = node.getBody();
10226
- if (body !== void 0 && !Node44.isBlock(body)) {
10227
- expr = body;
10228
- source = body;
10229
- }
10230
- }
10231
- if (expr === void 0 || source === void 0) {
10232
- return null;
10233
- }
10234
- const tree = jsxToRenderNode(expr);
10235
- if (tree === null) {
10236
- return null;
10237
- }
10238
- const component = tree.type === "element" ? tree.tag : "Fragment";
10239
- const terminal = {
10240
- kind: pattern.kind,
10241
- statusCode: null,
10242
- body: null,
10243
- exceptionType: null,
10244
- message: null,
10245
- component,
10246
- delegateTarget: null,
10247
- emitEvent: null,
10248
- renderTree: tree,
10249
- location: {
10250
- start: startLineOf(node),
10251
- end: endLineOf(node)
10252
- }
10253
- };
10254
- return { node, source, terminal };
10255
- }
10256
- function componentTargetOf(tagNode) {
10257
- if (!Node44.isIdentifier(tagNode) || !/^[A-Z]/.test(tagNode.getText())) {
10258
- return null;
10259
- }
10260
- const target = functionTargetOf(tagNode);
10261
- return target === null ? null : { file: target.file, name: target.name };
10262
- }
10263
- function jsxToRenderNode(node) {
10264
- if (Node44.isJsxElement(node)) {
10265
- const opening = node.getOpeningElement();
10266
- const tag = opening.getTagNameNode().getText();
10267
- const attrs = collectJsxAttributes(opening);
10268
- const target = componentTargetOf(opening.getTagNameNode());
10269
- const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
10270
- return {
10271
- type: "element",
10272
- tag,
10273
- children,
10274
- ...attrs !== null ? { attrs } : {},
10275
- ...target !== null ? { target } : {}
10276
- };
10277
- }
10278
- if (Node44.isJsxSelfClosingElement(node)) {
10279
- const tag = node.getTagNameNode().getText();
10280
- const attrs = collectJsxAttributes(node);
10281
- const target = componentTargetOf(node.getTagNameNode());
10282
- return {
10283
- type: "element",
10284
- tag,
10285
- children: [],
10286
- ...attrs !== null ? { attrs } : {},
10287
- ...target !== null ? { target } : {}
10288
- };
10289
- }
10290
- if (Node44.isJsxFragment(node)) {
10291
- const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
10292
- return { type: "element", tag: "Fragment", children };
10293
- }
10294
- if (Node44.isParenthesizedExpression(node)) {
10295
- return jsxToRenderNode(node.getExpression());
10296
- }
10297
- return null;
10298
- }
10299
- function collectJsxAttributes(opening) {
10300
- const entries = {};
10301
- for (const attr of opening.getAttributes()) {
10302
- if (Node44.isJsxSpreadAttribute(attr)) {
10303
- const exprText = attr.getExpression().getText();
10304
- entries[`...${exprText}`] = exprText;
10305
- continue;
10306
- }
10307
- if (!Node44.isJsxAttribute(attr)) {
10308
- continue;
10309
- }
10310
- const name = attr.getNameNode().getText();
10311
- const initializer = attr.getInitializer();
10312
- if (initializer === void 0) {
10313
- entries[name] = "";
10314
- continue;
10315
- }
10316
- if (Node44.isJsxExpression(initializer)) {
10317
- const inner = initializer.getExpression();
10318
- entries[name] = inner !== void 0 ? inner.getText() : "";
10319
- continue;
10320
- }
10321
- entries[name] = initializer.getText();
10322
- }
10323
- return Object.keys(entries).length > 0 ? entries : null;
10324
- }
10325
- function jsxChildToRenderNode(child) {
10326
- if (Node44.isJsxText(child)) {
10327
- const text = child.getLiteralText().trim();
10328
- if (text.length === 0) {
10329
- return null;
10330
- }
10331
- return { type: "text", value: text };
10332
- }
10333
- if (Node44.isJsxExpression(child)) {
10334
- const inner = child.getExpression();
10335
- if (inner === void 0) {
10336
- return null;
10337
- }
10338
- return jsxExpressionToRenderNode(inner);
10339
- }
10340
- if (Node44.isJsxElement(child) || Node44.isJsxSelfClosingElement(child) || Node44.isJsxFragment(child)) {
10341
- return jsxToRenderNode(child);
10342
- }
10343
- return null;
10344
- }
10345
- function jsxExpressionToRenderNode(expr) {
10346
- if (Node44.isParenthesizedExpression(expr)) {
10347
- return jsxExpressionToRenderNode(expr.getExpression());
10348
- }
10349
- if (Node44.isBinaryExpression(expr)) {
10350
- const op = expr.getOperatorToken().getText();
10351
- if (op === "&&") {
10352
- const right = renderAlternative(peelParens(expr.getRight()));
10353
- if (right.kind === "jsx") {
10354
- return {
10355
- type: "conditional",
10356
- condition: expr.getLeft().getText(),
10357
- whenTrue: right.node,
10358
- whenFalse: null
10359
- };
10360
- }
10361
- }
10362
- }
10363
- if (Node44.isConditionalExpression(expr)) {
10364
- const truthy = renderAlternative(peelParens(expr.getWhenTrue()));
10365
- const falsy = renderAlternative(peelParens(expr.getWhenFalse()));
10366
- if (truthy.kind === "jsx") {
10367
- return {
10368
- type: "conditional",
10369
- condition: expr.getCondition().getText(),
10370
- whenTrue: truthy.node,
10371
- whenFalse: falsy.kind === "jsx" ? falsy.node : null
10372
- };
10373
- }
10374
- if (falsy.kind === "jsx") {
10375
- return {
10376
- type: "conditional",
10377
- condition: `!(${expr.getCondition().getText()})`,
10378
- whenTrue: falsy.node,
10379
- whenFalse: null
10380
- };
10381
- }
10382
- }
10383
- return { type: "expression", sourceText: expr.getText() };
10384
- }
10385
- function renderAlternative(node) {
10386
- if (Node44.isNullLiteral(node)) {
10387
- return { kind: "noRender" };
10388
- }
10389
- if (Node44.isFalseLiteral(node)) {
10390
- return { kind: "noRender" };
10391
- }
10392
- if (Node44.isIdentifier(node) && node.getText() === "undefined") {
10393
- return { kind: "noRender" };
10394
- }
10395
- if (Node44.isJsxElement(node) || Node44.isJsxSelfClosingElement(node) || Node44.isJsxFragment(node)) {
10396
- const tree = jsxToRenderNode(node);
10397
- if (tree !== null) {
10398
- return { kind: "jsx", node: tree };
10399
- }
10400
- }
10401
- return { kind: "notStatic" };
10402
- }
10403
-
10404
- // src/terminals/returns.ts
10269
+ // src/terminals/extract.ts
10405
10270
  import {
10406
- Node as Node49
10271
+ Node as Node45
10407
10272
  } from "ts-morph";
10273
+ import { constantOf } from "@suss/values";
10408
10274
 
10409
10275
  // src/shapes/shapes.ts
10410
10276
  import {
10411
- Node as Node45,
10277
+ Node as Node44,
10412
10278
  SyntaxKind as SyntaxKind10
10413
10279
  } from "ts-morph";
10414
10280
 
@@ -10869,35 +10735,35 @@ function extractShapeWithArguments(node, substitutions) {
10869
10735
  }
10870
10736
  function extractShapeInner(node) {
10871
10737
  const unwrapped2 = peelSyntax(node);
10872
- if (boundArguments !== null && Node45.isIdentifier(unwrapped2)) {
10738
+ if (boundArguments !== null && Node44.isIdentifier(unwrapped2)) {
10873
10739
  const argument = boundArguments.get(unwrapped2.getText());
10874
10740
  if (argument !== void 0 && argument !== unwrapped2) {
10875
10741
  return extractShape(argument);
10876
10742
  }
10877
10743
  }
10878
- if (Node45.isObjectLiteralExpression(unwrapped2)) {
10744
+ if (Node44.isObjectLiteralExpression(unwrapped2)) {
10879
10745
  return shapeFromObjectLiteral(unwrapped2);
10880
10746
  }
10881
- if (Node45.isArrayLiteralExpression(unwrapped2)) {
10747
+ if (Node44.isArrayLiteralExpression(unwrapped2)) {
10882
10748
  return shapeFromArrayLiteral(unwrapped2);
10883
10749
  }
10884
- if (Node45.isStringLiteral(unwrapped2) || Node45.isNoSubstitutionTemplateLiteral(unwrapped2)) {
10750
+ if (Node44.isStringLiteral(unwrapped2) || Node44.isNoSubstitutionTemplateLiteral(unwrapped2)) {
10885
10751
  return { type: "literal", value: unwrapped2.getLiteralValue() };
10886
10752
  }
10887
- if (Node45.isTemplateExpression(unwrapped2)) {
10753
+ if (Node44.isTemplateExpression(unwrapped2)) {
10888
10754
  return { type: "text" };
10889
10755
  }
10890
- if (Node45.isNumericLiteral(unwrapped2)) {
10756
+ if (Node44.isNumericLiteral(unwrapped2)) {
10891
10757
  return numericLiteralShape(
10892
10758
  unwrapped2.getLiteralValue(),
10893
10759
  unwrapped2.getText()
10894
10760
  );
10895
10761
  }
10896
- if (Node45.isPrefixUnaryExpression(unwrapped2)) {
10762
+ if (Node44.isPrefixUnaryExpression(unwrapped2)) {
10897
10763
  const op = unwrapped2.getOperatorToken();
10898
10764
  if (op === SyntaxKind10.MinusToken || op === SyntaxKind10.PlusToken) {
10899
10765
  const inner = unwrapped2.getOperand();
10900
- if (Node45.isNumericLiteral(inner)) {
10766
+ if (Node44.isNumericLiteral(inner)) {
10901
10767
  const v3 = inner.getLiteralValue();
10902
10768
  const signed = op === SyntaxKind10.MinusToken ? -v3 : v3;
10903
10769
  const rawBody = inner.getText();
@@ -10906,7 +10772,7 @@ function extractShapeInner(node) {
10906
10772
  }
10907
10773
  }
10908
10774
  }
10909
- if (Node45.isBigIntLiteral(unwrapped2)) {
10775
+ if (Node44.isBigIntLiteral(unwrapped2)) {
10910
10776
  return { type: "ref", name: "bigint" };
10911
10777
  }
10912
10778
  const kind = unwrapped2.getKind();
@@ -10919,10 +10785,10 @@ function extractShapeInner(node) {
10919
10785
  if (kind === SyntaxKind10.NullKeyword) {
10920
10786
  return { type: "null" };
10921
10787
  }
10922
- if (Node45.isIdentifier(unwrapped2) && unwrapped2.getText() === "undefined") {
10788
+ if (Node44.isIdentifier(unwrapped2) && unwrapped2.getText() === "undefined") {
10923
10789
  return { type: "undefined" };
10924
10790
  }
10925
- if (Node45.isConditionalExpression(unwrapped2)) {
10791
+ if (Node44.isConditionalExpression(unwrapped2)) {
10926
10792
  const whenTrue = extractShape(unwrapped2.getWhenTrue());
10927
10793
  const whenFalse = extractShape(unwrapped2.getWhenFalse());
10928
10794
  const variants = [];
@@ -10953,7 +10819,7 @@ function shapeFromArrayLiteral(arr) {
10953
10819
  }
10954
10820
  const shapes = [];
10955
10821
  for (const el of elements) {
10956
- if (Node45.isSpreadElement(el)) {
10822
+ if (Node44.isSpreadElement(el)) {
10957
10823
  const inner = el.getExpression();
10958
10824
  const innerShape = extractShape(inner);
10959
10825
  if (innerShape?.type === "array") {
@@ -10972,7 +10838,7 @@ function shapeFromObjectLiteral(obj) {
10972
10838
  const properties = {};
10973
10839
  const unresolvedSpreads = [];
10974
10840
  for (const prop of obj.getProperties()) {
10975
- if (Node45.isPropertyAssignment(prop)) {
10841
+ if (Node44.isPropertyAssignment(prop)) {
10976
10842
  const name = propertyName2(prop);
10977
10843
  if (name === null) {
10978
10844
  continue;
@@ -10988,7 +10854,7 @@ function shapeFromObjectLiteral(obj) {
10988
10854
  };
10989
10855
  continue;
10990
10856
  }
10991
- if (Node45.isShorthandPropertyAssignment(prop)) {
10857
+ if (Node44.isShorthandPropertyAssignment(prop)) {
10992
10858
  const name = prop.getName();
10993
10859
  const valueExpr = prop.getNameNode();
10994
10860
  properties[name] = extractShape(valueExpr) ?? {
@@ -10997,7 +10863,7 @@ function shapeFromObjectLiteral(obj) {
10997
10863
  };
10998
10864
  continue;
10999
10865
  }
11000
- if (Node45.isSpreadAssignment(prop)) {
10866
+ if (Node44.isSpreadAssignment(prop)) {
11001
10867
  const expr = prop.getExpression();
11002
10868
  const spreadShape = extractShape(expr);
11003
10869
  if (spreadShape?.type === "record") {
@@ -11016,7 +10882,7 @@ function shapeFromObjectLiteral(obj) {
11016
10882
  }
11017
10883
  function propertyName2(prop) {
11018
10884
  const nameNode = prop.getNameNode();
11019
- if (Node45.isIdentifier(nameNode) || Node45.isStringLiteral(nameNode) || Node45.isNoSubstitutionTemplateLiteral(nameNode) || Node45.isNumericLiteral(nameNode)) {
10885
+ if (Node44.isIdentifier(nameNode) || Node44.isStringLiteral(nameNode) || Node44.isNoSubstitutionTemplateLiteral(nameNode) || Node44.isNumericLiteral(nameNode)) {
11020
10886
  return nameNode.getText().replace(/^["']|["']$/g, "");
11021
10887
  }
11022
10888
  return null;
@@ -11042,24 +10908,20 @@ function collapseVariants2(shapes) {
11042
10908
  }
11043
10909
 
11044
10910
  // src/terminals/extract.ts
11045
- import {
11046
- Node as Node46
11047
- } from "ts-morph";
11048
- import { constantOf } from "@suss/values";
11049
10911
  function unwrapAs(node) {
11050
10912
  return peelSyntax(node);
11051
10913
  }
11052
10914
  function unwrapJsonStringify(node) {
11053
10915
  const outer = unwrapAs(node);
11054
- if (!Node46.isCallExpression(outer)) {
10916
+ if (!Node45.isCallExpression(outer)) {
11055
10917
  return node;
11056
10918
  }
11057
10919
  const callee = outer.getExpression();
11058
- if (!Node46.isPropertyAccessExpression(callee) || callee.getName() !== "stringify") {
10920
+ if (!Node45.isPropertyAccessExpression(callee) || callee.getName() !== "stringify") {
11059
10921
  return node;
11060
10922
  }
11061
10923
  const receiver = callee.getExpression();
11062
- if (!Node46.isIdentifier(receiver) || receiver.getText() !== "JSON") {
10924
+ if (!Node45.isIdentifier(receiver) || receiver.getText() !== "JSON") {
11063
10925
  return node;
11064
10926
  }
11065
10927
  const args = outer.getArguments();
@@ -11069,7 +10931,7 @@ function unwrapJsonStringify(node) {
11069
10931
  return unwrapAs(args[0]);
11070
10932
  }
11071
10933
  function substituted(value, ctx) {
11072
- if (ctx.substitutions === void 0 || !Node46.isIdentifier(value)) {
10934
+ if (ctx.substitutions === void 0 || !Node45.isIdentifier(value)) {
11073
10935
  return value;
11074
10936
  }
11075
10937
  return ctx.substitutions.get(value.getText()) ?? value;
@@ -11082,10 +10944,10 @@ function extractStatusCode(ctx) {
11082
10944
  if (result !== null) {
11083
10945
  return result;
11084
10946
  }
11085
- if (ctx.extraction.defaultStatusCode !== void 0) {
11086
- return { type: "literal", value: ctx.extraction.defaultStatusCode };
11087
- }
11088
- return null;
10947
+ return declaredStatusCode(ctx.extraction);
10948
+ }
10949
+ function declaredStatusCode(extraction) {
10950
+ return extraction.defaultStatusCode !== void 0 ? { type: "literal", value: extraction.defaultStatusCode } : null;
11089
10951
  }
11090
10952
  function matchConstructorCode(ctorText, codes) {
11091
10953
  const fullMatch = codes[ctorText];
@@ -11113,10 +10975,10 @@ function statusFromProperty(obj, name, ctx) {
11113
10975
  if (prop === void 0) {
11114
10976
  return null;
11115
10977
  }
11116
- if (Node46.isShorthandPropertyAssignment(prop)) {
10978
+ if (Node45.isShorthandPropertyAssignment(prop)) {
11117
10979
  return { type: "dynamic", sourceText: prop.getName() };
11118
10980
  }
11119
- if (!Node46.isPropertyAssignment(prop)) {
10981
+ if (!Node45.isPropertyAssignment(prop)) {
11120
10982
  return null;
11121
10983
  }
11122
10984
  const value = prop.getInitializer();
@@ -11139,7 +11001,7 @@ function extractStatusCodeFromRule(ctx) {
11139
11001
  return null;
11140
11002
  }
11141
11003
  const first = ctx.throwCallArgs?.[0];
11142
- if (first !== void 0 && Node46.isNumericLiteral(first)) {
11004
+ if (first !== void 0 && Node45.isNumericLiteral(first)) {
11143
11005
  const value = first.getLiteralValue();
11144
11006
  if (value >= 100 && value <= 599) {
11145
11007
  return { type: "literal", value };
@@ -11152,7 +11014,7 @@ function extractStatusCodeFromRule(ctx) {
11152
11014
  return null;
11153
11015
  }
11154
11016
  const arg = ctx.throwCallArgs[sc.position];
11155
- if (arg === void 0 || !Node46.isNewExpression(arg)) {
11017
+ if (arg === void 0 || !Node45.isNewExpression(arg)) {
11156
11018
  return null;
11157
11019
  }
11158
11020
  const ctorText = arg.getExpression().getText();
@@ -11161,7 +11023,7 @@ function extractStatusCodeFromRule(ctx) {
11161
11023
  if (sc.from === "argumentProperty") {
11162
11024
  const args = ctx.throwCallArgs ?? ctx.calls?.[0]?.getArguments();
11163
11025
  const arg = args?.[sc.position];
11164
- if (arg === void 0 || !Node46.isObjectLiteralExpression(arg)) {
11026
+ if (arg === void 0 || !Node45.isObjectLiteralExpression(arg)) {
11165
11027
  return null;
11166
11028
  }
11167
11029
  return statusFromProperty(arg, sc.name, ctx);
@@ -11171,8 +11033,8 @@ function extractStatusCodeFromRule(ctx) {
11171
11033
  return null;
11172
11034
  }
11173
11035
  for (const prop of ctx.returnedObj.getProperties()) {
11174
- if (!Node46.isPropertyAssignment(prop)) {
11175
- if (Node46.isShorthandPropertyAssignment(prop)) {
11036
+ if (!Node45.isPropertyAssignment(prop)) {
11037
+ if (Node45.isShorthandPropertyAssignment(prop)) {
11176
11038
  if (prop.getName() === sc.name) {
11177
11039
  const resolved = ctx.substitutions?.get(prop.getName());
11178
11040
  if (resolved !== void 0) {
@@ -11230,8 +11092,8 @@ function extractBody(ctx) {
11230
11092
  return null;
11231
11093
  }
11232
11094
  for (const prop of ctx.returnedObj.getProperties()) {
11233
- if (!Node46.isPropertyAssignment(prop)) {
11234
- if (Node46.isShorthandPropertyAssignment(prop)) {
11095
+ if (!Node45.isPropertyAssignment(prop)) {
11096
+ if (Node45.isShorthandPropertyAssignment(prop)) {
11235
11097
  if (prop.getName() === b.name) {
11236
11098
  const resolved = ctx.substitutions?.get(prop.getName());
11237
11099
  if (resolved !== void 0) {
@@ -11285,6 +11147,199 @@ function extractBody(ctx) {
11285
11147
  return null;
11286
11148
  }
11287
11149
 
11150
+ // src/terminals/jsx.ts
11151
+ import { Node as Node46 } from "ts-morph";
11152
+ function tryMatchJsxReturn(node, pattern) {
11153
+ let expr;
11154
+ let source;
11155
+ if (Node46.isReturnStatement(node)) {
11156
+ expr = node.getExpression();
11157
+ source = node;
11158
+ } else if (Node46.isArrowFunction(node)) {
11159
+ const body = node.getBody();
11160
+ if (body !== void 0 && !Node46.isBlock(body)) {
11161
+ expr = body;
11162
+ source = body;
11163
+ }
11164
+ }
11165
+ if (expr === void 0 || source === void 0) {
11166
+ return null;
11167
+ }
11168
+ const tree = jsxToRenderNode(expr);
11169
+ if (tree === null) {
11170
+ return null;
11171
+ }
11172
+ const component = tree.type === "element" ? tree.tag : "Fragment";
11173
+ const terminal = {
11174
+ kind: pattern.kind,
11175
+ statusCode: null,
11176
+ body: null,
11177
+ exceptionType: null,
11178
+ message: null,
11179
+ component,
11180
+ delegateTarget: null,
11181
+ emitEvent: null,
11182
+ renderTree: tree,
11183
+ location: {
11184
+ start: startLineOf(node),
11185
+ end: endLineOf(node)
11186
+ }
11187
+ };
11188
+ return { node, source, terminal };
11189
+ }
11190
+ function componentTargetOf(tagNode) {
11191
+ if (!Node46.isIdentifier(tagNode) || !/^[A-Z]/.test(tagNode.getText())) {
11192
+ return null;
11193
+ }
11194
+ const target = functionTargetOf(tagNode);
11195
+ return target === null ? null : { file: target.file, name: target.name };
11196
+ }
11197
+ function jsxToRenderNode(node) {
11198
+ if (Node46.isJsxElement(node)) {
11199
+ const opening = node.getOpeningElement();
11200
+ const tag = opening.getTagNameNode().getText();
11201
+ const attrs = collectJsxAttributes(opening);
11202
+ const target = componentTargetOf(opening.getTagNameNode());
11203
+ const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
11204
+ return {
11205
+ type: "element",
11206
+ tag,
11207
+ children,
11208
+ ...attrs !== null ? { attrs } : {},
11209
+ ...target !== null ? { target } : {}
11210
+ };
11211
+ }
11212
+ if (Node46.isJsxSelfClosingElement(node)) {
11213
+ const tag = node.getTagNameNode().getText();
11214
+ const attrs = collectJsxAttributes(node);
11215
+ const target = componentTargetOf(node.getTagNameNode());
11216
+ return {
11217
+ type: "element",
11218
+ tag,
11219
+ children: [],
11220
+ ...attrs !== null ? { attrs } : {},
11221
+ ...target !== null ? { target } : {}
11222
+ };
11223
+ }
11224
+ if (Node46.isJsxFragment(node)) {
11225
+ const children = node.getJsxChildren().map(jsxChildToRenderNode).filter((n) => n !== null);
11226
+ return { type: "element", tag: "Fragment", children };
11227
+ }
11228
+ if (Node46.isParenthesizedExpression(node)) {
11229
+ return jsxToRenderNode(node.getExpression());
11230
+ }
11231
+ return null;
11232
+ }
11233
+ function collectJsxAttributes(opening) {
11234
+ const entries = {};
11235
+ for (const attr of opening.getAttributes()) {
11236
+ if (Node46.isJsxSpreadAttribute(attr)) {
11237
+ const exprText = attr.getExpression().getText();
11238
+ entries[`...${exprText}`] = exprText;
11239
+ continue;
11240
+ }
11241
+ if (!Node46.isJsxAttribute(attr)) {
11242
+ continue;
11243
+ }
11244
+ const name = attr.getNameNode().getText();
11245
+ const initializer = attr.getInitializer();
11246
+ if (initializer === void 0) {
11247
+ entries[name] = "";
11248
+ continue;
11249
+ }
11250
+ if (Node46.isJsxExpression(initializer)) {
11251
+ const inner = initializer.getExpression();
11252
+ entries[name] = inner !== void 0 ? inner.getText() : "";
11253
+ continue;
11254
+ }
11255
+ entries[name] = initializer.getText();
11256
+ }
11257
+ return Object.keys(entries).length > 0 ? entries : null;
11258
+ }
11259
+ function jsxChildToRenderNode(child) {
11260
+ if (Node46.isJsxText(child)) {
11261
+ const text = child.getLiteralText().trim();
11262
+ if (text.length === 0) {
11263
+ return null;
11264
+ }
11265
+ return { type: "text", value: text };
11266
+ }
11267
+ if (Node46.isJsxExpression(child)) {
11268
+ const inner = child.getExpression();
11269
+ if (inner === void 0) {
11270
+ return null;
11271
+ }
11272
+ return jsxExpressionToRenderNode(inner);
11273
+ }
11274
+ if (Node46.isJsxElement(child) || Node46.isJsxSelfClosingElement(child) || Node46.isJsxFragment(child)) {
11275
+ return jsxToRenderNode(child);
11276
+ }
11277
+ return null;
11278
+ }
11279
+ function jsxExpressionToRenderNode(expr) {
11280
+ if (Node46.isParenthesizedExpression(expr)) {
11281
+ return jsxExpressionToRenderNode(expr.getExpression());
11282
+ }
11283
+ if (Node46.isBinaryExpression(expr)) {
11284
+ const op = expr.getOperatorToken().getText();
11285
+ if (op === "&&") {
11286
+ const right = renderAlternative(peelParens(expr.getRight()));
11287
+ if (right.kind === "jsx") {
11288
+ return {
11289
+ type: "conditional",
11290
+ condition: expr.getLeft().getText(),
11291
+ whenTrue: right.node,
11292
+ whenFalse: null
11293
+ };
11294
+ }
11295
+ }
11296
+ }
11297
+ if (Node46.isConditionalExpression(expr)) {
11298
+ const truthy = renderAlternative(peelParens(expr.getWhenTrue()));
11299
+ const falsy = renderAlternative(peelParens(expr.getWhenFalse()));
11300
+ if (truthy.kind === "jsx") {
11301
+ return {
11302
+ type: "conditional",
11303
+ condition: expr.getCondition().getText(),
11304
+ whenTrue: truthy.node,
11305
+ whenFalse: falsy.kind === "jsx" ? falsy.node : null
11306
+ };
11307
+ }
11308
+ if (falsy.kind === "jsx") {
11309
+ return {
11310
+ type: "conditional",
11311
+ condition: `!(${expr.getCondition().getText()})`,
11312
+ whenTrue: falsy.node,
11313
+ whenFalse: null
11314
+ };
11315
+ }
11316
+ }
11317
+ return { type: "expression", sourceText: expr.getText() };
11318
+ }
11319
+ function renderAlternative(node) {
11320
+ if (Node46.isNullLiteral(node)) {
11321
+ return { kind: "noRender" };
11322
+ }
11323
+ if (Node46.isFalseLiteral(node)) {
11324
+ return { kind: "noRender" };
11325
+ }
11326
+ if (Node46.isIdentifier(node) && node.getText() === "undefined") {
11327
+ return { kind: "noRender" };
11328
+ }
11329
+ if (Node46.isJsxElement(node) || Node46.isJsxSelfClosingElement(node) || Node46.isJsxFragment(node)) {
11330
+ const tree = jsxToRenderNode(node);
11331
+ if (tree !== null) {
11332
+ return { kind: "jsx", node: tree };
11333
+ }
11334
+ }
11335
+ return { kind: "notStatic" };
11336
+ }
11337
+
11338
+ // src/terminals/returns.ts
11339
+ import {
11340
+ Node as Node49
11341
+ } from "ts-morph";
11342
+
11288
11343
  // src/terminals/helperResolution.ts
11289
11344
  import { Node as Node47, SyntaxKind as SyntaxKind11 } from "ts-morph";
11290
11345
  function resolveHelperReturn(call, resolveCallee2) {
@@ -11856,10 +11911,9 @@ function tryMatchReturnStatement(node, pattern, func, allPatterns) {
11856
11911
  return null;
11857
11912
  }
11858
11913
  function buildReturnTerminal(locationNode, pattern, body) {
11859
- const statusCode = pattern.extraction.defaultStatusCode !== void 0 ? { type: "literal", value: pattern.extraction.defaultStatusCode } : null;
11860
11914
  const terminal = {
11861
11915
  kind: pattern.kind,
11862
- statusCode,
11916
+ statusCode: declaredStatusCode(pattern.extraction),
11863
11917
  body,
11864
11918
  exceptionType: null,
11865
11919
  message: null,
@@ -12148,15 +12202,15 @@ function functionMayFallThrough(func) {
12148
12202
  }
12149
12203
  return true;
12150
12204
  }
12151
- function makeFallthroughTerminal(func) {
12205
+ function makeFallthroughTerminal(func, pattern) {
12152
12206
  const body = func.getBody();
12153
12207
  const anchor = body ?? func;
12154
12208
  const line = endLineOf(anchor);
12155
12209
  return {
12156
12210
  node: anchor,
12157
12211
  terminal: {
12158
- kind: "return",
12159
- statusCode: null,
12212
+ kind: pattern.kind,
12213
+ statusCode: declaredStatusCode(pattern.extraction),
12160
12214
  body: null,
12161
12215
  exceptionType: null,
12162
12216
  message: null,
@@ -12242,7 +12296,7 @@ function countUnmatchedReturns(func, terminals, barriers = NO_BARRIERS) {
12242
12296
  });
12243
12297
  return unmatched;
12244
12298
  }
12245
- function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2) {
12299
+ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2, collectCallAccounting = false) {
12246
12300
  const terminals = findTerminals(
12247
12301
  func,
12248
12302
  terminalPatterns,
@@ -12276,15 +12330,15 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12276
12330
  terminals.map(({ node }) => node),
12277
12331
  barriers
12278
12332
  );
12279
- const wantsFallthrough = terminalPatterns.some(
12333
+ const fallthroughPattern = terminalPatterns.find(
12280
12334
  (p) => p.match.type === "functionFallthrough"
12281
12335
  );
12282
- if (wantsFallthrough && functionMayFallThrough(func)) {
12336
+ if (fallthroughPattern !== void 0 && functionMayFallThrough(func)) {
12283
12337
  const hasDefaultTerminal = terminals.some(
12284
12338
  ({ node }) => (byTerminal.get(node) ?? []).some(isDefaultConditionList)
12285
12339
  );
12286
12340
  if (!hasDefaultTerminal) {
12287
- const synthetic = makeFallthroughTerminal(func);
12341
+ const synthetic = makeFallthroughTerminal(func, fallthroughPattern);
12288
12342
  terminals.push(synthetic);
12289
12343
  const pathResult = computePathConditions(
12290
12344
  func,
@@ -12330,8 +12384,8 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12330
12384
  seenBranches.add(key);
12331
12385
  return true;
12332
12386
  });
12387
+ const terminalNodes = new Set(terminals.map(({ node }) => node));
12333
12388
  if (invocations.length > 0) {
12334
- const terminalNodes = new Set(terminals.map(({ node }) => node));
12335
12389
  const sideEffects = invocations.filter(
12336
12390
  (i) => !writesTerminal(i.node, terminalNodes)
12337
12391
  );
@@ -12347,7 +12401,39 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
12347
12401
  branch.extraEffects = [...branch.extraEffects ?? [], ...extra];
12348
12402
  }
12349
12403
  }
12350
- return { branches: distinctBranches, terminals };
12404
+ return {
12405
+ branches: distinctBranches,
12406
+ terminals,
12407
+ ...collectCallAccounting ? {
12408
+ callAccounting: invocations.map(
12409
+ (site) => accountFor(site, terminalNodes, distinctBranches)
12410
+ )
12411
+ } : {}
12412
+ };
12413
+ }
12414
+ function accountFor(site, terminalNodes, branches) {
12415
+ const { node, line } = site;
12416
+ const callee = node.getExpression().getText();
12417
+ if (writesTerminal(node, terminalNodes)) {
12418
+ return { node, callee, line, outcome: "terminal" };
12419
+ }
12420
+ const call = {
12421
+ line,
12422
+ alwaysRuns: site.alwaysRuns,
12423
+ preconditions: preconditionsOf(site.effect)
12424
+ };
12425
+ if (branches.some((branch) => firesOn(call, branch))) {
12426
+ return { node, callee, line, outcome: "invocation" };
12427
+ }
12428
+ const reachable = branches.some(
12429
+ (branch) => site.alwaysRuns || runsBefore(line, branch.terminal.location.end)
12430
+ );
12431
+ return {
12432
+ node,
12433
+ callee,
12434
+ line,
12435
+ outcome: reachable ? "unrecorded" : "unreachable"
12436
+ };
12351
12437
  }
12352
12438
 
12353
12439
  // src/bootstrap/sourceFileLookup.ts
@@ -12562,7 +12648,7 @@ function unwrapContractInit(init) {
12562
12648
  }
12563
12649
  return null;
12564
12650
  }
12565
- function extractEndpointContract(endpointNode, pattern, framework) {
12651
+ function extractEndpointContract(endpointNode, pattern, framework, resolution) {
12566
12652
  if (!Node54.isObjectLiteralExpression(endpointNode)) {
12567
12653
  return null;
12568
12654
  }
@@ -12587,9 +12673,10 @@ function extractEndpointContract(endpointNode, pattern, framework) {
12587
12673
  }
12588
12674
  }
12589
12675
  if (propName === pattern.responseExtraction.property) {
12590
- const val = prop.getInitializer();
12591
- if (val !== void 0 && Node54.isObjectLiteralExpression(val)) {
12592
- for (const respProp of val.getProperties()) {
12676
+ const written = prop.getInitializer();
12677
+ const val = written === void 0 ? null : objectLiteralOf(written, resolution);
12678
+ if (val !== null) {
12679
+ for (const respProp of propertiesOf(val, resolution)) {
12593
12680
  if (!Node54.isPropertyAssignment(respProp)) {
12594
12681
  continue;
12595
12682
  }
@@ -12628,7 +12715,12 @@ function readContract(unit, pattern, framework, resolution) {
12628
12715
  if (endpointNode === null) {
12629
12716
  return null;
12630
12717
  }
12631
- return extractEndpointContract(endpointNode, pattern, framework);
12718
+ return extractEndpointContract(
12719
+ endpointNode,
12720
+ pattern,
12721
+ framework,
12722
+ resolution
12723
+ );
12632
12724
  }
12633
12725
  const routerInfo = findRouterCall(unit);
12634
12726
  if (routerInfo === null) {
@@ -12649,7 +12741,12 @@ function readContract(unit, pattern, framework, resolution) {
12649
12741
  if (endpointInit === void 0) {
12650
12742
  continue;
12651
12743
  }
12652
- return extractEndpointContract(endpointInit, pattern, framework);
12744
+ return extractEndpointContract(
12745
+ endpointInit,
12746
+ pattern,
12747
+ framework,
12748
+ resolution
12749
+ );
12653
12750
  }
12654
12751
  return null;
12655
12752
  }
@@ -12726,7 +12823,12 @@ function readContractForClientCall(callExpression, methodName, pattern, framewor
12726
12823
  if (endpointInit === void 0) {
12727
12824
  continue;
12728
12825
  }
12729
- return extractEndpointContract(endpointInit, pattern, framework);
12826
+ return extractEndpointContract(
12827
+ endpointInit,
12828
+ pattern,
12829
+ framework,
12830
+ resolution
12831
+ );
12730
12832
  }
12731
12833
  return null;
12732
12834
  }
@@ -13486,7 +13588,11 @@ function recordEdge(edgesByChild, candidate) {
13486
13588
  // src/discovery/wrapperIndex.ts
13487
13589
  import { Node as Node58 } from "ts-morph";
13488
13590
  import { joinMountedPath as joinMountedPath3 } from "@suss/resolution";
13489
- var NO_WRAPPERS = { wrappersFor: () => [], unitsIn: () => [] };
13591
+ var NO_WRAPPERS = {
13592
+ wrappersFor: () => [],
13593
+ unfollowedFor: () => [],
13594
+ unitsIn: () => []
13595
+ };
13490
13596
  var ANONYMOUS = "<anon>";
13491
13597
  function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13492
13598
  const workByPack = /* @__PURE__ */ new Map();
@@ -13512,21 +13618,32 @@ function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13512
13618
  }
13513
13619
  }
13514
13620
  const bySubject = /* @__PURE__ */ new Map();
13621
+ const unfollowedBySubject = /* @__PURE__ */ new Map();
13515
13622
  const unitsByFile = /* @__PURE__ */ new Map();
13623
+ const stopsSeen = /* @__PURE__ */ new Set();
13516
13624
  let found = false;
13517
13625
  for (const [packName, work] of workByPack) {
13518
13626
  const candidates = [];
13519
13627
  for (const { sourceFile, pattern, match, wraps } of work) {
13520
- candidates.push(
13521
- ...discoverWrapperRegistrations(
13522
- sourceFile,
13523
- pattern,
13524
- match,
13525
- wraps,
13526
- resolution,
13527
- mountPrefixes
13528
- )
13628
+ const scan = discoverWrapperRegistrations(
13629
+ sourceFile,
13630
+ pattern,
13631
+ match,
13632
+ wraps,
13633
+ resolution,
13634
+ mountPrefixes
13529
13635
  );
13636
+ candidates.push(...scan.candidates);
13637
+ for (const { subjectId, callId, stop } of scan.unresolved) {
13638
+ if (stopsSeen.has(callId)) {
13639
+ continue;
13640
+ }
13641
+ stopsSeen.add(callId);
13642
+ found = true;
13643
+ const stops = unfollowedBySubject.get(subjectId) ?? [];
13644
+ stops.push(stop);
13645
+ unfollowedBySubject.set(subjectId, stops);
13646
+ }
13530
13647
  }
13531
13648
  for (const candidate of mostSpecific(candidates)) {
13532
13649
  found = true;
@@ -13544,6 +13661,7 @@ function buildWrapperIndex(packsByFile, resolution, mountPrefixes) {
13544
13661
  }
13545
13662
  return {
13546
13663
  wrappersFor: (subjectId) => bySubject.get(subjectId) ?? [],
13664
+ unfollowedFor: (subjectId) => unfollowedBySubject.get(subjectId) ?? [],
13547
13665
  unitsIn: (file, pack) => unitsByFile.get(unitKey(file, pack)) ?? []
13548
13666
  };
13549
13667
  }
@@ -13577,10 +13695,20 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13577
13695
  match.importName,
13578
13696
  resolution
13579
13697
  );
13698
+ if ("constructorOption" in wraps) {
13699
+ return discoverConstructorOptions(
13700
+ sourceFile,
13701
+ pattern,
13702
+ wraps,
13703
+ subjects,
13704
+ resolution
13705
+ );
13706
+ }
13580
13707
  if (subjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution)) {
13581
- return [];
13708
+ return { candidates: [], unresolved: [] };
13582
13709
  }
13583
13710
  const candidates = [];
13711
+ const unresolved = [];
13584
13712
  sourceFile.forEachDescendant((node) => {
13585
13713
  if (!Node58.isCallExpression(node)) {
13586
13714
  return;
@@ -13603,8 +13731,13 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13603
13731
  if (targetArg === void 0) {
13604
13732
  return;
13605
13733
  }
13734
+ const subjectId = nodeId(subjectNode);
13606
13735
  const target = functionValueOf(targetArg, resolution);
13607
13736
  if (target === null) {
13737
+ const stop = factoryStopOf(targetArg);
13738
+ if (stop !== null) {
13739
+ unresolved.push({ subjectId, callId: nodeId(node), stop });
13740
+ }
13608
13741
  return;
13609
13742
  }
13610
13743
  if (wraps.arity !== void 0 && target.getParameters().length !== wraps.arity) {
@@ -13614,29 +13747,136 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
13614
13747
  if (scope === null) {
13615
13748
  return;
13616
13749
  }
13617
- const subjectId = nodeId(subjectNode);
13618
13750
  const mountedScope = scope === void 0 ? void 0 : mountedAt(subjectId, mountPrefixes, scope);
13619
- const named2 = functionNameOrAnon(target) !== ANONYMOUS;
13620
- candidates.push({
13621
- subjectId,
13622
- callId: nodeId(node),
13623
- targetId: nodeId(target),
13624
- byArity: wraps.arity !== void 0,
13625
- target,
13626
- pattern,
13627
- registeredIn: sourceFile.getFilePath(),
13628
- named: named2,
13629
- reference: {
13630
- file: target.getSourceFile().getFilePath(),
13631
- // A function written out at the registration call has no name
13632
- // of its own, so it goes by the method that registered it.
13633
- name: named2 ? functionNameOrAnon(target) : wraps.method,
13634
- ...wraps.throwParam === void 0 ? {} : { onThrow: true },
13635
- ...mountedScope === void 0 ? {} : { scope: mountedScope }
13636
- }
13637
- });
13751
+ candidates.push(
13752
+ candidateOf(
13753
+ {
13754
+ subjectId,
13755
+ callId: nodeId(node),
13756
+ target,
13757
+ label: labelFor(targetArg, wraps),
13758
+ byArity: wraps.arity !== void 0,
13759
+ onThrow: wraps.throwParam !== void 0,
13760
+ scope: mountedScope
13761
+ },
13762
+ pattern,
13763
+ sourceFile
13764
+ )
13765
+ );
13638
13766
  });
13639
- return candidates;
13767
+ return { candidates, unresolved };
13768
+ }
13769
+ function discoverConstructorOptions(sourceFile, pattern, wraps, subjects, resolution) {
13770
+ const candidates = [];
13771
+ const unresolved = [];
13772
+ const seen = /* @__PURE__ */ new Set();
13773
+ for (const construction of subjects.values()) {
13774
+ const subjectId = nodeId(construction);
13775
+ if (seen.has(subjectId) || construction.getSourceFile() !== sourceFile) {
13776
+ continue;
13777
+ }
13778
+ seen.add(subjectId);
13779
+ const option = constructorOptionOf(construction, wraps, resolution);
13780
+ if (option === void 0) {
13781
+ continue;
13782
+ }
13783
+ const callId = nodeId(option);
13784
+ const written = propertyValueOf(option);
13785
+ const target = propertyFunctionOf(option, resolution);
13786
+ if (target === null) {
13787
+ const stop = written === null ? null : factoryStopOf(written);
13788
+ if (stop !== null) {
13789
+ unresolved.push({ subjectId, callId, stop });
13790
+ }
13791
+ continue;
13792
+ }
13793
+ candidates.push(
13794
+ candidateOf(
13795
+ {
13796
+ subjectId,
13797
+ callId,
13798
+ target,
13799
+ label: (written === null ? void 0 : factoryNameOf(written)) ?? wraps.constructorOption,
13800
+ byArity: false,
13801
+ onThrow: wraps.throwParam !== void 0,
13802
+ scope: void 0
13803
+ },
13804
+ pattern,
13805
+ sourceFile
13806
+ )
13807
+ );
13808
+ }
13809
+ return { candidates, unresolved };
13810
+ }
13811
+ function constructorOptionOf(construction, wraps, resolution) {
13812
+ if (!(Node58.isNewExpression(construction) || Node58.isCallExpression(construction))) {
13813
+ return void 0;
13814
+ }
13815
+ const optionsArg = construction.getArguments()[wraps.targetPosition];
13816
+ if (optionsArg === void 0) {
13817
+ return void 0;
13818
+ }
13819
+ const options = objectLiteralOf(optionsArg, resolution);
13820
+ if (options === null) {
13821
+ return void 0;
13822
+ }
13823
+ return propertiesOf(options, resolution).find(
13824
+ (property) => propertyNameOf(property) === wraps.constructorOption
13825
+ );
13826
+ }
13827
+ function candidateOf(registration, pattern, sourceFile) {
13828
+ const { target, label, onThrow, scope } = registration;
13829
+ const named2 = functionNameOrAnon(target) !== ANONYMOUS;
13830
+ return {
13831
+ subjectId: registration.subjectId,
13832
+ callId: registration.callId,
13833
+ targetId: nodeId(target),
13834
+ byArity: registration.byArity,
13835
+ target,
13836
+ pattern,
13837
+ registeredIn: sourceFile.getFilePath(),
13838
+ named: named2,
13839
+ reference: {
13840
+ file: target.getSourceFile().getFilePath(),
13841
+ name: named2 ? functionNameOrAnon(target) : label,
13842
+ ...onThrow ? { onThrow: true } : {},
13843
+ ...scope === void 0 ? {} : { scope }
13844
+ }
13845
+ };
13846
+ }
13847
+ function labelFor(targetArg, wraps) {
13848
+ return factoryNameOf(targetArg) ?? wraps.method;
13849
+ }
13850
+ function factoryNameOf(targetArg) {
13851
+ const written = factKeyOf(targetArg);
13852
+ if (!Node58.isCallExpression(written)) {
13853
+ return void 0;
13854
+ }
13855
+ const callee = written.getExpression();
13856
+ if (Node58.isIdentifier(callee)) {
13857
+ return callee.getText();
13858
+ }
13859
+ if (Node58.isPropertyAccessExpression(callee)) {
13860
+ return callee.getName();
13861
+ }
13862
+ return void 0;
13863
+ }
13864
+ function factoryStopOf(targetArg) {
13865
+ const written = factKeyOf(targetArg);
13866
+ if (!Node58.isCallExpression(written)) {
13867
+ return null;
13868
+ }
13869
+ const callee = factoryNameOf(targetArg);
13870
+ if (callee === void 0) {
13871
+ return null;
13872
+ }
13873
+ const reason = classifyStop(
13874
+ declarationsBehind(written.getExpression().getSymbol())
13875
+ );
13876
+ if (!worthRecording(reason)) {
13877
+ return null;
13878
+ }
13879
+ return { callee, reason: "unresolvedWrapper" };
13640
13880
  }
13641
13881
  function mountedAt(subjectId, mountPrefixes, scope) {
13642
13882
  const prefix = mountPrefixes?.prefixForId?.(subjectId) ?? "";
@@ -14011,22 +14251,23 @@ import { Node as Node60 } from "ts-morph";
14011
14251
  import { functionCallBinding, TargetPlacements } from "@suss/behavioral-ir";
14012
14252
  import { Database as Database3, evaluate as evaluate4, lit as lit4, rule as rule4, variable as variable2 } from "@suss/datalog";
14013
14253
  import { assembleSummary as assembleSummary2 } from "@suss/extractor";
14254
+ var GENERIC_TERMINALS = [
14255
+ { kind: "return", match: { type: "returnStatement" }, extraction: {} },
14256
+ { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
14257
+ {
14258
+ // Implicit fall-through at the end of a function body: covers
14259
+ // void-returning orchestrators that never write `return`.
14260
+ kind: "return",
14261
+ match: { type: "functionFallthrough" },
14262
+ extraction: {}
14263
+ }
14264
+ ];
14014
14265
  var reachablePack = {
14015
14266
  name: "reachable",
14016
14267
  languages: ["typescript"],
14017
14268
  protocol: "in-process",
14018
14269
  discovery: [],
14019
- terminals: [
14020
- { kind: "return", match: { type: "returnStatement" }, extraction: {} },
14021
- { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
14022
- {
14023
- // Implicit fall-through at the end of a function body: covers
14024
- // void-returning orchestrators that never write `return`.
14025
- kind: "return",
14026
- match: { type: "functionFallthrough" },
14027
- extraction: {}
14028
- }
14029
- ],
14270
+ terminals: GENERIC_TERMINALS,
14030
14271
  inputMapping: { type: "allPositional" }
14031
14272
  };
14032
14273
  function nodeKey(func) {
@@ -15831,10 +16072,10 @@ var FUNCTION_FALLTHROUGH_TERMINAL = {
15831
16072
  match: { type: "functionFallthrough" },
15832
16073
  extraction: {}
15833
16074
  };
15834
- function thrownValueAt(unit) {
15835
- return unit.pattern?.wraps?.throwParam;
16075
+ function frameworkValueAt(unit) {
16076
+ return unit.pattern?.wraps?.throwParam ?? unit.pattern?.wraps?.resultParam;
15836
16077
  }
15837
- function pastThrownValue(terminals, at) {
16078
+ function pastFrameworkValue(terminals, at) {
15838
16079
  if (at === void 0) {
15839
16080
  return terminals;
15840
16081
  }
@@ -15848,7 +16089,7 @@ function pastThrownValue(terminals, at) {
15848
16089
  } : pattern
15849
16090
  );
15850
16091
  }
15851
- function rolesPastThrownValue(mapping, at) {
16092
+ function rolesPastFrameworkValue(mapping, at) {
15852
16093
  if (at === void 0 || mapping.type !== "positionalParams") {
15853
16094
  return mapping;
15854
16095
  }
@@ -15874,7 +16115,7 @@ function continuationTerminal(unit) {
15874
16115
  }
15875
16116
  function terminalsFor(unit, pack) {
15876
16117
  const declared = [
15877
- ...unit.terminals ?? pastThrownValue(pack.terminals, thrownValueAt(unit)),
16118
+ ...unit.terminals ?? pastFrameworkValue(pack.terminals, frameworkValueAt(unit)),
15878
16119
  ...continuationTerminal(unit)
15879
16120
  ];
15880
16121
  if (unit.callSite === void 0) {
@@ -15892,7 +16133,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
15892
16133
  }
15893
16134
  const params = extractParameters(
15894
16135
  func,
15895
- unit.inputMapping ?? rolesPastThrownValue(pack.inputMapping, thrownValueAt(unit))
16136
+ unit.inputMapping ?? rolesPastFrameworkValue(pack.inputMapping, frameworkValueAt(unit))
15896
16137
  );
15897
16138
  const extracted = extractRawBranches(
15898
16139
  func,
@@ -16394,8 +16635,10 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
16394
16635
  }
16395
16636
  }
16396
16637
  const summary = assembleSummary3(raw, options);
16397
- if (unit.unfollowed !== void 0 && options?.gapHandling !== "silent") {
16398
- summary.gaps.push(unfollowedCallGap(unit.unfollowed));
16638
+ if (options?.gapHandling !== "silent") {
16639
+ for (const stop of unfollowedStopsOf(unit, wrappers)) {
16640
+ summary.gaps.push(unfollowedCallGap(stop));
16641
+ }
16399
16642
  }
16400
16643
  if (unit.metadata !== void 0 && Object.keys(unit.metadata).length > 0) {
16401
16644
  summary.metadata = {
@@ -16448,6 +16691,13 @@ function stampWrappers(raw, unit, wrappers) {
16448
16691
  recordFileDependency(wrapper.file);
16449
16692
  }
16450
16693
  }
16694
+ function unfollowedStopsOf(unit, wrappers) {
16695
+ const stops = unit.unfollowed === void 0 ? [] : [unit.unfollowed];
16696
+ if (wrappers === void 0 || unit.registrationSubjectId === void 0) {
16697
+ return stops;
16698
+ }
16699
+ return [...stops, ...wrappers.unfollowedFor(unit.registrationSubjectId)];
16700
+ }
16451
16701
  function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
16452
16702
  for (const pack of frameworks) {
16453
16703
  for (const declared of pack.libraryEnvVars ?? []) {
@@ -17162,7 +17412,7 @@ function createTypeScriptAdapter(suppliedConfig) {
17162
17412
  });
17163
17413
  const composed = timer.time(
17164
17414
  "composeWrappers",
17165
- () => composeWrappers(enriched)
17415
+ () => composeWrappers(enriched, config.extractorOptions ?? {})
17166
17416
  );
17167
17417
  if (config.onTiming !== void 0) {
17168
17418
  config.onTiming(timer.report());
@@ -17786,6 +18036,8 @@ function stubEvidence(options) {
17786
18036
  }
17787
18037
  export {
17788
18038
  ADAPTER_VERSION,
18039
+ GENERIC_TERMINALS,
18040
+ NO_BARRIERS,
17789
18041
  ResolutionStore,
17790
18042
  TypeScriptWhySession,
17791
18043
  accessContextFor,
@@ -17806,6 +18058,7 @@ export {
17806
18058
  findNearestTsconfig,
17807
18059
  findTerminals,
17808
18060
  functionTargetOf,
18061
+ hasBody,
17809
18062
  importDeclarationsOf,
17810
18063
  importedNamesOf,
17811
18064
  importedRootsOf,
@@ -17821,6 +18074,7 @@ export {
17821
18074
  resolveAliasedSymbol,
17822
18075
  resolveSubject,
17823
18076
  rootIdentifier,
18077
+ startsItsOwnScope,
17824
18078
  stubEvidence,
17825
18079
  stubEvidenceIn,
17826
18080
  workspaceRootFor