@suss/adapter-typescript 0.37.1 → 0.38.1

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,409 @@ 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 = containsReordered(typeNode) ? withMembersOrdered(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
+ var isReordered = (node) => ts.isUnionTypeNode(node) || ts.isTypeLiteralNode(node);
148
+ function containsReordered(node) {
149
+ if (isReordered(node)) {
150
+ return true;
151
+ }
152
+ return ts.forEachChild(node, (child) => containsReordered(child) || void 0) === true;
153
+ }
154
+ function withMembersOrdered(root, file) {
155
+ const result = ts.transform(root, [
156
+ (context) => {
157
+ const visit = (node) => {
158
+ const visited = ts.visitEachChild(node, visit, context);
159
+ if (ts.isUnionTypeNode(visited)) {
160
+ return context.factory.updateUnionTypeNode(
161
+ visited,
162
+ context.factory.createNodeArray(inStableOrder(visited.types, file))
163
+ );
164
+ }
165
+ if (ts.isTypeLiteralNode(visited)) {
166
+ return context.factory.updateTypeLiteralNode(
167
+ visited,
168
+ context.factory.createNodeArray(inNameOrder(visited.members, file))
169
+ );
170
+ }
171
+ return visited;
172
+ };
173
+ return (node) => visit(node);
174
+ }
175
+ ]);
176
+ const [ordered] = result.transformed;
177
+ result.dispose();
178
+ return ordered;
179
+ }
180
+ function inNameOrder(members, file) {
181
+ return members.map((member) => ({
182
+ member,
183
+ name: member.name === void 0 ? "" : printed(member.name, file)
184
+ })).sort((a, b) => compareText(a.name, b.name)).map(({ member }) => member);
185
+ }
186
+ function inStableOrder(members, file) {
187
+ return members.map((member) => ({
188
+ member,
189
+ text: withoutImportQualifiers(printed(member, file))
190
+ })).sort((a, b) => compareMembers(a.text, b.text)).map(({ member }) => member);
191
+ }
192
+ function compareMembers(a, b) {
193
+ return nullishRank(a) - nullishRank(b) || compareText(a, b);
194
+ }
195
+ function nullishRank(text3) {
196
+ if (text3 === "null") {
197
+ return 1;
198
+ }
199
+ if (text3 === "undefined") {
200
+ return 2;
201
+ }
202
+ return 0;
203
+ }
204
+ function compareText(a, b) {
205
+ if (a < b) {
206
+ return -1;
207
+ }
208
+ if (a > b) {
209
+ return 1;
210
+ }
211
+ return 0;
212
+ }
213
+ function stablePropertyName(symbol, reader) {
214
+ const name = symbol.getName();
215
+ if (!name.startsWith("__@")) {
216
+ return name;
217
+ }
218
+ const checker = reader.getProject().getTypeChecker().compilerObject;
219
+ return checker.symbolToString(symbol.compilerSymbol);
220
+ }
221
+
222
+ // src/shapes/definitions.ts
223
+ var current = null;
224
+ var definitionsInProgress = () => current;
225
+ function createDefinitionTable() {
226
+ const shapes = /* @__PURE__ */ new Map();
227
+ const deferred = [];
228
+ let settling = false;
229
+ return {
230
+ has: (key) => shapes.has(key),
231
+ reserve: (key) => {
232
+ shapes.set(key, null);
233
+ },
234
+ define: (key, shape) => {
235
+ shapes.set(key, shape);
236
+ },
237
+ defer: (key, expand) => {
238
+ deferred.push({ key, expand });
239
+ },
240
+ settle: () => {
241
+ if (settling) {
242
+ return;
243
+ }
244
+ settling = true;
245
+ try {
246
+ for (let next = deferred.shift(); next; next = deferred.shift()) {
247
+ shapes.set(next.key, next.expand());
248
+ }
249
+ } finally {
250
+ settling = false;
251
+ }
252
+ },
253
+ // Sorted because the walk meets types in the checker's union order,
254
+ // which changes between runs.
255
+ collected: () => {
256
+ const out = {};
257
+ let any = false;
258
+ const keys = [...shapes.keys()].sort(compareText);
259
+ for (const key of keys) {
260
+ const shape = shapes.get(key) ?? null;
261
+ if (shape !== null) {
262
+ out[key] = shape;
263
+ any = true;
264
+ }
265
+ }
266
+ return any ? out : null;
267
+ },
268
+ shapeOf: (key) => shapes.get(key)
269
+ };
270
+ }
271
+ function withDefinitionsNoted(read2) {
272
+ const table = current;
273
+ if (table === null) {
274
+ return { value: read2(), noted: [] };
275
+ }
276
+ const keys = /* @__PURE__ */ new Set();
277
+ current = {
278
+ has: (key) => {
279
+ keys.add(key);
280
+ return table.has(key);
281
+ },
282
+ reserve: (key) => {
283
+ keys.add(key);
284
+ table.reserve(key);
285
+ },
286
+ define: (key, shape) => {
287
+ keys.add(key);
288
+ table.define(key, shape);
289
+ },
290
+ defer: (key, expand) => {
291
+ keys.add(key);
292
+ table.defer(key, expand);
293
+ },
294
+ settle: () => table.settle(),
295
+ collected: () => table.collected(),
296
+ shapeOf: (key) => table.shapeOf(key)
297
+ };
298
+ let value;
299
+ try {
300
+ value = read2();
301
+ } finally {
302
+ current = table;
303
+ }
304
+ const noted = [];
305
+ for (const key of keys) {
306
+ const shape = table.shapeOf(key);
307
+ if (shape === null || shape === void 0) {
308
+ return { value, noted: null };
309
+ }
310
+ noted.push([key, shape]);
311
+ }
312
+ return { value, noted };
313
+ }
314
+ function writeNotedDefinitions(noted) {
315
+ const table = current;
316
+ if (table === null) {
317
+ return;
318
+ }
319
+ for (const [key, shape] of noted) {
320
+ if (!table.has(key)) {
321
+ table.define(key, structuredClone(shape));
322
+ }
323
+ }
324
+ }
325
+ function withDefinitions(read2) {
326
+ const table = createDefinitionTable();
327
+ const outer = current;
328
+ current = table;
329
+ try {
330
+ return { value: read2(), definitions: table.collected() };
331
+ } finally {
332
+ current = outer;
333
+ }
334
+ }
335
+
336
+ // src/bodyWalks.ts
337
+ var keptByBody = /* @__PURE__ */ new WeakMap();
338
+ var talliesByRecognizer = /* @__PURE__ */ new WeakMap();
339
+ var ids = /* @__PURE__ */ new WeakMap();
340
+ var nextId = 1;
341
+ function noteRecognizerTally(recognizer, tally) {
342
+ talliesByRecognizer.set(recognizer, tally);
343
+ }
344
+ function walkBodyOnce(body, inputs, walk) {
345
+ const key = keyOf(inputs);
346
+ const facts = inputs.resolution?.sourceFactCount() ?? -1;
347
+ const kept = keptByBody.get(body)?.find((one) => one.key === key && one.facts === facts);
348
+ if (kept !== void 0) {
349
+ return replay(kept);
350
+ }
351
+ const before = new Map(
352
+ [...talliesOf(inputs)].map((tally) => [tally, tally.effectsRecognized])
353
+ );
354
+ const read2 = withFilesNoted(() => withDefinitionsNoted(walk));
355
+ const { value, noted } = read2.value;
356
+ const factsAfter = inputs.resolution?.sourceFactCount() ?? -1;
357
+ if (noted === null || factsAfter !== facts) {
358
+ return value;
359
+ }
360
+ const effectsByTally = /* @__PURE__ */ new Map();
361
+ for (const [tally, count] of before) {
362
+ effectsByTally.set(tally, tally.effectsRecognized - count);
363
+ }
364
+ const entry = {
365
+ key,
366
+ facts,
367
+ walk: copyWalk(value),
368
+ definitions: noted.map(([name, shape]) => [name, structuredClone(shape)]),
369
+ files: read2.files,
370
+ effectsByTally
371
+ };
372
+ const forBody = keptByBody.get(body);
373
+ if (forBody === void 0) {
374
+ keptByBody.set(body, [entry]);
375
+ } else {
376
+ forBody.push(entry);
377
+ }
378
+ return value;
379
+ }
380
+ function replay(kept) {
381
+ writeNotedDefinitions(kept.definitions);
382
+ for (const filePath of kept.files) {
383
+ recordFileDependency(filePath);
384
+ }
385
+ for (const [tally, count] of kept.effectsByTally) {
386
+ tally.effectsRecognized += count;
387
+ }
388
+ return copyWalk(kept.walk);
389
+ }
390
+ function keyOf(inputs) {
391
+ const barrierIds = [...inputs.barriers].map(idOf).sort((a, b) => a - b);
392
+ return [
393
+ inputs.terminalPatterns.map(idOf).join(","),
394
+ idOf(inputs.invocationRecognizers),
395
+ idOf(inputs.accessRecognizers),
396
+ barrierIds.join(","),
397
+ inputs.resolution === void 0 ? 0 : idOf(inputs.resolution),
398
+ inputs.callbacks.map((present) => present ? 1 : 0).join(""),
399
+ // A walk with no table to write named types into notes none, so it
400
+ // cannot stand in for one that has a table.
401
+ definitionsInProgress() === null ? 0 : 1
402
+ ].join("|");
403
+ }
404
+ function idOf(value) {
405
+ const known = ids.get(value);
406
+ if (known !== void 0) {
407
+ return known;
408
+ }
409
+ const id = nextId++;
410
+ ids.set(value, id);
411
+ return id;
412
+ }
413
+ function talliesOf(inputs) {
414
+ const tallies = /* @__PURE__ */ new Set();
415
+ for (const recognizer of [
416
+ ...inputs.invocationRecognizers,
417
+ ...inputs.accessRecognizers
418
+ ]) {
419
+ const tally = talliesByRecognizer.get(recognizer);
420
+ if (tally !== void 0) {
421
+ tallies.add(tally);
422
+ }
423
+ }
424
+ return tallies;
425
+ }
426
+ function copyWalk(walk) {
427
+ return {
428
+ terminals: walk.terminals.map((found) => ({
429
+ ...found,
430
+ terminal: structuredClone(found.terminal),
431
+ ...found.whenAlso === void 0 ? {} : { whenAlso: copyCondition(found.whenAlso) }
432
+ })),
433
+ invocations: walk.invocations.map((site) => ({
434
+ ...site,
435
+ effect: structuredClone(site.effect)
436
+ })),
437
+ recognized: walk.recognized.map((site) => ({
438
+ ...site,
439
+ effect: structuredClone(site.effect),
440
+ preconditions: site.preconditions.map(copyCondition),
441
+ ...site.slots === void 0 ? {} : { slots: site.slots.map((slot) => ({ ...slot })) }
442
+ }))
443
+ };
444
+ }
445
+ function copyCondition(condition) {
446
+ return structuredClone(condition);
447
+ }
448
+
46
449
  // src/conditions.ts
47
450
  import {
48
451
  Node as Node46
@@ -145,11 +548,11 @@ import { normalizeCalleeText } from "@suss/behavioral-ir";
145
548
 
146
549
  // src/facts/store.ts
147
550
  import { Node as Node9 } from "ts-morph";
148
- import { isTestFile } from "@suss/behavioral-ir";
149
551
  import {
150
552
  clearRelations,
151
553
  Database as Database2,
152
554
  evaluate as evaluate2,
555
+ noteReads,
153
556
  proofOf,
154
557
  tupleKey,
155
558
  tupleKeyParts,
@@ -312,7 +715,7 @@ function createPerFileCache() {
312
715
 
313
716
  // src/walk/rawDescent.ts
314
717
  import {
315
- ts
718
+ ts as ts2
316
719
  } from "ts-morph";
317
720
  function statementsOfKind(file, kind) {
318
721
  const found = [];
@@ -334,10 +737,10 @@ function forEachDescendantWhere(root, accepts, visit, skips = runsNothing) {
334
737
  if (accepts(raw) && visit(wrapperOf(root, raw)) === true) {
335
738
  return void 0;
336
739
  }
337
- ts.forEachChild(raw, walk);
740
+ ts2.forEachChild(raw, walk);
338
741
  return void 0;
339
742
  };
340
- ts.forEachChild(root.compilerNode, walk);
743
+ ts2.forEachChild(root.compilerNode, walk);
341
744
  }
342
745
  function runsNothing(raw) {
343
746
  return isTypeOnlySyntax(raw) || isAmbient(raw);
@@ -346,18 +749,18 @@ function skipsNothing() {
346
749
  return false;
347
750
  }
348
751
  function isTypeOnlySyntax(raw) {
349
- return raw.kind >= ts.SyntaxKind.FirstTypeNode && raw.kind <= ts.SyntaxKind.LastTypeNode || TYPE_DECLARATIONS.has(raw.kind);
752
+ return raw.kind >= ts2.SyntaxKind.FirstTypeNode && raw.kind <= ts2.SyntaxKind.LastTypeNode || TYPE_DECLARATIONS.has(raw.kind);
350
753
  }
351
754
  var TYPE_DECLARATIONS = /* @__PURE__ */ new Set([
352
- ts.SyntaxKind.TypeAliasDeclaration,
353
- ts.SyntaxKind.InterfaceDeclaration,
354
- ts.SyntaxKind.TypeParameter
755
+ ts2.SyntaxKind.TypeAliasDeclaration,
756
+ ts2.SyntaxKind.InterfaceDeclaration,
757
+ ts2.SyntaxKind.TypeParameter
355
758
  ]);
356
759
  function isAmbient(raw) {
357
- if (!ts.canHaveModifiers(raw)) {
760
+ if (!ts2.canHaveModifiers(raw)) {
358
761
  return false;
359
762
  }
360
- return ts.getModifiers(raw)?.some((modifier) => modifier.kind === ts.SyntaxKind.DeclareKeyword) ?? false;
763
+ return ts2.getModifiers(raw)?.some((modifier) => modifier.kind === ts2.SyntaxKind.DeclareKeyword) ?? false;
361
764
  }
362
765
  function wrapperOf(anchor, raw) {
363
766
  return anchor._getNodeFromCompilerNode(
@@ -483,31 +886,16 @@ function indexFunctions(sf) {
483
886
  },
484
887
  isTypeOnlySyntax
485
888
  );
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);
889
+ return index;
505
890
  }
506
- function recordMountPrefix(childId, prefix) {
507
- activeSink?.mountPrefixes.set(childId, prefix);
891
+
892
+ // src/testCode.ts
893
+ import { isTestFile } from "@suss/behavioral-ir";
894
+ function inTestFile(node) {
895
+ return isTestFile(node.getSourceFile().getFilePath());
508
896
  }
509
- function recordUnitClaim(key, pack) {
510
- activeSink?.claims.push({ key, pack });
897
+ function testCallingProduction(call, callee) {
898
+ return inTestFile(call) && !inTestFile(callee);
511
899
  }
512
900
 
513
901
  // src/facts/containerWrites.ts
@@ -519,7 +907,7 @@ import {
519
907
  ModuleDeclarationKind,
520
908
  Node as Node6,
521
909
  SyntaxKind as SyntaxKind6,
522
- ts as ts4
910
+ ts as ts5
523
911
  } from "ts-morph";
524
912
  import {
525
913
  addEntryRows,
@@ -541,7 +929,7 @@ import { Node as Node3, SyntaxKind as SyntaxKind4 } from "ts-morph";
541
929
  // src/bootstrap/lazyProjectInit.ts
542
930
  import fs2 from "fs/promises";
543
931
  import path3 from "path";
544
- import { ts as ts2 } from "ts-morph";
932
+ import { ts as ts3 } from "ts-morph";
545
933
 
546
934
  // src/facts/generatedModules.ts
547
935
  import fs from "fs";
@@ -1173,7 +1561,7 @@ function resolutionCacheFor(project, options) {
1173
1561
  if (existing !== void 0) {
1174
1562
  return existing;
1175
1563
  }
1176
- const fresh = ts2.createModuleResolutionCache(
1564
+ const fresh = ts3.createModuleResolutionCache(
1177
1565
  project.getFileSystem().getCurrentDirectory(),
1178
1566
  (name) => name,
1179
1567
  options
@@ -1207,11 +1595,11 @@ function readSpecifierPaths(project, filePath, options, host) {
1207
1595
  if (text3 === null) {
1208
1596
  return [];
1209
1597
  }
1210
- const scanned = ts2.preProcessFile(text3, true, true);
1598
+ const scanned = ts3.preProcessFile(text3, true, true);
1211
1599
  const cache = resolutionCacheFor(project, options);
1212
1600
  const paths = [];
1213
1601
  for (const imported of scanned.importedFiles) {
1214
- const resolved = ts2.resolveModuleName(
1602
+ const resolved = ts3.resolveModuleName(
1215
1603
  imported.fileName,
1216
1604
  filePath,
1217
1605
  options,
@@ -1237,13 +1625,13 @@ function readFileText(project, filePath) {
1237
1625
  }
1238
1626
  function parseTsconfig(tsConfigFilePath, seen = /* @__PURE__ */ new Set()) {
1239
1627
  seen.add(tsConfigFilePath);
1240
- const configFile = ts2.readConfigFile(tsConfigFilePath, ts2.sys.readFile);
1628
+ const configFile = ts3.readConfigFile(tsConfigFilePath, ts3.sys.readFile);
1241
1629
  if (configFile.error !== void 0) {
1242
1630
  return { fileNames: [], options: {} };
1243
1631
  }
1244
- const parsed = ts2.parseJsonConfigFileContent(
1632
+ const parsed = ts3.parseJsonConfigFileContent(
1245
1633
  configFile.config,
1246
- ts2.sys,
1634
+ ts3.sys,
1247
1635
  path3.dirname(tsConfigFilePath),
1248
1636
  /*existingOptions*/
1249
1637
  void 0,
@@ -1252,7 +1640,7 @@ function parseTsconfig(tsConfigFilePath, seen = /* @__PURE__ */ new Set()) {
1252
1640
  if (parsed.fileNames.length > 0) {
1253
1641
  return { fileNames: parsed.fileNames, options: parsed.options };
1254
1642
  }
1255
- const referenced = (parsed.projectReferences ?? []).map((reference) => ts2.resolveProjectReferencePath(reference)).filter((referencePath) => !seen.has(referencePath)).map((referencePath) => parseTsconfig(referencePath, seen));
1643
+ const referenced = (parsed.projectReferences ?? []).map((reference) => ts3.resolveProjectReferencePath(reference)).filter((referencePath) => !seen.has(referencePath)).map((referencePath) => parseTsconfig(referencePath, seen));
1256
1644
  const fileNames = [
1257
1645
  ...new Set(referenced.flatMap((reference) => reference.fileNames))
1258
1646
  ];
@@ -1326,17 +1714,17 @@ function addImportersOfMatches(fileImports, matched, options) {
1326
1714
  }
1327
1715
  function internalSpecifierResolver(files, options) {
1328
1716
  const fileSet = new Set(files);
1329
- const cache = ts2.createModuleResolutionCache(
1330
- ts2.sys.getCurrentDirectory(),
1717
+ const cache = ts3.createModuleResolutionCache(
1718
+ ts3.sys.getCurrentDirectory(),
1331
1719
  (name) => name,
1332
1720
  options
1333
1721
  );
1334
1722
  return (fromFile, spec) => {
1335
- const resolved = ts2.resolveModuleName(
1723
+ const resolved = ts3.resolveModuleName(
1336
1724
  spec,
1337
1725
  fromFile,
1338
1726
  options,
1339
- ts2.sys,
1727
+ ts3.sys,
1340
1728
  cache
1341
1729
  ).resolvedModule?.resolvedFileName;
1342
1730
  if (resolved === void 0) {
@@ -1345,7 +1733,7 @@ function internalSpecifierResolver(files, options) {
1345
1733
  if (fileSet.has(resolved)) {
1346
1734
  return resolved;
1347
1735
  }
1348
- const real = ts2.sys.realpath?.(resolved) ?? resolved;
1736
+ const real = ts3.sys.realpath?.(resolved) ?? resolved;
1349
1737
  return fileSet.has(real) ? real : null;
1350
1738
  };
1351
1739
  }
@@ -1380,7 +1768,7 @@ async function readImportsConcurrently(paths, marks) {
1380
1768
  const p = paths[i];
1381
1769
  try {
1382
1770
  const text3 = await fs2.readFile(p, "utf-8");
1383
- const pre = ts2.preProcessFile(text3, true, false);
1771
+ const pre = ts3.preProcessFile(text3, true, false);
1384
1772
  results[i] = {
1385
1773
  path: p,
1386
1774
  importedModules: pre.importedFiles.map((f) => f.fileName),
@@ -1616,7 +2004,7 @@ function keyNameOf(name) {
1616
2004
  }
1617
2005
 
1618
2006
  // src/facts/assignments.ts
1619
- import { Node as Node5, SyntaxKind as SyntaxKind5, ts as ts3, VariableDeclarationKind } from "ts-morph";
2007
+ import { Node as Node5, SyntaxKind as SyntaxKind5, ts as ts4, VariableDeclarationKind } from "ts-morph";
1620
2008
  import { valueLeftByWrites } from "@suss/resolution";
1621
2009
  var byFile = createPerFileCache();
1622
2010
  var byDeclaration = /* @__PURE__ */ new WeakMap();
@@ -1814,11 +2202,11 @@ function assignmentsInFile(sourceFile) {
1814
2202
  return found;
1815
2203
  }
1816
2204
  function mayWrite(raw) {
1817
- if (ts3.isBinaryExpression(raw)) {
1818
- return isAssignmentOperator(raw.operatorToken.kind) && (ts3.isIdentifier(raw.left) || ts3.isPropertyAccessExpression(raw.left));
2205
+ if (ts4.isBinaryExpression(raw)) {
2206
+ return isAssignmentOperator(raw.operatorToken.kind) && (ts4.isIdentifier(raw.left) || ts4.isPropertyAccessExpression(raw.left));
1819
2207
  }
1820
- if (ts3.isPrefixUnaryExpression(raw) || ts3.isPostfixUnaryExpression(raw)) {
1821
- return (raw.operator === SyntaxKind5.PlusPlusToken || raw.operator === SyntaxKind5.MinusMinusToken) && ts3.isIdentifier(raw.operand);
2208
+ if (ts4.isPrefixUnaryExpression(raw) || ts4.isPostfixUnaryExpression(raw)) {
2209
+ return (raw.operator === SyntaxKind5.PlusPlusToken || raw.operator === SyntaxKind5.MinusMinusToken) && ts4.isIdentifier(raw.operand);
1822
2210
  }
1823
2211
  return false;
1824
2212
  }
@@ -1879,7 +2267,7 @@ function isReadBefore(declaration, position) {
1879
2267
  const sourceFile = declaration.getSourceFile();
1880
2268
  const file = sourceFile.compilerNode;
1881
2269
  const readsHere = (raw) => {
1882
- if (!ts3.isIdentifier(raw) || raw.getText(file) !== name) {
2270
+ if (!ts4.isIdentifier(raw) || raw.getText(file) !== name) {
1883
2271
  return false;
1884
2272
  }
1885
2273
  const node = wrapperOf(sourceFile, raw);
@@ -1899,9 +2287,9 @@ function someBefore(root, position, test) {
1899
2287
  if (test(raw)) {
1900
2288
  return true;
1901
2289
  }
1902
- return ts3.forEachChild(raw, visit);
2290
+ return ts4.forEachChild(raw, visit);
1903
2291
  };
1904
- return ts3.forEachChild(root, visit) === true;
2292
+ return ts4.forEachChild(root, visit) === true;
1905
2293
  }
1906
2294
  var OWN_BODY_KINDS = /* @__PURE__ */ new Set([
1907
2295
  SyntaxKind5.FunctionDeclaration,
@@ -2003,7 +2391,7 @@ function isPropertyReadBefore(statements, name, property, position) {
2003
2391
  return someBefore(
2004
2392
  statements.compilerNode,
2005
2393
  position,
2006
- (raw) => ts3.isPropertyAccessExpression(raw) && raw.name.getText(file) === property && raw.expression.getText(file) === name && !isWriteTarget(wrapperOf(statements, raw))
2394
+ (raw) => ts4.isPropertyAccessExpression(raw) && raw.name.getText(file) === property && raw.expression.getText(file) === name && !isWriteTarget(wrapperOf(statements, raw))
2007
2395
  );
2008
2396
  }
2009
2397
 
@@ -2075,7 +2463,7 @@ function moduleKeyOf(declaration) {
2075
2463
  }
2076
2464
  function compilerResolvedPathOf(declaration, specifier) {
2077
2465
  const project = declaration.getProject();
2078
- const result = ts4.resolveModuleName(
2466
+ const result = ts5.resolveModuleName(
2079
2467
  specifier,
2080
2468
  declaration.getSourceFile().getFilePath(),
2081
2469
  project.getCompilerOptions(),
@@ -2386,7 +2774,7 @@ function spellingsHandedOn(paths, sourceFile) {
2386
2774
  const found = [];
2387
2775
  forEachDescendantWhere(
2388
2776
  sourceFile,
2389
- (raw) => ts4.isPropertyAccessExpression(raw) && paths.includes(compilerDottedPathOf(raw, file) ?? ""),
2777
+ (raw) => ts5.isPropertyAccessExpression(raw) && paths.includes(compilerDottedPathOf(raw, file) ?? ""),
2390
2778
  (node) => {
2391
2779
  if (flowsOnward(node)) {
2392
2780
  found.push(node);
@@ -2397,14 +2785,14 @@ function spellingsHandedOn(paths, sourceFile) {
2397
2785
  return found;
2398
2786
  }
2399
2787
  function compilerDottedPathOf(raw, file) {
2400
- if (ts4.isIdentifier(raw)) {
2788
+ if (ts5.isIdentifier(raw)) {
2401
2789
  return raw.getText(file);
2402
2790
  }
2403
- if (ts4.isMetaProperty(raw)) {
2404
- const keyword = ts4.tokenToString(raw.keywordToken);
2791
+ if (ts5.isMetaProperty(raw)) {
2792
+ const keyword = ts5.tokenToString(raw.keywordToken);
2405
2793
  return `${keyword}.${raw.name.getText(file)}`;
2406
2794
  }
2407
- if (!ts4.isPropertyAccessExpression(raw)) {
2795
+ if (!ts5.isPropertyAccessExpression(raw)) {
2408
2796
  return null;
2409
2797
  }
2410
2798
  const object = compilerDottedPathOf(raw.expression, file);
@@ -2425,7 +2813,7 @@ function dottedPathOf(expression) {
2425
2813
  return expression.getText();
2426
2814
  }
2427
2815
  if (Node6.isMetaProperty(expression)) {
2428
- const keyword = ts4.tokenToString(expression.getKeywordToken());
2816
+ const keyword = ts5.tokenToString(expression.getKeywordToken());
2429
2817
  return `${keyword}.${expression.getName()}`;
2430
2818
  }
2431
2819
  if (!Node6.isPropertyAccessExpression(expression)) {
@@ -3767,7 +4155,12 @@ var NOT_BASE_FACTS = /* @__PURE__ */ new Set([
3767
4155
  ...RESOLUTION_PROGRAM.rules.map((r) => r.head.relation),
3768
4156
  ...ASKING_RELATIONS
3769
4157
  ]);
4158
+ var DERIVED_RELATIONS = /* @__PURE__ */ new Set([
4159
+ ...NOT_BASE_FACTS,
4160
+ ...resolutionUnderProgram().rules.map((r) => r.head.relation)
4161
+ ]);
3770
4162
  var QUERY_FACTS = queryFacts(RESOLUTION_PROGRAM);
4163
+ var DERIVED_AGAIN = new Set(QUERY_FACTS);
3771
4164
  var MODULE_DEMANDS = RESOLUTION_PROGRAM.demands.filter(
3772
4165
  (one) => (one.relation === "moduleExport" || one.relation === "moduleForwards") && one.bound[0] === true
3773
4166
  ).map((one) => one.demand);
@@ -3803,6 +4196,8 @@ var ResolutionStore = class _ResolutionStore {
3803
4196
  constructionSites = /* @__PURE__ */ new Map();
3804
4197
  /** What each local's reads do with it, by its declaration. A local's function is walked whole, so an answer stays true. */
3805
4198
  localUses = /* @__PURE__ */ new Map();
4199
+ /** See `remembered`. */
4200
+ answers = /* @__PURE__ */ new Map();
3806
4201
  /** Files the most recent query read, for the memo to keep. */
3807
4202
  lastQueryWalked = [];
3808
4203
  /** See `environmentSiteFiles`; null until the first env question. */
@@ -3854,12 +4249,18 @@ var ResolutionStore = class _ResolutionStore {
3854
4249
  */
3855
4250
  resolveCallableSources(value, alsoFrom) {
3856
4251
  const target = factKeyOf(value);
3857
- return this.askAbout(
3858
- target,
3859
- "wanted",
3860
- () => this.lookupSources(target),
3861
- alsoFrom
3862
- );
4252
+ const key = `sources|${nodeId(target)}|${alsoFrom?.getFilePath() ?? ""}`;
4253
+ return [
4254
+ ...this.remembered(
4255
+ key,
4256
+ () => this.askAbout(
4257
+ target,
4258
+ "wanted",
4259
+ () => this.lookupSources(target),
4260
+ alsoFrom
4261
+ )
4262
+ )
4263
+ ];
3863
4264
  }
3864
4265
  /**
3865
4266
  * `resolveCallable` for several values, asked as one question. A file
@@ -3921,7 +4322,65 @@ var ResolutionStore = class _ResolutionStore {
3921
4322
  }
3922
4323
  resolveObject(value) {
3923
4324
  const target = factKeyOf(value);
3924
- return this.askAbout(target, "wanted", () => this.lookupObject(target));
4325
+ return this.remembered(
4326
+ `object|${nodeId(target)}`,
4327
+ () => this.askAbout(target, "wanted", () => this.lookupObject(target))
4328
+ );
4329
+ }
4330
+ /**
4331
+ * A question asked again, when no fact its derivation looked up has
4332
+ * arrived since, reads the same files and derives the same answer, so
4333
+ * the store gives back the one it found before. The files that answer
4334
+ * read still count as read.
4335
+ *
4336
+ * Noting the lookups costs the question a share of its time, and most
4337
+ * questions are asked once, so a question notes them only once it has
4338
+ * had to be asked again. Until then, as without demand, only an
4339
+ * unchanged fact count says the answer is the same. Without demand the
4340
+ * rules keep every conclusion between questions, so a question's own
4341
+ * lookups miss what earlier questions derived for it. A question that
4342
+ * started on another question's leftovers gets the count too.
4343
+ */
4344
+ remembered(key, ask) {
4345
+ const known = this.answers.get(key);
4346
+ if (known !== void 0 && this.stillDerives(known.check)) {
4347
+ for (const walkedPath of known.walked) {
4348
+ recordFileDependency(walkedPath);
4349
+ }
4350
+ return known.answer;
4351
+ }
4352
+ const { value, reads } = known === void 0 || QUERY_FACTS.length === 0 ? { value: ask(), reads: null } : noteReads(this.db, DERIVED_AGAIN, ask);
4353
+ this.answers.set(key, {
4354
+ answer: value,
4355
+ walked: [...this.lastQueryWalked],
4356
+ check: reads ?? this.sourceFactCount()
4357
+ });
4358
+ return value;
4359
+ }
4360
+ /**
4361
+ * Facts added since the last derivation have not reached the relations
4362
+ * the rules keep between questions, so the reads cannot say yet what
4363
+ * those facts change.
4364
+ */
4365
+ stillDerives(check) {
4366
+ if (typeof check === "number") {
4367
+ return check === this.sourceFactCount();
4368
+ }
4369
+ return !this.stale && check.unchangedIn(this.db);
4370
+ }
4371
+ /**
4372
+ * How many facts the store has read from source. They are only ever
4373
+ * added, so work that asked the store questions and found this number
4374
+ * unchanged afterwards would get the same answers asking again.
4375
+ */
4376
+ sourceFactCount() {
4377
+ let count = 0;
4378
+ for (const relation of this.db.relationNames()) {
4379
+ if (!DERIVED_RELATIONS.has(relation)) {
4380
+ count += this.db.size(relation);
4381
+ }
4382
+ }
4383
+ return count;
3925
4384
  }
3926
4385
  /**
3927
4386
  * The function a call returns: `app.use(requireCaller(config))`
@@ -4021,7 +4480,7 @@ var ResolutionStore = class _ResolutionStore {
4021
4480
  writtenAnswers(values, wanted) {
4022
4481
  const found = /* @__PURE__ */ new Map();
4023
4482
  const asked = /* @__PURE__ */ new Map();
4024
- const keyOf = /* @__PURE__ */ new Map();
4483
+ const keyOf2 = /* @__PURE__ */ new Map();
4025
4484
  for (const value of values) {
4026
4485
  const target = factKeyOf(value);
4027
4486
  const key = nodeId(this.sharedDeclarationFor(target));
@@ -4036,7 +4495,7 @@ var ResolutionStore = class _ResolutionStore {
4036
4495
  if (!asked.has(key)) {
4037
4496
  asked.set(key, target);
4038
4497
  }
4039
- keyOf.set(value, key);
4498
+ keyOf2.set(value, key);
4040
4499
  }
4041
4500
  if (asked.size === 0) {
4042
4501
  return found;
@@ -4051,7 +4510,7 @@ var ResolutionStore = class _ResolutionStore {
4051
4510
  });
4052
4511
  }
4053
4512
  });
4054
- for (const [value, key] of keyOf) {
4513
+ for (const [value, key] of keyOf2) {
4055
4514
  found.set(value, this.writtenValues.get(key)?.[wanted] ?? null);
4056
4515
  }
4057
4516
  return found;
@@ -5334,9 +5793,6 @@ function namesPackage2(moduleKey, packages) {
5334
5793
  packages
5335
5794
  );
5336
5795
  }
5337
- function inTestFile(node) {
5338
- return isTestFile(node.getSourceFile().getFilePath());
5339
- }
5340
5796
  function declarationOf(value) {
5341
5797
  const nameNode = Node9.isPropertyAccessExpression(value) ? value.getNameNode() : value;
5342
5798
  return nameNode.getSymbol()?.getDeclarations()[0] ?? value;
@@ -6433,7 +6889,7 @@ function settledChannel(argument, resolution) {
6433
6889
  }
6434
6890
 
6435
6891
  // src/discovery/importedCalls.ts
6436
- import { Node as Node14, ts as ts5 } from "ts-morph";
6892
+ import { Node as Node14, ts as ts6 } from "ts-morph";
6437
6893
  function callsResolvingTo(sourceFile, resolution, spec) {
6438
6894
  return [
6439
6895
  ...callsByOriginName(
@@ -6451,7 +6907,8 @@ function importedCallOrigins(sourceFile, resolution, module) {
6451
6907
  const candidates = [];
6452
6908
  forEachDescendantWhere(sourceFile, callsANamedCallee, (node) => {
6453
6909
  const call = node;
6454
- if (!declaredLocally(call.getExpression())) {
6910
+ const callee = call.getExpression();
6911
+ if (!declaredLocally(callee) && !importedFromAnotherPackage(callee, module)) {
6455
6912
  candidates.push(call);
6456
6913
  }
6457
6914
  return false;
@@ -6479,7 +6936,27 @@ function callsNamed(found, names) {
6479
6936
  return matched;
6480
6937
  }
6481
6938
  function callsANamedCallee(raw) {
6482
- return (ts5.isCallExpression(raw) || ts5.isNewExpression(raw)) && ts5.isIdentifier(raw.expression);
6939
+ return (ts6.isCallExpression(raw) || ts6.isNewExpression(raw)) && ts6.isIdentifier(raw.expression);
6940
+ }
6941
+ var samePackage = (specifier, module) => specifier === module || specifier.startsWith(`${module}/`) || module.startsWith(`${specifier}/`);
6942
+ function importOf(declaration) {
6943
+ if (Node14.isImportSpecifier(declaration)) {
6944
+ return declaration.getImportDeclaration();
6945
+ }
6946
+ const parent = Node14.isImportClause(declaration) ? declaration.getParent() : void 0;
6947
+ return parent !== void 0 && Node14.isImportDeclaration(parent) ? parent : void 0;
6948
+ }
6949
+ function importedFromAnotherPackage(callee, module) {
6950
+ const declarations = callee.getSymbol()?.getDeclarations() ?? [];
6951
+ return declarations.length > 0 && declarations.every((declaration) => {
6952
+ const from = importOf(declaration);
6953
+ if (from === void 0) {
6954
+ return false;
6955
+ }
6956
+ const resolved = from.getModuleSpecifierSourceFile();
6957
+ const specifier = from.getModuleSpecifierValue();
6958
+ return (resolved === void 0 || resolved.isInNodeModules()) && !specifier.startsWith(".") && !samePackage(specifier, module);
6959
+ });
6483
6960
  }
6484
6961
  function declaredLocally(callee) {
6485
6962
  const declarations = callee.getSymbol()?.getDeclarations() ?? [];
@@ -8884,7 +9361,7 @@ function evaluateObjectLiteralAsJson(node) {
8884
9361
  return node.getLiteralValue();
8885
9362
  }
8886
9363
  if (Node25.isNumericLiteral(node)) {
8887
- return Number(node.getText());
9364
+ return node.getLiteralValue();
8888
9365
  }
8889
9366
  const kind = node.getText();
8890
9367
  if (kind === "true") {
@@ -9392,6 +9869,9 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context, ch
9392
9869
  const units = [];
9393
9870
  let unfollowed = 0;
9394
9871
  for (const { call, argument } of passed) {
9872
+ if (testCallingProduction(call, parameter)) {
9873
+ continue;
9874
+ }
9395
9875
  const caller = enclosingFunctionRoot(call);
9396
9876
  if (caller === null || !Node28.isCallExpression(call)) {
9397
9877
  unfollowed++;
@@ -11008,7 +11488,7 @@ function creationSiteBehind(parameter, resolution) {
11008
11488
  }
11009
11489
  function registrationSubjectIdsOf(sourceFile, matches, resolution) {
11010
11490
  const seenImports = /* @__PURE__ */ new Set();
11011
- const ids = /* @__PURE__ */ new Set();
11491
+ const ids2 = /* @__PURE__ */ new Set();
11012
11492
  for (const match of matches) {
11013
11493
  const importKey = `${match.importModule}::${match.importName}`;
11014
11494
  if (seenImports.has(importKey)) {
@@ -11021,17 +11501,17 @@ function registrationSubjectIdsOf(sourceFile, matches, resolution) {
11021
11501
  match.importName,
11022
11502
  resolution
11023
11503
  ).values()) {
11024
- ids.add(nodeId(node));
11504
+ ids2.add(nodeId(node));
11025
11505
  }
11026
11506
  for (const node of unboundConstructionsOf(
11027
11507
  sourceFile,
11028
11508
  match.importModule,
11029
11509
  match.importName
11030
11510
  )) {
11031
- ids.add(nodeId(node));
11511
+ ids2.add(nodeId(node));
11032
11512
  }
11033
11513
  }
11034
- return ids;
11514
+ return ids2;
11035
11515
  }
11036
11516
  function unboundConstructionsOf(sourceFile, importModule, importName) {
11037
11517
  const spellings = importedReferenceSpellings(
@@ -13085,7 +13565,7 @@ import {
13085
13565
  import {
13086
13566
  Node as Node47,
13087
13567
  SyntaxKind as SyntaxKind20,
13088
- ts as ts6
13568
+ ts as ts7
13089
13569
  } from "ts-morph";
13090
13570
  import {
13091
13571
  UnmodeledFlow
@@ -13109,7 +13589,7 @@ function exitKindOf(stmt, endings) {
13109
13589
  if (raw.kind === SyntaxKind20.ThrowStatement) {
13110
13590
  sawThrow = true;
13111
13591
  }
13112
- ts6.forEachChild(raw, visit);
13592
+ ts7.forEachChild(raw, visit);
13113
13593
  return void 0;
13114
13594
  };
13115
13595
  visit(root);
@@ -13222,7 +13702,7 @@ function lowerCallbacksIn(parts, ctx) {
13222
13702
  if (runsNothing(raw)) {
13223
13703
  return void 0;
13224
13704
  }
13225
- ts6.forEachChild(raw, visit);
13705
+ ts7.forEachChild(raw, visit);
13226
13706
  return void 0;
13227
13707
  };
13228
13708
  visit(root);
@@ -13599,126 +14079,6 @@ import { Node as Node51 } from "ts-morph";
13599
14079
 
13600
14080
  // src/values/receiverType.ts
13601
14081
  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
14082
  function receiverTypesOf(node) {
13723
14083
  const type = node.getType();
13724
14084
  if (type.isAny()) {
@@ -15732,67 +16092,6 @@ import {
15732
16092
  // src/shapes/typeShapes.ts
15733
16093
  import { createHash } from "crypto";
15734
16094
  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
16095
  var MAX_DEPTH = 6;
15797
16096
  var OPAQUE_NAMED_TYPES = /* @__PURE__ */ new Set([
15798
16097
  "Date",
@@ -15830,16 +16129,17 @@ function overloadIsAGuess(node) {
15830
16129
  if (signatures.length < 2) {
15831
16130
  return false;
15832
16131
  }
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) => {
16132
+ const untypedArgument = node.getArguments().some((argument) => {
15840
16133
  const type = argument.getType();
15841
16134
  return type.isAny() || type.isNever();
15842
16135
  });
16136
+ if (!untypedArgument) {
16137
+ return false;
16138
+ }
16139
+ const returns = new Set(
16140
+ signatures.map((signature) => signature.getReturnType().getText(node))
16141
+ );
16142
+ return returns.size >= 2;
15843
16143
  }
15844
16144
  function typeToShape(type, ctx) {
15845
16145
  if (type.isAny()) {
@@ -17018,7 +17318,7 @@ function evaluateComparison(condition, substitutions) {
17018
17318
  }
17019
17319
  function numberFor(node, substitutions) {
17020
17320
  const resolved = Node62.isIdentifier(node) && substitutions.has(node.getText()) ? substitutions.get(node.getText()) : node;
17021
- return Node62.isNumericLiteral(resolved) ? Number(resolved.getText()) : null;
17321
+ return Node62.isNumericLiteral(resolved) ? resolved.getLiteralValue() : null;
17022
17322
  }
17023
17323
  var helperBySymbol = createProgramMemo();
17024
17324
  function memoizedLocalHelper(callee, resolveCallee2) {
@@ -17920,33 +18220,50 @@ var RETURNS_AND_SCOPES = /* @__PURE__ */ new Set([
17920
18220
  SyntaxKind27.ReturnStatement
17921
18221
  ]);
17922
18222
  function extractRawBranches(func, terminalPatterns, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf, resolveCallee2, collectCallAccounting = false, keepsArms, handOff) {
17923
- const terminals = findTerminals(
18223
+ const { terminals, invocations, recognized } = walkBodyOnce(
17924
18224
  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,
18225
+ {
18226
+ terminalPatterns,
17935
18227
  invocationRecognizers,
17936
- barriers,
17937
- resolution,
17938
- originatesFrom,
17939
- anchorCallsOf
17940
- ),
17941
- ...runAccessRecognizers(
17942
- func,
17943
18228
  accessRecognizers,
17944
18229
  barriers,
17945
18230
  resolution,
17946
- originatesFrom,
17947
- anchorCallsOf
17948
- )
17949
- ];
18231
+ callbacks: [
18232
+ originatesFrom !== void 0,
18233
+ anchorCallsOf !== void 0,
18234
+ resolveCallee2 !== void 0
18235
+ ]
18236
+ },
18237
+ () => ({
18238
+ terminals: findTerminals(
18239
+ func,
18240
+ terminalPatterns,
18241
+ barriers,
18242
+ resolution,
18243
+ originatesFrom,
18244
+ resolveCallee2
18245
+ ),
18246
+ invocations: extractInvocationEffects(func, barriers),
18247
+ recognized: [
18248
+ ...runInvocationRecognizers(
18249
+ func,
18250
+ invocationRecognizers,
18251
+ barriers,
18252
+ resolution,
18253
+ originatesFrom,
18254
+ anchorCallsOf
18255
+ ),
18256
+ ...runAccessRecognizers(
18257
+ func,
18258
+ accessRecognizers,
18259
+ barriers,
18260
+ resolution,
18261
+ originatesFrom,
18262
+ anchorCallsOf
18263
+ )
18264
+ ]
18265
+ })
18266
+ );
17950
18267
  const endings = new Set(
17951
18268
  terminals.filter(({ terminal }) => terminal.kind === "exit").map(({ node }) => node)
17952
18269
  );
@@ -19813,6 +20130,9 @@ function callSitesOf(func, store) {
19813
20130
  if (!Node73.isCallExpression(passed.call)) {
19814
20131
  continue;
19815
20132
  }
20133
+ if (testCallingProduction(passed.call, func)) {
20134
+ continue;
20135
+ }
19816
20136
  const key = siteKey(passed.call);
19817
20137
  const site = byCall.get(key) ?? {
19818
20138
  call: passed.call,
@@ -23280,13 +23600,15 @@ function resolveContractField(callSite, pack, field, resolution) {
23280
23600
  return value ?? void 0;
23281
23601
  }
23282
23602
  function reportingTo(recognize, tally) {
23283
- return (node, ctx) => {
23603
+ const reporting = (node, ctx) => {
23284
23604
  const effects = recognize(node, ctx);
23285
23605
  if (effects !== null) {
23286
23606
  tally.effectsRecognized += effects.length;
23287
23607
  }
23288
23608
  return effects;
23289
23609
  };
23610
+ noteRecognizerTally(reporting, tally);
23611
+ return reporting;
23290
23612
  }
23291
23613
  function collectInvocationRecognizers(frameworks, tallies) {
23292
23614
  const out = [];