@suss/adapter-typescript 0.37.1 → 0.38.0

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
@@ -43,6 +43,396 @@ import {
43
43
  } from "ts-morph";
44
44
  import { firesOn, runsBefore, splitAtHandOffs } from "@suss/extractor";
45
45
 
46
+ // src/depTracking.ts
47
+ function createDependencySink() {
48
+ return { files: /* @__PURE__ */ new Set(), mountPrefixes: /* @__PURE__ */ new Map(), claims: [] };
49
+ }
50
+ var activeSink = null;
51
+ function withDependencySink(sink, fn) {
52
+ const previous = activeSink;
53
+ activeSink = sink;
54
+ try {
55
+ return fn();
56
+ } finally {
57
+ activeSink = previous;
58
+ }
59
+ }
60
+ function withFilesNoted(fn) {
61
+ const outer = activeSink;
62
+ const noted = createDependencySink();
63
+ activeSink = noted;
64
+ let value;
65
+ try {
66
+ value = fn();
67
+ } finally {
68
+ activeSink = outer;
69
+ }
70
+ if (outer !== null) {
71
+ for (const filePath of noted.files) {
72
+ outer.files.add(filePath);
73
+ }
74
+ for (const [childId, prefix] of noted.mountPrefixes) {
75
+ outer.mountPrefixes.set(childId, prefix);
76
+ }
77
+ outer.claims.push(...noted.claims);
78
+ }
79
+ return { value, files: [...noted.files] };
80
+ }
81
+ function recordFileDependency(filePath) {
82
+ activeSink?.files.add(filePath);
83
+ }
84
+ function recordMountPrefix(childId, prefix) {
85
+ activeSink?.mountPrefixes.set(childId, prefix);
86
+ }
87
+ function recordUnitClaim(key, pack) {
88
+ activeSink?.claims.push({ key, pack });
89
+ }
90
+
91
+ // src/shapes/typeText.ts
92
+ import { ts } from "ts-morph";
93
+ var PRINTER = ts.createPrinter({
94
+ removeComments: true,
95
+ newLine: ts.NewLineKind.LineFeed
96
+ });
97
+ var NO_FILE = ts.createSourceFile("", "", ts.ScriptTarget.Latest);
98
+ function defaultFlags(enclosing) {
99
+ const flags = ts.TypeFormatFlags.UseTypeOfFunction | ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseFullyQualifiedType | ts.TypeFormatFlags.WriteTypeArgumentsOfSignature;
100
+ if (enclosing?.getKind() === ts.SyntaxKind.TypeAliasDeclaration) {
101
+ return flags | ts.TypeFormatFlags.InTypeAlias;
102
+ }
103
+ return flags;
104
+ }
105
+ function stableTypeText(type, reader, enclosing, flags) {
106
+ const format = flags ?? defaultFlags(enclosing);
107
+ const place = enclosing?.compilerNode ?? NOWHERE;
108
+ const atPlace = printedAt.get(place) ?? /* @__PURE__ */ new WeakMap();
109
+ printedAt.set(place, atPlace);
110
+ const byFormat = atPlace.get(type.compilerType) ?? /* @__PURE__ */ new Map();
111
+ atPlace.set(type.compilerType, byFormat);
112
+ const kept = byFormat.get(format);
113
+ if (kept !== void 0) {
114
+ return kept;
115
+ }
116
+ const text3 = printedType(type, reader, enclosing, format);
117
+ byFormat.set(format, text3);
118
+ return text3;
119
+ }
120
+ var NOWHERE = {};
121
+ var printedAt = /* @__PURE__ */ new WeakMap();
122
+ function printedType(type, reader, enclosing, format) {
123
+ const checker = reader.getProject().getTypeChecker().compilerObject;
124
+ const enclosingNode = enclosing?.compilerNode;
125
+ const typeNode = checker.typeToTypeNode(
126
+ type.compilerType,
127
+ enclosingNode,
128
+ format & ts.TypeFormatFlags.NodeBuilderFlagsMask | ts.NodeBuilderFlags.IgnoreErrors
129
+ );
130
+ if (typeNode === void 0) {
131
+ return withoutImportQualifiers(
132
+ checker.typeToString(type.compilerType, enclosingNode, format)
133
+ );
134
+ }
135
+ const file = enclosing?.getSourceFile().compilerNode ?? NO_FILE;
136
+ const ordered = containsUnion(typeNode) ? withUnionMembersOrdered(typeNode, file) : typeNode;
137
+ return withoutImportQualifiers(printed(ordered, file));
138
+ }
139
+ var IMPORT_QUALIFIER = /import\("[^"]*"\)\./g;
140
+ var withoutImportQualifiers = (text3) => text3.replace(IMPORT_QUALIFIER, "");
141
+ function printed(node, file) {
142
+ return PRINTER.printNode(ts.EmitHint.Unspecified, node, file).replace(
143
+ /\n/g,
144
+ ""
145
+ );
146
+ }
147
+ function containsUnion(node) {
148
+ if (ts.isUnionTypeNode(node)) {
149
+ return true;
150
+ }
151
+ return ts.forEachChild(node, (child) => containsUnion(child) || void 0) === true;
152
+ }
153
+ function withUnionMembersOrdered(root, file) {
154
+ const result = ts.transform(root, [
155
+ (context) => {
156
+ const visit = (node) => {
157
+ const visited = ts.visitEachChild(node, visit, context);
158
+ if (!ts.isUnionTypeNode(visited)) {
159
+ return visited;
160
+ }
161
+ return context.factory.updateUnionTypeNode(
162
+ visited,
163
+ context.factory.createNodeArray(inStableOrder(visited.types, file))
164
+ );
165
+ };
166
+ return (node) => visit(node);
167
+ }
168
+ ]);
169
+ const [ordered] = result.transformed;
170
+ result.dispose();
171
+ return ordered;
172
+ }
173
+ function inStableOrder(members, file) {
174
+ return members.map((member) => ({
175
+ member,
176
+ text: withoutImportQualifiers(printed(member, file))
177
+ })).sort((a, b) => compareMembers(a.text, b.text)).map(({ member }) => member);
178
+ }
179
+ function compareMembers(a, b) {
180
+ return nullishRank(a) - nullishRank(b) || compareText(a, b);
181
+ }
182
+ function nullishRank(text3) {
183
+ if (text3 === "null") {
184
+ return 1;
185
+ }
186
+ if (text3 === "undefined") {
187
+ return 2;
188
+ }
189
+ return 0;
190
+ }
191
+ function compareText(a, b) {
192
+ if (a < b) {
193
+ return -1;
194
+ }
195
+ if (a > b) {
196
+ return 1;
197
+ }
198
+ return 0;
199
+ }
200
+ function stablePropertyName(symbol, reader) {
201
+ const name = symbol.getName();
202
+ if (!name.startsWith("__@")) {
203
+ return name;
204
+ }
205
+ const checker = reader.getProject().getTypeChecker().compilerObject;
206
+ return checker.symbolToString(symbol.compilerSymbol);
207
+ }
208
+
209
+ // src/shapes/definitions.ts
210
+ var current = null;
211
+ var definitionsInProgress = () => current;
212
+ function createDefinitionTable() {
213
+ const shapes = /* @__PURE__ */ new Map();
214
+ const deferred = [];
215
+ let settling = false;
216
+ return {
217
+ has: (key) => shapes.has(key),
218
+ reserve: (key) => {
219
+ shapes.set(key, null);
220
+ },
221
+ define: (key, shape) => {
222
+ shapes.set(key, shape);
223
+ },
224
+ defer: (key, expand) => {
225
+ deferred.push({ key, expand });
226
+ },
227
+ settle: () => {
228
+ if (settling) {
229
+ return;
230
+ }
231
+ settling = true;
232
+ try {
233
+ for (let next = deferred.shift(); next; next = deferred.shift()) {
234
+ shapes.set(next.key, next.expand());
235
+ }
236
+ } finally {
237
+ settling = false;
238
+ }
239
+ },
240
+ // Sorted because the walk meets types in the checker's union order,
241
+ // which changes between runs.
242
+ collected: () => {
243
+ const out = {};
244
+ let any = false;
245
+ const keys = [...shapes.keys()].sort(compareText);
246
+ for (const key of keys) {
247
+ const shape = shapes.get(key) ?? null;
248
+ if (shape !== null) {
249
+ out[key] = shape;
250
+ any = true;
251
+ }
252
+ }
253
+ return any ? out : null;
254
+ },
255
+ shapeOf: (key) => shapes.get(key)
256
+ };
257
+ }
258
+ function withDefinitionsNoted(read2) {
259
+ const table = current;
260
+ if (table === null) {
261
+ return { value: read2(), noted: [] };
262
+ }
263
+ const keys = /* @__PURE__ */ new Set();
264
+ current = {
265
+ has: (key) => {
266
+ keys.add(key);
267
+ return table.has(key);
268
+ },
269
+ reserve: (key) => {
270
+ keys.add(key);
271
+ table.reserve(key);
272
+ },
273
+ define: (key, shape) => {
274
+ keys.add(key);
275
+ table.define(key, shape);
276
+ },
277
+ defer: (key, expand) => {
278
+ keys.add(key);
279
+ table.defer(key, expand);
280
+ },
281
+ settle: () => table.settle(),
282
+ collected: () => table.collected(),
283
+ shapeOf: (key) => table.shapeOf(key)
284
+ };
285
+ let value;
286
+ try {
287
+ value = read2();
288
+ } finally {
289
+ current = table;
290
+ }
291
+ const noted = [];
292
+ for (const key of keys) {
293
+ const shape = table.shapeOf(key);
294
+ if (shape === null || shape === void 0) {
295
+ return { value, noted: null };
296
+ }
297
+ noted.push([key, shape]);
298
+ }
299
+ return { value, noted };
300
+ }
301
+ function writeNotedDefinitions(noted) {
302
+ const table = current;
303
+ if (table === null) {
304
+ return;
305
+ }
306
+ for (const [key, shape] of noted) {
307
+ if (!table.has(key)) {
308
+ table.define(key, structuredClone(shape));
309
+ }
310
+ }
311
+ }
312
+ function withDefinitions(read2) {
313
+ const table = createDefinitionTable();
314
+ const outer = current;
315
+ current = table;
316
+ try {
317
+ return { value: read2(), definitions: table.collected() };
318
+ } finally {
319
+ current = outer;
320
+ }
321
+ }
322
+
323
+ // src/bodyWalks.ts
324
+ var keptByBody = /* @__PURE__ */ new WeakMap();
325
+ var talliesByRecognizer = /* @__PURE__ */ new WeakMap();
326
+ var ids = /* @__PURE__ */ new WeakMap();
327
+ var nextId = 1;
328
+ function noteRecognizerTally(recognizer, tally) {
329
+ talliesByRecognizer.set(recognizer, tally);
330
+ }
331
+ function walkBodyOnce(body, inputs, walk) {
332
+ const key = keyOf(inputs);
333
+ const facts = inputs.resolution?.sourceFactCount() ?? -1;
334
+ const kept = keptByBody.get(body)?.find((one) => one.key === key && one.facts === facts);
335
+ if (kept !== void 0) {
336
+ return replay(kept);
337
+ }
338
+ const before = new Map(
339
+ [...talliesOf(inputs)].map((tally) => [tally, tally.effectsRecognized])
340
+ );
341
+ const read2 = withFilesNoted(() => withDefinitionsNoted(walk));
342
+ const { value, noted } = read2.value;
343
+ const factsAfter = inputs.resolution?.sourceFactCount() ?? -1;
344
+ if (noted === null || factsAfter !== facts) {
345
+ return value;
346
+ }
347
+ const effectsByTally = /* @__PURE__ */ new Map();
348
+ for (const [tally, count] of before) {
349
+ effectsByTally.set(tally, tally.effectsRecognized - count);
350
+ }
351
+ const entry = {
352
+ key,
353
+ facts,
354
+ walk: copyWalk(value),
355
+ definitions: noted.map(([name, shape]) => [name, structuredClone(shape)]),
356
+ files: read2.files,
357
+ effectsByTally
358
+ };
359
+ const forBody = keptByBody.get(body);
360
+ if (forBody === void 0) {
361
+ keptByBody.set(body, [entry]);
362
+ } else {
363
+ forBody.push(entry);
364
+ }
365
+ return value;
366
+ }
367
+ function replay(kept) {
368
+ writeNotedDefinitions(kept.definitions);
369
+ for (const filePath of kept.files) {
370
+ recordFileDependency(filePath);
371
+ }
372
+ for (const [tally, count] of kept.effectsByTally) {
373
+ tally.effectsRecognized += count;
374
+ }
375
+ return copyWalk(kept.walk);
376
+ }
377
+ function keyOf(inputs) {
378
+ const barrierIds = [...inputs.barriers].map(idOf).sort((a, b) => a - b);
379
+ return [
380
+ inputs.terminalPatterns.map(idOf).join(","),
381
+ idOf(inputs.invocationRecognizers),
382
+ idOf(inputs.accessRecognizers),
383
+ barrierIds.join(","),
384
+ inputs.resolution === void 0 ? 0 : idOf(inputs.resolution),
385
+ inputs.callbacks.map((present) => present ? 1 : 0).join(""),
386
+ // A walk with no table to write named types into notes none, so it
387
+ // cannot stand in for one that has a table.
388
+ definitionsInProgress() === null ? 0 : 1
389
+ ].join("|");
390
+ }
391
+ function idOf(value) {
392
+ const known = ids.get(value);
393
+ if (known !== void 0) {
394
+ return known;
395
+ }
396
+ const id = nextId++;
397
+ ids.set(value, id);
398
+ return id;
399
+ }
400
+ function talliesOf(inputs) {
401
+ const tallies = /* @__PURE__ */ new Set();
402
+ for (const recognizer of [
403
+ ...inputs.invocationRecognizers,
404
+ ...inputs.accessRecognizers
405
+ ]) {
406
+ const tally = talliesByRecognizer.get(recognizer);
407
+ if (tally !== void 0) {
408
+ tallies.add(tally);
409
+ }
410
+ }
411
+ return tallies;
412
+ }
413
+ function copyWalk(walk) {
414
+ return {
415
+ terminals: walk.terminals.map((found) => ({
416
+ ...found,
417
+ terminal: structuredClone(found.terminal),
418
+ ...found.whenAlso === void 0 ? {} : { whenAlso: copyCondition(found.whenAlso) }
419
+ })),
420
+ invocations: walk.invocations.map((site) => ({
421
+ ...site,
422
+ effect: structuredClone(site.effect)
423
+ })),
424
+ recognized: walk.recognized.map((site) => ({
425
+ ...site,
426
+ effect: structuredClone(site.effect),
427
+ preconditions: site.preconditions.map(copyCondition),
428
+ ...site.slots === void 0 ? {} : { slots: site.slots.map((slot) => ({ ...slot })) }
429
+ }))
430
+ };
431
+ }
432
+ function copyCondition(condition) {
433
+ return structuredClone(condition);
434
+ }
435
+
46
436
  // src/conditions.ts
