@cplieger/deadset-ts 8.0.0-dev.2 → 8.0.0-dev.21

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 (431) hide show
  1. package/README.md +3 -1
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/dist/bin/mapper-connection.js +2 -1
  4. package/dist/bin/node-host.js +24 -5
  5. package/dist/src/alias-chain.js +35 -9
  6. package/dist/src/amd.js +81 -0
  7. package/dist/src/analysis.js +274 -496
  8. package/dist/src/augmentations.js +64 -3
  9. package/dist/src/automatic-types.js +10 -29
  10. package/dist/src/bindings.js +382 -0
  11. package/dist/src/commands/held.js +499 -0
  12. package/dist/src/commands/read.js +161 -0
  13. package/dist/src/commands/shell.js +169 -0
  14. package/dist/src/commands/tools.js +1847 -0
  15. package/dist/src/component-files.js +3 -2
  16. package/dist/src/component-layer.js +4 -2
  17. package/dist/src/config.js +6 -82
  18. package/dist/src/conformance-record.js +2 -2
  19. package/dist/src/consumers.js +10 -38
  20. package/dist/src/contextual-uses.js +57 -26
  21. package/dist/src/contract/config.generated.js +561 -0
  22. package/dist/src/contract/contract.generated.js +1370 -0
  23. package/dist/src/contract/report.generated.js +866 -0
  24. package/dist/src/dependencies.js +165 -122
  25. package/dist/src/destructuring.js +1 -19
  26. package/dist/src/edges.js +38 -122
  27. package/dist/src/element-access.js +181 -168
  28. package/dist/src/entries/build-scripts.js +222 -0
  29. package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
  30. package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
  31. package/dist/src/entries/conventions.js +740 -0
  32. package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
  33. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
  34. package/dist/src/entries/run-directories.js +77 -0
  35. package/dist/src/entries/sources.js +144 -0
  36. package/dist/src/entries/static-module.js +670 -0
  37. package/dist/src/entries/test-runners.js +650 -0
  38. package/dist/src/entries/workflow-steps.js +345 -0
  39. package/dist/src/exemptions/argument-places.js +131 -0
  40. package/dist/src/exemptions/calls.js +338 -0
  41. package/dist/src/exemptions/classes.js +13 -0
  42. package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
  43. package/dist/src/exemptions/detectors.js +48 -0
  44. package/dist/src/{enum-group.js → exemptions/enum-group.js} +9 -16
  45. package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +7 -11
  46. package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +76 -54
  47. package/dist/src/exemptions/generated-file.js +241 -0
  48. package/dist/src/{injection-container.js → exemptions/injection-container.js} +20 -26
  49. package/dist/src/exemptions/installed-globals.js +173 -0
  50. package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
  51. package/dist/src/exemptions/lifecycle.js +150 -0
  52. package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
  53. package/dist/src/exemptions/serialization-contract.js +1276 -0
  54. package/dist/src/exemptions/sites.js +182 -0
  55. package/dist/src/{template-field.js → exemptions/template-field.js} +13 -23
  56. package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
  57. package/dist/src/exposure.js +400 -0
  58. package/dist/src/extensions.js +37 -0
  59. package/dist/src/facts.js +38 -0
  60. package/dist/src/failure.js +36 -0
  61. package/dist/src/file-facts.js +3 -26
  62. package/dist/src/findings/completion.js +109 -56
  63. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  64. package/dist/src/findings/emitters.js +27 -41
  65. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  66. package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
  67. package/dist/src/findings/interfaces.js +4 -5
  68. package/dist/src/findings/intra-function.js +16 -13
  69. package/dist/src/findings/non-code-artifacts.js +17 -12
  70. package/dist/src/findings/reads-and-writes.js +86 -27
  71. package/dist/src/findings/self-check.js +44 -30
  72. package/dist/src/findings/unused-declarations.js +29 -45
  73. package/dist/src/findings/visibility-narrowing.js +23 -31
  74. package/dist/src/findings-pass.js +13 -21
  75. package/dist/src/fs/document.js +52 -0
  76. package/dist/src/fs/manifest.js +67 -0
  77. package/dist/src/fs/tree.js +88 -0
  78. package/dist/src/git-ignore.js +39 -26
  79. package/dist/src/import-bindings.js +73 -28
  80. package/dist/src/index/sites.js +557 -0
  81. package/dist/src/index/values.js +124 -0
  82. package/dist/src/index.js +2 -2
  83. package/dist/src/indexed-access.js +523 -478
  84. package/dist/src/intra-function-parts.js +63 -43
  85. package/dist/src/inventory/commonjs.js +507 -0
  86. package/dist/src/inventory/enumerate.js +167 -0
  87. package/dist/src/inventory/syntax.js +236 -0
  88. package/dist/src/inventory.js +323 -724
  89. package/dist/src/javascript-file.js +48 -0
  90. package/dist/src/json-document.js +198 -67
  91. package/dist/src/keyof-comparisons.js +126 -107
  92. package/dist/src/kinds.js +3 -366
  93. package/dist/src/{components.js → liveness/components.js} +2 -3
  94. package/dist/src/{graph.js → liveness/graph.js} +52 -28
  95. package/dist/src/liveness/matrix.js +99 -0
  96. package/dist/src/liveness/sweep.js +558 -0
  97. package/dist/src/load/classify.js +289 -0
  98. package/dist/src/load/diagnostics.js +87 -0
  99. package/dist/src/load/discover.js +285 -0
  100. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  101. package/dist/src/load/expected-module.js +136 -0
  102. package/dist/src/load/load.js +27 -0
  103. package/dist/src/load/module-lookup.js +37 -0
  104. package/dist/src/load/package-scope.js +56 -0
  105. package/dist/src/load/plan.js +313 -0
  106. package/dist/src/load/specifiers.js +113 -0
  107. package/dist/src/load/synthesize.js +152 -0
  108. package/dist/src/load/test-globals.js +207 -0
  109. package/dist/src/load/test-joins.js +82 -0
  110. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  111. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  112. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
  113. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  114. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  115. package/dist/src/module-declarations.js +70 -85
  116. package/dist/src/overrides.js +134 -112
  117. package/dist/src/partial.js +2 -1
  118. package/dist/src/paths.js +82 -16
  119. package/dist/src/platform-calls.js +78 -0
  120. package/dist/src/platform-symbols.js +134 -0
  121. package/dist/src/position.js +43 -1
  122. package/dist/src/print.js +11 -79
  123. package/dist/src/product.js +13 -0
  124. package/dist/src/query.js +95 -0
  125. package/dist/src/ref.js +12 -20
  126. package/dist/src/references/read-rule.js +1 -0
  127. package/dist/src/references/read-rules.js +48 -0
  128. package/dist/src/references/sink.js +237 -0
  129. package/dist/src/references/sources.js +450 -0
  130. package/dist/src/references.js +117 -1074
  131. package/dist/src/report-schema.js +51 -0
  132. package/dist/src/report.js +57 -41
  133. package/dist/src/reporters/annotations.js +15 -8
  134. package/dist/src/reporters/sarif.js +16 -10
  135. package/dist/src/reporters/template.js +4 -19
  136. package/dist/src/resolve.js +246 -730
  137. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
  138. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
  139. package/dist/src/roots/manifest.js +216 -0
  140. package/dist/src/roots/published-reach.js +412 -0
  141. package/dist/src/roots/root-set.js +487 -0
  142. package/dist/src/roots/unseen-loads.js +236 -0
  143. package/dist/src/run.js +66 -77
  144. package/dist/src/schema.js +320 -343
  145. package/dist/src/scope.js +38 -115
  146. package/dist/src/session.js +37 -29
  147. package/dist/src/source-files.js +6 -10
  148. package/dist/src/stores.js +4 -2
  149. package/dist/src/suppress-file.js +25 -28
  150. package/dist/src/suppress-inline.js +36 -83
  151. package/dist/src/suppress.js +50 -71
  152. package/dist/src/symbol-keys.js +92 -88
  153. package/dist/src/tests/file-class.js +76 -0
  154. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  155. package/dist/src/{test-support.js → tests/support.js} +5 -12
  156. package/dist/src/text/compare.js +26 -0
  157. package/dist/src/text/glob.js +194 -0
  158. package/dist/src/text/named-files.js +298 -0
  159. package/dist/src/text/yaml.js +684 -0
  160. package/dist/src/type-errors.js +11 -232
  161. package/dist/src/type-parameter-reads.js +178 -139
  162. package/dist/src/unimplemented-callees.js +262 -242
  163. package/dist/src/unwrap.js +23 -0
  164. package/dist/src/usage.js +10 -0
  165. package/dist/src/vendor/unbash/ansi-c.js +132 -0
  166. package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
  167. package/dist/src/vendor/unbash/arithmetic.js +591 -0
  168. package/dist/src/vendor/unbash/assignment.js +131 -0
  169. package/dist/src/vendor/unbash/chars.js +48 -0
  170. package/dist/src/vendor/unbash/command.js +41 -0
  171. package/dist/src/vendor/unbash/expansion.js +80 -0
  172. package/dist/src/vendor/unbash/glob.js +268 -0
  173. package/dist/src/vendor/unbash/heredoc.js +27 -0
  174. package/dist/src/vendor/unbash/internal-types.js +1 -0
  175. package/dist/src/vendor/unbash/lexer.js +4274 -0
  176. package/dist/src/vendor/unbash/limits.js +3 -0
  177. package/dist/src/vendor/unbash/parser.js +1366 -0
  178. package/dist/src/vendor/unbash/parts.js +57 -0
  179. package/dist/src/vendor/unbash/substitution-context.js +8 -0
  180. package/dist/src/vendor/unbash/traversal.js +212 -0
  181. package/dist/src/vendor/unbash/types.js +1 -0
  182. package/dist/src/vendor/unbash/word-value.js +61 -0
  183. package/dist/src/vendor/unbash/word.js +40 -0
  184. package/dist/src/verbs/analyze.js +194 -127
  185. package/dist/src/verbs/describe.js +13 -18
  186. package/dist/src/verbs/explain.js +85 -139
  187. package/dist/src/verbs/print-config.js +2 -1
  188. package/dist/src/verbs/print-projects.js +6 -4
  189. package/dist/src/verbs/print-retained.js +30 -6
  190. package/dist/src/verbs/print-roots.js +4 -2
  191. package/dist/src/verbs/verb.js +1 -5
  192. package/dist/src/verbs/version.js +14 -3
  193. package/jsr.json +2 -1
  194. package/package.json +14 -8
  195. package/src/alias-chain.ts +40 -11
  196. package/src/amd.ts +130 -0
  197. package/src/analysis.ts +426 -733
  198. package/src/augmentations.ts +93 -4
  199. package/src/automatic-types.ts +12 -30
  200. package/src/bindings.ts +460 -0
  201. package/src/commands/held.ts +632 -0
  202. package/src/commands/read.ts +242 -0
  203. package/src/commands/shell.ts +228 -0
  204. package/src/commands/tools.ts +2382 -0
  205. package/src/component-files.ts +4 -2
  206. package/src/component-layer.ts +4 -2
  207. package/src/config.ts +31 -302
  208. package/src/conformance-record.ts +2 -2
  209. package/src/consumers.ts +16 -66
  210. package/src/contextual-uses.ts +66 -34
  211. package/src/contract/config.generated.ts +821 -0
  212. package/src/contract/contract.generated.ts +1453 -0
  213. package/src/contract/report.generated.ts +1241 -0
  214. package/src/dependencies.ts +223 -128
  215. package/src/destructuring.ts +1 -31
  216. package/src/edges.ts +41 -140
  217. package/src/element-access.ts +190 -187
  218. package/src/entries/build-scripts.ts +292 -0
  219. package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
  220. package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
  221. package/src/{conventions.ts → entries/conventions.ts} +367 -583
  222. package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
  223. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
  224. package/src/entries/run-directories.ts +108 -0
  225. package/src/entries/sources.ts +266 -0
  226. package/src/entries/static-module.ts +858 -0
  227. package/src/entries/test-runners.ts +835 -0
  228. package/src/entries/workflow-steps.ts +432 -0
  229. package/src/exemptions/argument-places.ts +198 -0
  230. package/src/exemptions/calls.ts +483 -0
  231. package/src/exemptions/classes.ts +49 -0
  232. package/src/exemptions/decorator.ts +56 -0
  233. package/src/exemptions/detectors.ts +75 -0
  234. package/src/{enum-group.ts → exemptions/enum-group.ts} +10 -19
  235. package/src/{erased-stores.ts → exemptions/erased-stores.ts} +7 -14
  236. package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +85 -66
  237. package/src/exemptions/generated-file.ts +318 -0
  238. package/src/{injection-container.ts → exemptions/injection-container.ts} +29 -34
  239. package/src/exemptions/installed-globals.ts +255 -0
  240. package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
  241. package/src/exemptions/lifecycle.ts +286 -0
  242. package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
  243. package/src/exemptions/serialization-contract.ts +1729 -0
  244. package/src/exemptions/sites.ts +293 -0
  245. package/src/{template-field.ts → exemptions/template-field.ts} +22 -32
  246. package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
  247. package/src/exposure.ts +492 -0
  248. package/src/extensions.ts +45 -0
  249. package/src/facts.ts +46 -0
  250. package/src/failure.ts +63 -0
  251. package/src/file-facts.ts +15 -25
  252. package/src/finding.ts +10 -10
  253. package/src/findings/boundary.ts +11 -0
  254. package/src/findings/completion.ts +133 -64
  255. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  256. package/src/findings/emitter.ts +9 -10
  257. package/src/findings/emitters.ts +38 -52
  258. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  259. package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
  260. package/src/findings/interfaces.ts +7 -9
  261. package/src/findings/intra-function.ts +16 -14
  262. package/src/findings/non-code-artifacts.ts +24 -15
  263. package/src/findings/reads-and-writes.ts +118 -41
  264. package/src/findings/self-check.ts +51 -38
  265. package/src/findings/unused-declarations.ts +31 -49
  266. package/src/findings/visibility-narrowing.ts +36 -40
  267. package/src/findings-pass.ts +15 -31
  268. package/src/fs/document.ts +70 -0
  269. package/src/fs/manifest.ts +96 -0
  270. package/src/fs/tree.ts +110 -0
  271. package/src/git-ignore.ts +50 -26
  272. package/src/host.ts +12 -5
  273. package/src/import-bindings.ts +98 -29
  274. package/src/index/sites.ts +781 -0
  275. package/src/index/values.ts +170 -0
  276. package/src/index.ts +2 -2
  277. package/src/indexed-access.ts +570 -527
  278. package/src/intra-function-parts.ts +73 -44
  279. package/src/inventory/commonjs.ts +696 -0
  280. package/src/inventory/enumerate.ts +203 -0
  281. package/src/inventory/syntax.ts +311 -0
  282. package/src/inventory.ts +442 -895
  283. package/src/javascript-file.ts +54 -0
  284. package/src/json-document.ts +241 -76
  285. package/src/keyof-comparisons.ts +133 -122
  286. package/src/kinds.ts +5 -413
  287. package/src/{components.ts → liveness/components.ts} +4 -6
  288. package/src/{graph.ts → liveness/graph.ts} +100 -35
  289. package/src/liveness/matrix.ts +162 -0
  290. package/src/liveness/sweep.ts +855 -0
  291. package/src/load/classify.ts +433 -0
  292. package/src/load/diagnostics.ts +154 -0
  293. package/src/load/discover.ts +471 -0
  294. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  295. package/src/load/expected-module.ts +194 -0
  296. package/src/load/load.ts +73 -0
  297. package/src/load/module-lookup.ts +50 -0
  298. package/src/load/package-scope.ts +74 -0
  299. package/src/load/plan.ts +513 -0
  300. package/src/load/specifiers.ts +171 -0
  301. package/src/load/synthesize.ts +230 -0
  302. package/src/load/test-globals.ts +251 -0
  303. package/src/load/test-joins.ts +98 -0
  304. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  305. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  306. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
  307. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  308. package/src/{workspace.ts → load/workspace.ts} +90 -134
  309. package/src/module-declarations.ts +74 -98
  310. package/src/overrides.ts +162 -134
  311. package/src/partial.ts +3 -2
  312. package/src/paths.ts +92 -16
  313. package/src/platform-calls.ts +119 -0
  314. package/src/platform-symbols.ts +189 -0
  315. package/src/position.ts +44 -1
  316. package/src/print.ts +12 -92
  317. package/src/product.ts +18 -0
  318. package/src/query.ts +201 -10
  319. package/src/ref.ts +12 -20
  320. package/src/references/read-rule.ts +60 -0
  321. package/src/references/read-rules.ts +55 -0
  322. package/src/references/sink.ts +314 -0
  323. package/src/references/sources.ts +632 -0
  324. package/src/references.ts +187 -1450
  325. package/src/report-schema.ts +68 -0
  326. package/src/report.ts +103 -226
  327. package/src/reporters/annotations.ts +22 -11
  328. package/src/reporters/json.ts +1 -1
  329. package/src/reporters/reporter.ts +2 -4
  330. package/src/reporters/reporters.ts +1 -1
  331. package/src/reporters/sarif.ts +29 -16
  332. package/src/reporters/template.ts +4 -20
  333. package/src/reporters/text.ts +3 -3
  334. package/src/resolve.ts +284 -1115
  335. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +26 -29
  336. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
  337. package/src/roots/manifest.ts +303 -0
  338. package/src/roots/published-reach.ts +486 -0
  339. package/src/{roots.ts → roots/root-set.ts} +286 -469
  340. package/src/roots/unseen-loads.ts +345 -0
  341. package/src/run.ts +75 -91
  342. package/src/schema.ts +391 -388
  343. package/src/scope.ts +46 -138
  344. package/src/session.ts +87 -55
  345. package/src/source-files.ts +6 -11
  346. package/src/stores.ts +15 -3
  347. package/src/suppress-file.ts +24 -33
  348. package/src/suppress-inline.ts +55 -91
  349. package/src/suppress.ts +78 -79
  350. package/src/symbol-keys.ts +104 -107
  351. package/src/tests/file-class.ts +135 -0
  352. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  353. package/src/{test-support.ts → tests/support.ts} +15 -21
  354. package/src/text/compare.ts +27 -0
  355. package/src/text/glob.ts +210 -0
  356. package/src/text/named-files.ts +388 -0
  357. package/src/text/yaml.ts +747 -0
  358. package/src/type-errors.ts +18 -288
  359. package/src/type-parameter-reads.ts +187 -158
  360. package/src/unimplemented-callees.ts +297 -280
  361. package/src/unwrap.ts +34 -0
  362. package/src/usage.ts +10 -0
  363. package/src/vendor/unbash/LICENSE +15 -0
  364. package/src/vendor/unbash/VENDORED.md +16 -0
  365. package/src/vendor/unbash/ansi-c.ts +132 -0
  366. package/src/vendor/unbash/arithmetic-parts.ts +53 -0
  367. package/src/vendor/unbash/arithmetic.ts +641 -0
  368. package/src/vendor/unbash/assignment.ts +163 -0
  369. package/src/vendor/unbash/chars.ts +48 -0
  370. package/src/vendor/unbash/command.ts +47 -0
  371. package/src/vendor/unbash/expansion.ts +144 -0
  372. package/src/vendor/unbash/glob.ts +284 -0
  373. package/src/vendor/unbash/heredoc.ts +34 -0
  374. package/src/vendor/unbash/internal-types.ts +94 -0
  375. package/src/vendor/unbash/lexer.ts +4352 -0
  376. package/src/vendor/unbash/limits.ts +3 -0
  377. package/src/vendor/unbash/parser.ts +1612 -0
  378. package/src/vendor/unbash/parts.ts +96 -0
  379. package/src/vendor/unbash/substitution-context.ts +20 -0
  380. package/src/vendor/unbash/traversal.ts +182 -0
  381. package/src/vendor/unbash/types.ts +660 -0
  382. package/src/vendor/unbash/word-value.ts +64 -0
  383. package/src/vendor/unbash/word.ts +50 -0
  384. package/src/verbs/analyze.ts +255 -164
  385. package/src/verbs/describe.ts +19 -17
  386. package/src/verbs/explain.ts +93 -169
  387. package/src/verbs/print-config.ts +2 -1
  388. package/src/verbs/print-projects.ts +6 -4
  389. package/src/verbs/print-retained.ts +36 -6
  390. package/src/verbs/print-roots.ts +4 -2
  391. package/src/verbs/verb.ts +2 -8
  392. package/src/verbs/version.ts +15 -3
  393. package/dist/src/base-url.js +0 -18
  394. package/dist/src/calls.js +0 -155
  395. package/dist/src/conventions.js +0 -937
  396. package/dist/src/discover.js +0 -468
  397. package/dist/src/entry-points.js +0 -529
  398. package/dist/src/exempt-classes.js +0 -75
  399. package/dist/src/exempt.js +0 -209
  400. package/dist/src/generated-file.js +0 -95
  401. package/dist/src/glob.js +0 -65
  402. package/dist/src/manifest.js +0 -165
  403. package/dist/src/matrix.js +0 -246
  404. package/dist/src/pnpm-workspace.js +0 -120
  405. package/dist/src/roots.js +0 -672
  406. package/dist/src/serialization-contract.js +0 -611
  407. package/dist/src/setup-failure.js +0 -23
  408. package/dist/src/sweep.js +0 -224
  409. package/dist/src/test-joins.js +0 -101
  410. package/dist/src/value-flow.js +0 -74
  411. package/dist/src/version.js +0 -7
  412. package/dist/src/workflow-steps.js +0 -185
  413. package/src/base-url.ts +0 -24
  414. package/src/calls.ts +0 -225
  415. package/src/decorator.ts +0 -78
  416. package/src/discover.ts +0 -653
  417. package/src/entry-points.ts +0 -687
  418. package/src/exempt-classes.ts +0 -123
  419. package/src/exempt.ts +0 -323
  420. package/src/generated-file.ts +0 -111
  421. package/src/glob.ts +0 -67
  422. package/src/manifest.ts +0 -234
  423. package/src/matrix.ts +0 -423
  424. package/src/pnpm-workspace.ts +0 -135
  425. package/src/serialization-contract.ts +0 -849
  426. package/src/setup-failure.ts +0 -39
  427. package/src/sweep.ts +0 -347
  428. package/src/test-joins.ts +0 -120
  429. package/src/value-flow.ts +0 -104
  430. package/src/version.ts +0 -7
  431. package/src/workflow-steps.ts +0 -216
