@prestack/thread 0.1.21 → 0.1.23

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 (138) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/dist/core/attach.js +21 -0
  3. package/dist/core/descend.js +6 -3
  4. package/dist/core/generators/commit.js +55 -3
  5. package/dist/core/generators/dev.d.ts +1 -0
  6. package/dist/core/generators/dev.js +69 -0
  7. package/dist/derivation/generators/build.d.ts +3 -1
  8. package/dist/derivation/generators/build.js +104 -22
  9. package/dist/derivation/generators/release.js +74 -25
  10. package/dist/derivation/release.js +3 -3
  11. package/dist/integrations/cli.d.ts +0 -1
  12. package/dist/integrations/cli.js +69 -11
  13. package/dist/integrations/declared.d.ts +2 -0
  14. package/dist/integrations/declared.js +6 -1
  15. package/dist/integrations/pglite.js +13 -3
  16. package/dist/integrations/target.d.ts +3 -0
  17. package/dist/integrity/attach.js +1 -5
  18. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  19. package/dist/operations/access.js +3 -2
  20. package/dist/operations/capabilities.d.ts +2 -0
  21. package/dist/operations/capabilities.js +2 -0
  22. package/dist/operations/generators/capability.js +237 -24
  23. package/dist/operations/generators/compile.js +150 -50
  24. package/dist/operations/generators/seed.d.ts +3 -0
  25. package/dist/operations/generators/seed.js +25 -3
  26. package/dist/operations/nodes.d.ts +2 -2
  27. package/package.json +1 -1
  28. package/dist/core/actor.ts +0 -21
  29. package/dist/core/attach.ts +0 -613
  30. package/dist/core/chain.ts +0 -145
  31. package/dist/core/condition.ts +0 -227
  32. package/dist/core/descend.ts +0 -1185
  33. package/dist/core/errors/catalogue.ts +0 -314
  34. package/dist/core/errors/classes.ts +0 -51
  35. package/dist/core/errors/error.ts +0 -152
  36. package/dist/core/errors/namespaces.ts +0 -19
  37. package/dist/core/finding.ts +0 -37
  38. package/dist/core/generators/commit.ts +0 -157
  39. package/dist/core/init.ts +0 -222
  40. package/dist/core/invoked.ts +0 -52
  41. package/dist/core/round.ts +0 -74
  42. package/dist/core/rows.ts +0 -506
  43. package/dist/core/scope.ts +0 -20
  44. package/dist/core/thread.ts +0 -29
  45. package/dist/core/tree.ts +0 -90
  46. package/dist/core/types.ts +0 -351
  47. package/dist/derivation/attach.ts +0 -57
  48. package/dist/derivation/generators/build.ts +0 -371
  49. package/dist/derivation/generators/check.ts +0 -118
  50. package/dist/derivation/generators/determinism.ts +0 -145
  51. package/dist/derivation/generators/release.ts +0 -236
  52. package/dist/derivation/release.ts +0 -43
  53. package/dist/integrations/attach.ts +0 -187
  54. package/dist/integrations/cli.ts +0 -606
  55. package/dist/integrations/declared.ts +0 -34
  56. package/dist/integrations/disk.ts +0 -466
  57. package/dist/integrations/generators/read.ts +0 -127
  58. package/dist/integrations/generators/vault.ts +0 -210
  59. package/dist/integrations/https.ts +0 -216
  60. package/dist/integrations/pglite.ts +0 -206
  61. package/dist/integrations/secret.ts +0 -54
  62. package/dist/integrations/target.ts +0 -672
  63. package/dist/integrations/vault.ts +0 -148
  64. package/dist/integrity/attach.ts +0 -150
  65. package/dist/integrity/check-threads.ts +0 -166
  66. package/dist/integrity/constraint.ts +0 -1246
  67. package/dist/integrity/generators/compiler.ts +0 -138
  68. package/dist/integrity/generators/gate.ts +0 -227
  69. package/dist/integrity/generators/issues.ts +0 -160
  70. package/dist/integrity/generators/publish.ts +0 -233
  71. package/dist/integrity/generators/trial.ts +0 -359
  72. package/dist/integrity/lint.ts +0 -214
  73. package/dist/integrity/lsp/bin.ts +0 -44
  74. package/dist/integrity/lsp/diagnostics.ts +0 -92
  75. package/dist/integrity/lsp/findings.ts +0 -63
  76. package/dist/integrity/lsp/lint.ts +0 -124
  77. package/dist/integrity/lsp/protocol.ts +0 -78
  78. package/dist/integrity/lsp/server.ts +0 -224
  79. package/dist/integrity/slopnator.ts +0 -324
  80. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  81. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  82. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  83. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  84. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  85. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  86. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  87. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  88. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  89. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  90. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  91. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  92. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  93. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  94. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  95. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  96. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  97. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  98. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  99. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  100. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  101. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  102. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  103. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  104. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  105. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  106. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  107. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  108. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  109. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  110. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  111. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  112. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  113. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  114. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  115. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  116. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  117. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  118. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  119. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  120. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  121. package/dist/integrity/sweep.ts +0 -395
  122. package/dist/knowledge/attach.ts +0 -327
  123. package/dist/knowledge/generators/docsgen.ts +0 -186
  124. package/dist/knowledge/generators/glossary.ts +0 -73
  125. package/dist/knowledge/glossary.ts +0 -6
  126. package/dist/operations/access.ts +0 -131
  127. package/dist/operations/attach.ts +0 -408
  128. package/dist/operations/capabilities.ts +0 -88
  129. package/dist/operations/dispatch.ts +0 -294
  130. package/dist/operations/error-results.ts +0 -31
  131. package/dist/operations/generators/capability.ts +0 -1054
  132. package/dist/operations/generators/compile.ts +0 -701
  133. package/dist/operations/generators/permits.ts +0 -229
  134. package/dist/operations/generators/seed.ts +0 -359
  135. package/dist/operations/generators/trial.ts +0 -666
  136. package/dist/operations/nodes.ts +0 -53
  137. package/dist/operations/operations.ts +0 -91
  138. package/dist/operations/result.ts +0 -28