47
437
  import {
48
438
  Node as Node46
@@ -145,7 +535,6 @@ import { normalizeCalleeText } from "@suss/behavioral-ir";
145
535
 
146
536
  // src/facts/store.ts
147
537
  import { Node as Node9 } from "ts-morph";
148
- import { isTestFile } from "@suss/behavioral-ir";
149
538
  import {
150
539
  clearRelations,
151
540
  Database as Database2,
@@ -312,7 +701,7 @@ function createPerFileCache() {
312
701
 
313
702
  // src/walk/rawDescent.ts
314
703
  import {
315
- ts
704
+ ts as ts2
316
705
  } from "ts-morph";
317
706
  function statementsOfKind(file, kind) {
318
707
  const found = [];
@@ -334,10 +723,10 @@ function forEachDescendantWhere(root, accepts, visit, skips = runsNothing) {
334
723
  if (accepts(raw) && visit(wrapperOf(root, raw)) === true) {
335
724
  return void 0;
336
725
  }
337
- ts.forEachChild(raw, walk);
726
+ ts2.forEachChild(raw, walk);
338
727
  return void 0;
339
728
  };
340
- ts.forEachChild(root.compilerNode, walk);
729
+ ts2.forEachChild(root.compilerNode, walk);
341
730
  }
342
731
  function runsNothing(raw) {
343
732
  return isTypeOnlySyntax(raw) || isAmbient(raw);
@@ -346,18 +735,18 @@ function skipsNothing() {
346
735
  return false;
347
736
  }
348
737
  function isTypeOnlySyntax(raw) {
349
- return raw.kind >= ts.SyntaxKind.FirstTypeNode && raw.kind <= ts.SyntaxKind.LastTypeNode || TYPE_DECLARATIONS.has(raw.kind);
738
+ return raw.kind >= ts2.SyntaxKind.FirstTypeNode && raw.kind <= ts2.SyntaxKind.LastTypeNode || TYPE_DECLARATIONS.has(raw.kind);
350
739
  }
351
740
  var TYPE_DECLARATIONS = /* @__PURE__ */ new Set([
352
- ts.SyntaxKind.TypeAliasDeclaration,
353
- ts.SyntaxKind.InterfaceDeclaration,
354
- ts.SyntaxKind.TypeParameter
741
+ ts2.SyntaxKind.TypeAliasDeclaration,
742
+ ts2.SyntaxKind.InterfaceDeclaration,
743
+ ts2.SyntaxKind.TypeParameter
355
744
  ]);
356
745
  function isAmbient(raw) {
357
- if (!ts.canHaveModifiers(raw)) {
746
+ if (!ts2.canHaveModifiers(raw)) {
358
747
  return false;
359
748
  }
360
- return ts.getModifiers(raw)?.some((modifier) => modifier.kind === ts.SyntaxKind.DeclareKeyword) ?? false;
749
+ return ts2.getModifiers(raw)?.some((modifier) => modifier.kind === ts2.SyntaxKind.DeclareKeyword) ?? false;
361
750
  }
362
751
  function wrapperOf(anchor, raw) {
363
752
  return anchor._getNodeFromCompilerNode(
@@ -476,38 +865,23 @@ function indexFunctions(sf) {
476
865
  const bySpan = spanKey({ start: node.getStart(), end: node.getEnd() });
477
866
  index.set(bySpan, node);
478
867
  const key = rangeKey({ start: startLineOf(node), end: endLineOf(node) });
479
- if (!index.has(key)) {
480
- index.set(key, node);
481
- }
482
- return false;
483
- },
484
- isTypeOnlySyntax
485
- );
486
- return index;
487
- }
488
-
489
- // src/depTracking.ts
490
- function createDependencySink() {
491
- return { files: /* @__PURE__ */ new Set(), mountPrefixes: /* @__PURE__ */ new Map(), claims: [] };
492
- }
493
- var activeSink = null;
494
- function withDependencySink(sink, fn) {
495
- const previous = activeSink;
496
- activeSink = sink;
497
- try {
498
- return fn();
499
- } finally {
500
- activeSink = previous;
501
- }
502
- }
503
- function recordFileDependency(filePath) {
504
- activeSink?.files.add(filePath);
868
+ if (!index.has(key)) {
869
+ index.set(key, node);
870
+ }
871
+ return false;
872
+ },
873
+ isTypeOnlySyntax
874
+ );
875
+ return index;
505
876
  }
506
- function recordMountPrefix(childId, prefix) {
507
- activeSink?.mountPrefixes.set(childId, prefix);
877
+
878
+ // src/testCode.ts
879
+ import { isTestFile } from "@suss/behavioral-ir";
880
+ function inTestFile(node) {
881
+ return isTestFile(node.getSourceFile().getFilePath());
508
882
  }
509
- function recordUnitClaim(key, pack) {
510
- activeSink?.claims.push({ key, pack });
883
+ function testCallingProduction(call, callee) {
884
+ return inTestFile(call) && !inTestFile(callee);
511
885
  }
512
886
 
513
887
  // src/facts/containerWrites.ts
@@ -519,7 +893,7 @@ import {
519
893
  ModuleDeclarationKind,
520
894
  Node as Node6,
521
895
  SyntaxKind as SyntaxKind6,
522
- ts as ts4
896
+ ts as ts5
523
897
  } from "ts-morph";
524
898
  import {
525
899
  addEntryRows,
@@ -541,7 +915,7 @@ import { Node as Node3, SyntaxKind as SyntaxKind4 } from "ts-morph";
541
915
  // src/bootstrap/lazyProjectInit.ts
542
916
  import fs2 from "fs/promises";
543
917
  import path3 from "path";
544
- import { ts as ts2 } from "ts-morph";
918
+ import { ts as ts3 } from "ts-morph";
545
919
 
546
920
  // src/facts/generatedModules.ts
547
921
  import fs from "fs";
@@ -1173,7 +1547,7 @@ function resolutionCacheFor(project, options) {
1173
1547
  if (existing !== void 0) {
1174
1548
  return existing;
1175
1549
  }
1176
- const fresh = ts2.createModuleResolutionCache(
1550
+ const fresh = ts3.createModuleResolutionCache(
1177
1551
  project.getFileSystem().getCurrentDirectory(),
1178
1552
  (name) => name,
1179
1553
  options
@@ -1207,11 +1581,11 @@ function readSpecifierPaths(project, filePath, options, host) {
1207
1581
  if (text3 === null) {
1208
1582
  return [];
1209
1583
  }
1210
- const scanned = ts2.preProcessFile(text3, true, true);
1584
+ const scanned = ts3.preProcessFile(text3, true, true);
1211
1585
  const cache = resolutionCacheFor(project, options);
1212
1586
  const paths = [];
1213
1587
  for (const imported of scanned.importedFiles) {
1214
- const resolved = ts2.resolveModuleName(
1588
+ const resolved = ts3.resolveModuleName(
1215
1589
  imported.fileName,
1216
1590
  filePath,
1217
1591
  options,
@@ -1237,13 +1611,13 @@ function readFileText(project, filePath) {
1237
1611
  }
1238
1612
  function parseTsconfig(tsConfigFilePath, seen = /* @__PURE__ */ new Set()) {
1239
1613
  seen.add(tsConfigFilePath);
1240
- const configFile = ts2.readConfigFile(tsConfigFilePath, ts2.sys.readFile);
1614
+ const configFile = ts3.readConfigFile(tsConfigFilePath, ts3.sys.readFile);
1241
1615
  if (configFile.error !== void 0) {
1242
1616
  return { fileNames: [], options: {} };
1243
1617
  }
1244
- const parsed = ts2.parseJsonConfigFileContent(
1618
+ const parsed = ts3.parseJsonConfigFileContent(
1245
1619
  configFile.config,
1246
- ts2.sys,
1620
+ ts3.sys,
1247
1621
  path3.dirname(tsConfigFilePath),
1248
1622
  /*existingOptions*/
1249
1623
  void 0,
@@ -1252,7 +1626,7 @@ function parseTsconfig(tsConfigFilePath, seen = /* @__PURE__ */ new Set()) {
1252
1626
  if (parsed.fileNames.length > 0) {
1253
1627
  return { fileNames: parsed.fileNames, options: parsed.options };
1254
1628
  }
1255
- const referenced = (parsed.projectReferences ?? []).map((reference) => ts2.resolveProjectReferencePath(reference)).filter((referencePath) => !seen.has(referencePath)).map((referencePath) => parseTsconfig(referencePath, seen));
1629
+ const referenced = (parsed.projectReferences ?? []).map((reference) => ts3.resolveProjectReferencePath(reference)).filter((referencePath) => !seen.has(referencePath)).map((referencePath) => parseTsconfig(referencePath, seen));
1256
1630
  const fileNames = [
1257
1631
  ...new Set(referenced.flatMap((reference) => reference.fileNames))
1258
1632
  ];
@@ -1326,17 +1700,17 @@ function addImportersOfMatches(fileImports, matched, options) {
1326
1700
  }
1327
1701
  function internalSpecifierResolver(files, options) {
1328
1702
  const fileSet = new Set(files);
1329
- const cache = ts2.createModuleResolutionCache(
1330
- ts2.sys.getCurrentDirectory(),
1703
+ const cache = ts3.createModuleResolutionCache(
1704
+ ts3.sys.getCurrentDirectory(),
1331
1705
  (name) => name,
1332
1706
  options
1333
1707
  );
1334
1708
  return (fromFile, spec) => {
1335
- const resolved = ts2.resolveModuleName(
1709
+ const resolved = ts3.resolveModuleName(
1336
1710
  spec,
1337
1711
  fromFile,
1338
1712
  options,
1339
- ts2.sys,
1713
+ ts3.sys,
1340
1714
  cache
1341
1715
  ).resolvedModule?.resolvedFileName;
1342
1716
  if (resolved === void 0) {
@@ -1345,7 +1719,7 @@ function internalSpecifierResolver(files, options) {
1345
1719
  if (fileSet.has(resolved)) {
1346
1720
  return resolved;
1347
1721
  }
1348
- const real = ts2.sys.realpath?.(resolved) ?? resolved;
1722
+ const real = ts3.sys.realpath?.(resolved) ?? resolved;
1349
1723
  return fileSet.has(real) ? real : null;
1350
1724
  };
1351
1725
  }
@@ -1380,7 +1754,7 @@ async function readImportsConcurrently(paths, marks) {
1380
1754
  const p = paths[i];
1381
1755
  try {
1382
1756
  const text3 = await fs2.readFile(p, "utf-8");
1383
- const pre = ts2.preProcessFile(text3, true, false);
1757
+ const pre = ts3.preProcessFile(text3, true, false);
1384
1758
  results[i] = {
1385
1759
  path: p,
1386
1760
  importedModules: pre.importedFiles.map((f) => f.fileName),
@@ -1616,7 +1990,7 @@ function keyNameOf(name) {
1616
1990
  }
1617
1991
 
1618
1992
  // src/facts/assignments.ts
1619
- import { Node as Node5, SyntaxKind as SyntaxKind5, ts as ts3, VariableDeclarationKind } from "ts-morph";
1993
+ import { Node as Node5, SyntaxKind as SyntaxKind5, ts as ts4, VariableDeclarationKind } from "ts-morph";
1620
1994
  import { valueLeftByWrites } from "@suss/resolution";
1621
1995
  var byFile = createPerFileCache();
1622
1996
  var byDeclaration = /* @__PURE__ */ new WeakMap();
@@ -1814,11 +2188,11 @@ function assignmentsInFile(sourceFile) {
1814
2188
  return found;
1815
2189
  }
1816
2190
  function mayWrite(raw) {
1817
- if (ts3.isBinaryExpression(raw)) {
1818
- return isAssignmentOperator(raw.operatorToken.kind) && (ts3.isIdentifier(raw.left) || ts3.isPropertyAccessExpression(raw.left));
2191
+ if (ts4.isBinaryExpression(raw)) {
2192
+ return isAssignmentOperator(raw.operatorToken.kind) && (ts4.isIdentifier(raw.left) || ts4.isPropertyAccessExpression(raw.left));
1819
2193
  }
1820
- if (ts3.isPrefixUnaryExpression(raw) || ts3.isPostfixUnaryExpression(raw)) {
1821
- return (raw.operator === SyntaxKind5.PlusPlusToken || raw.operator === SyntaxKind5.MinusMinusToken) && ts3.isIdentifier(raw.operand);
2194
+ if (ts4.isPrefixUnaryExpression(raw) || ts4.isPostfixUnaryExpression(raw)) {
2195
+ return (raw.operator === SyntaxKind5.PlusPlusToken || raw.operator === SyntaxKind5.MinusMinusToken) && ts4.isIdentifier(raw.operand);
1822
2196
  }
1823
2197
  return false;
1824
2198
  }
@@ -1879,7 +2253,7 @@ function isReadBefore(declaration, position) {
1879
2253
  const sourceFile = declaration.getSourceFile();
1880
2254
  const file = sourceFile.compilerNode;
1881
2255
  const readsHere = (raw) => {
1882
- if (!ts3.isIdentifier(raw) || raw.getText(file) !== name) {
2256
+ if (!ts4.isIdentifier(raw) || raw.getText(file) !== name) {
1883
2257
  return false;
1884
2258
  }
1885
2259
  const node = wrapperOf(sourceFile, raw);
@@ -1899,9 +2273,9 @@ function someBefore(root, position, test) {
1899
2273
  if (test(raw)) {
1900
2274
  return true;
1901
2275
  }
1902
- return ts3.forEachChild(raw, visit);
2276
+ return ts4.forEachChild(raw, visit);
1903
2277
  };
1904
- return ts3.forEachChild(root, visit) === true;
2278
+ return ts4.forEachChild(root, visit) === true;
1905
2279
  }
1906
2280
  var OWN_BODY_KINDS = /* @__PURE__ */ new Set([
1907
2281
  SyntaxKind5.FunctionDeclaration,
@@ -2003,7 +2377,7 @@ function isPropertyReadBefore(statements, name, property, position) {
2003
2377
  return someBefore(
2004
2378
  statements.compilerNode,
2005
2379
  position,
2006
- (raw) => ts3.isPropertyAccessExpression(raw) && raw.name.getText(file) === property && raw.expression.getText(file) === name && !isWriteTarget(wrapperOf(statements, raw))
2380
+ (raw) => ts4.isPropertyAccessExpression(raw) && raw.name.getText(file) === property && raw.expression.getText(file) === name && !isWriteTarget(wrapperOf(statements, raw))
2007
2381
  );
2008
2382
  }
2009
2383
 
@@ -2075,7 +2449,7 @@ function moduleKeyOf(declaration) {
2075
2449
  }
2076
2450
  function compilerResolvedPathOf(declaration, specifier) {
2077
2451
  const project = declaration.getProject();
2078
- const result = ts4.resolveModuleName(
2452
+ const result = ts5.resolveModuleName(
2079
2453
  specifier,
2080
2454
  declaration.getSourceFile().getFilePath(),
2081
2455
  project.getCompilerOptions(),
@@ -2386,7 +2760,7 @@ function spellingsHandedOn(paths, sourceFile) {
2386
2760
  const found = [];
2387
2761
  forEachDescendantWhere(
2388
2762
  sourceFile,
2389
- (raw) => ts4.isPropertyAccessExpression(raw) && paths.includes(compilerDottedPathOf(raw, file) ?? ""),
2763
+ (raw) => ts5.isPropertyAccessExpression(raw) && paths.includes(compilerDottedPathOf(raw, file) ?? ""),
2390
2764
  (node) => {
2391
2765
  if (flowsOnward(node)) {
2392
2766
  found.push(node);
@@ -2397,14 +2771,14 @@ function spellingsHandedOn(paths, sourceFile) {
2397
2771
  return found;
2398
2772
  }
2399
2773
  function compilerDottedPathOf(raw, file) {
2400
- if (ts4.isIdentifier(raw)) {
2774
+ if (ts5.isIdentifier(raw)) {
2401
2775
  return raw.getText(file);
2402
2776
  }
2403
- if (ts4.isMetaProperty(raw)) {
2404
- const keyword = ts4.tokenToString(raw.keywordToken);
2777
+ if (ts5.isMetaProperty(raw)) {
2778
+ const keyword = ts5.tokenToString(raw.keywordToken);
2405
2779
  return `${keyword}.${raw.name.getText(file)}`;
2406
2780
  }
2407
- if (!ts4.isPropertyAccessExpression(raw)) {
2781
+ if (!ts5.isPropertyAccessExpression(raw)) {
2408
2782
  return null;
2409
2783
  }
2410
2784
  const object = compilerDottedPathOf(raw.expression, file);
@@ -2425,7 +2799,7 @@ function dottedPathOf(expression) {
2425
2799
  return expression.getText();
2426
2800
  }
2427
2801
  if (Node6.isMetaProperty(expression)) {
2428
- const keyword = ts4.tokenToString(expression.getKeywordToken());
2802
+ const keyword = ts5.tokenToString(expression.getKeywordToken());
2429
2803
  return `${keyword}.${expression.getName()}`;
2430
2804
  }
2431
2805
  if (!Node6.isPropertyAccessExpression(expression)) {
@@ -3767,6 +4141,10 @@ var NOT_BASE_FACTS = /* @__PURE__ */ new Set([
3767
4141
  ...RESOLUTION_PROGRAM.rules.map((r) => r.head.relation),
3768
4142
  ...ASKING_RELATIONS
3769
4143
  ]);
4144
+ var DERIVED_RELATIONS = /* @__PURE__ */ new Set([
4145
+ ...NOT_BASE_FACTS,
4146
+ ...resolutionUnderProgram().rules.map((r) => r.head.relation)
4147
+ ]);
3770
4148
  var QUERY_FACTS = queryFacts(RESOLUTION_PROGRAM);
3771
4149
  var MODULE_DEMANDS = RESOLUTION_PROGRAM.demands.filter(
3772
4150
  (one) => (one.relation === "moduleExport" || one.relation === "moduleForwards") && one.bound[0] === true
@@ -3803,6 +4181,8 @@ var ResolutionStore = class _ResolutionStore {
3803
4181
  constructionSites = /* @__PURE__ */ new Map();
3804
4182
  /** What each local's reads do with it, by its declaration. A local's function is walked whole, so an answer stays true. */
3805
4183
  localUses = /* @__PURE__ */ new Map();
4184
+ /** See `remembered`. */
4185
+ answers = /* @__PURE__ */ new Map();
3806
4186
  /** Files the most recent query read, for the memo to keep. */
3807
4187
  lastQueryWalked = [];
3808
4188
  /** See `environmentSiteFiles`; null until the first env question. */
@@ -3854,12 +4234,18 @@ var ResolutionStore = class _ResolutionStore {
3854
4234
  */
3855
4235
  resolveCallableSources(value, alsoFrom) {
3856
4236
  const target = factKeyOf(value);
3857
- return this.askAbout(
3858
- target,
3859
- "wanted",
3860
- () => this.lookupSources(target),
3861
- alsoFrom
3862
- );
4237
+ const key = `sources|${nodeId(target)}|${alsoFrom?.getFilePath() ?? ""}`;
4238
+ return [
4239
+ ...this.remembered(
4240
+ key,
4241
+ () => this.askAbout(
4242
+ target,
4243
+ "wanted",
4244
+ () => this.lookupSources(target),
4245
+ alsoFrom
4246
+ )
4247
+ )
4248
+ ];
3863
4249
  }
3864
4250
  /**
3865
4251
  * `resolveCallable` for several values, asked as one question. A file
@@ -3921,7 +4307,45 @@ var ResolutionStore = class _ResolutionStore {
3921
4307
  }
3922
4308
  resolveObject(value) {
3923
4309
  const target = factKeyOf(value);
3924
- return this.askAbout(target, "wanted", () => this.lookupObject(target));
4310
+ return this.remembered(
4311
+ `object|${nodeId(target)}`,
4312
+ () => this.askAbout(target, "wanted", () => this.lookupObject(target))
4313
+ );
4314
+ }
4315
+ /**
4316
+ * A question asked again over the same facts reads the same files and
4317
+ * derives the same answer, so the store gives back the one it found
4318
+ * before. The files that answer read still count as read.
4319
+ */
4320
+ remembered(key, ask) {
4321
+ const known = this.answers.get(key);
4322
+ if (known !== void 0 && known.facts === this.sourceFactCount()) {
4323
+ for (const walkedPath of known.walked) {
4324
+ recordFileDependency(walkedPath);
4325
+ }
4326
+ return known.answer;
4327
+ }
4328
+ const answer = ask();
4329
+ this.answers.set(key, {
4330
+ answer,
4331
+ walked: [...this.lastQueryWalked],
4332
+ facts: this.sourceFactCount()
4333
+ });
4334
+ return answer;
4335
+ }
4336
+ /**
4337
+ * How many facts the store has read from source. They are only ever
4338
+ * added, so work that asked the store questions and found this number
4339
+ * unchanged afterwards would get the same answers asking again.
4340
+ */
4341
+ sourceFactCount() {
4342
+ let count = 0;
4343
+ for (const relation of this.db.relationNames()) {
4344
+ if (!DERIVED_RELATIONS.has(relation)) {
4345
+ count += this.db.size(relation);
4346
+ }
4347
+ }
4348
+ return count;
3925
4349
  }
3926
4350
  /**
3927
4351
  * The function a call returns: `app.use(requireCaller(config))`
@@ -4021,7 +4445,7 @@ var ResolutionStore = class _ResolutionStore {
4021
4445
  writtenAnswers(values, wanted) {
4022
4446
  const found = /* @__PURE__ */ new Map();
4023
4447
  const asked = /* @__PURE__ */ new Map();
4024
- const keyOf = /* @__PURE__ */ new Map();
4448
+ const keyOf2 = /* @__PURE__ */ new Map();
4025
4449
  for (const value of values) {
4026
4450
  const target = factKeyOf(value);
4027
4451
  const key = nodeId(this.sharedDeclarationFor(target));
@@ -4036,7 +4460,7 @@ var ResolutionStore = class _ResolutionStore {
4036
4460
  if (!asked.has(key)) {
4037
4461
  asked.set(key, target);
4038
4462
  }
4039
- keyOf.set(value, key);
4463
+ keyOf2.set(value, key);
4040
4464
  }
4041
4465
  if (asked.size === 0) {
4042
4466
  return found;
@@ -4051,7 +4475,7 @@ var ResolutionStore = class _ResolutionStore {
4051
4475
  });
4052
4476
  }
4053
4477
  });
4054
- for (const [value, key] of keyOf) {
4478
+ for (const [value, key] of keyOf2) {
4055
4479
  found.set(value, this.writtenValues.get(key)?.[wanted] ?? null);
4056
4480
  }
4057
4481
  return found;
@@ -5334,9 +5758,6 @@ function namesPackage2(moduleKey, packages) {
5334
5758
  packages
5335
5759
  );
5336
5760
  }
5337
- function inTestFile(node) {
5338
- return isTestFile(node.getSourceFile().getFilePath());
5339
- }
5340
5761
  function declarationOf(value) {
5341
5762
  const nameNode = Node9.isPropertyAccessExpression(value) ? value.getNameNode() : value;
5342
5763
  return nameNode.getSymbol()?.getDeclarations()[0] ?? value;
@@ -6433,7 +6854,7 @@ function settledChannel(argument, resolution) {
6433
6854
  }
6434
6855
 
6435
6856
  // src/discovery/importedCalls.ts
6436
- import { Node as Node14, ts as ts5 } from "ts-morph";
6857
+ import { Node as Node14, ts as ts6 } from "ts-morph";
6437
6858
  function callsResolvingTo(sourceFile, resolution, spec) {
6438
6859
  return [
6439
6860
  ...callsByOriginName(
@@ -6479,7 +6900,7 @@ function callsNamed(found, names) {
6479
6900
  return matched;
6480
6901
  }
6481
6902
  function callsANamedCallee(raw) {
6482
- return (ts5.isCallExpression(raw) || ts5.isNewExpression(raw)) && ts5.isIdentifier(raw.expression);
6903
+ return (ts6.isCallExpression(raw) || ts6.isNewExpression(raw)) && ts6.isIdentifier(raw.expression);
6483
6904
  }
6484
6905
  function declaredLocally(callee) {
6485
6906
  const declarations = callee.getSymbol()?.getDeclarations() ?? [];
@@ -8884,7 +9305,7 @@ function evaluateObjectLiteralAsJson(node) {
8884
9305
  return node.getLiteralValue();
8885
9306
  }
8886
9307
  if (Node25.isNumericLiteral(node)) {
8887
- return Number(node.getText());
9308
+ return node.getLiteralValue();
8888
9309
  }
8889
9310
  const kind = node.getText();
8890
9311
  if (kind === "true") {
@@ -9392,6 +9813,9 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context, ch
9392
9813
  const units = [];
9393
9814
  let unfollowed = 0;
9394
9815
  for (const { call, argument } of passed) {
9816
+ if (testCallingProduction(call, parameter)) {
9817
+ continue;
9818
+ }
9395
9819
  const caller = enclosingFunctionRoot(call);
9396
9820
  if (caller === null || !Node28.isCallExpression(call)) {
9397
9821
  unfollowed++;
@@ -11008,7 +11432,7 @@ function creationSiteBehind(parameter, resolution) {
11008
11432
  }
11009
11433
  function registrationSubjectIdsOf(sourceFile, matches, resolution) {
11010
11434
  const seenImports = /* @__PURE__ */ new Set();
11011
- const ids = /* @__PURE__ */ new Set();
11435
+ const ids2 = /* @__PURE__ */ new Set();
11012
11436
  for (const match of matches) {
11013
11437
  const importKey = `${match.importModule}::${match.importName}`;
11014
11438
  if (seenImports.has(importKey)) {
@@ -11021,17 +11445,17 @@ function registrationSubjectIdsOf(sourceFile, matches, resolution) {
11021
11445
  match.importName,
11022
11446
  resolution
11023
11447
  ).values()) {
11024
- ids.add(nodeId(node));
11448
+ ids2.add(nodeId(node));
11025
11449
  }
11026
11450
  for (const node of unboundConstructionsOf(
11027
11451
  sourceFile,
11028
11452
  match.importModule,
11029
11453
  match.importName
11030
11454
  )) {
11031
- ids.add(nodeId(node));
11455
+ ids2.add(nodeId(node));
11032
11456
  }
11033
11457
  }
11034
- return ids;
11458
+ return ids2;
11035
11459
  }
11036
11460
  function unboundConstructionsOf(sourceFile, importModule, importName) {
11037
11461
  const spellings = importedReferenceSpellings(
@@ -13085,7 +13509,7 @@ import {
13085
13509
  import {
13086
13510
  Node as Node47,
13087
13511
  SyntaxKind as SyntaxKind20,
13088
- ts as ts6
13512
+ ts as ts7
13089
13513
  } from "ts-morph";
13090
13514
  import {
13091
13515
  UnmodeledFlow
@@ -13109,7 +13533,7 @@ function exitKindOf(stmt, endings) {
13109
13533
  if (raw.kind === SyntaxKind20.ThrowStatement) {
13110
13534
  sawThrow = true;
13111
13535
  }
13112
- ts6.forEachChild(raw, visit);
13536
+ ts7.forEachChild(raw, visit);
13113
13537
  return void 0;
13114
13538
  };
13115
13539
  visit(root);
@@ -13222,7 +13646,7 @@ function lowerCallbacksIn(parts, ctx) {
13222
13646
  if (runsNothing(raw)) {
13223
13647
  return void 0;
13224
13648
  }
13225
- ts6.forEachChild(raw, visit);
13649
+ ts7.forEachChild(raw, visit);
13226
13650
  return void 0;
13227
13651
  };
13228
13652
  visit(root);
@@ -13599,126 +14023,6 @@ import { Node as Node51 } from "ts-morph";
13599
14023
 
13600
14024
  // src/values/receiverType.ts
13601
14025
  import { Node as Node50 } from "ts-morph";
13602
-
13603
- // src/shapes/typeText.ts
13604
- import { ts as ts7 } from "ts-morph";
13605
- var PRINTER = ts7.createPrinter({
13606
- removeComments: true,
13607
- newLine: ts7.NewLineKind.LineFeed
13608
- });
13609
- var NO_FILE = ts7.createSourceFile("", "", ts7.ScriptTarget.Latest);
13610
- function defaultFlags(enclosing) {
13611
- const flags = ts7.TypeFormatFlags.UseTypeOfFunction | ts7.TypeFormatFlags.NoTruncation | ts7.TypeFormatFlags.UseFullyQualifiedType | ts7.TypeFormatFlags.WriteTypeArgumentsOfSignature;
13612
- if (enclosing?.getKind() === ts7.SyntaxKind.TypeAliasDeclaration) {
13613
- return flags | ts7.TypeFormatFlags.InTypeAlias;
13614
- }
13615
- return flags;
13616
- }
13617
- function stableTypeText(type, reader, enclosing, flags) {
13618
- const format = flags ?? defaultFlags(enclosing);
13619
- const place = enclosing?.compilerNode ?? NOWHERE;
13620
- const atPlace = printedAt.get(place) ?? /* @__PURE__ */ new WeakMap();
13621
- printedAt.set(place, atPlace);
13622
- const byFormat = atPlace.get(type.compilerType) ?? /* @__PURE__ */ new Map();
13623
- atPlace.set(type.compilerType, byFormat);
13624
- const kept = byFormat.get(format);
13625
- if (kept !== void 0) {
13626
- return kept;
13627
- }
13628
- const text3 = printedType(type, reader, enclosing, format);
13629
- byFormat.set(format, text3);
13630
- return text3;
13631
- }
13632
- var NOWHERE = {};
13633
- var printedAt = /* @__PURE__ */ new WeakMap();
13634
- function printedType(type, reader, enclosing, format) {
13635
- const checker = reader.getProject().getTypeChecker().compilerObject;
13636
- const enclosingNode = enclosing?.compilerNode;
13637
- const typeNode = checker.typeToTypeNode(
13638
- type.compilerType,
13639
- enclosingNode,
13640
- format & ts7.TypeFormatFlags.NodeBuilderFlagsMask | ts7.NodeBuilderFlags.IgnoreErrors
13641
- );
13642
- if (typeNode === void 0) {
13643
- return withoutImportQualifiers(
13644
- checker.typeToString(type.compilerType, enclosingNode, format)
13645
- );
13646
- }
13647
- const file = enclosing?.getSourceFile().compilerNode ?? NO_FILE;
13648
- const ordered = containsUnion(typeNode) ? withUnionMembersOrdered(typeNode, file) : typeNode;
13649
- return withoutImportQualifiers(printed(ordered, file));
13650
- }
13651
- var IMPORT_QUALIFIER = /import\("[^"]*"\)\./g;
13652
- var withoutImportQualifiers = (text3) => text3.replace(IMPORT_QUALIFIER, "");
13653
- function printed(node, file) {
13654
- return PRINTER.printNode(ts7.EmitHint.Unspecified, node, file).replace(
13655
- /\n/g,
13656
- ""
13657
- );
13658
- }
13659
- function containsUnion(node) {
13660
- if (ts7.isUnionTypeNode(node)) {
13661
- return true;
13662
- }
13663
- return ts7.forEachChild(node, (child) => containsUnion(child) || void 0) === true;
13664
- }
13665
- function withUnionMembersOrdered(root, file) {
13666
- const result = ts7.transform(root, [
13667
- (context) => {
13668
- const visit = (node) => {
13669
- const visited = ts7.visitEachChild(node, visit, context);
13670
- if (!ts7.isUnionTypeNode(visited)) {
13671
- return visited;
13672
- }
13673
- return context.factory.updateUnionTypeNode(
13674
- visited,
13675
- context.factory.createNodeArray(inStableOrder(visited.types, file))
13676
- );
13677
- };
13678
- return (node) => visit(node);
13679
- }
13680
- ]);
13681
- const [ordered] = result.transformed;
13682
- result.dispose();
13683
- return ordered;
13684
- }
13685
- function inStableOrder(members, file) {
13686
- return members.map((member) => ({
13687
- member,
13688
- text: withoutImportQualifiers(printed(member, file))
13689
- })).sort((a, b) => compareMembers(a.text, b.text)).map(({ member }) => member);
13690
- }
13691
- function compareMembers(a, b) {
13692
- return nullishRank(a) - nullishRank(b) || compareText(a, b);
13693
- }
13694
- function nullishRank(text3) {
13695
- if (text3 === "null") {
13696
- return 1;
13697
- }
13698
- if (text3 === "undefined") {
13699
- return 2;
13700
- }
13701
- return 0;
13702
- }
13703
- function compareText(a, b) {
13704
- if (a < b) {
13705
- return -1;
13706
- }
13707
- if (a > b) {
13708
- return 1;
13709
- }
13710
- return 0;
13711
- }
13712
- function stablePropertyName(symbol, reader) {
13713
- const name = symbol.getName();
13714
- if (!name.startsWith("__@")) {
13715
- return name;
13716
- }
13717
- const checker = reader.getProject().getTypeChecker().compilerObject;
13718
- return checker.symbolToString(symbol.compilerSymbol);
13719
- }
13720
-
13721
- // src/values/receiverType.ts
13722
14026
  function receiverTypesOf(node) {
13723
14027
  const type = node.getType();
13724
14028
  if (type.isAny()) {
@@ -15732,67 +16036,6 @@ import {
15732
16036
  // src/shapes/typeShapes.ts
15733
16037
  import { createHash } from "crypto";
15734
16038
  import { Node as Node58, TypeFormatFlags } from "ts-morph";
15735
-
15736
- // src/shapes/definitions.ts
15737
- var current = null;
15738
- var definitionsInProgress = () => current;
15739
- function createDefinitionTable() {
15740
- const shapes = /* @__PURE__ */ new Map();
15741
- const deferred = [];
15742
- let settling = false;
15743
- return {
15744
- has: (key) => shapes.has(key),
15745
- reserve: (key) => {
15746
- shapes.set(key, null);
15747
- },
15748
- define: (key, shape) => {
15749
- shapes.set(key, shape);
15750
- },
15751
- defer: (key, expand) => {
15752
- deferred.push({ key, expand });
15753
- },
15754
- settle: () => {
15755
- if (settling) {
15756
- return;
15757
- }
15758
- settling = true;
15759
- try {
15760
- for (let next = deferred.shift(); next; next = deferred.shift()) {
15761
- shapes.set(next.key, next.expand());
15762
- }
15763
- } finally {
15764
- settling = false;
15765
- }
15766
- },
15767
- // Sorted because the walk meets types in the checker's union order,
15768
- // which changes between runs.
15769
- collected: () => {
15770
- const out = {};
15771
- let any = false;
15772
- const keys = [...shapes.keys()].sort(compareText);
15773
- for (const key of keys) {
15774
- const shape = shapes.get(key) ?? null;
15775
- if (shape !== null) {
15776
- out[key] = shape;
15777
- any = true;
15778
- }
15779
- }
15780
- return any ? out : null;
15781
- }
15782
- };
15783
- }
15784
- function withDefinitions(read2) {
15785
- const table = createDefinitionTable();
15786
- const outer = current;
15787
- current = table;
15788
- try {
15789
- return { value: read2(), definitions: table.collected() };
15790
- } finally {
15791
- current = outer;
15792
- }
15793
- }
15794
-
15795
- // src/shapes/typeShapes.ts
15796
16039
  var MAX_DEPTH = 6;
15797
16040
  var OPAQUE_NAMED_TYPES = /* @__PURE__ */ new Set([
15798
16041
  "Date",
@@ -15830,16 +16073,17 @@ function overloadIsAGuess(node) {
15830
16073
  if (signatures.length < 2) {
15831
16074
  return false;
15832
16075
  }
15833
- const returns = new Set(
15834
- signatures.map((signature) => signature.getReturnType().getText(node))
15835
- );
15836
- if (returns.size < 2) {
15837
- return false;
15838
- }
15839
- return node.getArguments().some((argument) => {
16076
+ const untypedArgument = node.getArguments().some((argument) => {
15840
16077
  const type = argument.getType();
15841
16078
  return type.isAny() || type.isNever();
15842
16079
  });
16080
+ if (!untypedArgument) {
16081
+ return false;
16082
+ }
16083
+ const returns = new Set(
16084
+ signatures.map((signature) => signature.getReturnType().getText(node))
16085
+ );
16086
+ return returns.size >= 2;
15843
16087
  }
15844
16088
  function typeToShape(type, ctx) {
15845
16089
  if (type.isAny()) {
@@ -17018,7 +17262,7 @@ function evaluateComparison(condition, substitutions) {
17018
17262
  }
17019
17263
  function numberFor(node, substitutions) {
17020
17264
  const resolved = Node62.isIdentifier(node) && substitutions.has(node.getText()) ? substitutions.get(node.getText()) : node;
17021
- return Node62.isNumericLiteral(resolved) ? Number(resolved.getText()) : null;
17265
+ return Node62.isNumericLiteral(resolved) ? resolved.getLiteralValue() : null;
17022
17266
  }
17023
17267
  var helperBySymbol = createProgramMemo();
17024
17268
  function memoizedLocalHelper(callee, resolveCallee2) {
@@ -17920,33 +18164,50 @@ var RETURNS_AND_SCOPES = /* @__PURE__ */ new Set([
17920
18164
  SyntaxKind27.ReturnStatement
17921
18165
  ]);
17922
18166
  function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2, collectCallAccounting = false, keepsArms, handOff) {
17923
- const terminals = findTerminals(
18167
+ const { terminals, invocations, recognized } = walkBodyOnce(
17924
18168
  func,
17925
- terminalPatterns,
17926
- barriers,
17927
- resolution,
17928
- originatesFrom,
17929
- resolveCallee2
17930
- );
17931
- const invocations = extractInvocationEffects(func, barriers);
17932
- const recognized = [
17933
- ...runInvocationRecognizers(
17934
- func,
18169
+ {
18170
+ terminalPatterns,
17935
18171
  invocationRecognizers,
17936
- barriers,
17937
- resolution,
17938
- originatesFrom,
17939
- anchorCallsOf
17940
- ),
17941
- ...runAccessRecognizers(
17942
- func,
17943
18172
  accessRecognizers,
17944
18173
  barriers,
17945
18174
  resolution,
17946
- originatesFrom,
17947
- anchorCallsOf
17948
- )
17949
- ];
18175
+ callbacks: [
18176
+ originatesFrom !== void 0,
18177
+ anchorCallsOf !== void 0,
18178
+ resolveCallee2 !== void 0
18179
+ ]
18180
+ },
18181
+ () => ({
18182
+ terminals: findTerminals(
18183
+ func,
18184
+ terminalPatterns,
18185
+ barriers,
18186
+ resolution,
18187
+ originatesFrom,
18188
+ resolveCallee2
18189
+ ),
18190
+ invocations: extractInvocationEffects(func, barriers),
18191
+ recognized: [
18192
+ ...runInvocationRecognizers(
18193
+ func,
18194
+ invocationRecognizers,
18195
+ barriers,
18196
+ resolution,
18197
+ originatesFrom,
18198
+ anchorCallsOf
18199
+ ),
18200
+ ...runAccessRecognizers(
18201
+ func,
18202
+ accessRecognizers,
18203
+ barriers,
18204
+ resolution,
18205
+ originatesFrom,
18206
+ anchorCallsOf
18207
+ )
18208
+ ]
18209
+ })
18210
+ );
17950
18211
  const endings = new Set(
17951
18212
  terminals.filter(({ terminal }) => terminal.kind === "exit").map(({ node }) => node)
17952
18213
  );
@@ -19813,6 +20074,9 @@ function callSitesOf(func, store) {
19813
20074
  if (!Node73.isCallExpression(passed.call)) {
19814
20075
  continue;
19815
20076
  }
20077
+ if (testCallingProduction(passed.call, func)) {
20078
+ continue;
20079
+ }
19816
20080
  const key = siteKey(passed.call);
19817
20081
  const site = byCall.get(key) ?? {
19818
20082
  call: passed.call,
@@ -23280,13 +23544,15 @@ function resolveContractField(callSite, pack, field, resolution) {
23280
23544
  return value ?? void 0;
23281
23545
  }
23282
23546
  function reportingTo(recognize, tally) {
23283
- return (node, ctx) => {
23547
+ const reporting = (node, ctx) => {
23284
23548
  const effects = recognize(node, ctx);
23285
23549
  if (effects !== null) {
23286
23550
  tally.effectsRecognized += effects.length;
23287
23551
  }
23288
23552
  return effects;
23289
23553
  };
23554
+ noteRecognizerTally(reporting, tally);
23555
+ return reporting;
23290
23556
  }
23291
23557
  function collectInvocationRecognizers(frameworks, tallies) {
23292
23558
  const out = [];