@@ -0,0 +1,747 @@
1
+ /**
2
+ * The value of a YAML document, the one reader of YAML the analysis has, in the subset
3
+ * configurations and workflow files write: block and flow mappings and sequences, plain,
4
+ * quoted and block scalars as [YAML 1.2](https://yaml.org/spec/1.2.2/) decodes them, the core
5
+ * schema's scalars as {@link CORE} and {@link NUMBER} read them, anchors, aliases and comments.
6
+ * A document using anything else, a tag, a complex key, an anchor on a key or a second
7
+ * document, or whose aliases grow it past {@link ALIAS_BUDGET}, has no value here.
8
+ */
9
+
10
+ import { isRecord } from "../json-document.ts";
11
+
12
+ /** One line of the document with its comment removed, by its indentation, its content and its row. */
13
+ interface Line {
14
+ readonly indent: number;
15
+ readonly text: string;
16
+ /** Its index among the document's lines, blank and comment lines included. */
17
+ readonly row: number;
18
+ /** Whether a tab stands in its indentation, which YAML forbids. */
19
+ readonly tabbed: boolean;
20
+ /** Whether a comment ends it, which ends a plain scalar. */
21
+ readonly commented: boolean;
22
+ }
23
+
24
+ /** A failure to read the document within the subset; it ends the whole reading. */
25
+ class Unsupported extends Error {}
26
+
27
+ /** The characters after which a quote opens a quoted scalar rather than standing in a plain one. */
28
+ const SCALAR_START = new Set(["", ":", "-", "[", "{", ","]);
29
+
30
+ /** The plain scalars YAML's core schema reads as something other than a string. */
31
+ const CORE: ReadonlyMap<string, unknown> = new Map<string, unknown>([
32
+ ["true", true],
33
+ ["True", true],
34
+ ["TRUE", true],
35
+ ["false", false],
36
+ ["False", false],
37
+ ["FALSE", false],
38
+ ["null", null],
39
+ ["Null", null],
40
+ ["NULL", null],
41
+ ["~", null],
42
+ ["", null],
43
+ ]);
44
+
45
+ /**
46
+ * The core schema's integers and floats, https://yaml.org/spec/1.2.2/#1032-tag-resolution, but
47
+ * `.inf` and `.nan`, which JSON has no value for, so they stay strings.
48
+ */
49
+ const NUMBER = /^(?:[-+]?(?:\d+|\d*\.\d+|\d+\.\d*)(?:[eE][-+]?\d+)?|0o[0-7]+|0x[\da-fA-F]+)$/u;
50
+
51
+ /**
52
+ * The size, in nodes and characters, a document's aliases may add to its value. Aliases share
53
+ * the value they name, but a consumer walking or serializing it expands each one, and an alias
54
+ * of a collection of aliases doubles that work at each level; eemeli/yaml's `maxAliasCount`
55
+ * bounds the same: https://eemeli.org/yaml/#options
56
+ */
57
+ const ALIAS_BUDGET = 100_000;
58
+
59
+ /** The escapes of a double-quoted scalar: https://yaml.org/spec/1.2.2/#57-escaped-characters */
60
+ const ESCAPES: ReadonlyMap<string, string> = new Map([
61
+ ["0", "\0"],
62
+ ["a", "\x07"],
63
+ ["b", "\b"],
64
+ ["t", "\t"],
65
+ ["\t", "\t"],
66
+ ["n", "\n"],
67
+ ["v", "\v"],
68
+ ["f", "\f"],
69
+ ["r", "\r"],
70
+ ["e", "\x1b"],
71
+ [" ", " "],
72
+ ['"', '"'],
73
+ ["/", "/"],
74
+ ["\\", "\\"],
75
+ ["N", "\x85"],
76
+ ["_", "\xa0"],
77
+ ["L", "\u2028"],
78
+ ["P", "\u2029"],
79
+ ]);
80
+
81
+ /** The escapes of a double-quoted scalar that a code point's hexadecimal digits follow, with their count. */
82
+ const CODE_ESCAPES: ReadonlyMap<string, number> = new Map([
83
+ ["x", 2],
84
+ ["u", 4],
85
+ ["U", 8],
86
+ ]);
87
+
88
+ /** A block scalar's header, its comment removed: https://yaml.org/spec/1.2.2/#81-block-scalar-styles */
89
+ const BLOCK_HEADER = /^([|>])(?:([1-9])([+-]?)|([+-]?)([1-9]?))$/u;
90
+
91
+ /** An anchor before a node, and the white space after it: https://yaml.org/spec/1.2.2/#692-node-anchors */
92
+ const ANCHOR = /^&([^\s,[\]{}]+)(?:\s+|$)/u;
93
+
94
+ /** An alias node: https://yaml.org/spec/1.2.2/#71-alias-nodes */
95
+ const ALIAS = /^\*([^\s,[\]{}]+)/u;
96
+
97
+ /** The match of `pattern`, written from `^`, at index `at` of `text`, which it does not copy. */
98
+ function matchAt(pattern: RegExp, text: string, at: number): RegExpExecArray | null {
99
+ const sticky = new RegExp(pattern.source.slice(1), "uy");
100
+ sticky.lastIndex = at;
101
+ return sticky.exec(text);
102
+ }
103
+
104
+ /** The text of one line before its comment: a `#` at the line's start or after a blank, outside quotes. */
105
+ function withoutComment(line: string): string {
106
+ let quote: string | undefined;
107
+ let previous = "";
108
+ for (let at = 0; at < line.length; at += 1) {
109
+ const char = line.charAt(at);
110
+ if (quote !== undefined) {
111
+ if (quote === '"' && char === "\\") {
112
+ at += 1;
113
+ } else if (char === quote) {
114
+ quote = undefined;
115
+ }
116
+ continue;
117
+ }
118
+ if ((char === "'" || char === '"') && SCALAR_START.has(previous)) {
119
+ quote = char;
120
+ } else if (char === "#" && (at === 0 || /\s/u.test(line.charAt(at - 1)))) {
121
+ return line.slice(0, at);
122
+ }
123
+ if (!/\s/u.test(char)) {
124
+ previous = char;
125
+ }
126
+ }
127
+ return line;
128
+ }
129
+
130
+ /** The number of spaces a line starts with. */
131
+ function spacesOf(line: string): number {
132
+ return line.length - line.replace(/^ +/u, "").length;
133
+ }
134
+
135
+ function afterBlanks(text: string, at: number): number {
136
+ let end = at;
137
+ while (text[end] === " " || text[end] === "\t") {
138
+ end += 1;
139
+ }
140
+ return end;
141
+ }
142
+
143
+ /** The text one escape of a double-quoted scalar stands for, from its letter on, and its length. */
144
+ function escaped(text: string, at: number): { text: string; length: number } | undefined {
145
+ const letter = text.charAt(at);
146
+ const digits = CODE_ESCAPES.get(letter);
147
+ if (digits === undefined) {
148
+ const one = ESCAPES.get(letter);
149
+ return one === undefined ? undefined : { text: one, length: 1 };
150
+ }
151
+ const code = text.slice(at + 1, at + 1 + digits);
152
+ const point = /^[\da-f]+$/iu.test(code) ? Number.parseInt(code, 16) : Number.NaN;
153
+ return code.length === digits && point <= 0x10ffff
154
+ ? { text: String.fromCodePoint(point), length: 1 + digits }
155
+ : undefined;
156
+ }
157
+
158
+ /**
159
+ * The value of the quoted scalar whose opening quote is at `start` of `text`, and the index
160
+ * after its closing quote, or `undefined` where it does not close or holds an escape YAML does
161
+ * not define. A line break folds as YAML folds it, after the white space before it, which an
162
+ * escape keeps: https://yaml.org/spec/1.2.2/#73-flow-scalar-styles
163
+ */
164
+ function quotedScalar(text: string, start: number): { value: string; end: number } | undefined {
165
+ const quote = text[start];
166
+ let value = "";
167
+ let kept = 0;
168
+ let at = start + 1;
169
+ while (at < text.length) {
170
+ const char = text[at] ?? "";
171
+ if (char === "\n") {
172
+ value = value.slice(0, kept) + value.slice(kept).replace(/[ \t]+$/u, "");
173
+ let breaks = 0;
174
+ for (; text[at] === "\n"; breaks += 1) {
175
+ at = afterBlanks(text, at + 1);
176
+ }
177
+ value += breaks === 1 ? " " : "\n".repeat(breaks - 1);
178
+ } else if (char === quote && !(quote === "'" && text[at + 1] === "'")) {
179
+ return { value, end: at + 1 };
180
+ } else if (char === "'" && quote === "'") {
181
+ value += "'";
182
+ at += 2;
183
+ } else if (char === "\\" && quote === '"' && text[at + 1] === "\n") {
184
+ at = afterBlanks(text, at + 2);
185
+ } else if (char === "\\" && quote === '"') {
186
+ const escape = escaped(text, at + 1);
187
+ if (escape === undefined) {
188
+ return undefined;
189
+ }
190
+ value += escape.text;
191
+ at += 1 + escape.length;
192
+ } else {
193
+ value += char;
194
+ at += 1;
195
+ continue;
196
+ }
197
+ kept = value.length;
198
+ }
199
+ return undefined;
200
+ }
201
+
202
+ /**
203
+ * The value of a scalar's lines, each without its indentation, a blank one empty: a literal
204
+ * block's, `|`, joined by `\n`, and a folded block's, `>`, or a plain one's with each line
205
+ * break folded: https://yaml.org/spec/1.2.2/#813-folded-style
206
+ */
207
+ function foldedLines(lines: readonly string[], kind: "|" | ">" | "plain"): string {
208
+ let value = "";
209
+ let previous: string | undefined;
210
+ let empty = 0;
211
+ for (const line of lines) {
212
+ if (line.trim() === "") {
213
+ empty += previous === undefined && kind === "plain" ? 0 : 1;
214
+ continue;
215
+ }
216
+ const folds =
217
+ kind !== "|" && previous !== undefined && !/^[ \t]/u.test(previous) && !/^[ \t]/u.test(line);
218
+ value +=
219
+ previous === undefined
220
+ ? "\n".repeat(empty)
221
+ : folds && empty === 0
222
+ ? " "
223
+ : "\n".repeat(empty + (folds ? 0 : 1));
224
+ value += line;
225
+ previous = line;
226
+ empty = 0;
227
+ }
228
+ return value;
229
+ }
230
+
231
+ /** The value one scalar's text on one line denotes, quoted or plain. */
232
+ function scalar(text: string): unknown {
233
+ const value = text.trim();
234
+ if (value.startsWith("'") || value.startsWith('"')) {
235
+ const read = quotedScalar(value, 0);
236
+ if (read?.end !== value.length) {
237
+ throw new Unsupported();
238
+ }
239
+ return read.value;
240
+ }
241
+ if (/^[&*!|>%@`?#]/u.test(value) || value === "---" || value === "...") {
242
+ throw new Unsupported();
243
+ }
244
+ if (/:(?:\s|$)/u.test(value)) {
245
+ throw new Unsupported();
246
+ }
247
+ if (CORE.has(value)) {
248
+ return CORE.get(value);
249
+ }
250
+ return NUMBER.test(value) ? Number(value) : value;
251
+ }
252
+
253
+ /** Sets `key` of `object` as an own member, so a key such as `__proto__` changes no prototype. */
254
+ function setMember(object: Record<string, unknown>, key: string, value: unknown): void {
255
+ Object.defineProperty(object, key, {
256
+ value,
257
+ enumerable: true,
258
+ writable: true,
259
+ configurable: true,
260
+ });
261
+ }
262
+
263
+ /** The size of a value once every value it shares is expanded, each node 1 and each string its length more. */
264
+ function sizeOf(value: unknown, sizes: WeakMap<object, number>): number {
265
+ if (typeof value === "string") {
266
+ return 1 + value.length;
267
+ }
268
+ if (typeof value !== "object" || value === null) {
269
+ return 1;
270
+ }
271
+ const known = sizes.get(value);
272
+ if (known !== undefined) {
273
+ return known;
274
+ }
275
+ const children = Array.isArray(value) ? value : Object.entries(value).flat();
276
+ const size = children.reduce<number>((sum, child) => sum + sizeOf(child, sizes), 1);
277
+ sizes.set(value, size);
278
+ return size;
279
+ }
280
+
281
+ /** The position of a mapping key's `:` in one line's content, or -1 where the content is no mapping entry. */
282
+ function keyEnd(text: string): number {
283
+ let quote: string | undefined;
284
+ for (let at = 0; at < text.length; at += 1) {
285
+ const char = text.charAt(at);
286
+ if (quote !== undefined) {
287
+ if (quote === '"' && char === "\\") {
288
+ at += 1;
289
+ } else if (char === quote) {
290
+ quote = undefined;
291
+ }
292
+ } else if (at === 0 && (char === "'" || char === '"')) {
293
+ quote = char;
294
+ } else if (char === "[" || char === "{") {
295
+ return -1;
296
+ } else if (char === ":" && (at + 1 === text.length || /\s/u.test(text.charAt(at + 1)))) {
297
+ return at;
298
+ }
299
+ }
300
+ return -1;
301
+ }
302
+
303
+ /** Whether one line's content is a block sequence item. */
304
+ function isItem(text: string): boolean {
305
+ return text === "-" || text.startsWith("- ");
306
+ }
307
+
308
+ /** Whether one line's content opens a block collection: a sequence item or a mapping key. */
309
+ function opensCollection(text: string): boolean {
310
+ return isItem(text) || keyEnd(text) >= 0;
311
+ }
312
+
313
+ /** Reads the document's lines as one value. */
314
+ class Reader {
315
+ private at = 0;
316
+ private readonly lines: Line[];
317
+ /** Every line of the document, as written. */
318
+ private readonly raw: readonly string[];
319
+ /** The whole document, its lines joined by `\n`, and where each line starts in it. */
320
+ private readonly text: string;
321
+ private readonly starts: readonly number[];
322
+ /** Whether a line break ends the document's last line. */
323
+ private readonly broken: boolean;
324
+ private readonly anchors = new Map<string, unknown>();
325
+ /** The size the aliases read so far add, against {@link ALIAS_BUDGET}. */
326
+ private expanded = 0;
327
+ private readonly sizes = new WeakMap<object, number>();
328
+
329
+ constructor(lines: Line[], raw: readonly string[], broken: boolean) {
330
+ this.lines = lines;
331
+ this.raw = raw;
332
+ this.broken = broken;
333
+ this.text = raw.join("\n");
334
+ let start = 0;
335
+ this.starts = raw.map((line) => {
336
+ const here = start;
337
+ start += line.length + 1;
338
+ return here;
339
+ });
340
+ }
341
+
342
+ /** The whole document's value. */
343
+ document(): unknown {
344
+ const first = this.peek();
345
+ if (first === undefined) {
346
+ return null;
347
+ }
348
+ const value = this.block(first.indent);
349
+ if (this.at < this.lines.length) {
350
+ throw new Unsupported();
351
+ }
352
+ return value;
353
+ }
354
+
355
+ /** The next line to read; one indented with a tab is outside the subset. */
356
+ private peek(): Line | undefined {
357
+ const line = this.lines[this.at];
358
+ if (line?.tabbed === true) {
359
+ throw new Unsupported();
360
+ }
361
+ return line;
362
+ }
363
+
364
+ /** The value the anchor `name` names, its size counted against {@link ALIAS_BUDGET}. */
365
+ private alias(name: string): unknown {
366
+ if (!this.anchors.has(name)) {
367
+ throw new Unsupported();
368
+ }
369
+ const value = this.anchors.get(name);
370
+ this.expanded += sizeOf(value, this.sizes);
371
+ if (this.expanded > ALIAS_BUDGET) {
372
+ throw new Unsupported();
373
+ }
374
+ return value;
375
+ }
376
+
377
+ /** Moves past every line before row `end`. */
378
+ private skipTo(end: number): void {
379
+ while ((this.lines[this.at]?.row ?? end) < end) {
380
+ this.at += 1;
381
+ }
382
+ }
383
+
384
+ /** The block node whose first line is the next one, at `indent`. */
385
+ private block(indent: number): unknown {
386
+ const line = this.peek();
387
+ if (line === undefined) {
388
+ return null;
389
+ }
390
+ if (isItem(line.text)) {
391
+ return this.sequence(indent);
392
+ }
393
+ if (keyEnd(line.text) >= 0) {
394
+ return this.mapping(indent);
395
+ }
396
+ this.at += 1;
397
+ return this.value(line, line.indent, line.text, indent - 1, false);
398
+ }
399
+
400
+ /** The node below a key or an item marker with nothing after it: the deeper block, else null. */
401
+ private below(indent: number, sequenceAtIndent: boolean): unknown {
402
+ const next = this.peek();
403
+ if (
404
+ next === undefined ||
405
+ !(next.indent > indent || (sequenceAtIndent && next.indent === indent && isItem(next.text)))
406
+ ) {
407
+ return null;
408
+ }
409
+ if (opensCollection(next.text)) {
410
+ return this.block(next.indent);
411
+ }
412
+ this.at += 1;
413
+ return this.value(next, next.indent, next.text, indent, false);
414
+ }
415
+
416
+ /**
417
+ * The node `text` starts, at column `column` of `line`, which is read, after a key or an item
418
+ * marker at `indent`: an alias, a node an anchor names, a block scalar, a quoted scalar, or a
419
+ * flow collection or plain scalar whose text runs over the next lines indented deeper.
420
+ */
421
+ private value(
422
+ line: Line,
423
+ column: number,
424
+ text: string,
425
+ indent: number,
426
+ sequenceAtIndent: boolean,
427
+ ): unknown {
428
+ const alias = ALIAS.exec(text);
429
+ if (alias !== null) {
430
+ const [whole, name = ""] = alias;
431
+ if (whole !== text) {
432
+ throw new Unsupported();
433
+ }
434
+ return this.alias(name);
435
+ }
436
+ const anchor = ANCHOR.exec(text);
437
+ if (anchor !== null) {
438
+ const rest = text.slice(anchor[0].length);
439
+ if (opensCollection(rest)) {
440
+ throw new Unsupported();
441
+ }
442
+ const value =
443
+ rest === ""
444
+ ? this.below(indent, sequenceAtIndent)
445
+ : this.value(line, column + anchor[0].length, rest, indent, sequenceAtIndent);
446
+ this.anchors.set(anchor[1] ?? "", value);
447
+ return value;
448
+ }
449
+ const header = BLOCK_HEADER.exec(text);
450
+ if (header !== null) {
451
+ return this.blockScalar(header, line.row, indent);
452
+ }
453
+ if (/^['"[{]/u.test(text)) {
454
+ const start = (this.starts[line.row] ?? 0) + column;
455
+ const read = this.flow(start);
456
+ this.close(line.row, start, read.end);
457
+ return read.value;
458
+ }
459
+ const parts = [line];
460
+ for (let next = this.peek(); next !== undefined && next.indent > indent; next = this.peek()) {
461
+ parts.push(next);
462
+ this.at += 1;
463
+ }
464
+ const texts = [text, ...parts.slice(1).map((one) => one.text)];
465
+ const lines = parts.flatMap((part, at) => {
466
+ const after = parts[at + 1];
467
+ const between = after === undefined ? [] : this.raw.slice(part.row + 1, after.row);
468
+ // A comment ends a plain scalar, so a line of it after one is outside YAML.
469
+ if (after !== undefined && (part.commented || between.some((one) => one.trim() !== ""))) {
470
+ throw new Unsupported();
471
+ }
472
+ return [texts[at] ?? "", ...between.map(() => "")];
473
+ });
474
+ return scalar(foldedLines(lines, "plain"));
475
+ }
476
+
477
+ /**
478
+ * The block scalar whose header is on row `row`, under a node at `indent`: its lines are those
479
+ * after it that are blank or indented to its content's indentation, the indentation indicator's
480
+ * or else its first line's that is not blank, with its chomping read as YAML reads it.
481
+ */
482
+ private blockScalar(header: RegExpExecArray, row: number, indent: number): string {
483
+ const [, style] = header;
484
+ const digit = header[2] ?? header[5] ?? "";
485
+ const chomp = header[3] ?? header[4] ?? "";
486
+ const first = this.raw.slice(row + 1).find((one) => one.trim() !== "");
487
+ const content =
488
+ digit === ""
489
+ ? first === undefined || spacesOf(first) <= indent
490
+ ? Number.POSITIVE_INFINITY
491
+ : spacesOf(first)
492
+ : Math.max(indent, 0) + Number(digit);
493
+ let end = row + 1;
494
+ while (
495
+ end < this.raw.length &&
496
+ ((this.raw[end] ?? "").trim() === "" || spacesOf(this.raw[end] ?? "") >= content)
497
+ ) {
498
+ end += 1;
499
+ }
500
+ this.skipTo(end);
501
+ const body = this.raw
502
+ .slice(row + 1, end)
503
+ .map((one) => one.slice(Math.min(content, spacesOf(one))));
504
+ const value = foldedLines(body, style === ">" ? ">" : "|");
505
+ const unbroken = end === this.raw.length && !this.broken ? 1 : 0;
506
+ const last = body.findLastIndex((one) => one.trim() !== "");
507
+ const breaks = Math.max(body.length - Math.max(last, 0) - unbroken, 0);
508
+ const kept = chomp === "+" ? breaks : chomp === "-" || value === "" ? 0 : Math.min(breaks, 1);
509
+ return `${value}${"\n".repeat(kept)}`;
510
+ }
511
+
512
+ /**
513
+ * Reads one flow node of the document's text from `start`, answering its value and where it
514
+ * ends: a flow collection, whose lines and comments it reads through, an alias, a node an
515
+ * anchor names, a quoted scalar, or a plain scalar, its line breaks folded.
516
+ */
517
+ private flow(start: number): { readonly value: unknown; readonly end: number } {
518
+ const text = this.text;
519
+ let at = start;
520
+ const skip = (): void => {
521
+ for (;;) {
522
+ while (at < text.length && /\s/u.test(text.charAt(at))) {
523
+ at += 1;
524
+ }
525
+ if (text.charAt(at) !== "#" || !/\s/u.test(text.charAt(at - 1))) {
526
+ return;
527
+ }
528
+ const lineEnd = text.indexOf("\n", at);
529
+ at = lineEnd < 0 ? text.length : lineEnd;
530
+ }
531
+ };
532
+ skip();
533
+ const open = text.charAt(at);
534
+ const alias = open === "*" ? matchAt(ALIAS, text, at) : null;
535
+ if (alias !== null) {
536
+ const [whole, name = ""] = alias;
537
+ return { value: this.alias(name), end: at + whole.length };
538
+ }
539
+ const anchor = open === "&" ? matchAt(ANCHOR, text, at) : null;
540
+ if (anchor !== null) {
541
+ const read = this.flow(at + anchor[0].length);
542
+ this.anchors.set(anchor[1] ?? "", read.value);
543
+ return read;
544
+ }
545
+ if (open === "[" || open === "{") {
546
+ const close = open === "[" ? "]" : "}";
547
+ const items: unknown[] = [];
548
+ const object: Record<string, unknown> = {};
549
+ at += 1;
550
+ for (;;) {
551
+ skip();
552
+ if (text.charAt(at) === close) {
553
+ return { value: open === "[" ? items : object, end: at + 1 };
554
+ }
555
+ const item = this.flow(at);
556
+ at = item.end;
557
+ skip();
558
+ if (open === "{") {
559
+ if (text.charAt(at) !== ":" || typeof item.value !== "string") {
560
+ throw new Unsupported();
561
+ }
562
+ const held = this.flow(at + 1);
563
+ setMember(object, item.value, held.value);
564
+ at = held.end;
565
+ skip();
566
+ } else {
567
+ items.push(item.value);
568
+ }
569
+ if (text.charAt(at) === ",") {
570
+ at += 1;
571
+ } else if (text.charAt(at) !== close) {
572
+ throw new Unsupported();
573
+ }
574
+ }
575
+ }
576
+ if (open === "'" || open === '"') {
577
+ const read = quotedScalar(text, at);
578
+ if (read === undefined) {
579
+ throw new Unsupported();
580
+ }
581
+ return read;
582
+ }
583
+ let end = at;
584
+ while (
585
+ end < text.length &&
586
+ !",]}".includes(text.charAt(end)) &&
587
+ !(text.charAt(end) === ":" && /[\s,\]}]/u.test(text.charAt(end + 1) || " ")) &&
588
+ !(text.charAt(end) === "#" && /\s/u.test(text.charAt(end - 1)))
589
+ ) {
590
+ end += 1;
591
+ }
592
+ const lines = text
593
+ .slice(at, end)
594
+ .split("\n")
595
+ .map((one) => one.trim());
596
+ return { value: scalar(foldedLines(lines, "plain")), end };
597
+ }
598
+
599
+ /**
600
+ * Ends the flow node read from `start`, on row `row`, to `end`: the rest of its last line must
601
+ * be blank or a comment, and every line it runs over is read.
602
+ */
603
+ private close(row: number, start: number, end: number): void {
604
+ const lineEnd = this.text.indexOf("\n", end);
605
+ const tail = this.text.slice(end, lineEnd < 0 ? this.text.length : lineEnd);
606
+ if (!/^(?:\s*$|\s+#)/u.test(tail)) {
607
+ throw new Unsupported();
608
+ }
609
+ this.skipTo(row + (this.text.slice(start, end).match(/\n/gu)?.length ?? 0) + 1);
610
+ }
611
+
612
+ private mapping(indent: number): Record<string, unknown> {
613
+ const object: Record<string, unknown> = {};
614
+ for (let line = this.peek(); line?.indent === indent; line = this.peek()) {
615
+ const end = keyEnd(line.text);
616
+ if (end < 0) {
617
+ throw new Unsupported();
618
+ }
619
+ const key = scalar(line.text.slice(0, end));
620
+ if (typeof key === "object" && key !== null) {
621
+ throw new Unsupported();
622
+ }
623
+ this.at += 1;
624
+ const after = line.text.slice(end + 1);
625
+ const inline = after.trim();
626
+ const column = line.indent + end + 1 + after.length - after.trimStart().length;
627
+ setMember(
628
+ object,
629
+ String(key),
630
+ inline === "" ? this.below(indent, true) : this.value(line, column, inline, indent, true),
631
+ );
632
+ }
633
+ const left = this.peek();
634
+ if (left !== undefined && left.indent > indent) {
635
+ throw new Unsupported();
636
+ }
637
+ return object;
638
+ }
639
+
640
+ private sequence(indent: number): unknown[] {
641
+ const items: unknown[] = [];
642
+ for (let line = this.peek(); line?.indent === indent && isItem(line.text); line = this.peek()) {
643
+ const content = line.text.slice(1);
644
+ const inline = content.trimStart();
645
+ const column = indent + 1 + content.length - inline.length;
646
+ if (opensCollection(inline)) {
647
+ // A node on the item's own line continues at its own column: read it as a block there.
648
+ this.lines[this.at] = { ...line, indent: column, text: inline };
649
+ items.push(this.block(column));
650
+ } else {
651
+ this.at += 1;
652
+ items.push(
653
+ inline === ""
654
+ ? this.below(indent, false)
655
+ : this.value(line, column, inline, indent, false),
656
+ );
657
+ }
658
+ }
659
+ const left = this.peek();
660
+ if (left !== undefined && left.indent > indent) {
661
+ throw new Unsupported();
662
+ }
663
+ return items;
664
+ }
665
+ }
666
+
667
+ /** The JSON value one YAML document's text denotes, or undefined where it lies outside the subset. */
668
+ export function yamlDocument(text: string): unknown {
669
+ const raw = text.replace(/^\uFEFF/u, "").split(/\r?\n/u);
670
+ const broken = raw.length > 1 && raw.at(-1) === "";
671
+ if (broken) {
672
+ raw.pop();
673
+ }
674
+ const lines: Line[] = [];
675
+ for (const [row, line] of raw.entries()) {
676
+ const content = withoutComment(line).trimEnd();
677
+ if (/^(?:---|\.\.\.)(?:\s|$)/u.test(content) || content.startsWith("%")) {
678
+ if (content === "---" && lines.length === 0) {
679
+ continue;
680
+ }
681
+ return undefined;
682
+ }
683
+ const trimmed = content.trimStart();
684
+ if (trimmed !== "") {
685
+ lines.push({
686
+ indent: content.length - trimmed.length,
687
+ text: trimmed,
688
+ row,
689
+ tabbed: /^ *\t/u.test(line),
690
+ commented: content !== line.trimEnd(),
691
+ });
692
+ }
693
+ }
694
+ try {
695
+ return new Reader(lines, raw, broken).document();
696
+ } catch (error) {
697
+ if (error instanceof Unsupported) {
698
+ return undefined;
699
+ }
700
+ throw error;
701
+ }
702
+ }
703
+
704
+ /** What one top-level key of a YAML document holds, read by {@link yamlKey}. */
705
+ type YamlKey =
706
+ | { readonly kind: "value"; readonly value: unknown }
707
+ | { readonly kind: "absent" }
708
+ | { readonly kind: "refused"; readonly reason: string };
709
+
710
+ /** The key one line of the document's top-level mapping opens, or undefined where it opens none. */
711
+ function topLevelKey(raw: string): string | undefined {
712
+ const content = withoutComment(raw).trimEnd();
713
+ const end = /^\s/u.test(content) ? -1 : keyEnd(content);
714
+ if (end < 0) {
715
+ return undefined;
716
+ }
717
+ try {
718
+ const key = scalar(content.slice(0, end));
719
+ return typeof key === "object" && key !== null ? undefined : String(key);
720
+ } catch {
721
+ return undefined;
722
+ }
723
+ }
724
+
725
+ /**
726
+ * The value of one key of a document's top-level block mapping, read from that key's lines
727
+ * alone, so a document whose other keys lie outside the subset still yields it. A key
728
+ * declared twice, or one whose own lines lie outside the subset or name an anchor written
729
+ * under another key, is refused.
730
+ */
731
+ export function yamlKey(text: string, key: string): YamlKey {
732
+ const lines = text.replace(/^\uFEFF/u, "").split(/\r?\n/u);
733
+ const starts = lines.flatMap((line, at) => (topLevelKey(line) === undefined ? [] : [at]));
734
+ const named = starts.filter((at) => topLevelKey(lines[at] ?? "") === key);
735
+ const [start] = named;
736
+ if (start === undefined) {
737
+ return { kind: "absent" };
738
+ }
739
+ if (named.length > 1) {
740
+ return { kind: "refused", reason: `the ${key} key is declared more than once` };
741
+ }
742
+ const end = starts.find((at) => at > start) ?? lines.length;
743
+ const value = yamlDocument(lines.slice(start, end).join("\n"));
744
+ return isRecord(value)
745
+ ? { kind: "value", value: value[key] }
746
+ : { kind: "refused", reason: `the value of ${key} is outside the YAML subset read here` };
747
+ }