@prestack/thread 0.1.21 → 0.1.22

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 (137) hide show
  1. package/CHANGELOG.md +109 -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 +68 -0
  7. package/dist/core/tree.d.ts +1 -0
  8. package/dist/core/tree.js +7 -1
  9. package/dist/derivation/generators/build.d.ts +3 -1
  10. package/dist/derivation/generators/build.js +103 -22
  11. package/dist/derivation/generators/release.js +74 -26
  12. package/dist/derivation/release.js +3 -3
  13. package/dist/integrations/cli.js +69 -10
  14. package/dist/integrations/pglite.js +13 -3
  15. package/dist/integrations/target.d.ts +3 -0
  16. package/dist/integrity/attach.js +1 -5
  17. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  18. package/dist/operations/access.js +3 -2
  19. package/dist/operations/capabilities.d.ts +2 -0
  20. package/dist/operations/capabilities.js +2 -0
  21. package/dist/operations/generators/capability.js +234 -24
  22. package/dist/operations/generators/compile.js +150 -50
  23. package/dist/operations/generators/seed.d.ts +3 -0
  24. package/dist/operations/generators/seed.js +25 -3
  25. package/dist/operations/nodes.d.ts +2 -2
  26. package/package.json +1 -1
  27. package/dist/core/actor.ts +0 -21
  28. package/dist/core/attach.ts +0 -613
  29. package/dist/core/chain.ts +0 -145
  30. package/dist/core/condition.ts +0 -227
  31. package/dist/core/descend.ts +0 -1185
  32. package/dist/core/errors/catalogue.ts +0 -314
  33. package/dist/core/errors/classes.ts +0 -51
  34. package/dist/core/errors/error.ts +0 -152
  35. package/dist/core/errors/namespaces.ts +0 -19
  36. package/dist/core/finding.ts +0 -37
  37. package/dist/core/generators/commit.ts +0 -157
  38. package/dist/core/init.ts +0 -222
  39. package/dist/core/invoked.ts +0 -52
  40. package/dist/core/round.ts +0 -74
  41. package/dist/core/rows.ts +0 -506
  42. package/dist/core/scope.ts +0 -20
  43. package/dist/core/thread.ts +0 -29
  44. package/dist/core/tree.ts +0 -90
  45. package/dist/core/types.ts +0 -351
  46. package/dist/derivation/attach.ts +0 -57
  47. package/dist/derivation/generators/build.ts +0 -371
  48. package/dist/derivation/generators/check.ts +0 -118
  49. package/dist/derivation/generators/determinism.ts +0 -145
  50. package/dist/derivation/generators/release.ts +0 -236
  51. package/dist/derivation/release.ts +0 -43
  52. package/dist/integrations/attach.ts +0 -187
  53. package/dist/integrations/cli.ts +0 -606
  54. package/dist/integrations/declared.ts +0 -34
  55. package/dist/integrations/disk.ts +0 -466
  56. package/dist/integrations/generators/read.ts +0 -127
  57. package/dist/integrations/generators/vault.ts +0 -210
  58. package/dist/integrations/https.ts +0 -216
  59. package/dist/integrations/pglite.ts +0 -206
  60. package/dist/integrations/secret.ts +0 -54
  61. package/dist/integrations/target.ts +0 -672
  62. package/dist/integrations/vault.ts +0 -148
  63. package/dist/integrity/attach.ts +0 -150
  64. package/dist/integrity/check-threads.ts +0 -166
  65. package/dist/integrity/constraint.ts +0 -1246
  66. package/dist/integrity/generators/compiler.ts +0 -138
  67. package/dist/integrity/generators/gate.ts +0 -227
  68. package/dist/integrity/generators/issues.ts +0 -160
  69. package/dist/integrity/generators/publish.ts +0 -233
  70. package/dist/integrity/generators/trial.ts +0 -359
  71. package/dist/integrity/lint.ts +0 -214
  72. package/dist/integrity/lsp/bin.ts +0 -44
  73. package/dist/integrity/lsp/diagnostics.ts +0 -92
  74. package/dist/integrity/lsp/findings.ts +0 -63
  75. package/dist/integrity/lsp/lint.ts +0 -124
  76. package/dist/integrity/lsp/protocol.ts +0 -78
  77. package/dist/integrity/lsp/server.ts +0 -224
  78. package/dist/integrity/slopnator.ts +0 -324
  79. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  80. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  81. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  82. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  83. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  84. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  85. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  86. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  87. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  88. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  89. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  90. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  91. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  92. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  93. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  94. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  95. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  96. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  97. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  98. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  99. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  100. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  101. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  102. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  103. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  104. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  105. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  106. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  107. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  108. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  109. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  110. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  111. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  112. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  113. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  114. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  115. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  116. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  117. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  118. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  119. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  120. package/dist/integrity/sweep.ts +0 -395
  121. package/dist/knowledge/attach.ts +0 -327
  122. package/dist/knowledge/generators/docsgen.ts +0 -186
  123. package/dist/knowledge/generators/glossary.ts +0 -73
  124. package/dist/knowledge/glossary.ts +0 -6
  125. package/dist/operations/access.ts +0 -131
  126. package/dist/operations/attach.ts +0 -408
  127. package/dist/operations/capabilities.ts +0 -88
  128. package/dist/operations/dispatch.ts +0 -294
  129. package/dist/operations/error-results.ts +0 -31
  130. package/dist/operations/generators/capability.ts +0 -1054
  131. package/dist/operations/generators/compile.ts +0 -701
  132. package/dist/operations/generators/permits.ts +0 -229
  133. package/dist/operations/generators/seed.ts +0 -359
  134. package/dist/operations/generators/trial.ts +0 -666
  135. package/dist/operations/nodes.ts +0 -53
  136. package/dist/operations/operations.ts +0 -91
  137. 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)