@prestack/thread 0.1.3 → 0.1.5

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 (270) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/dist/core/actor.d.ts +1 -1
  3. package/dist/core/actor.js +5 -4
  4. package/dist/core/actor.ts +5 -4
  5. package/dist/core/attach.d.ts +0 -7
  6. package/dist/core/attach.js +38 -67
  7. package/dist/core/attach.ts +34 -80
  8. package/dist/core/chain.js +13 -17
  9. package/dist/core/chain.ts +11 -15
  10. package/dist/core/condition.d.ts +4 -4
  11. package/dist/core/condition.js +2 -2
  12. package/dist/core/descend.d.ts +4 -2
  13. package/dist/core/descend.js +91 -23
  14. package/dist/core/descend.ts +90 -21
  15. package/dist/core/errors/catalogue.d.ts +52 -0
  16. package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
  17. package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
  18. package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
  19. package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
  20. package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
  21. package/dist/core/errors/namespaces.d.ts +3 -0
  22. package/dist/core/errors/namespaces.js +15 -0
  23. package/dist/core/errors/namespaces.ts +19 -0
  24. package/dist/core/finding.js +2 -2
  25. package/dist/core/finding.ts +1 -2
  26. package/dist/core/init.d.ts +1 -1
  27. package/dist/core/init.js +51 -5
  28. package/dist/core/init.ts +58 -5
  29. package/dist/core/invoked.d.ts +1 -1
  30. package/dist/core/invoked.js +3 -1
  31. package/dist/core/invoked.ts +5 -2
  32. package/dist/core/round.js +36 -22
  33. package/dist/core/round.ts +26 -9
  34. package/dist/core/rows.d.ts +4 -0
  35. package/dist/core/rows.js +31 -2
  36. package/dist/core/rows.ts +33 -1
  37. package/dist/core/scope.d.ts +1 -0
  38. package/dist/core/scope.js +1 -0
  39. package/dist/core/scope.ts +2 -0
  40. package/dist/core/thread.d.ts +3 -3
  41. package/dist/core/tree.d.ts +4 -1
  42. package/dist/core/tree.js +26 -6
  43. package/dist/core/tree.ts +29 -6
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.js +3 -1
  46. package/dist/core/types.ts +2 -1
  47. package/dist/derivation/attach.js +3 -2
  48. package/dist/derivation/attach.ts +2 -1
  49. package/dist/derivation/generators/build.js +6 -6
  50. package/dist/derivation/generators/build.ts +1 -1
  51. package/dist/derivation/generators/check.js +1 -1
  52. package/dist/derivation/generators/determinism.js +1 -1
  53. package/dist/derivation/generators/release.js +60 -47
  54. package/dist/derivation/generators/release.ts +66 -43
  55. package/dist/derivation/release.d.ts +7 -3
  56. package/dist/derivation/release.js +14 -1
  57. package/dist/derivation/release.ts +22 -2
  58. package/dist/integrations/attach.d.ts +4 -0
  59. package/dist/integrations/attach.js +37 -56
  60. package/dist/integrations/attach.ts +54 -60
  61. package/dist/integrations/cli.d.ts +37 -0
  62. package/dist/integrations/cli.js +440 -0
  63. package/dist/integrations/cli.ts +543 -0
  64. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  65. package/dist/integrations/disk.js +435 -0
  66. package/dist/integrations/disk.ts +465 -0
  67. package/dist/integrations/generators/read.js +6 -6
  68. package/dist/integrations/generators/read.ts +4 -4
  69. package/dist/integrations/generators/vault.js +10 -11
  70. package/dist/integrations/generators/vault.ts +4 -5
  71. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  72. package/dist/integrations/https.js +170 -0
  73. package/dist/integrations/https.ts +216 -0
  74. package/dist/integrations/pglite.d.ts +26 -0
  75. package/dist/integrations/pglite.js +143 -0
  76. package/dist/integrations/pglite.ts +206 -0
  77. package/dist/integrations/secret.d.ts +8 -2
  78. package/dist/integrations/secret.js +3 -5
  79. package/dist/integrations/secret.ts +10 -6
  80. package/dist/integrations/target.d.ts +155 -14
  81. package/dist/integrations/target.js +171 -50
  82. package/dist/integrations/target.ts +377 -61
  83. package/dist/integrations/vault.d.ts +1 -0
  84. package/dist/integrations/vault.js +20 -5
  85. package/dist/integrations/vault.ts +17 -5
  86. package/dist/integrity/attach.js +8 -77
  87. package/dist/integrity/attach.ts +7 -76
  88. package/dist/integrity/check-threads.js +5 -5
  89. package/dist/integrity/check-threads.ts +2 -2
  90. package/dist/integrity/constraint.d.ts +0 -1
  91. package/dist/integrity/constraint.js +267 -366
  92. package/dist/integrity/constraint.ts +267 -375
  93. package/dist/integrity/generators/compiler.js +14 -17
  94. package/dist/integrity/generators/compiler.ts +10 -15
  95. package/dist/integrity/generators/gate.js +15 -15
  96. package/dist/integrity/generators/gate.ts +10 -10
  97. package/dist/integrity/generators/issues.js +7 -7
  98. package/dist/integrity/generators/publish.js +39 -57
  99. package/dist/integrity/generators/publish.ts +44 -55
  100. package/dist/integrity/generators/trial.d.ts +3 -3
  101. package/dist/integrity/generators/trial.js +133 -165
  102. package/dist/integrity/generators/trial.ts +145 -169
  103. package/dist/integrity/lint.js +9 -13
  104. package/dist/integrity/lint.ts +7 -10
  105. package/dist/integrity/lsp/bin.js +5 -4
  106. package/dist/integrity/lsp/bin.ts +3 -1
  107. package/dist/integrity/lsp/diagnostics.js +3 -3
  108. package/dist/integrity/lsp/diagnostics.ts +1 -1
  109. package/dist/integrity/lsp/lint.js +19 -30
  110. package/dist/integrity/lsp/lint.ts +16 -32
  111. package/dist/integrity/lsp/server.js +3 -3
  112. package/dist/integrity/slopnator.d.ts +3 -13
  113. package/dist/integrity/slopnator.js +59 -38
  114. package/dist/integrity/slopnator.ts +54 -38
  115. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  116. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  117. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  118. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  119. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  120. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  122. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  123. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  124. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  125. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  126. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  127. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  128. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  129. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  130. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  131. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  132. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  133. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  134. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  135. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  136. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  138. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  139. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  140. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  141. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  142. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  143. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  144. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  145. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  146. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  147. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  149. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  150. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  151. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  152. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  153. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  155. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  156. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  157. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  158. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  159. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  160. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  161. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  162. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  164. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  165. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  166. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  167. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  168. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  169. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  170. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  171. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  172. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  173. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  174. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  175. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  176. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  177. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  178. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  180. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  181. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  182. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  183. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  184. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  185. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  186. package/dist/integrity/sweep.d.ts +1 -0
  187. package/dist/integrity/sweep.js +105 -42
  188. package/dist/integrity/sweep.ts +115 -42
  189. package/dist/knowledge/attach.js +4 -3
  190. package/dist/knowledge/attach.ts +3 -2
  191. package/dist/knowledge/generators/docsgen.js +8 -6
  192. package/dist/knowledge/generators/docsgen.ts +4 -2
  193. package/dist/knowledge/generators/glossary.js +4 -4
  194. package/dist/knowledge/generators/glossary.ts +1 -1
  195. package/dist/operations/access.d.ts +23 -0
  196. package/dist/operations/access.js +28 -1
  197. package/dist/operations/access.ts +43 -0
  198. package/dist/operations/attach.js +33 -113
  199. package/dist/operations/attach.ts +51 -117
  200. package/dist/operations/capabilities.d.ts +14 -5
  201. package/dist/operations/capabilities.js +13 -4
  202. package/dist/operations/capabilities.ts +27 -8
  203. package/dist/operations/dispatch.d.ts +8 -2
  204. package/dist/operations/dispatch.js +128 -185
  205. package/dist/operations/dispatch.ts +138 -191
  206. package/dist/operations/error-results.d.ts +1 -1
  207. package/dist/operations/error-results.js +2 -2
  208. package/dist/operations/error-results.ts +2 -2
  209. package/dist/operations/generators/capability.d.ts +11 -1
  210. package/dist/operations/generators/capability.js +481 -224
  211. package/dist/operations/generators/capability.ts +562 -233
  212. package/dist/operations/generators/compile.js +66 -39
  213. package/dist/operations/generators/compile.ts +78 -39
  214. package/dist/operations/generators/permits.js +11 -12
  215. package/dist/operations/generators/permits.ts +3 -4
  216. package/dist/operations/generators/seed.d.ts +1 -0
  217. package/dist/operations/generators/seed.js +19 -17
  218. package/dist/operations/generators/seed.ts +22 -14
  219. package/dist/operations/generators/trial.d.ts +6 -8
  220. package/dist/operations/generators/trial.js +200 -70
  221. package/dist/operations/generators/trial.ts +230 -93
  222. package/dist/operations/nodes.d.ts +15 -16
  223. package/dist/operations/nodes.ts +16 -17
  224. package/dist/operations/operations.d.ts +40 -4
  225. package/dist/operations/operations.js +1 -0
  226. package/dist/operations/operations.ts +61 -4
  227. package/dist/operations/result.d.ts +2 -2
  228. package/dist/operations/result.ts +2 -2
  229. package/package.json +1 -4
  230. package/dist/core/answers.d.ts +0 -171
  231. package/dist/core/answers.js +0 -789
  232. package/dist/core/answers.ts +0 -999
  233. package/dist/core/client.d.ts +0 -63
  234. package/dist/core/client.js +0 -249
  235. package/dist/core/client.ts +0 -289
  236. package/dist/core/files.js +0 -171
  237. package/dist/core/files.ts +0 -222
  238. package/dist/integrations/module.d.ts +0 -6
  239. package/dist/integrations/module.js +0 -153
  240. package/dist/integrations/module.ts +0 -191
  241. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  242. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  243. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  244. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  245. package/dist/operations/cost.d.ts +0 -14
  246. package/dist/operations/cost.js +0 -37
  247. package/dist/operations/cost.ts +0 -58
  248. package/dist/operations/errors/errors.d.ts +0 -52
  249. package/dist/operations/http.js +0 -124
  250. package/dist/operations/http.ts +0 -169
  251. package/dist/operations/memory.d.ts +0 -104
  252. package/dist/operations/memory.js +0 -365
  253. package/dist/operations/memory.ts +0 -470
  254. package/dist/operations/metering.d.ts +0 -55
  255. package/dist/operations/metering.js +0 -247
  256. package/dist/operations/metering.ts +0 -328
  257. package/dist/operations/queue-policy.d.ts +0 -3
  258. package/dist/operations/queue-policy.js +0 -8
  259. package/dist/operations/queue-policy.ts +0 -10
  260. package/dist/operations/seams.d.ts +0 -239
  261. package/dist/operations/seams.js +0 -45
  262. package/dist/operations/seams.ts +0 -364
  263. package/dist/operations/store.d.ts +0 -14
  264. package/dist/operations/store.js +0 -1
  265. package/dist/operations/store.ts +0 -29
  266. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  267. /package/dist/{operations → core}/errors/classes.js +0 -0
  268. /package/dist/{operations → core}/errors/classes.ts +0 -0
  269. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  270. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
