@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/mapper-connection.js +2 -1
  3. package/dist/bin/node-host.js +24 -5
  4. package/dist/src/analysis.js +181 -341
  5. package/dist/src/augmentations.js +106 -13
  6. package/dist/src/automatic-types.js +10 -29
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +2 -3
  10. package/dist/src/config.js +6 -82
  11. package/dist/src/configuration-files.js +28 -176
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +1 -1
  14. package/dist/src/consumers.js +3 -30
  15. package/dist/src/contextual-uses.js +56 -9
  16. package/dist/src/contract/config.generated.js +558 -0
  17. package/dist/src/contract/contract.generated.js +1244 -0
  18. package/dist/src/contract/report.generated.js +862 -0
  19. package/dist/src/convention-rows.js +73 -3
  20. package/dist/src/conventions.js +36 -66
  21. package/dist/src/dependencies.js +103 -40
  22. package/dist/src/edges.js +38 -122
  23. package/dist/src/element-access.js +4 -6
  24. package/dist/src/entry-point-gaps.js +2 -8
  25. package/dist/src/entry-points.js +11 -27
  26. package/dist/src/enum-group.js +3 -10
  27. package/dist/src/erased-stores.js +4 -6
  28. package/dist/src/exempt-classes.js +1 -63
  29. package/dist/src/exempt.js +21 -40
  30. package/dist/src/failure.js +36 -0
  31. package/dist/src/file-facts.js +2 -25
  32. package/dist/src/findings/completion.js +139 -72
  33. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  34. package/dist/src/findings/emitters.js +5 -5
  35. package/dist/src/findings/interfaces.js +4 -5
  36. package/dist/src/findings/intra-function.js +13 -10
  37. package/dist/src/findings/non-code-artifacts.js +7 -8
  38. package/dist/src/findings/reads-and-writes.js +16 -13
  39. package/dist/src/findings/self-check.js +10 -12
  40. package/dist/src/findings/unused-declarations.js +41 -32
  41. package/dist/src/findings/visibility-narrowing.js +19 -16
  42. package/dist/src/findings-pass.js +8 -15
  43. package/dist/src/framework-lifecycle.js +7 -7
  44. package/dist/src/fs/document.js +52 -0
  45. package/dist/src/fs/manifest.js +72 -0
  46. package/dist/src/fs/tree.js +88 -0
  47. package/dist/src/generated-file.js +7 -4
  48. package/dist/src/git-ignore.js +39 -26
  49. package/dist/src/graph.js +5 -3
  50. package/dist/src/html-entries.js +6 -33
  51. package/dist/src/in-operands.js +2 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/injection-container.js +1 -1
  54. package/dist/src/intra-function-parts.js +63 -43
  55. package/dist/src/inventory.js +2 -21
  56. package/dist/src/javascript-file.js +33 -0
  57. package/dist/src/json-document.js +198 -67
  58. package/dist/src/keyof-comparisons.js +4 -6
  59. package/dist/src/kinds.js +3 -366
  60. package/dist/src/load/classify.js +270 -0
  61. package/dist/src/load/diagnostics.js +80 -0
  62. package/dist/src/load/discover.js +277 -0
  63. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  64. package/dist/src/load/expected-module.js +136 -0
  65. package/dist/src/load/load.js +27 -0
  66. package/dist/src/load/module-lookup.js +37 -0
  67. package/dist/src/load/plan.js +313 -0
  68. package/dist/src/load/specifiers.js +115 -0
  69. package/dist/src/load/synthesize.js +140 -0
  70. package/dist/src/load/test-globals.js +207 -0
  71. package/dist/src/load/test-joins.js +82 -0
  72. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  73. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  74. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
  75. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  76. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  77. package/dist/src/manifest.js +49 -54
  78. package/dist/src/matrix.js +76 -4
  79. package/dist/src/mdx-entries.js +3 -26
  80. package/dist/src/outside-tests.js +1 -1
  81. package/dist/src/partial.js +2 -1
  82. package/dist/src/paths.js +26 -15
  83. package/dist/src/position.js +2 -1
  84. package/dist/src/print.js +11 -79
  85. package/dist/src/product.js +13 -0
  86. package/dist/src/query.js +1 -0
  87. package/dist/src/ref.js +1 -0
  88. package/dist/src/references.js +48 -30
  89. package/dist/src/report-schema.js +51 -0
  90. package/dist/src/report.js +57 -41
  91. package/dist/src/reporters/annotations.js +15 -8
  92. package/dist/src/reporters/sarif.js +16 -10
  93. package/dist/src/reporters/template.js +4 -19
  94. package/dist/src/resolve.js +245 -729
  95. package/dist/src/roots.js +273 -87
  96. package/dist/src/run.js +66 -77
  97. package/dist/src/schema.js +320 -343
  98. package/dist/src/scope.js +38 -115
  99. package/dist/src/serialization-contract.js +595 -21
  100. package/dist/src/session.js +34 -29
  101. package/dist/src/source-files.js +1 -1
  102. package/dist/src/suppress-file.js +25 -28
  103. package/dist/src/suppress-inline.js +3 -2
  104. package/dist/src/suppress.js +33 -51
  105. package/dist/src/sweep.js +7 -9
  106. package/dist/src/symbol-keys.js +3 -5
  107. package/dist/src/template-field.js +9 -19
  108. package/dist/src/test-support.js +8 -11
  109. package/dist/src/text/compare.js +26 -0
  110. package/dist/src/text/glob.js +194 -0
  111. package/dist/src/text/named-files.js +54 -0
  112. package/dist/src/text/shell.js +159 -0
  113. package/dist/src/text/yaml.js +393 -0
  114. package/dist/src/type-errors.js +11 -232
  115. package/dist/src/unseen-loads.js +100 -0
  116. package/dist/src/unwrap.js +23 -0
  117. package/dist/src/usage.js +10 -0
  118. package/dist/src/verbs/analyze.js +166 -126
  119. package/dist/src/verbs/describe.js +9 -16
  120. package/dist/src/verbs/explain.js +10 -7
  121. package/dist/src/verbs/print-config.js +2 -1
  122. package/dist/src/verbs/print-projects.js +6 -4
  123. package/dist/src/verbs/print-retained.js +4 -2
  124. package/dist/src/verbs/print-roots.js +4 -2
  125. package/dist/src/verbs/verb.js +1 -5
  126. package/dist/src/verbs/version.js +14 -3
  127. package/dist/src/workflow-steps.js +62 -64
  128. package/jsr.json +1 -1
  129. package/package.json +4 -3
  130. package/src/analysis.ts +245 -502
  131. package/src/augmentations.ts +135 -17
  132. package/src/automatic-types.ts +11 -29
  133. package/src/build-scripts.ts +543 -0
  134. package/src/component-layer.ts +4 -2
  135. package/src/components.ts +2 -4
  136. package/src/config.ts +31 -302
  137. package/src/configuration-files.ts +34 -179
  138. package/src/configured-declarations.ts +18 -23
  139. package/src/conformance-record.ts +1 -1
  140. package/src/consumers.ts +6 -57
  141. package/src/contextual-uses.ts +64 -12
  142. package/src/contract/config.generated.ts +818 -0
  143. package/src/contract/contract.generated.ts +1308 -0
  144. package/src/contract/report.generated.ts +1237 -0
  145. package/src/convention-rows.ts +85 -3
  146. package/src/conventions.ts +46 -84
  147. package/src/dependencies.ts +118 -46
  148. package/src/edges.ts +41 -140
  149. package/src/element-access.ts +4 -7
  150. package/src/entry-point-gaps.ts +3 -9
  151. package/src/entry-points.ts +12 -38
  152. package/src/enum-group.ts +2 -11
  153. package/src/erased-stores.ts +3 -7
  154. package/src/exempt-classes.ts +1 -75
  155. package/src/exempt.ts +36 -50
  156. package/src/failure.ts +63 -0
  157. package/src/file-facts.ts +9 -24
  158. package/src/finding.ts +14 -9
  159. package/src/findings/boundary.ts +2 -0
  160. package/src/findings/completion.ts +181 -84
  161. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  162. package/src/findings/emitter.ts +8 -9
  163. package/src/findings/emitters.ts +7 -7
  164. package/src/findings/interfaces.ts +4 -6
  165. package/src/findings/intra-function.ts +13 -11
  166. package/src/findings/non-code-artifacts.ts +9 -11
  167. package/src/findings/reads-and-writes.ts +20 -16
  168. package/src/findings/self-check.ts +11 -17
  169. package/src/findings/unused-declarations.ts +45 -32
  170. package/src/findings/visibility-narrowing.ts +20 -17
  171. package/src/findings-pass.ts +10 -19
  172. package/src/framework-lifecycle.ts +7 -7
  173. package/src/fs/document.ts +70 -0
  174. package/src/fs/manifest.ts +92 -0
  175. package/src/fs/tree.ts +110 -0
  176. package/src/generated-file.ts +7 -4
  177. package/src/git-ignore.ts +50 -26
  178. package/src/graph.ts +5 -3
  179. package/src/host.ts +12 -5
  180. package/src/html-entries.ts +6 -33
  181. package/src/in-operands.ts +2 -1
  182. package/src/index.ts +1 -1
  183. package/src/injection-container.ts +1 -1
  184. package/src/intra-function-parts.ts +73 -44
  185. package/src/inventory.ts +2 -20
  186. package/src/javascript-file.ts +37 -0
  187. package/src/json-document.ts +241 -76
  188. package/src/keyof-comparisons.ts +3 -7
  189. package/src/kinds.ts +5 -413
  190. package/src/load/classify.ts +397 -0
  191. package/src/load/diagnostics.ts +144 -0
  192. package/src/load/discover.ts +455 -0
  193. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  194. package/src/load/expected-module.ts +188 -0
  195. package/src/load/load.ts +73 -0
  196. package/src/load/module-lookup.ts +50 -0
  197. package/src/load/plan.ts +504 -0
  198. package/src/load/specifiers.ts +168 -0
  199. package/src/load/synthesize.ts +211 -0
  200. package/src/load/test-globals.ts +251 -0
  201. package/src/load/test-joins.ts +98 -0
  202. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  203. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  204. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
  205. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  206. package/src/{workspace.ts → load/workspace.ts} +90 -134
  207. package/src/manifest.ts +81 -75
  208. package/src/matrix.ts +122 -4
  209. package/src/mdx-entries.ts +3 -26
  210. package/src/outside-tests.ts +1 -1
  211. package/src/partial.ts +3 -2
  212. package/src/paths.ts +27 -15
  213. package/src/position.ts +2 -1
  214. package/src/print.ts +12 -92
  215. package/src/product.ts +18 -0
  216. package/src/query.ts +11 -0
  217. package/src/ref.ts +1 -0
  218. package/src/references.ts +52 -31
  219. package/src/report-schema.ts +68 -0
  220. package/src/report.ts +101 -224
  221. package/src/reporters/annotations.ts +22 -11
  222. package/src/reporters/json.ts +1 -1
  223. package/src/reporters/reporter.ts +2 -4
  224. package/src/reporters/reporters.ts +1 -1
  225. package/src/reporters/sarif.ts +29 -16
  226. package/src/reporters/template.ts +4 -20
  227. package/src/reporters/text.ts +3 -3
  228. package/src/resolve.ts +283 -1114
  229. package/src/roots.ts +339 -104
  230. package/src/run.ts +75 -91
  231. package/src/schema.ts +391 -388
  232. package/src/scope.ts +46 -138
  233. package/src/serialization-contract.ts +763 -22
  234. package/src/session.ts +70 -54
  235. package/src/source-files.ts +1 -1
  236. package/src/suppress-file.ts +24 -33
  237. package/src/suppress-inline.ts +2 -2
  238. package/src/suppress.ts +43 -58
  239. package/src/sweep.ts +23 -9
  240. package/src/symbol-keys.ts +2 -5
  241. package/src/template-field.ts +15 -25
  242. package/src/test-support.ts +17 -13
  243. package/src/text/compare.ts +27 -0
  244. package/src/text/glob.ts +210 -0
  245. package/src/text/named-files.inputs.json +1522 -0
  246. package/src/text/named-files.ts +68 -0
  247. package/src/text/shell.ts +161 -0
  248. package/src/text/yaml.ts +423 -0
  249. package/src/type-errors.ts +18 -288
  250. package/src/unseen-loads.ts +134 -0
  251. package/src/unwrap.ts +34 -0
  252. package/src/usage.ts +10 -0
  253. package/src/verbs/analyze.ts +224 -162
  254. package/src/verbs/describe.ts +14 -15
  255. package/src/verbs/explain.ts +10 -11
  256. package/src/verbs/print-config.ts +2 -1
  257. package/src/verbs/print-projects.ts +6 -4
  258. package/src/verbs/print-retained.ts +4 -2
  259. package/src/verbs/print-roots.ts +4 -2
  260. package/src/verbs/verb.ts +2 -8
  261. package/src/verbs/version.ts +15 -3
  262. package/src/workflow-steps.ts +74 -64
  263. package/dist/src/base-url.js +0 -18
  264. package/dist/src/discover.js +0 -468
  265. package/dist/src/glob.js +0 -65
  266. package/dist/src/pnpm-workspace.js +0 -120
  267. package/dist/src/setup-failure.js +0 -23
  268. package/dist/src/test-joins.js +0 -101
  269. package/dist/src/version.js +0 -7
  270. package/src/base-url.ts +0 -24
  271. package/src/discover.ts +0 -653
  272. package/src/glob.ts +0 -67
  273. package/src/pnpm-workspace.ts +0 -135
  274. package/src/setup-failure.ts +0 -39
  275. package/src/test-joins.ts +0 -120
  276. package/src/version.ts +0 -7