@@ -1,138 +0,0 @@
1
-
2
- import { readdirSync, readFileSync, writeFileSync } from '../../integrations/disk.ts'
3
- import { join, resolve } from 'node:path'
4
- import { argv, stderr, stdout } from 'node:process'
5
-
6
- import { exit } from '../../core/invoked.ts'
7
- import { ROOT, THREAD_AT, descend, uncompiled } from '../../core/descend.ts'
8
-
9
- import { forbids, runChecker, type Finding } from '../../core/finding.ts'
10
-
11
- function refuse(why: string): never {
12
- stderr.write(`compiler: ${why}\n`)
13
- exit(1)
14
- }
15
-
16
- const SOURCE = /\.(?:ts|tsx|mts|cts)$/
17
- const DECLARING = /\.d\.[cm]?ts$/
18
-
19
- const INSTALLED = 'node_' + 'modules'
20
-
21
- const pass = await descend()
22
-
23
- const stopped = [...pass.broken, ...pass.findings].filter(forbids)[0]
24
- if (stopped !== undefined) {
25
- refuse(
26
- 'the descent is broken, so what this tree declares is short — and a short list of declarations ' +
27
- `leaves out more of the app than it should, which is a compiler that agrees with anything. ${stopped.detail} (${stopped.file ?? ''})`,
28
- )
29
- }
30
-
31
- const WORD = 'thread compiler'
32
-
33
- const SELF = [...pass.threaded.entries()]
34
- .flatMap(([at, held]) => held.map((one) => ({ at, row: one.row })))
35
- .filter((one) => (one.row.by ?? '') === WORD)
36
- .find((one) => !one.at.split('/').includes(INSTALLED))
37
-
38
- if (SELF === undefined) {
39
- refuse(
40
- `no row in this tree names a file of this app with \`by: '${WORD}'\`, so nothing says which config is this app's. ` +
41
- 'Run `thread attach integrity`, which declares it.',
42
- )
43
- }
44
-
45
- const AT = SELF.at
46
- const PATH = resolve(ROOT, AT)
47
-
48
- const declared = new Set([THREAD_AT, ...pass.threaded.keys()])
49
-
50
- function scan(rel: string): { readonly declares: boolean; readonly holds: boolean; readonly out: readonly string[] } {
51
- let entries
52
- try {
53
- entries = readdirSync(join(ROOT, rel === '' ? '.' : rel), { withFileTypes: true })
54
- } catch {
55
- return { declares: false, holds: false, out: [] }
56
- }
57
-
58
- let declares = false
59
- let holds = false
60
- const out: string[] = []
61
-
62
- for (const entry of [...entries].sort((one, two) => one.name.localeCompare(two.name))) {
63
- if (entry.name === '.git' || entry.name === INSTALLED) continue
64
- const at = rel === '' ? entry.name : `${rel}/${entry.name}`
65
-
66
- if (entry.isDirectory()) {
67
- const held = scan(at)
68
- if (held.declares) {
69
- declares = true
70
- holds = true
71
- out.push(...held.out)
72
- } else if (held.holds) {
73
- holds = true
74
- out.push(at)
75
- }
76
- continue
77
- }
78
-
79
- if (declared.has(at)) {
80
- declares = true
81
- holds = true
82
- continue
83
- }
84
- if (!SOURCE.test(entry.name) || DECLARING.test(entry.name)) continue
85
- holds = true
86
- out.push(at)
87
- }
88
-
89
- return { declares, holds, out }
90
- }
91
-
92
- const held = scan('')
93
-
94
- if (!held.declares) {
95
- refuse(
96
- 'no TypeScript file in this tree is declared by anything, so every one of them would be left out ' +
97
- 'and the compiler would pass by reading nothing. Declare something with `thread create` first.',
98
- )
99
- }
100
-
101
- const NEXT = '.next'
102
-
103
- const leave = [...new Set([...held.out, NEXT])].sort()
104
-
105
- let current: Record<string, unknown>
106
- try {
107
- current = JSON.parse(readFileSync(PATH, 'utf8')) as Record<string, unknown>
108
- } catch (cause) {
109
- refuse(`${AT} could not be read as JSON, so what it already states cannot be kept — ${String(cause)}`)
110
- }
111
-
112
- const wanted = JSON.stringify({ ...current, exclude: leave }, null, 2)
113
- const has = JSON.stringify(current.exclude ?? []) === JSON.stringify(leave)
114
-
115
- if (argv.includes('--check')) {
116
- const findings: Finding[] = []
117
- if (!has) {
118
- findings.push({
119
- file: AT,
120
- detail: `does not leave out ${String(leave.length)} path(s) nothing in this tree declares, so the compiler is holding this app to code it never wrote`,
121
- instead:
122
- 'run `thread compiler`. What the compiler reads is derived from what the tree declares — an edit here is a declaration somebody skipped, and a path put here by hand is taken back out on the next settle',
123
- })
124
- }
125
- findings.push(...uncompiled())
126
- runChecker('compiler', findings, `${String(leave.length)} path(s) left out, over ${String(pass.declarations)} declaration(s)`)
127
- exit(findings.length > 0 ? 1 : 0)
128
-
129
- }
130
-
131
- if (has) {
132
- stdout.write(`compiler: ${AT} already leaves out exactly what this tree declares nothing under.\n`)
133
- exit(0)
134
- }
135
-
136
- writeFileSync(PATH, `${wanted}\n`)
137
- stdout.write(`compiler: wrote ${AT} — ${String(leave.length)} path(s) left out, declared by nobody: ${leave.join(', ')}.\n`)
138
- exit(0)
@@ -1,227 +0,0 @@
1
- import { starts } from '../../integrations/cli.ts'
2
- import { createHash } from 'node:crypto'
3
- import { join } from 'node:path'
4
- import { argv, execPath, stderr, stdout } from 'node:process'
5
- import { descend } from '../../core/descend.ts'
6
- import { attached, existsSync, here, readdirSync, readFileSync, rmSync, writeFileSync } from '../../integrations/disk.ts'
7
- import { LOADER, exit } from '../../core/invoked.ts'
8
- import { valuesIn } from '../../core/rows.ts'
9
- import { ORGANISATION, ours } from '../../core/scope.ts'
10
- import { findTree, inTree } from '../../core/tree.ts'
11
-
12
- const TREE = findTree()
13
- const ROOT = TREE.at
14
-
15
- const STEP_TIMEOUT = 600_000
16
-
17
- const { GATE } = valuesIn(readFileSync(inTree(TREE, 'gate.ts'))) as { GATE: { derives?: unknown; declares?: unknown } }
18
- const { GENERATORS } = valuesIn(readFileSync(inTree(TREE, 'derivation.ts'))) as { GENERATORS: readonly Record<string, unknown>[] }
19
-
20
- function refuse(why: string): never {
21
- stderr.write(`gate: ${why}\n`)
22
- exit(1)
23
- }
24
-
25
- interface Row {
26
- readonly id?: string
27
- readonly at?: string
28
- readonly by?: string
29
- readonly runs?: string
30
- }
31
-
32
- const rowsOf = (edge: unknown): readonly Row[] => (Array.isArray(edge) ? (edge as readonly Row[]) : [])
33
-
34
- interface Step {
35
- readonly id: string
36
- readonly runs: string
37
- }
38
-
39
- const walked = await descend()
40
-
41
- const every: readonly Row[] = [
42
- ...rowsOf(GATE.declares),
43
- ...rowsOf(GENERATORS),
44
- ...[...walked.threaded.values()].flat().map((one) => one.row as Row),
45
- ...[...walked.stated.values()].map((one) => one as Row),
46
- ]
47
-
48
- function steps(): readonly Step[] {
49
- const found: Step[] = []
50
- const claimed = new Set<string>()
51
-
52
- for (const row of every) {
53
- const runs = (row.runs ?? '').trim()
54
- if (runs === '' || claimed.has(runs)) continue
55
- claimed.add(runs)
56
- found.push({ id: row.id ?? runs, runs })
57
- }
58
-
59
- if (found.length === 0) {
60
- refuse('no row in this tree names a command to check it, so the gate would pass by running nothing')
61
- }
62
-
63
- return found
64
- }
65
-
66
- const writers: readonly Row[] = [
67
- ...rowsOf(GATE.declares),
68
- ...rowsOf(GENERATORS),
69
- ...[...walked.threaded.values()].flat().filter((one) => one.list === 'declares' && (one.row as Row).by === undefined).map((one) => one.row as Row),
70
- ...[...walked.stated.values()].map((one) => one as Row),
71
- ]
72
-
73
- const written = new Map<string, string>()
74
-
75
- for (const row of writers) {
76
- const by = (row.by ?? '').trim()
77
- const at = (row.at ?? '').trim()
78
- if (by !== '' && at !== '' && !written.has(by)) written.set(by, at)
79
- }
80
-
81
- function ran(runs: string): number {
82
- const said = runs.trim().split(/\s+/)
83
- const spent = { at: ROOT, shows: true, within: STEP_TIMEOUT } as const
84
-
85
- if (said[0] !== 'thread') {
86
- const other = starts(said[0] ?? '', said.slice(1), spent)
87
- return other.there ? other.code : 1
88
- }
89
-
90
- const of = `thread ${said[1] ?? ''}`
91
- const at = written.get(of)
92
- if (at === undefined) {
93
- stderr.write(`gate: nothing in this tree is written by \`${of}\`, so this step names a command that does not exist here\n`)
94
- return 1
95
- }
96
-
97
- if (attached()) {
98
- const ordered = here().run(join(ROOT, at), said.slice(2))
99
- stdout.write(ordered.out)
100
- stderr.write(ordered.err)
101
- return ordered.there ? ordered.code : 1
102
- }
103
-
104
- const spun = starts(execPath, [...LOADER, join(ROOT, at), ...said.slice(2)], spent)
105
- return spun.there ? spun.code : 1
106
- }
107
-
108
- function named(flag: string): readonly string[] | undefined {
109
- const at = argv.indexOf(flag)
110
- if (at === -1) return undefined
111
- return (argv[at + 1] ?? '').split(',').map((one) => one.trim()).filter(Boolean)
112
- }
113
-
114
- const all = steps()
115
-
116
- if (argv.includes('--list')) {
117
- stdout.write(`gate: ${String(all.length)} step(s) declared\n`)
118
- for (const step of all) stdout.write(` ${step.id} — ${step.runs}\n`)
119
- exit(0)
120
- }
121
-
122
- const only = named('--only')
123
- const skip = named('--skip') ?? []
124
- const known = new Set(all.map((step) => step.id))
125
- const unknown = [...(only ?? []), ...skip].filter((id) => !known.has(id))
126
-
127
- if (unknown.length > 0) {
128
- stderr.write(`gate: no declared step is called ${unknown.join(', ')} — a filter naming nothing would run a shorter gate and exit 0\n`)
129
- stderr.write(`gate: the declared steps are ${[...known].join(', ')}\n`)
130
- exit(1)
131
- }
132
-
133
- const chosen = all.filter((step) => (only === undefined || only.includes(step.id)) && !skip.includes(step.id))
134
- const dropped = all.filter((step) => !chosen.includes(step))
135
-
136
- if (chosen.length === 0) {
137
- stderr.write('gate: the filter selected no steps, and a gate that runs nothing must not exit 0\n')
138
- exit(1)
139
- }
140
-
141
- const failed: Step[] = []
142
- const unmeasured: Step[] = []
143
- let passed = 0
144
-
145
- for (const [index, step] of chosen.entries()) {
146
- const at = `step ${String(index + 1)} of ${String(chosen.length)}`
147
- stdout.write(`\ngate: ${at} — ${step.runs} (${step.id})\n`)
148
-
149
- if (attached() && step.runs.trim().split(/\s+/)[0] !== 'thread') {
150
- unmeasured.push(step)
151
- stdout.write(
152
- `gate: ${at} — NOT RUN. \`${step.runs}\` is not a thread command, so it reads the disk of the machine this is\n` +
153
- `running on rather than the tree being answered for. It would report on the wrong files, and a step that\n` +
154
- `passed against the wrong machine says nothing about this one.\n`,
155
- )
156
- continue
157
- }
158
-
159
- const began = Date.now()
160
- const code = ran(step.runs)
161
-
162
- stdout.write(`gate: ${at} — exited ${String(code)} in ${String(Date.now() - began)} ms\n`)
163
- if (code === 0) passed += 1
164
- else failed.push(step)
165
- }
166
-
167
- stdout.write(`\ngate: ${String(passed)} of ${String(chosen.length)} steps passed.\n`)
168
-
169
- if (dropped.length > 0) {
170
- stdout.write(`gate: PARTIAL RUN — ${String(dropped.length)} of ${String(all.length)} declared step(s) did not run: ${dropped.map((step) => step.id).join(', ')}\n`)
171
- stdout.write('gate: this is not a gate pass. `thread gate` with no filter runs every step the tree declares.\n')
172
- }
173
-
174
- function fingerprint(): { readonly of: Record<string, string>; readonly versions: Record<string, string> } {
175
- const of: Record<string, string> = {}
176
- const hash = (at: string): void => {
177
- if (!existsSync(join(ROOT, at))) return
178
- of[at] = createHash('sha256').update(readFileSync(join(ROOT, at))).digest('hex').slice(0, 16)
179
- }
180
- const sheets = join(ROOT, '_threads')
181
- if (existsSync(sheets)) {
182
- for (const name of readdirSync(sheets)) if (name.endsWith('.ts')) hash(join('_threads', name))
183
- }
184
-
185
- const versions: Record<string, string> = {}
186
- const held = join(ROOT, 'node_modules', ORGANISATION)
187
- if (existsSync(held)) {
188
- for (const name of readdirSync(held)) {
189
- const pkg = `${ORGANISATION}/${name}`
190
- if (!ours(pkg)) continue
191
- const at = join(held, name, 'package.json')
192
- if (!existsSync(at)) continue
193
- const read = JSON.parse(readFileSync(at, 'utf8')) as { version?: string }
194
- if (typeof read.version === 'string') versions[pkg] = read.version
195
- }
196
- }
197
- return { of, versions }
198
- }
199
-
200
- const RECEIPT_AT = join('.thread', 'gate.json')
201
- const RECEIPT = join(ROOT, RECEIPT_AT)
202
-
203
- if (unmeasured.length > 0) {
204
- stdout.write(
205
- `gate: ${String(unmeasured.length)} step(s) could not be measured here — ${unmeasured.map((step) => step.id).join(', ')}. Each one\n` +
206
- `runs a tool that is not part of this framework, so it reads whatever disk it was started on. This is not a\n` +
207
- `gate pass, and no receipt is written for a run that left them unanswered.\n`,
208
- )
209
- }
210
-
211
- if (failed.length === 0 && dropped.length === 0 && unmeasured.length === 0) {
212
- writeFileSync(RECEIPT, `${JSON.stringify({ passed: chosen.length, ...fingerprint() }, null, 2)}\n`, 'utf8')
213
- stdout.write(`gate: wrote ${RECEIPT_AT} — what compiles is checked against this, so a tree that skipped the gate does not build.\n`)
214
- } else if (existsSync(RECEIPT)) {
215
-
216
- rmSync(RECEIPT)
217
- stdout.write(`gate: removed ${RECEIPT_AT} — the tree no longer passes, so nothing may compile against an older run.\n`)
218
- }
219
-
220
- if (failed.length === 0) exit(unmeasured.length === 0 ? 0 : 1)
221
-
222
- stderr.write(
223
- `gate: FAILED — ${String(failed.length)} of ${String(chosen.length)} step(s). The other ${String(passed)} ran and passed; a failing step stops nothing.\n`,
224
- )
225
- for (const step of failed) stderr.write(` - ${step.id} — run it alone with \`${step.runs}\`\n`)
226
- stderr.write('gate: every step above is its own command. Run the failing one on its own to work on it, and `thread issues` for everything wrong with the tree at once.\n')
227
- exit(1)
@@ -1,160 +0,0 @@
1
- import { argv, stderr, stdout } from 'node:process'
2
-
3
- import { exit } from '../../core/invoked.ts'
4
- import { uncompiled } from '../../core/descend.ts'
5
-
6
- import { THREAD_AT, descend, refusals, trackedFiles, unthreaded, type Finding } from '../check-threads.ts'
7
- import { CONSTRAINTS } from '../constraint.ts'
8
- import { ruleMessage } from '../lint.ts'
9
- import { ROOT, load, rules } from '../slopnator.ts'
10
- import { sweep, tracked as governed } from '../sweep.ts'
11
-
12
- const WITHIN = '--in'
13
-
14
- const CHECK = '--check'
15
-
16
- const checking = argv.includes(CHECK)
17
-
18
- const scoped = argv.indexOf(WITHIN)
19
- const under = scoped === -1 ? undefined : argv[scoped + 1]
20
- if (scoped !== -1 && (under === undefined || under.startsWith('-'))) {
21
- stderr.write(`thread issues ${WITHIN} <path> — which directory to report on. The totals still count the whole tree.\n`)
22
- exit(1)
23
- }
24
-
25
- const inside = (one: Finding): boolean =>
26
- under === undefined || one.file === undefined || one.file === under || one.file.startsWith(`${under.replace(/\/+$/, '')}/`)
27
-
28
- const pass = await descend()
29
- const tracked = trackedFiles()
30
- const kept = new Set(tracked)
31
-
32
- const found = refusals(pass, tracked)
33
-
34
- const loose = new Set(unthreaded(pass, tracked))
35
-
36
- const governing = [...CONSTRAINTS, ...((await rules()) ?? [])]
37
- const named = new Map(governing.map((one) => [one.id, one]))
38
- const ran = sweep(ROOT, governing, await load(ROOT, governing), governed(ROOT))
39
-
40
- const refused: readonly Finding[] = [
41
- ...ran.findings.map((one): Finding => {
42
- const rule = named.get(one.rule)
43
- return {
44
- detail: `${rule === undefined ? one.rule : ruleMessage(rule)} Found: ${one.found}.`,
45
- instead: rule?.instead ?? `read what ${one.rule} refuses and stop writing it`,
46
- ...(one.file === '' ? {} : { file: one.file }),
47
- }
48
- }),
49
- ...ran.unrun.map((one): Finding => ({
50
- detail: `${one} DID NOT RUN — no slopnator is on disk for it, so nothing here measured that rule and its absence from this list says nothing about it`,
51
- instead: 'write the walk the rule names, or delete the rule and its row together — half a rule reads as a whole one from every direction',
52
- })),
53
- ]
54
-
55
- const warned = found.filter((one) => one.severity === 'warning')
56
- const firm = found.filter((one) => one.severity !== 'warning')
57
- const cut = firm.filter((one) => one.file !== undefined && loose.has(one.file))
58
- const rest = firm.filter((one) => !(one.file !== undefined && loose.has(one.file)))
59
- const wrong = uncompiled().filter((one) => one.file === undefined || kept.has(one.file))
60
-
61
- const where = (one: Finding): string => {
62
- const line = one.at?.line
63
- const column = one.at?.column
64
- return `${one.file ?? '(nowhere named)'}${line === undefined ? '' : `:${String(line)}${column === undefined ? '' : `:${String(column)}`}`}`
65
- }
66
-
67
- const listed = (held: readonly Finding[]): readonly string[] =>
68
- held.flatMap((one) => [` ${where(one)}`, ` ${one.detail}`, ` fix: ${one.instead}`, ''])
69
-
70
- const out: string[] = []
71
-
72
- let shown = 0
73
-
74
- const group = (title: string, taught: string, held: readonly Finding[]): void => {
75
- if (held.length === 0) return
76
- const here = held.filter(inside)
77
- shown += here.length
78
- if (here.length === 0) return
79
- out.push(
80
- '',
81
- `${title} — ${String(held.length)}${here.length === held.length ? '' : `, ${String(here.length)} of them under ${String(under)}`}`,
82
- taught,
83
- '',
84
- ...listed(here),
85
- )
86
- }
87
-
88
- group(
89
- 'files nothing declares',
90
- ' every file here is named by a row, or it is not part of this app. The descent starts at\n' +
91
- ` ${THREAD_AT} and walks what each row declares; whatever it never arrives at is not wired,\n` +
92
- ' however well it reads. Declare it with `thread create`, or delete it.',
93
- cut,
94
- )
95
-
96
- group(
97
- 'files that will not compile',
98
- ' nothing in this tree builds, serves or gates while one of its files does not typecheck.\n' +
99
- ' Read off the same program the descent judged the declarations with.',
100
- wrong,
101
- )
102
-
103
- group(
104
- 'declarations this tree refuses',
105
- ' the files are reached and they compile; these are the things the descent will not accept about\n' +
106
- ' how they are declared.',
107
- rest,
108
- )
109
-
110
- group(
111
- 'what the rules refuse',
112
- ' every rule this framework ships and every rule this app added, run over the files each one\n' +
113
- ` governs — ${String(ran.rules)} rule(s) over ${String(ran.swept)} of ${String(ran.tracked)} tracked file(s).\n` +
114
- ' This is what `thread sweep` reports, said here so one read answers the whole question.',
115
- refused,
116
- )
117
-
118
- group(
119
- 'derivations nothing has produced yet',
120
- ' each row here states what a generator would write, and no file is there. A generator that refuses\n' +
121
- ' to write an empty file is doing its job, so the descent says these and does not refuse them, and\n' +
122
- ' neither does this. Run the command the row names in `by` to produce the file.',
123
- warned,
124
- )
125
-
126
- const total = cut.length + wrong.length + rest.length + refused.length
127
-
128
- const saidNotCounted = (): void => {
129
- if (warned.length === 0) return
130
- stdout.write(`issues: ${String(warned.length)} derivation(s) nothing has produced yet — listed below, said and not counted.\n`)
131
- }
132
-
133
- if (total === 0) {
134
- stdout.write(
135
- `issues: none — ${String(tracked.length)} tracked file(s), every one of them declared by a row, and all of them compile.\n` +
136
- 'issues: that is what a threaded tree is. Nothing here is written by hand; everything here was declared.\n',
137
- )
138
- saidNotCounted()
139
- if (out.length > 0) stdout.write(`${out.join('\n')}\n`)
140
- exit(0)
141
- }
142
-
143
- stdout.write(
144
- `issues: ${String(total)} — ${String(cut.length)} file(s) nothing declares, ${String(wrong.length)} that will not compile, ` +
145
- `${String(rest.length)} declared in a way it refuses, ${String(refused.length)} the rules refuse, over ${String(tracked.length)} tracked file(s).\n`,
146
- )
147
- saidNotCounted()
148
- stdout.write(
149
- under === undefined
150
- ? `issues: listing all ${String(total)} below. \`thread issues ${WITHIN} <path>\` reports on one directory instead.\n`
151
- : `issues: listing the ${String(shown)} of them under ${under}. The counts above are the whole tree.\n`,
152
- )
153
- stdout.write(
154
- checking
155
- ? `issues: ${CHECK} refuses while any of these stand. Without it this reads and never refuses, so it still answers on a tree that is entirely broken.\n`
156
- : `issues: this reads and never refuses, so it works on a tree that is entirely broken. Run it with ${CHECK}, which is what the gate runs, to have it refuse instead.\n`,
157
- )
158
-
159
- stdout.write(`${out.join('\n')}\n`)
160
- exit(checking ? 1 : 0)