@@ -1,9 +1,5 @@
1
- import * as disk from 'node:fs'
2
- import { answeredBy, existsSync, readFileSync, type Answering, type Kind, type Listed } from '../../core/files.ts'
3
- import { basename, dirname, join, resolve } from 'node:path'
4
- import { pathToFileURL } from 'node:url'
5
-
6
- import { exit, invoked } from '../../core/invoked.ts'
1
+ import { answeredBy, standing } from '../../integrations/disk.ts'
2
+ import { resolve } from 'node:path'
7
3
 
8
4
  import type { Linter as LinterType, Rule } from 'eslint'
9
5
  import { Linter } from 'eslint'
@@ -11,12 +7,9 @@ import { Linter } from 'eslint'
11
7
  import type { Constraint, Proof, Sourced } from '../constraint.ts'
12
8
 
13
9
  const TREE = findTree()
14
- import { CONSTRAINTS } from '../constraint.ts'
15
- import { NOT_WRITTEN, ROOT, governs, load, proofsOf, reads, rules, slopnatorName, sweeps, write, type Byproduct, type Slopnator } from '../slopnator.ts'
10
+ import { NOT_WRITTEN, ROOT, governs, proofsOf, reads, slopnatorName, sweeps, visits, type Slopnator } from '../slopnator.ts'
16
11
  import { forget } from '../slopnators/sl_unthreaded.ts'