@@ -19,7 +19,7 @@ import {
19
19
  import { SymbolFlags, type NodeHandle, type Symbol as TSSymbol } from "@typescript/native/sync";
20
20
  import type { AliasChains } from "./alias-chain.ts";
21
21
  import { resolvedTargets } from "./calls.ts";
22
- import type { Config, DeclarationEntry } from "./config.ts";
22
+ import type { DeclarationEntry, ResolvedConfig } from "./config.ts";
23
23
  import { declaredComponent, type Inventory } from "./inventory.ts";
24
24
  import { must, Unanswerable } from "./query.ts";
25
25
  import { nameComponent } from "./ref.ts";
@@ -252,21 +252,21 @@ export function resolveEntries<Brand>(
252
252
  }
253
253
  const modules = modulesNamed(
254
254
  project,
255
- new Set(entries.flatMap((entry) => (entry.shape === "module" ? [entry.module] : []))),
255
+ new Set(entries.flatMap((entry) => ("module" in entry ? [entry.module] : []))),
256
256
  );
257
257
  return entries.map((entry) => {
258
- if (entry.shape === "symbol") {
258
+ if ("symbol" in entry) {
259
259
  return { entry, handles: new Set<string>(), ids: new Set(byRef.get(entry.symbol) ?? []) };
260
260
  }
261
261
  const { components, static: isStatic } = componentsOf(
262
- entry.shape === "module" ? entry.name : entry.global,
262
+ "module" in entry ? entry.name : entry.global,
263
263
  );
264
264
  const [first = "", ...rest] = components;
265
265
  let start: TSSymbol | undefined;
266
266
  if (isStatic && rest.length === 0) {
267
267
  // Only a member is static, and a path of one component names no member.
268
268
  start = undefined;
269
- } else if (entry.shape === "module") {
269
+ } else if ("module" in entry) {
270
270
  const module = modules.get(entry.module);
271
271
  start = module === undefined ? undefined : memberOf(project, module, first, false);
272
272
  } else if (nameComponent(first) === first) {
@@ -329,14 +329,10 @@ export function entriesAt<Brand>(
329
329
  * program the display name the inventory gives it.
330
330
  */
331
331
  export function spelledEntry(entry: DeclarationEntry, held: Inventory): string {
332
- switch (entry.shape) {
333
- case "symbol":
334
- return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
335
- case "module":
336
- return entry.name;
337
- case "global":
338
- return entry.global;
332
+ if ("symbol" in entry) {
333
+ return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
339
334
  }
335
+ return "module" in entry ? entry.name : entry.global;
340
336
  }
341
337
 
342
338
  /** One entry of a configuration key that names a declaration, and the key's dotted path. */
@@ -353,14 +349,17 @@ const LIFECYCLE_KEY = "ts.lifecycle_contracts";
353
349
  * the injection registrations, each lifecycle contract's components and then its bases,
354
350
  * and the serializers.
355
351
  */
356
- export function configuredDeclarations(config: Config): readonly ConfiguredDeclaration[] {
352
+ export function configuredDeclarations(config: ResolvedConfig): readonly ConfiguredDeclaration[] {
357
353
  return [
358
- ...config.ts.injectionRegistrations.map((entry) => ({
354
+ ...config.ts.injection_registrations.map((entry) => ({
359
355
  key: "ts.injection_registrations",
360
356
  entry,
361
357
  })),
362
- ...config.ts.lifecycleContracts.flatMap((contract) =>
363
- [...contract.components, ...contract.bases].map((entry) => ({ key: LIFECYCLE_KEY, entry })),
358
+ ...config.ts.lifecycle_contracts.flatMap((contract) =>
359
+ [...(contract.components ?? []), ...(contract.bases ?? [])].map((entry) => ({
360
+ key: LIFECYCLE_KEY,
361
+ entry,
362
+ })),
364
363
  ),
365
364
  ...config.ts.serializers.map((entry) => ({ key: "ts.serializers", entry })),
366
365
  ];
@@ -396,12 +395,8 @@ export function declarationsNamed<Brand>(
396
395
  * sign and its name, and a global entry's path after a number sign.
397
396
  */
398
397
  export function configuredRef(entry: DeclarationEntry): string {
399
- switch (entry.shape) {
400
- case "symbol":
401
- return entry.symbol;
402
- case "module":
403
- return `${entry.module}#${entry.name}`;
404
- case "global":
405
- return `#${entry.global}`;
398
+ if ("symbol" in entry) {
399
+ return entry.symbol;
406
400
  }
401
+ return "module" in entry ? `${entry.module}#${entry.name}` : `#${entry.global}`;
407
402
  }
@@ -4,7 +4,7 @@ export const RECORD = {
4
4
  conformance: {
5
5
  corpusVersion: "6.0.0",
6
6
  result: "pass",
7
- digest: "sha256:76c07ac14bda6e302444918d832b01844bac6f29b6f22a742d538804480c0769",
7
+ digest: "sha256:0d33564807a356fd3e2760ddaa0645d758bdb9a96ee74a392675e444343bbf20",
8
8
  },
9
9
  gaps: [],
10
10
  } as const;
package/src/consumers.ts CHANGED
@@ -1,60 +1,9 @@
1
- /**
2
- * The consumers a scope declares, loaded beside the target: each a directory on the
3
- * local filesystem whose compiler configurations open as projects of the run's one
4
- * snapshot, and whose references to the target's declarations count against them.
5
- */
6
-
7
- import type { Config } from "./config.ts";
8
- import { consumerProjects } from "./discover.ts";
1
+ import type { PartialConfig } from "./config.ts";
9
2
  import type { Host } from "./host.ts";
10
- import { inventory, packageScope } from "./inventory.ts";
3
+ import { inventory } from "./inventory.ts";
11
4
  import { relativePath } from "./paths.ts";
12
5
  import { references, type ConsumerSide, type Reference } from "./references.ts";
13
- import type { Module, Scope } from "./scope.ts";
14
- import { narrowedTo, type Engine, type ProjectView } from "./session.ts";
15
-
16
- /** One consumer the run loaded. */
17
- export interface LoadedConsumer {
18
- /** The name the consumer publishes itself under, which every finding names it by. */
19
- readonly id: string;
20
- /** The consumer's directory, absolute. */
21
- readonly path: string;
22
- }
23
-
24
- /** One declared consumer and the compiler configurations it opens as. */
25
- interface ConsumerLoad {
26
- readonly consumer: LoadedConsumer;
27
- /** The configuration files, absolute, in discovery order, none a target project's. */
28
- readonly configFiles: readonly string[];
29
- }
30
-
31
- /** The name one module publishes itself under: the scope's, else its manifest's. */
32
- export function moduleIdentity(host: Host, module: Module): string {
33
- return module.id === "" ? packageScope(host, module.path)("index.ts").package : module.id;
34
- }
35
-
36
- /**
37
- * Every consumer the scope declares, in its order, with the projects it opens as. A
38
- * configuration that is also one of the target's projects, or an earlier consumer's, is
39
- * that project's alone. A consumer whose directory is absent, is no directory or holds
40
- * no configuration ends the run naming it, before any project opens.
41
- */
42
- export function consumerLoads(
43
- engine: Engine,
44
- host: Host,
45
- scope: Scope,
46
- targetConfigFiles: readonly string[],
47
- ): readonly ConsumerLoad[] {
48
- const opened = new Set(targetConfigFiles);
49
- return scope.consumers.map((module) => {
50
- const configFiles = consumerProjects(engine, host, module.path).filter((configFile) => {
51
- const fresh = !opened.has(configFile);
52
- opened.add(configFile);
53
- return fresh;
54
- });
55
- return { consumer: { id: moduleIdentity(host, module), path: module.path }, configFiles };
56
- });
57
- }
6
+ import { narrowedTo, type ProjectView } from "./session.ts";
58
7
 
59
8
  /**
60
9
  * Every reference one consumer project's own files make to a declaration of the target.
@@ -68,7 +17,7 @@ export function consumerReferences<Brand>(
68
17
  host: Host,
69
18
  targetRoot: string,
70
19
  consumer: ConsumerSide,
71
- config: Config,
20
+ config: PartialConfig,
72
21
  ): readonly Reference[] {
73
22
  const held = inventory(
74
23
  narrowedTo(project, (file) => relativePath(targetRoot, file.fileName) !== undefined),
@@ -76,10 +25,10 @@ export function consumerReferences<Brand>(
76
25
  targetRoot,
77
26
  );
78
27
  const found = references(project, held, targetRoot, {
79
- testFiles: config.ts.testFiles,
28
+ testFiles: config.ts.test_files,
80
29
  consumer,
81
30
  }).references;
82
- if (config.analysis.consumerTests !== "production") {
31
+ if (config.analysis.consumer_tests !== "production") {
83
32
  return found;
84
33
  }
85
34
  return found.map((reference) => (reference.test ? { ...reference, test: false } : reference));
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * The uses contextual types make of the target's members, which no name node resolves to.
3
3
  * An object-literal property writes the member of its name on each constituent of the
4
- * literal's contextual type that declares one. A value reaching a position typed by another
5
- * object type reads each of its members that type declares, through array elements and
6
- * the parameters and results of functions and constructors too; a class instance there is
7
- * the interface-satisfaction class's evidence instead.
4
+ * literal's contextual type that declares one, and so does a property of a literal whose
5
+ * type is inferred, where a value of that type reaches such a position. A value reaching a
6
+ * position typed by another object type reads each of its members that type declares,
7
+ * through array elements and the parameters and results of functions and constructors
8
+ * too; a class instance there is the interface-satisfaction class's evidence instead.
8
9
  */
9
10
 
10
11
  import {
@@ -109,10 +110,12 @@ interface Checked {
109
110
  readonly source: Type;
110
111
  readonly targets: readonly Type[];
111
112
  /**
112
- * Whether the source is declared outside the target, so code the analysis does not
113
- * hold reads the members of the target's types it declares.
113
+ * What the source is: a type whose members the target declares, which the position
114
+ * reads; one declared outside the target, so code the analysis does not hold reads the
115
+ * members of the target's types it declares; or an object literal's own type, whose
116
+ * properties write the position's members of their names.
114
117
  */
115
- readonly outside: boolean;
118
+ readonly sourceKind: "members" | "outside" | "literal";
116
119
  }
117
120
 
118
121
  /** One member of an object type, by the inventory's declarations and the checker's declaration handles. */
@@ -287,6 +290,37 @@ export function contextualUses<Brand>(
287
290
  return members.length > 0 && members.every((member) => !member.own);
288
291
  };
289
292
 
293
+ const literalTypes = new Map<number, boolean>();
294
+ /**
295
+ * Whether an object type is an object literal's own, inferred where no contextual type
296
+ * names it: every member a property an object literal of the target writes.
297
+ */
298
+ const literalTyped = (part: Type): boolean => {
299
+ let known = literalTypes.get(part.id);
300
+ if (known === undefined) {
301
+ const members = [...membersOf(part).values()];
302
+ known =
303
+ members.length > 0 &&
304
+ members.every(
305
+ (member) =>
306
+ member.ids.length === 0 &&
307
+ member.declarations.length > 0 &&
308
+ member.declarations.every((handle) => literalProperty(handle) !== undefined),
309
+ );
310
+ literalTypes.set(part.id, known);
311
+ }
312
+ return known;
313
+ };
314
+ /** The name node of the object literal property one declaration handle names, if it is one. */
315
+ const literalProperty = (handle: NodeHandle): Node | undefined => {
316
+ const node = project.declarationAt(handle)?.node;
317
+ return node !== undefined &&
318
+ (isPropertyAssignment(node) || isShorthandPropertyAssignment(node)) &&
319
+ isObjectLiteralExpression(node.parent)
320
+ ? node.name
321
+ : undefined;
322
+ };
323
+
290
324
  const elements = new Map<number, Type | undefined>();
291
325
  /** The element type of an array type, read once per type. */
292
326
  const elementOf = (type: Type): Type | undefined => {
@@ -379,9 +413,11 @@ export function contextualUses<Brand>(
379
413
  const sources = partsOf(source).filter((one) => !targets.some((part) => part.id === one.id));
380
414
  for (const one of sources) {
381
415
  if (carries(one)) {
382
- into.push({ source: one, targets, outside: false });
416
+ into.push({ source: one, targets, sourceKind: "members" });
383
417
  } else if (outside(one)) {
384
- into.push({ source: one, targets, outside: true });
418
+ into.push({ source: one, targets, sourceKind: "outside" });
419
+ } else if (literalTyped(one)) {
420
+ into.push({ source: one, targets, sourceKind: "literal" });
385
421
  }
386
422
  const element = elementOf(one);
387
423
  const called = element === undefined ? callsOf(one) : [];
@@ -423,7 +459,7 @@ export function contextualUses<Brand>(
423
459
  }
424
460
  seen.add(type.id);
425
461
  return partsOf(type).some((part) => {
426
- if (carries(part) || outside(part)) {
462
+ if (carries(part) || outside(part) || literalTyped(part)) {
427
463
  return true;
428
464
  }
429
465
  const element = elementOf(part);
@@ -444,6 +480,8 @@ export function contextualUses<Brand>(
444
480
  cap,
445
481
  );
446
482
  const reads: ContextualUse[] = [];
483
+ // A literal property writes a position's members once, however many values carry it there.
484
+ const carriedWrites = new Set<string>();
447
485
  sourced.forEach(({ flow, type }, index) => {
448
486
  const position = positions[index];
449
487
  if (position === undefined || !isAnswered(position)) {
@@ -452,10 +490,24 @@ export function contextualUses<Brand>(
452
490
  const checked: Checked[] = [];
453
491
  compared(type, position, checked, new Set());
454
492
  const ids = new Set<string>();
455
- for (const { source, targets, outside: held } of checked) {
493
+ for (const { source, targets, sourceKind } of checked) {
456
494
  const tables = targets.map(membersOf);
457
495
  const sourceMembers = membersOf(source);
458
- if (held) {
496
+ if (sourceKind === "literal") {
497
+ for (const [name, member] of sourceMembers) {
498
+ const written = [...new Set(tables.flatMap((table) => table.get(name)?.ids ?? []))];
499
+ for (const at of written.length === 0 ? [] : member.declarations) {
500
+ const node = literalProperty(at);
501
+ const key = `${at.path}#${String(at.index)}>${written.join(",")}`;
502
+ if (node !== undefined && !carriedWrites.has(key)) {
503
+ carriedWrites.add(key);
504
+ writes.push({ at: node, from: flow.from, ids: written });
505
+ }
506
+ }
507
+ }
508
+ continue;
509
+ }
510
+ if (sourceKind === "outside") {
459
511
  for (const table of tables) {
460
512
  for (const [name, member] of table) {
461
513
  if (sourceMembers.has(name)) {