17
- import { findTree, inTree } from '../../core/tree.ts'
18
-
19
- export const TRIALS_PATH = `${findTree().name}/_generated/trials.ts`
12
+ import { findTree } from '../../core/tree.ts'
20
13
 
21
14
  const PROBE = 'probe.tsx'
22
15
 
@@ -59,6 +52,7 @@ export interface Run {
59
52
  readonly passed: number
60
53
  readonly failed: number
61
54
  readonly measured: number
55
+ readonly spent: ReadonlyMap<string, number>
62
56
  }
63
57
 
64
58
  const canonical = (at: string): string => at.replace(/^threads\//, `${TREE.name}/`)
@@ -112,14 +106,6 @@ export function deriveTrials(all: readonly Constraint[], slopnators: ReadonlyMap
112
106
  return { trials: trials.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) }
113
107
  }
114
108
 
115
- export function trialsByproduct(derivation: Derivation): Byproduct {
116
- return {
117
- path: TRIALS_PATH,
118
- content: `export const RULE_TRIALS = ${JSON.stringify(derivation.trials, null, 2)} as const
119
- `,
120
- }
121
- }
122
-
123
109
  function config(rule: Constraint, module: Rule.RuleModule, parser: unknown): Parameters<LinterType['verify']>[1] {
124
110
  return [
125
111
  {
@@ -134,90 +120,6 @@ function config(rule: Constraint, module: Rule.RuleModule, parser: unknown): Par
134
120
  ] as unknown as Parameters<LinterType['verify']>[1]
135
121
  }
136
122
 
137
- const flat = (at: string): string => (at.length > 1 && at.endsWith('/') ? at.replace(/\/+$/, '') : at)
138
-
139
- const under = (at: string): boolean => at.startsWith(ROOT) || `${at}/` === ROOT
140
-
141
- const own = (at: string): boolean => under(at) && !at.slice(ROOT.length).split('/').includes('node_modules')
142
-
143
- function beside(at: string): readonly disk.Dirent[] {
144
- try {
145
- return disk.readdirSync(at, { withFileTypes: true })
146
- } catch {
147
- return []
148
- }
149
- }
150
-
151
- function outside(at: string): Kind {
152
- const one = disk.statSync(at, { throwIfNoEntry: false })
153
- if (one === undefined) return 'none'
154
- return one.isFile() ? 'file' : one.isDirectory() ? 'folder' : 'other'
155
- }
156
-
157
- const GONE = 'is not in the scenario this trial stands, and while it runs the scenario is the whole tree.'
158
-
159
- function answering(files: readonly Sourced[]): Answering {
160
- const held = new Map<string, string>(files.map((one) => [resolve(ROOT, one.at), one.source]))
161
- const folders = new Set<string>()
162
- const parents = (at: string): void => {
163
- for (let up = dirname(at); under(`${up}/`); up = dirname(up)) folders.add(up)
164
- }
165
- for (const at of held.keys()) parents(at)
166
-
167
- const answers: Answering = {
168
- kind: (at) => (held.has(at) ? 'file' : folders.has(at) ? 'folder' : own(at) ? 'none' : outside(at)),
169
- read: (at) => {
170
- const said = held.get(at)
171
- if (said !== undefined) return said
172
- if (own(at)) throw new Error(`${at} ${GONE}`)
173
- return disk.readFileSync(at, 'utf8')
174
- },
175
- write: (at, said) => {
176
- held.set(at, said)
177
- parents(at)
178
- },
179
- list: (at) => {
180
- const inside = new Map<string, Listed>()
181
- const found = own(at) ? [] : folders.has(at) ? beside(at) : disk.readdirSync(at, { withFileTypes: true })
182
- for (const one of found) inside.set(one.name, { name: one.name, kind: one.isDirectory() ? 'folder' : 'file' })
183
- for (const path of folders) if (dirname(path) === at) inside.set(basename(path), { name: basename(path), kind: 'folder' })
184
- for (const path of held.keys()) if (dirname(path) === at) inside.set(basename(path), { name: basename(path), kind: 'file' })
185
- return [...inside.values()]
186
- },
187
- stat: (at) => {
188
- const said = held.get(at)
189
- if (said !== undefined) return { size: said.length, modified: 0 }
190
- if (folders.has(at)) return { size: 0, modified: 0 }
191
- if (own(at)) throw new Error(`${at} ${GONE}`)
192
- const one = disk.statSync(at)
193
- return { size: one.size, modified: one.mtimeMs }
194
- },
195
- remove: (at) => {
196
- held.delete(at)
197
- folders.delete(at)
198
- },
199
- real: (at) => (own(at) || held.has(at) || folders.has(at) ? at : disk.realpathSync(at)),
200
- fetch: () => {
201
- throw new Error('a trial answers orders against a tree it made up, so there is nowhere here to install anything into.')
202
- },
203
- }
204
-
205
- return {
206
- kind: (at) => answers.kind(flat(at)),
207
- read: (at) => answers.read(flat(at)),
208
- write: (at, said) => {
209
- answers.write(flat(at), said)
210
- },
211
- list: (at) => answers.list(flat(at)),
212
- stat: (at) => answers.stat(flat(at)),
213
- remove: (at) => {
214
- answers.remove(flat(at))
215
- },
216
- real: (at) => answers.real(flat(at)),
217
- fetch: (at) => answers.fetch(flat(at)),
218
- }
219
- }
220
-
221
123
  export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule, parser: unknown, slopnator?: Slopnator): Outcome {
222
124
  const linter = new Linter()
223
125
  const failures: string[] = []
@@ -237,7 +139,7 @@ export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule
237
139
  let parsed = false
238
140
  let satisfied = false
239
141
 
240
- answeredBy(answering(trial.scenario.files))
142
+ answeredBy(standing(ROOT, new Map<string, string>(trial.scenario.files.map((one) => [resolve(ROOT, one.at), one.source]))))
241
143
  forget()
242
144
 
243
145
  try {
@@ -287,6 +189,138 @@ export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule
287
189
  return { id: trial.id, rule: rule.id, passed: failures.length === 0, parsed, failures }
288
190
  }
289
191
 
192
+ const judged = (who: readonly Constraint[], rules: Readonly<Record<string, Rule.RuleModule>>, parser: unknown): Parameters<LinterType['verify']>[1] =>
193
+ [
194
+ {
195
+ files: ['**/*.ts', '**/*.tsx'],
196
+ languageOptions: {
197
+ parser,
198
+ parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
199
+ },
200
+ plugins: { rule: { rules: Object.fromEntries(who.map((one) => [one.id, rules[one.id] as Rule.RuleModule])) } },
201
+ rules: Object.fromEntries(who.map((one) => ['rule/' + one.id, 'error'])),
202
+ },
203
+ ] as unknown as Parameters<LinterType['verify']>[1]
204
+
205
+ const failed = (thrown: unknown): string => 'could not run on it: ' + (thrown instanceof Error ? thrown.message : String(thrown))
206
+
207
+ const parseable = (source: string, at: string, parser: unknown): ((said: string) => string) =>
208
+ WRAPPINGS.find((wrap) => !new Linter().verify(wrap(source), judged([], {}, parser), at).some((one) => one.fatal === true)) ??
209
+ ((said: string): string => said)
210
+
211
+ function refusals(
212
+ scenario: Scenario,
213
+ source: string,
214
+ judges: readonly Constraint[],
215
+ rules: Readonly<Record<string, Rule.RuleModule>>,
216
+ parser: unknown,
217
+ slopnators: ReadonlyMap<string, Slopnator>,
218
+ ): ReadonlyMap<string, ReadonlySet<string>> {
219
+ const found = new Map<string, Set<string>>()
220
+ const note = (who: string, said: string): void => {
221
+ const held = found.get(who) ?? new Set<string>()
222
+ held.add(said)
223
+ found.set(who, held)
224
+ }
225
+ const walking = judges.filter((one) => {
226
+ const held = slopnators.get(slopnatorName(one.id))
227
+ return held !== undefined && visits(held) && rules[one.id] !== undefined
228
+ })
229
+ const linted = (who: readonly Constraint[]): void => {
230
+ const messages = new Linter().verify(source, judged(who, rules, parser), scenario.at)
231
+ if (messages.some((one) => one.fatal === true)) return
232
+ for (const one of messages) {
233
+ const by = String(one.ruleId ?? '').replace('rule/', '')
234
+ if (by !== '') note(by, one.message)
235
+ }
236
+ }
237
+
238
+ const files = scenario.files.map((one) => (one.at === scenario.at ? { at: one.at, source } : one))
239
+ answeredBy(standing(ROOT, new Map<string, string>(files.map((one) => [resolve(ROOT, one.at), one.source]))))
240
+ forget()
241
+ try {
242
+ try {
243
+ linted(walking)
244
+ } catch {
245
+ for (const one of walking) {
246
+ try {
247
+ linted([one])
248
+ } catch (thrown) {
249
+ note(one.id, failed(thrown))
250
+ }
251
+ }
252
+ }
253
+ for (const one of judges) {
254
+ const held = slopnators.get(slopnatorName(one.id))
255
+ if (held === undefined) continue
256
+ if (visits(held)) continue
257
+ try {
258
+ if (sweeps(held)) {
259
+ const said = held.holds(undefined, { filename: scenario.at, text: source, ancestors: () => [], comments: () => [] })
260
+ if (said !== false && said !== NOT_WRITTEN) note(one.id, typeof said === 'string' ? said : slopnatorName(one.id))
261
+ continue
262
+ }
263
+ if (!reads(held)) for (const said of held.checks(ROOT)) note(one.id, said)
264
+ } catch (thrown) {
265
+ note(one.id, failed(thrown))
266
+ }
267
+ }
268
+ } finally {
269
+ answeredBy(undefined)
270
+ forget()
271
+ }
272
+ return found
273
+ }
274
+
275
+ export function crossed(
276
+ trials: readonly Trial[],
277
+ all: readonly Constraint[],
278
+ rules: Readonly<Record<string, Rule.RuleModule>>,
279
+ parser: unknown,
280
+ slopnators: ReadonlyMap<string, Slopnator> = new Map<string, Slopnator>(),
281
+ spent: Map<string, number> = new Map<string, number>(),
282
+ ): readonly Outcome[] {
283
+ const found: Outcome[] = []
284
+ const blanks = new Map<string, ReadonlyMap<string, ReadonlySet<string>>>()
285
+
286
+ for (const trial of trials) {
287
+ if (trial.verdict.reports) continue
288
+ const target = trial.scenario.files.find((one) => one.at === trial.scenario.at)
289
+ if (target === undefined) continue
290
+
291
+ const judges = all.filter((one) => governs(one, trial.scenario.at))
292
+ if (!judges.some((one) => one.id !== trial.rule)) continue
293
+ const began = Date.now()
294
+
295
+ const wrap = parseable(target.source, trial.scenario.at, parser)
296
+ const emptied = JSON.stringify([wrap(''), trial.scenario.files.map((one) => (one.at === trial.scenario.at ? { at: one.at, source: '' } : one))])
297
+ const blank = blanks.get(emptied) ?? refusals(trial.scenario, wrap(''), judges, rules, parser, slopnators)
298
+ blanks.set(emptied, blank)
299
+ const said = refusals(trial.scenario, wrap(target.source), judges, rules, parser, slopnators)
300
+
301
+ spent.set(trial.rule, (spent.get(trial.rule) ?? 0) + Date.now() - began)
302
+
303
+ for (const [who, messages] of said) {
304
+ if (who === trial.rule) continue
305
+ for (const message of messages) {
306
+ if (blank.get(who)?.has(message) === true) continue
307
+ found.push({
308
+ id: trial.id + '_under_' + who.replace(/-/g, '_'),
309
+ rule: trial.rule,
310
+ passed: false,
311
+ parsed: true,
312
+ failures: [
313
+ 'the form it recommends is refused by ' + who + ', so this law tells somebody to write what another law bans. ' +
314
+ who + ' said: ' + (message.split('Found: ').pop() ?? message),
315
+ ],
316
+ })
317
+ }
318
+ }
319
+ }
320
+
321
+ return found
322
+ }
323
+
290
324
  export function runTrials(
291
325
  trials: readonly Trial[],
292
326
  all: readonly Constraint[],
@@ -295,6 +329,7 @@ export function runTrials(
295
329
  slopnators?: ReadonlyMap<string, Slopnator>,
296
330
  ): Run {
297
331
  const byName = new Map(all.map((rule) => [rule.id, rule]))
332
+ const spent = new Map<string, number>()
298
333
  const outcomes = trials.map((trial): Outcome => {
299
334
  const rule = byName.get(trial.rule)
300
335
  const module = rules[trial.rule]
@@ -304,10 +339,13 @@ export function runTrials(
304
339
  rule: trial.rule,
305
340
  passed: false,
306
341
  parsed: false,
307
- failures: [`no rule named "${trial.rule}" is registered, so this trial is about nothing — regenerate with \`thread trials\``],
342
+ failures: [`the rule "${trial.rule}" has no slopnator on disk, so this trial is about nothing — the file to write is the one its row names in \`at\``],
308
343
  }
309
344
  }
310
- return runTrial(trial, rule, module, parser, slopnators?.get(slopnatorName(rule.id)))
345
+ const began = Date.now()
346
+ const outcome = runTrial(trial, rule, module, parser, slopnators?.get(slopnatorName(rule.id)))
347
+ spent.set(trial.rule, (spent.get(trial.rule) ?? 0) + Date.now() - began)
348
+ return outcome
311
349
  })
312
350
 
313
351
  return {
@@ -315,69 +353,7 @@ export function runTrials(
315
353
  passed: outcomes.filter((outcome) => outcome.passed).length,
316
354
  failed: outcomes.filter((outcome) => !outcome.passed).length,
317
355
  measured: outcomes.filter((outcome) => outcome.parsed).length,
356
+ spent,
318
357
  }
319
358
  }
320
359
 
321
- if (invoked(import.meta.url)) {
322
- const own = await rules()
323
- const governing = own ?? CONSTRAINTS
324
- const source =
325
- own === undefined
326
- ? `the ${String(CONSTRAINTS.length)} constraint(s) this tree authors`
327
- : `the ${String(own.length)} rule(s) this tree declares — a shipped constraint is proved in the package that ships it, against the paths that package governs`
328
- const slopnators = await load(ROOT, governing)
329
- const derivation = deriveTrials(governing, slopnators)
330
- const byproduct = trialsByproduct(derivation)
331
-
332
- if (process.argv.includes('--check')) {
333
- const path = join(ROOT, byproduct.path)
334
- const current = existsSync(path) ? readFileSync(path, 'utf8') : ''
335
- const drifted = current !== byproduct.content
336
- if (drifted) console.error(`trial: ${byproduct.path} is not what the rules derive. Run \`thread trials\` and commit the result.`)
337
- console.log(`trial: ${String(derivation.trials.length)} derived from ${source}, ${String(drifted ? 1 : 0)} findings.`)
338
- exit(drifted ? 1 : 0)
339
- }
340
-
341
- if (process.argv.includes('--try')) {
342
- const at = process.argv.indexOf('--rule')
343
- const only = at === -1 ? undefined : (process.argv[at + 1] ?? '').split(',').map((one) => one.trim()).filter(Boolean)
344
- const unknown = (only ?? []).filter((id) => !governing.some((rule) => rule.id === id))
345
- if (unknown.length > 0) {
346
- console.error(`trial: no rule is called ${unknown.join(', ')} — a filter naming nothing would measure nothing and exit 0`)
347
- exit(1)
348
- }
349
- const chosen = only === undefined ? derivation.trials : derivation.trials.filter((trial) => only.includes(trial.rule))
350
- if (chosen.length === 0) {
351
- if (only !== undefined) {
352
- console.error('trial: the filter selected no trials, and measuring nothing must not exit 0')
353
- exit(1)
354
- }
355
- if (governing.length > 0) {
356
- console.error(
357
- `trial: ${String(governing.length)} rule(s) govern and none derived a trial — every rule owes a \`wrong\` and a \`right\`, so measuring nothing must not exit 0`,
358
- )
359
- exit(1)
360
- }
361
- console.log(`trial: nothing to prove — ${source}.`)
362
- exit(0)
363
- }
364
-
365
- const { default: plugin } = await import('../lint.ts')
366
- const parser = (await import('typescript-eslint')).parser
367
- const run = runTrials(chosen, governing, plugin.rules, parser, slopnators)
368
- for (const outcome of run.outcomes) {
369
- if (outcome.passed) continue
370
- for (const failure of outcome.failures) console.error(`trial: ${outcome.id} — ${failure}`)
371
- }
372
- console.log(`trial: ${String(run.passed)} of ${String(run.outcomes.length)} passed, ${String(run.measured)} measured.`)
373
- if (only !== undefined) {
374
- console.log(
375
- `trial: PARTIAL RUN — ${String(derivation.trials.length - chosen.length)} of ${String(derivation.trials.length)} derived trials did not run. \`thread trials\` with no arguments runs every one.`,
376
- )
377
- }
378
- exit(run.failed === 0 ? 0 : 1)
379
- }
380
-
381
- write(ROOT, byproduct)
382
- console.log(`trial: wrote ${byproduct.path} — ${String(derivation.trials.length)} trials from ${source}.`)
383
- }
@@ -1,9 +1,9 @@
1
- import { execFileSync } from 'node:child_process';
1
+ import { starts } from '../integrations/cli.js';
2
2
  import { Linter } from 'eslint';
3
3
  import tseslint from 'typescript-eslint';
4
- import { THING } from "../core/scope.js";
5
- import { CONSTRAINTS, shown } from "./constraint.js";
6
- import { NOT_WRITTEN, governs, reads, visits, load, ROOT, rules as appRules, slopnatorName, } from "./slopnator.js";
4
+ import { THING } from '../core/scope.js';
5
+ import { CONSTRAINTS, shown } from './constraint.js';
6
+ import { NOT_WRITTEN, governs, subject, reads, visits, load, ROOT, rules as appRules, slopnatorName, } from './slopnator.js';
7
7
  const PROBE = {
8
8
  filename: 'probe.ts',
9
9
  text: '',
@@ -61,7 +61,7 @@ export function ruleRule(rule, slopnators) {
61
61
  return {};
62
62
  const visit = (node) => {
63
63
  const at = {
64
- filename: context.filename,
64
+ filename: context.filename.startsWith(ROOT) ? context.filename.slice(ROOT.length) : context.filename,
65
65
  text: context.sourceCode.getText(),
66
66
  ancestors: () => context.sourceCode.getAncestors(node),
67
67
  comments: () => context.sourceCode.getAllComments().map((comment) => ({
@@ -89,7 +89,7 @@ export function ruleRules(slopnators) {
89
89
  return Object.fromEntries(GOVERNING.map((rule) => [rule.id, ruleRule(rule, slopnators)]));
90
90
  }
91
91
  export function ruleSeverities() {
92
- return Object.fromEntries(GOVERNING.map((rule) => [`lint/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error']));
92
+ return Object.fromEntries(GOVERNING.filter((rule) => subject(rule)).map((rule) => [`lint/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error']));
93
93
  }
94
94
  const GOVERNING = [...CONSTRAINTS, ...((await appRules()) ?? [])];
95
95
  const SLOPNATORS = await load(ROOT, GOVERNING);
@@ -98,12 +98,8 @@ export const plugin = { rules: ruleRules(SLOPNATORS) };
98
98
  export default plugin;
99
99
  function ignored() {
100
100
  try {
101
- return execFileSync('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'], {
102
- cwd: ROOT,
103
- encoding: 'utf8',
104
- stdio: ['ignore', 'pipe', 'ignore'],
105
- })
106
- .split('\n')
101
+ return starts('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'], { at: ROOT })
102
+ .out.split('\n')
107
103
  .filter(Boolean)
108
104
  .map((one) => (one.endsWith('/') ? one + '**' : one));
109
105
  }
@@ -138,7 +134,7 @@ export function theRules() {
138
134
  parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
139
135
  },
140
136
  plugins: { [`@${THING}`]: plugin },
141
- rules: Object.fromEntries(GOVERNING.map((rule) => [`@${THING}/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error'])),
137
+ rules: Object.fromEntries(GOVERNING.filter((rule) => subject(rule)).map((rule) => [`@${THING}/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error'])),
142
138
  },
143
139
  ];
144
140
  return (file, source) => linter
@@ -1,4 +1,4 @@
1
- import { execFileSync } from 'node:child_process'
1
+ import { starts } from '../integrations/cli.ts'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
4
  import { Linter, type Rule } from 'eslint'
@@ -9,6 +9,7 @@ import { CONSTRAINTS, shown, type Constraint } from './constraint.ts'
9
9
  import {
10
10
  NOT_WRITTEN,
11
11
  governs,
12
+ subject,
12
13
  reads,
13
14
  visits,
14
15
  load,
@@ -91,7 +92,7 @@ export function ruleRule(rule: Constraint, slopnators: ReadonlyMap<string, Slopn
91
92
  if (found === undefined || !visits(found)) return {}
92
93
  const visit = (node: unknown): void => {
93
94
  const at: SlopnatorContext = {
94
- filename: context.filename,
95
+ filename: context.filename.startsWith(ROOT) ? context.filename.slice(ROOT.length) : context.filename,
95
96
  text: context.sourceCode.getText(),
96
97
  ancestors: () => context.sourceCode.getAncestors(node as never),
97
98
  comments: () =>
@@ -122,7 +123,7 @@ export function ruleRules(slopnators: ReadonlyMap<string, Slopnator>): Readonly<
122
123
 
123
124
  export function ruleSeverities(): Readonly<Record<string, 'error' | 'warn'>> {
124
125
  return Object.fromEntries(
125
- GOVERNING.map((rule) => [`lint/${rule.id}`, rule.severity === 'warning' ? ('warn' as const) : ('error' as const)]),
126
+ GOVERNING.filter((rule) => subject(rule)).map((rule) => [`lint/${rule.id}`, rule.severity === 'warning' ? ('warn' as const) : ('error' as const)]),
126
127
  )
127
128
  }
128
129
 
@@ -137,12 +138,8 @@ export default plugin
137
138
 
138
139
  function ignored(): readonly string[] {
139
140
  try {
140
- return execFileSync('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'], {
141
- cwd: ROOT,
142
- encoding: 'utf8',
143
- stdio: ['ignore', 'pipe', 'ignore'],
144
- })
145
- .split('\n')
141
+ return starts('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'], { at: ROOT })
142
+ .out.split('\n')
146
143
  .filter(Boolean)
147
144
  .map((one) => (one.endsWith('/') ? one + '**' : one))
148
145
  } catch {
@@ -201,7 +198,7 @@ export function theRules(): RuleRunner {
201
198
  },
202
199
  plugins: { [`@${THING}`]: plugin },
203
200
  rules: Object.fromEntries(
204
- GOVERNING.map((rule) => [`@${THING}/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error']),
201
+ GOVERNING.filter((rule) => subject(rule)).map((rule) => [`@${THING}/${rule.id}`, rule.severity === 'warning' ? 'warn' : 'error']),
205
202
  ),
206
203
  },
207
204
  ] as unknown as Parameters<InstanceType<typeof Linter>['verify']>[1]
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { cwd, exit, stderr, stdin, stdout } from 'node:process';
3
- import { repoSource } from "./diagnostics.js";
4
- import { encode, MessageBuffer } from "./protocol.js";
5
- import { LspServer } from "./server.js";
2
+ import { cwd, stderr, stdin, stdout } from 'node:process';
3
+ import { exit } from '../../core/invoked.js';
4
+ import { repoSource } from './diagnostics.js';
5
+ import { encode, MessageBuffer } from './protocol.js';
6
+ import { LspServer } from './server.js';
6
7
  const server = new LspServer({
7
8
  root: cwd(),
8
9
  sourceFor: repoSource,
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { cwd, exit, stderr, stdin, stdout } from 'node:process'
3
+ import { cwd, stderr, stdin, stdout } from 'node:process'
4
+
5
+ import { exit } from '../../core/invoked.ts'
4
6
 
5
7
  import { repoSource } from './diagnostics.ts'
6
8
  import { encode, MessageBuffer } from './protocol.ts'
@@ -1,7 +1,7 @@
1
- import { readFileSync } from "../../core/files.js";
1
+ import { readFileSync } from '../../integrations/disk.js';
2
2
  import { join } from 'node:path';
3
- import { detailOf, rangeOf } from "./findings.js";
4
- import { lintFindings } from "./lint.js";
3
+ import { detailOf, rangeOf } from './findings.js';
4
+ import { lintFindings } from './lint.js';
5
5
  export function repoSource(root) {
6
6
  return {
7
7
  lint: (relative, text) => lintFindings(root, relative, text),
@@ -1,4 +1,4 @@
1
- import { readFileSync } from '../../core/files.ts'
1
+ import { readFileSync } from '../../integrations/disk.ts'
2
2
  import { join } from 'node:path'
3
3
 
4
4
  import { detailOf, rangeOf, type Finding } from './findings.ts'
@@ -1,7 +1,7 @@
1
- import { spawn } from 'node:child_process';
2
- import { existsSync } from "../../core/files.js";
1
+ import { started } from '../../integrations/cli.js';
2
+ import { existsSync } from '../../integrations/disk.js';
3
3
  import { extname, join } from 'node:path';
4
- import { theRules } from "../lint.js";
4
+ import { theRules } from '../lint.js';
5
5
  const LINTABLE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.jsx']);
6
6
  export const isLintable = (relative) => LINTABLE.has(extname(relative));
7
7
  export const LINT_CONFIG = 'eslint.config.js';
@@ -64,32 +64,21 @@ export async function lintFindings(root, relative, text) {
64
64
  ...(suggestionsOf(message).length === 0 ? {} : { suggestions: suggestionsOf(message) }),
65
65
  })));
66
66
  }
67
- function runEslint(binary, root, relative, text) {
68
- return new Promise((resolve, reject) => {
69
- const child = spawn(binary, ['--format', 'json', '--no-warn-ignored', '--stdin', '--stdin-filename', relative], {
70
- cwd: root,
71
- shell: false,
72
- });
73
- let out = '';
74
- let err = '';
75
- child.stdout.on('data', (chunk) => (out += chunk.toString()));
76
- child.stderr.on('data', (chunk) => (err += chunk.toString()));
77
- child.on('error', (error) => reject(new LintUnavailableError(error.message)));
78
- child.on('close', () => {
79
- let parsed;
80
- try {
81
- parsed = JSON.parse(out);
82
- }
83
- catch {
84
- reject(new LintUnavailableError(err.trim() === '' ? `it wrote ${out.length} bytes of non-JSON` : err.trim()));
85
- return;
86
- }
87
- if (!Array.isArray(parsed)) {
88
- reject(new LintUnavailableError('its JSON report was not an array of files'));
89
- return;
90
- }
91
- resolve(parsed);
92
- });
93
- child.stdin.end(text);
67
+ async function runEslint(binary, root, relative, text) {
68
+ const went = await started(binary, ['--format', 'json', '--no-warn-ignored', '--stdin', '--stdin-filename', relative], {
69
+ at: root,
70
+ hears: text,
94
71
  });
72
+ if (!went.there)
73
+ throw new LintUnavailableError(went.why);
74
+ let parsed;
75
+ try {
76
+ parsed = JSON.parse(went.out);
77
+ }
78
+ catch {
79
+ throw new LintUnavailableError(went.err.trim() === '' ? `it wrote ${went.out.length} bytes of non-JSON` : went.err.trim());
80
+ }
81
+ if (!Array.isArray(parsed))
82
+ throw new LintUnavailableError('its JSON report was not an array of files');
83
+ return parsed;
95
84
  }
@@ -1,5 +1,5 @@
1
- import { spawn } from 'node:child_process'
2
- import { existsSync } from '../../core/files.ts'
1
+ import { started } from '../../integrations/cli.ts'
2
+ import { existsSync } from '../../integrations/disk.ts'
3
3
  import { extname, join } from 'node:path'
4
4
 
5
5
  import { theRules } from '../lint.ts'
@@ -101,40 +101,24 @@ export async function lintFindings(root: string, relative: string, text: string)
101
101
  )
102
102
  }
103
103
 
104
- function runEslint(
104
+ async function runEslint(
105
105
  binary: string,
106
106
  root: string,
107
107
  relative: string,
108
108
  text: string,
109
109
  ): Promise<readonly EslintFile[]> {
110
- return new Promise((resolve, reject) => {
111
-
112
- const child = spawn(binary, ['--format', 'json', '--no-warn-ignored', '--stdin', '--stdin-filename', relative], {
113
- cwd: root,
114
- shell: false,
115
- })
116
-
117
- let out = ''
118
- let err = ''
119
- child.stdout.on('data', (chunk: Buffer) => (out += chunk.toString()))
120
- child.stderr.on('data', (chunk: Buffer) => (err += chunk.toString()))
121
- child.on('error', (error) => reject(new LintUnavailableError(error.message)))
122
- child.on('close', () => {
123
-
124
- let parsed: unknown
125
- try {
126
- parsed = JSON.parse(out)
127
- } catch {
128
- reject(new LintUnavailableError(err.trim() === '' ? `it wrote ${out.length} bytes of non-JSON` : err.trim()))
129
- return
130
- }
131
- if (!Array.isArray(parsed)) {
132
- reject(new LintUnavailableError('its JSON report was not an array of files'))
133
- return
134
- }
135
- resolve(parsed as readonly EslintFile[])
136
- })
137
-
138
- child.stdin.end(text)
110
+ const went = await started(binary, ['--format', 'json', '--no-warn-ignored', '--stdin', '--stdin-filename', relative], {
111
+ at: root,
112
+ hears: text,
139
113
  })
114
+ if (!went.there) throw new LintUnavailableError(went.why)
115
+
116
+ let parsed: unknown
117
+ try {
118
+ parsed = JSON.parse(went.out)
119
+ } catch {
120
+ throw new LintUnavailableError(went.err.trim() === '' ? `it wrote ${went.out.length} bytes of non-JSON` : went.err.trim())
121
+ }
122
+ if (!Array.isArray(parsed)) throw new LintUnavailableError('its JSON report was not an array of files')
123
+ return parsed as readonly EslintFile[]
140
124
  }
@@ -1,8 +1,8 @@
1
1
  import { relative as relativeTo, resolve } from 'node:path';
2
2
  import { fileURLToPath, pathToFileURL } from 'node:url';
3
- import { named } from "../../core/scope.js";
4
- import { report, SOURCE_NAME } from "./diagnostics.js";
5
- import { METHOD_NOT_FOUND } from "./protocol.js";
3
+ import { named } from '../../core/scope.js';
4
+ import { report, SOURCE_NAME } from './diagnostics.js';
5
+ import { METHOD_NOT_FOUND } from './protocol.js';
6
6
  const SYNC_FULL = 1;
7
7
  export class LspServer {
8
8
  #root;