@prestack/thread 0.0.1

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 (248) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/dist/attach.d.ts +1 -0
  3. package/dist/attach.js +1 -0
  4. package/dist/core/actor.d.ts +12 -0
  5. package/dist/core/actor.js +8 -0
  6. package/dist/core/asking.d.ts +180 -0
  7. package/dist/core/asking.js +1109 -0
  8. package/dist/core/attach.d.ts +54 -0
  9. package/dist/core/attach.js +510 -0
  10. package/dist/core/chain.d.ts +16 -0
  11. package/dist/core/chain.js +114 -0
  12. package/dist/core/cli.d.ts +2 -0
  13. package/dist/core/cli.js +1682 -0
  14. package/dist/core/condition.d.ts +52 -0
  15. package/dist/core/condition.js +198 -0
  16. package/dist/core/descend.d.ts +81 -0
  17. package/dist/core/descend.js +879 -0
  18. package/dist/core/finding.d.ts +12 -0
  19. package/dist/core/finding.js +23 -0
  20. package/dist/core/generators/meta.d.ts +2 -0
  21. package/dist/core/generators/meta.js +823 -0
  22. package/dist/core/init.d.ts +15 -0
  23. package/dist/core/init.js +134 -0
  24. package/dist/core/invoked.d.ts +9 -0
  25. package/dist/core/invoked.js +41 -0
  26. package/dist/core/round.d.ts +1 -0
  27. package/dist/core/round.js +57 -0
  28. package/dist/core/rows.d.ts +26 -0
  29. package/dist/core/rows.js +354 -0
  30. package/dist/core/scope.d.ts +6 -0
  31. package/dist/core/scope.js +12 -0
  32. package/dist/core/thread.d.ts +26 -0
  33. package/dist/core/thread.js +5 -0
  34. package/dist/core/tree.d.ts +12 -0
  35. package/dist/core/tree.js +56 -0
  36. package/dist/core/types.d.ts +49 -0
  37. package/dist/core/types.js +311 -0
  38. package/dist/derivation/attach.d.ts +2 -0
  39. package/dist/derivation/attach.js +61 -0
  40. package/dist/derivation/generators/build.d.ts +16 -0
  41. package/dist/derivation/generators/build.js +217 -0
  42. package/dist/derivation/generators/check.d.ts +18 -0
  43. package/dist/derivation/generators/check.js +78 -0
  44. package/dist/derivation/generators/derivation.d.ts +3 -0
  45. package/dist/derivation/generators/derivation.js +273 -0
  46. package/dist/derivation/generators/determinism.d.ts +20 -0
  47. package/dist/derivation/generators/determinism.js +110 -0
  48. package/dist/derivation/generators/release.d.ts +3 -0
  49. package/dist/derivation/generators/release.js +183 -0
  50. package/dist/derivation/makers/release.d.ts +1 -0
  51. package/dist/derivation/makers/release.js +107 -0
  52. package/dist/derivation/release.d.ts +10 -0
  53. package/dist/derivation/release.js +12 -0
  54. package/dist/integrations/attach.d.ts +6 -0
  55. package/dist/integrations/attach.js +177 -0
  56. package/dist/integrations/generators/read.d.ts +1 -0
  57. package/dist/integrations/generators/read.js +92 -0
  58. package/dist/integrations/generators/sdk.d.ts +1 -0
  59. package/dist/integrations/generators/sdk.js +148 -0
  60. package/dist/integrations/generators/secret.d.ts +1 -0
  61. package/dist/integrations/generators/secret.js +233 -0
  62. package/dist/integrations/generators/target.d.ts +1 -0
  63. package/dist/integrations/generators/target.js +765 -0
  64. package/dist/integrations/generators/vault.d.ts +1 -0
  65. package/dist/integrations/generators/vault.js +164 -0
  66. package/dist/integrations/sdk.d.ts +6 -0
  67. package/dist/integrations/sdk.js +151 -0
  68. package/dist/integrations/secret.d.ts +16 -0
  69. package/dist/integrations/secret.js +30 -0
  70. package/dist/integrations/target.d.ts +49 -0
  71. package/dist/integrations/target.js +225 -0
  72. package/dist/integrations/vault.d.ts +12 -0
  73. package/dist/integrations/vault.js +104 -0
  74. package/dist/integrity/attach.d.ts +2 -0
  75. package/dist/integrity/attach.js +213 -0
  76. package/dist/integrity/check-threads.d.ts +10 -0
  77. package/dist/integrity/check-threads.js +153 -0
  78. package/dist/integrity/constraint.d.ts +16 -0
  79. package/dist/integrity/constraint.js +1105 -0
  80. package/dist/integrity/generators/compiler.d.ts +1 -0
  81. package/dist/integrity/generators/compiler.js +130 -0
  82. package/dist/integrity/generators/gate.d.ts +1 -0
  83. package/dist/integrity/generators/gate.js +172 -0
  84. package/dist/integrity/generators/issues.d.ts +1 -0
  85. package/dist/integrity/generators/issues.js +100 -0
  86. package/dist/integrity/generators/publish.d.ts +1 -0
  87. package/dist/integrity/generators/publish.js +186 -0
  88. package/dist/integrity/generators/rule.d.ts +2 -0
  89. package/dist/integrity/generators/rule.js +213 -0
  90. package/dist/integrity/generators/seal.d.ts +4 -0
  91. package/dist/integrity/generators/seal.js +101 -0
  92. package/dist/integrity/generators/trial.d.ts +39 -0
  93. package/dist/integrity/generators/trial.js +254 -0
  94. package/dist/integrity/lint.d.ts +50 -0
  95. package/dist/integrity/lint.js +161 -0
  96. package/dist/integrity/lsp/bin.d.ts +2 -0
  97. package/dist/integrity/lsp/bin.js +35 -0
  98. package/dist/integrity/lsp/diagnostics.d.ts +25 -0
  99. package/dist/integrity/lsp/diagnostics.js +57 -0
  100. package/dist/integrity/lsp/findings.d.ts +20 -0
  101. package/dist/integrity/lsp/findings.js +39 -0
  102. package/dist/integrity/lsp/lint.d.ts +7 -0
  103. package/dist/integrity/lsp/lint.js +95 -0
  104. package/dist/integrity/lsp/protocol.d.ts +20 -0
  105. package/dist/integrity/lsp/protocol.js +57 -0
  106. package/dist/integrity/lsp/server.d.ts +15 -0
  107. package/dist/integrity/lsp/server.js +171 -0
  108. package/dist/integrity/sealed.d.ts +1 -0
  109. package/dist/integrity/sealed.js +56 -0
  110. package/dist/integrity/slopnator.d.ts +90 -0
  111. package/dist/integrity/slopnator.js +267 -0
  112. package/dist/integrity/slopnators/sl_broken_lineage.d.ts +2 -0
  113. package/dist/integrity/slopnators/sl_broken_lineage.js +35 -0
  114. package/dist/integrity/slopnators/sl_declared_elsewhere.d.ts +2 -0
  115. package/dist/integrity/slopnators/sl_declared_elsewhere.js +33 -0
  116. package/dist/integrity/slopnators/sl_derived_drift.d.ts +8 -0
  117. package/dist/integrity/slopnators/sl_derived_drift.js +91 -0
  118. package/dist/integrity/slopnators/sl_hardcoded_style.d.ts +2 -0
  119. package/dist/integrity/slopnators/sl_hardcoded_style.js +46 -0
  120. package/dist/integrity/slopnators/sl_inert_island.d.ts +2 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +85 -0
  122. package/dist/integrity/slopnators/sl_inert_slopnator.d.ts +2 -0
  123. package/dist/integrity/slopnators/sl_inert_slopnator.js +76 -0
  124. package/dist/integrity/slopnators/sl_missing_row.d.ts +2 -0
  125. package/dist/integrity/slopnators/sl_missing_row.js +180 -0
  126. package/dist/integrity/slopnators/sl_no_barrel.d.ts +2 -0
  127. package/dist/integrity/slopnators/sl_no_barrel.js +41 -0
  128. package/dist/integrity/slopnators/sl_no_child_process.d.ts +2 -0
  129. package/dist/integrity/slopnators/sl_no_child_process.js +61 -0
  130. package/dist/integrity/slopnators/sl_no_comment.d.ts +2 -0
  131. package/dist/integrity/slopnators/sl_no_comment.js +41 -0
  132. package/dist/integrity/slopnators/sl_no_dispatch_bypass.d.ts +2 -0
  133. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +301 -0
  134. package/dist/integrity/slopnators/sl_no_import_cycle.d.ts +2 -0
  135. package/dist/integrity/slopnators/sl_no_import_cycle.js +42 -0
  136. package/dist/integrity/slopnators/sl_no_such_command.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_no_such_command.js +55 -0
  138. package/dist/integrity/slopnators/sl_only_takes_a_literal.d.ts +2 -0
  139. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +59 -0
  140. package/dist/integrity/slopnators/sl_secret_literal.d.ts +2 -0
  141. package/dist/integrity/slopnators/sl_secret_literal.js +89 -0
  142. package/dist/integrity/slopnators/sl_stale_path.d.ts +2 -0
  143. package/dist/integrity/slopnators/sl_stale_path.js +92 -0
  144. package/dist/integrity/slopnators/sl_twice_meant.d.ts +2 -0
  145. package/dist/integrity/slopnators/sl_twice_meant.js +30 -0
  146. package/dist/integrity/slopnators/sl_twice_parented.d.ts +2 -0
  147. package/dist/integrity/slopnators/sl_twice_parented.js +34 -0
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +2 -0
  149. package/dist/integrity/slopnators/sl_undeclared_crossing.js +78 -0
  150. package/dist/integrity/slopnators/sl_undeclared_destination.d.ts +2 -0
  151. package/dist/integrity/slopnators/sl_undeclared_destination.js +56 -0
  152. package/dist/integrity/slopnators/sl_undeclared_element.d.ts +2 -0
  153. package/dist/integrity/slopnators/sl_undeclared_element.js +34 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.d.ts +2 -0
  155. package/dist/integrity/slopnators/sl_undeclared_node.js +59 -0
  156. package/dist/integrity/slopnators/sl_undeclared_operation.d.ts +2 -0
  157. package/dist/integrity/slopnators/sl_undeclared_operation.js +120 -0
  158. package/dist/integrity/slopnators/sl_undeclared_variant.d.ts +2 -0
  159. package/dist/integrity/slopnators/sl_undeclared_variant.js +117 -0
  160. package/dist/integrity/slopnators/sl_undescribed.d.ts +2 -0
  161. package/dist/integrity/slopnators/sl_undescribed.js +54 -0
  162. package/dist/integrity/slopnators/sl_ungoverned_island.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_ungoverned_island.js +58 -0
  164. package/dist/integrity/slopnators/sl_unkept_rows.d.ts +2 -0
  165. package/dist/integrity/slopnators/sl_unkept_rows.js +104 -0
  166. package/dist/integrity/slopnators/sl_unnamed_island.d.ts +2 -0
  167. package/dist/integrity/slopnators/sl_unnamed_island.js +37 -0
  168. package/dist/integrity/slopnators/sl_unpaired_member.d.ts +2 -0
  169. package/dist/integrity/slopnators/sl_unpaired_member.js +114 -0
  170. package/dist/integrity/slopnators/sl_unpaired_slopnator.d.ts +2 -0
  171. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +60 -0
  172. package/dist/integrity/slopnators/sl_unprefixed_member.d.ts +2 -0
  173. package/dist/integrity/slopnators/sl_unprefixed_member.js +126 -0
  174. package/dist/integrity/slopnators/sl_unseamed_reach.d.ts +2 -0
  175. package/dist/integrity/slopnators/sl_unseamed_reach.js +73 -0
  176. package/dist/integrity/slopnators/sl_unthreaded.d.ts +56 -0
  177. package/dist/integrity/slopnators/sl_unthreaded.js +352 -0
  178. package/dist/integrity/slopnators/sl_untranslated_key.d.ts +2 -0
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +157 -0
  180. package/dist/integrity/slopnators/sl_unversioned.d.ts +2 -0
  181. package/dist/integrity/slopnators/sl_unversioned.js +31 -0
  182. package/dist/integrity/sweep.d.ts +24 -0
  183. package/dist/integrity/sweep.js +264 -0
  184. package/dist/knowledge/attach.d.ts +2 -0
  185. package/dist/knowledge/attach.js +339 -0
  186. package/dist/knowledge/generators/docsgen.d.ts +1 -0
  187. package/dist/knowledge/generators/docsgen.js +145 -0
  188. package/dist/knowledge/generators/glossary.d.ts +1 -0
  189. package/dist/knowledge/generators/glossary.js +56 -0
  190. package/dist/knowledge/generators/skills.d.ts +1 -0
  191. package/dist/knowledge/generators/skills.js +222 -0
  192. package/dist/knowledge/glossary.d.ts +6 -0
  193. package/dist/knowledge/glossary.js +1 -0
  194. package/dist/operations/access.d.ts +27 -0
  195. package/dist/operations/access.js +61 -0
  196. package/dist/operations/attach.d.ts +6 -0
  197. package/dist/operations/attach.js +414 -0
  198. package/dist/operations/capabilities.d.ts +37 -0
  199. package/dist/operations/capabilities.js +35 -0
  200. package/dist/operations/cost.d.ts +14 -0
  201. package/dist/operations/cost.js +37 -0
  202. package/dist/operations/dispatch.d.ts +14 -0
  203. package/dist/operations/dispatch.js +327 -0
  204. package/dist/operations/error-results.d.ts +4 -0
  205. package/dist/operations/error-results.js +25 -0
  206. package/dist/operations/errors/catalog.d.ts +58 -0
  207. package/dist/operations/errors/catalog.js +91 -0
  208. package/dist/operations/errors/classes.d.ts +73 -0
  209. package/dist/operations/errors/classes.js +22 -0
  210. package/dist/operations/errors/errors.d.ts +52 -0
  211. package/dist/operations/errors/errors.js +257 -0
  212. package/dist/operations/generators/capability.d.ts +5 -0
  213. package/dist/operations/generators/capability.js +603 -0
  214. package/dist/operations/generators/compile.d.ts +2 -0
  215. package/dist/operations/generators/compile.js +541 -0
  216. package/dist/operations/generators/grant.d.ts +1 -0
  217. package/dist/operations/generators/grant.js +267 -0
  218. package/dist/operations/generators/operation.d.ts +1 -0
  219. package/dist/operations/generators/operation.js +811 -0
  220. package/dist/operations/generators/permits.d.ts +1 -0
  221. package/dist/operations/generators/permits.js +196 -0
  222. package/dist/operations/generators/seed.d.ts +20 -0
  223. package/dist/operations/generators/seed.js +285 -0
  224. package/dist/operations/generators/trial.d.ts +53 -0
  225. package/dist/operations/generators/trial.js +376 -0
  226. package/dist/operations/http.d.ts +30 -0
  227. package/dist/operations/http.js +121 -0
  228. package/dist/operations/makers/capability.d.ts +1 -0
  229. package/dist/operations/makers/capability.js +117 -0
  230. package/dist/operations/makers/trial.d.ts +1 -0
  231. package/dist/operations/makers/trial.js +413 -0
  232. package/dist/operations/memory.d.ts +104 -0
  233. package/dist/operations/memory.js +365 -0
  234. package/dist/operations/metering.d.ts +55 -0
  235. package/dist/operations/metering.js +247 -0
  236. package/dist/operations/nodes.d.ts +35 -0
  237. package/dist/operations/nodes.js +15 -0
  238. package/dist/operations/operations.d.ts +18 -0
  239. package/dist/operations/operations.js +5 -0
  240. package/dist/operations/queue-policy.d.ts +3 -0
  241. package/dist/operations/queue-policy.js +8 -0
  242. package/dist/operations/result.d.ts +19 -0
  243. package/dist/operations/result.js +10 -0
  244. package/dist/operations/seams.d.ts +239 -0
  245. package/dist/operations/seams.js +45 -0
  246. package/dist/operations/store.d.ts +14 -0
  247. package/dist/operations/store.js +1 -0
  248. package/package.json +16 -0
@@ -0,0 +1,213 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { argv, stdout } from 'node:process';
4
+ import { exit } from "../../core/invoked.js";
5
+ import { ELSEWHERE, RECEIPT, answer, answering, asking, asksOf, bodied, given as pipedIn, guidance, issue, marked, named as answerNames, needed, only, ownAnswers, refuse, rowLines, seeding, spend, stating, within, wordsIn } from "../../core/asking.js";
6
+ import { holdsRow, listBounds, withRow } from "../../core/rows.js";
7
+ import { named } from "../../core/scope.js";
8
+ import { findTree, inTree } from "../../core/tree.js";
9
+ import { ROOT, SEEDED, declareSeeded, rules, scaffold, slopnatorName, write } from "../slopnator.js";
10
+ const TREE = findTree();
11
+ const FILE = 'rules.ts';
12
+ const LIST = 'RULES';
13
+ const at = within(TREE.at, inTree(TREE, FILE));
14
+ const kebab = (one) => one.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
15
+ const KEBAB = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
16
+ export const ASKS = asksOf('rule', 'Constraint', {
17
+ name: { guide: 'kebab-case, e.g. no-fetch-in-a-screen', asks: 'name: ' },
18
+ describes: {
19
+ guide: 'one sentence a reader who has not seen the code could act on',
20
+ asks: 'describes — one sentence a reader who has not seen the code could act on: ',
21
+ },
22
+ where: {
23
+ guide: 'comma separated path fragments this governs, e.g. app/, src/nodes/',
24
+ asks: 'where (comma separated path fragments this governs, e.g. app/, src/nodes/): ',
25
+ },
26
+ except: {
27
+ guide: 'optional — comma separated path fragments to leave out',
28
+ asks: 'except (comma separated, blank for none): ',
29
+ },
30
+ bans: { guide: 'the exact form this refuses, in one line', asks: 'bans — the exact form this refuses, in one line: ' },
31
+ because: { guide: 'a fact somebody measured — why this is refused', asks: 'because — a fact somebody measured: ' },
32
+ instead: { guide: 'what to do instead', asks: 'instead — what to do instead: ' },
33
+ wrong: {
34
+ guide: 'one line of the form this refuses — its trial',
35
+ asks: 'wrong — one line of the form this refuses, its trial: ',
36
+ },
37
+ right: {
38
+ guide: 'one line of the form this allows — its trial',
39
+ asks: 'right — one line of the form this allows, its trial: ',
40
+ },
41
+ severity: {
42
+ guide: 'optional — forbidden (default, leave blank) or warning',
43
+ asks: 'severity — forbidden or warning (blank for forbidden): ',
44
+ },
45
+ finds: {
46
+ guide: 'one sentence saying what the check looks for, read by whoever opens it next',
47
+ asks: 'finds — one sentence saying what the check looks for: ',
48
+ },
49
+ body: {
50
+ guide: 'the check itself, written where it is asked for. Leave it blank and the rule is declared with nothing behind it, which `npx thread trials` reports unproven',
51
+ asks: 'body: ',
52
+ optional: true,
53
+ },
54
+ }, ['id', 'at', 'version']);
55
+ const said = (one) => ASKS.find((each) => each.at === one)?.asks ?? `${one}: `;
56
+ import { quoted } from "../../core/rows.js";
57
+ const held = await rules();
58
+ if (held === undefined) {
59
+ refuse(`there is no ${at} declaring \`${LIST}\` here, so a rule has nowhere to go. ` +
60
+ `Run \`npx thread attach ${named('integrity')}\` — the sheet is the package's to write, not yours.`);
61
+ }
62
+ const existing = held;
63
+ const mark = (one) => marked([one.id, one.describes, one.bans, one.because, one.instead, one.wrong, one.right, JSON.stringify(one.where), one.severity ?? '']);
64
+ const listing = [
65
+ existing.length === 0
66
+ ? 'no rules of its own yet — this app enforces only what the packages it attached ship.'
67
+ : `${String(existing.length)} rule(s) this app adds of its own:`,
68
+ ...existing.map((one) => ` [${mark(one)}] ${one.id} — ${one.bans}${one.severity === 'warning' ? ' (warning)' : ''}`),
69
+ ].join('\n');
70
+ const words = wordsIn(['new']);
71
+ stdout.write(`${listing}\n`);
72
+ const flagged = argv.indexOf(RECEIPT);
73
+ if (flagged === -1) {
74
+ const nonce = issue(TREE.at, listing);
75
+ stdout.write([
76
+ '',
77
+ ...answering('rule'),
78
+ '',
79
+ ...guidance(ASKS),
80
+ '',
81
+ ` npx thread create rule new <name> ${RECEIPT} ${nonce}`,
82
+ '',
83
+ ...ELSEWHERE,
84
+ '',
85
+ `${RECEIPT} is spent by one run, and is refused if ${at} changed after it was issued.`,
86
+ '',
87
+ 'this writes three things, not one. The row goes in the sheet, the check goes in the file it ' +
88
+ 'seeds from the `body` you give, and the trial comes from `wrong` and `right`.',
89
+ '',
90
+ 'then it runs every generator this tree declares, that trial among them, so before this command ' +
91
+ 'returns you see whether the rule fires at the form it refuses and stays silent at the fix it ' +
92
+ 'names. Leave `body` blank and the row is declared with nothing behind it, and `npx thread ' +
93
+ 'trials` reports it unproven until somebody writes the check.',
94
+ '',
95
+ ].join('\n'));
96
+ stdout.write(`create: receipt ${nonce} — spent on use, and stale the moment ${at} changes.\n`);
97
+ exit(0);
98
+ }
99
+ spend(TREE.at, listing, argv[flagged + 1], at);
100
+ const all = pipedIn();
101
+ const { piped, ask, line, shut } = asking(all);
102
+ const own = ownAnswers(all);
103
+ if (piped)
104
+ only(own, answerNames(ASKS), '`new`');
105
+ const name = kebab(words[1] ?? (piped ? needed(own, 'name', '`new`') : await ask('name', said('name'))));
106
+ if (!KEBAB.test(name)) {
107
+ shut();
108
+ refuse(`\`${name}\` cannot be the id of a rule — it is kebab-case, e.g. no-fetch-in-a-screen.`);
109
+ }
110
+ if (existing.some((one) => one.id === name)) {
111
+ shut();
112
+ refuse(`\`${name}\` is already declared in ${at}. One id, one rule — two would silently keep whichever ran second.`);
113
+ }
114
+ const describes = piped
115
+ ? needed(own, 'describes', '`new`')
116
+ : await ask('describes', said('describes'));
117
+ if (describes === '') {
118
+ shut();
119
+ refuse(`\`${name}\` was given no meaning, and a row without one is a name nobody can read.`);
120
+ }
121
+ const whereSaid = piped ? needed(own, 'where', '`new`') : await ask('where', said('where'));
122
+ const paths = whereSaid.split(',').map((one) => one.trim()).filter((one) => one !== '');
123
+ if (paths.length === 0) {
124
+ shut();
125
+ refuse(`\`${name}\` names no path, so it would govern nothing — a rule that cannot fire is indistinguishable from a rule being obeyed.`);
126
+ }
127
+ const exceptSaid = piped ? (answer(own, 'except') ?? '') : await ask('except', said('except'));
128
+ const except = exceptSaid.split(',').map((one) => one.trim()).filter((one) => one !== '');
129
+ const bans = piped ? needed(own, 'bans', '`new`') : await ask('bans', said('bans'));
130
+ if (bans === '') {
131
+ shut();
132
+ refuse(`\`${name}\` names no \`bans\`, and a rule that does not say what form it refuses cannot be told from one refusing nothing.`);
133
+ }
134
+ const because = piped ? needed(own, 'because', '`new`') : await ask('because', said('because'));
135
+ if (because === '') {
136
+ shut();
137
+ refuse(`\`${name}\` names no \`because\`, and a refusal with no reason is an order rather than a rule.`);
138
+ }
139
+ const instead = piped ? needed(own, 'instead', '`new`') : await ask('instead', said('instead'));
140
+ if (instead === '') {
141
+ shut();
142
+ refuse(`\`${name}\` names no \`instead\`, so whoever it refuses is told only what they did wrong.`);
143
+ }
144
+ const wrong = piped ? needed(own, 'wrong', '`new`') : await ask('wrong', said('wrong'));
145
+ if (wrong === '') {
146
+ shut();
147
+ refuse(`\`${name}\` names no \`wrong\`, and a rule with nothing it refuses is a predicate with no trial to prove it fires.`);
148
+ }
149
+ const right = piped ? needed(own, 'right', '`new`') : await ask('right', said('right'));
150
+ if (right === '') {
151
+ shut();
152
+ refuse(`\`${name}\` names no \`right\`, and a rule that only shows what it refuses cannot prove it stays silent on the fix it recommends.`);
153
+ }
154
+ const severity = piped ? (answer(own, 'severity') ?? '') : await ask('severity', said('severity'));
155
+ if (severity !== '' && severity !== 'forbidden' && severity !== 'warning') {
156
+ shut();
157
+ refuse(`\`${severity}\` is not a severity — a rule is \`forbidden\` (the default, leave blank to use it) or \`warning\`.`);
158
+ }
159
+ const finds = piped ? needed(own, 'finds', '`new`') : await ask('finds', said('finds'));
160
+ if (finds === '') {
161
+ shut();
162
+ refuse(`\`${name}\` says nothing about what its check looks for, and that sentence is the only thing between the ` +
163
+ 'next person and opening the file. Say what THIS one looks for, not what a check is.');
164
+ }
165
+ const body = await bodied(`the check for \`${name}\``, scaffold(), piped, all, line);
166
+ const { stated, kids } = await stating(ASKS, all, piped, ask, line, ['name', 'id', 'at', 'describes', 'version', 'where', 'except', 'bans', 'because', 'instead', 'wrong', 'right', 'severity', 'finds', 'body']);
167
+ shut();
168
+ const row = [
169
+ ' {',
170
+ ` id: ${quoted(name)},`,
171
+ ` at: ${quoted(at)},`,
172
+ ` describes: ${quoted(describes)},`,
173
+ ' version: 1,',
174
+ ` where: { paths: [${paths.map(quoted).join(', ')}]${except.length === 0 ? '' : `, except: [${except.map(quoted).join(', ')}]`} },`,
175
+ ` bans: ${quoted(bans)},`,
176
+ ` because: ${quoted(because)},`,
177
+ ` instead: ${quoted(instead)},`,
178
+ ` wrong: ${quoted(wrong)},`,
179
+ ` right: ${quoted(right)},`,
180
+ ...(severity === '' || severity === 'forbidden' ? [] : [` severity: ${quoted(severity)},`]),
181
+ ...rowLines(' ', stated, kids),
182
+ ' },',
183
+ ].join('\n');
184
+ const path = inTree(TREE, FILE);
185
+ const text = readFileSync(path, 'utf8');
186
+ const where = listBounds(text, LIST);
187
+ if (where === undefined)
188
+ refuse(`${at} states no \`${LIST}\` list to declare a rule in.`);
189
+ if (holdsRow(text, where, name))
190
+ refuse(`\`${name}\` is already declared in ${at}.`);
191
+ writeFileSync(path, withRow(text, where, row), 'utf8');
192
+ stdout.write(`create: declared ${name} in ${at}.\n`);
193
+ const slopAt = `${SEEDED}/${slopnatorName(name)}.ts`;
194
+ if (existsSync(join(ROOT, slopAt))) {
195
+ stdout.write(`create: ${slopAt} is already on disk — left untouched, and declared below.\n`);
196
+ }
197
+ else {
198
+ write(ROOT, { path: slopAt, content: body ?? scaffold() });
199
+ stdout.write(body === undefined
200
+ ? `create: seeded ${slopAt} with nothing behind it — its \`holds\` answers NOT_WRITTEN.\n`
201
+ : `create: seeded ${slopAt} with the check you wrote.\n`);
202
+ }
203
+ for (const told of seeding(kids, ROOT))
204
+ stdout.write(`create: ${told}\n`);
205
+ const declared = await declareSeeded(ROOT, [name], finds);
206
+ if (declared.length > 0)
207
+ stdout.write(`create: declared the check for "${name}" in its own row.\n`);
208
+ stdout.write(body === undefined
209
+ ? `create: ${name} refuses nothing yet — write ${slopAt}, matching the shape any file already under ` +
210
+ 'slopnators/ uses. `npx thread trials` reports it unproven until you do.\n'
211
+ : `create: ${name} is declared and its check is written. The trial derived from \`wrong\` and \`right\` ` +
212
+ 'runs below — it proves the rule fires at the form it refuses and stays silent at the fix it names.\n');
213
+ exit(0);
@@ -0,0 +1,4 @@
1
+ export declare const SEALED_AT = "sealed.ts";
2
+ export declare function sealable(home?: string): readonly string[];
3
+ export declare function sealOf(home?: string): Readonly<Record<string, string>>;
4
+ export declare function content(seal: Readonly<Record<string, string>>): string;
@@ -0,0 +1,101 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { createHash } from 'node:crypto';
10
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
11
+ import { basename, dirname, join, relative, resolve } from 'node:path';
12
+ import { argv, stderr, stdout } from 'node:process';
13
+ import { fileURLToPath, pathToFileURL } from 'node:url';
14
+ import { runChecker } from "../../core/finding.js";
15
+ import { exit, invoked } from "../../core/invoked.js";
16
+ import { findTree, inTree } from "../../core/tree.js";
17
+ import { SEALED } from "../sealed.js";
18
+ export const SEALED_AT = 'sealed.ts';
19
+ const HERE = fileURLToPath(import.meta.url);
20
+ const FROM_SOURCE = HERE.endsWith('.ts');
21
+ const ROOT = FROM_SOURCE ? findTree().at : resolve(HERE, '../..');
22
+ const DOMAIN = basename(resolve(HERE, '../..'));
23
+ const WRITTEN = join(dirname(HERE), '..', SEALED_AT);
24
+ const BUILT = SEALED_AT.replace(/\.ts$/, '.js');
25
+ const CODE = /\.js$/;
26
+ const SHEET = 'packaging.ts';
27
+ async function declared() {
28
+ const sheet = inTree(findTree(), SHEET);
29
+ if (!existsSync(sheet))
30
+ return [];
31
+ const loaded = (await import(__rewriteRelativeImportExtension(pathToFileURL(sheet).href)));
32
+ return loaded.PACKAGES ?? [];
33
+ }
34
+ const PACKAGES = FROM_SOURCE ? await declared() : [];
35
+ function built() {
36
+ if (!FROM_SOURCE)
37
+ return ROOT;
38
+ const folders = PACKAGES.map((one) => one.folder ?? '').filter((one) => one !== '');
39
+ const holder = folders.find((folder) => existsSync(join(ROOT, folder, 'dist', DOMAIN)));
40
+ if (holder === undefined) {
41
+ stderr.write(`seal: no package built here holds \`${DOMAIN}\` — ${folders.length === 0 ? '_threads/packaging.ts declares no package' : `none of ${folders.join(', ')} has a dist/${DOMAIN}`}. ` +
42
+ `Run \`thread packaged --build\` first, and if no package row carries the ${DOMAIN} words, put them on one in _threads/packaging.ts.\n`);
43
+ exit(1);
44
+ }
45
+ return join(ROOT, holder, 'dist', DOMAIN);
46
+ }
47
+ export function sealable(home = built()) {
48
+ const found = [];
49
+ const step = (at) => {
50
+ for (const entry of readdirSync(at).sort()) {
51
+ const here = join(at, entry);
52
+ if (statSync(here).isDirectory())
53
+ step(here);
54
+ else if (CODE.test(entry) && relative(home, here) !== BUILT)
55
+ found.push(here);
56
+ }
57
+ };
58
+ step(home);
59
+ return found.sort();
60
+ }
61
+ export function sealOf(home = built()) {
62
+ const held = {};
63
+ for (const file of sealable(home)) {
64
+ held[relative(home, file)] = createHash('sha256').update(readFileSync(file)).digest('hex');
65
+ }
66
+ return held;
67
+ }
68
+ export function content(seal) {
69
+ const rows = Object.entries(seal).map(([at, hash]) => ` '${at}': '${hash}',`);
70
+ return [
71
+ 'export const SEALED: Readonly<Record<string, string>> = {',
72
+ ...rows,
73
+ '}',
74
+ '',
75
+ ].join('\n');
76
+ }
77
+ if (invoked(import.meta.url)) {
78
+ const seal = sealOf();
79
+ const emitted = content(seal);
80
+ if (argv.includes('--check')) {
81
+ const sealed = Object.keys(seal).length;
82
+ const findings = content(SEALED) === emitted
83
+ ? []
84
+ : [
85
+ {
86
+ detail: `${SEALED_AT} is not the bytes this package is made of`,
87
+ instead: 'run `thread seal` and commit what it writes — the seal is what proves an installed copy is the one that shipped',
88
+ file: relative(ROOT, WRITTEN),
89
+ },
90
+ ];
91
+ runChecker('seal', findings, `${String(sealed)} file(s) sealed`);
92
+ exit(findings.length === 0 ? 0 : 1);
93
+ }
94
+ if (!FROM_SOURCE) {
95
+ stderr.write('seal: this copy is installed, and a seal is written where the package is made. Nothing was rewritten — `thread seal --check` says whether this copy is the one that shipped.\n');
96
+ exit(1);
97
+ }
98
+ mkdirSync(dirname(WRITTEN), { recursive: true });
99
+ writeFileSync(WRITTEN, emitted);
100
+ stdout.write(`seal: wrote ${SEALED_AT} — ${String(Object.keys(seal).length)} file(s).\n`);
101
+ }
@@ -0,0 +1,39 @@
1
+ import type { Rule } from 'eslint';
2
+ import type { Constraint } from '../constraint.ts';
3
+ import { type Byproduct, type Slopnator, type Sourced } from '../slopnator.ts';
4
+ export declare const TRIALS_PATH: string;
5
+ export interface Scenario {
6
+ readonly files: readonly Sourced[];
7
+ readonly at: string;
8
+ }
9
+ export interface Trial {
10
+ readonly id: string;
11
+ readonly rule: string;
12
+ readonly name: string;
13
+ readonly scenario: Scenario;
14
+ readonly verdict: {
15
+ readonly reports: boolean;
16
+ };
17
+ readonly requires: string;
18
+ }
19
+ export interface Derivation {
20
+ readonly trials: readonly Trial[];
21
+ }
22
+ export interface Outcome {
23
+ readonly id: string;
24
+ readonly rule: string;
25
+ readonly passed: boolean;
26
+ readonly failures: readonly string[];
27
+ readonly parsed: boolean;
28
+ }
29
+ export interface Run {
30
+ readonly outcomes: readonly Outcome[];
31
+ readonly passed: number;
32
+ readonly failed: number;
33
+ readonly measured: number;
34
+ }
35
+ export declare function probeAt(rule: Constraint): string;
36
+ export declare function deriveTrials(all: readonly Constraint[], slopnators: ReadonlyMap<string, Slopnator>): Derivation;
37
+ export declare function trialsByproduct(derivation: Derivation): Byproduct;
38
+ export declare function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule, parser: unknown, slopnator?: Slopnator): Outcome;
39
+ export declare function runTrials(trials: readonly Trial[], all: readonly Constraint[], rules: Readonly<Record<string, Rule.RuleModule>>, parser: unknown, slopnators?: ReadonlyMap<string, Slopnator>): Run;
@@ -0,0 +1,254 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { exit, invoked } from "../../core/invoked.js";
4
+ import { Linter } from 'eslint';
5
+ const TREE = findTree();
6
+ import { CONSTRAINTS } from "../constraint.js";
7
+ import { NOT_WRITTEN, ROOT, governs, load, proofsOf, reads, rules, slopnatorName, sweeps, write } from "../slopnator.js";
8
+ import { forget } from "../slopnators/sl_unthreaded.js";
9
+ import { findTree } from "../../core/tree.js";
10
+ export const TRIALS_PATH = `${findTree().name}/_generated/trials.ts`;
11
+ const PROBE = 'probe.tsx';
12
+ const WRAPPINGS = [
13
+ (source) => source,
14
+ (source) => `const _probe = { ${source} }`,
15
+ (source) => `const _probe = <Probe ${source} />`,
16
+ (source) => `const _probe = () => { ${source} }`,
17
+ (source) => `const _probe = <Probe>{${source}}</Probe>`,
18
+ ];
19
+ export function probeAt(rule) {
20
+ const path = rule.where.paths[0] ?? 'src/';
21
+ if (path === '.')
22
+ return PROBE;
23
+ return path.endsWith('/') ? `${path}${PROBE}` : `${path}/${PROBE}`;
24
+ }
25
+ function requiresOf(rule, reports) {
26
+ return reports
27
+ ? `${rule.id} reports on this source, at a path it governs`
28
+ : `${rule.id} stays silent on this source — the fix it recommends is not itself refused`;
29
+ }
30
+ function scenarioOf(rule, proof) {
31
+ if (typeof proof !== 'string')
32
+ return proof;
33
+ const path = probeAt(rule);
34
+ return { files: [{ at: path, source: proof }], at: path };
35
+ }
36
+ function spoken(scenario) {
37
+ const target = scenario.files.find((file) => file.at === scenario.at) ?? scenario.files[0];
38
+ const rest = scenario.files.length - 1;
39
+ return `${target?.source ?? ''}${rest > 0 ? ` (beside ${String(rest)} more file(s))` : ''}`;
40
+ }
41
+ export function deriveTrials(all, slopnators) {
42
+ const trials = [];
43
+ for (const rule of all) {
44
+ const proofs = proofsOf(rule, slopnators);
45
+ const put = (proof, index, reports) => {
46
+ const scenario = scenarioOf(rule, proof);
47
+ trials.push({
48
+ id: `trl_rule_${rule.id.replace(/-/g, '_')}_${reports ? 'refuses' : 'accepts'}_${String(index)}`,
49
+ rule: rule.id,
50
+ name: `${rule.id} ${reports ? 'refuses' : 'accepts'}: ${spoken(scenario)}`,
51
+ scenario,
52
+ verdict: { reports },
53
+ requires: requiresOf(rule, reports),
54
+ });
55
+ };
56
+ proofs.wrong.forEach((proof, index) => { put(proof, index, true); });
57
+ proofs.right.forEach((proof, index) => { put(proof, index, false); });
58
+ }
59
+ return { trials: trials.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) };
60
+ }
61
+ export function trialsByproduct(derivation) {
62
+ return {
63
+ path: TRIALS_PATH,
64
+ content: `export const RULE_TRIALS = ${JSON.stringify(derivation.trials, null, 2)} as const
65
+ `,
66
+ };
67
+ }
68
+ function config(rule, module, parser) {
69
+ return [
70
+ {
71
+ files: ['**/*.ts', '**/*.tsx'],
72
+ languageOptions: {
73
+ parser,
74
+ parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
75
+ },
76
+ plugins: { rule: { rules: { [rule.id]: module } } },
77
+ rules: { [`rule/`]: 'error' },
78
+ },
79
+ ];
80
+ }
81
+ export function runTrial(trial, rule, module, parser, slopnator) {
82
+ const linter = new Linter();
83
+ const failures = [];
84
+ if (!governs(rule, trial.scenario.at)) {
85
+ return {
86
+ id: trial.id,
87
+ rule: rule.id,
88
+ passed: false,
89
+ parsed: false,
90
+ failures: [`the trial's own path "${trial.scenario.at}" is not one the rule governs, so nothing could have run`],
91
+ };
92
+ }
93
+ const target = trial.scenario.files.find((file) => file.at === trial.scenario.at);
94
+ if (target === undefined) {
95
+ return {
96
+ id: trial.id,
97
+ rule: rule.id,
98
+ passed: false,
99
+ parsed: false,
100
+ failures: [`the scenario writes no file at "${trial.scenario.at}", the path it lints, so nothing could have been measured`],
101
+ };
102
+ }
103
+ for (const file of trial.scenario.files) {
104
+ if (!existsSync(join(ROOT, file.at)))
105
+ continue;
106
+ return {
107
+ id: trial.id,
108
+ rule: rule.id,
109
+ passed: false,
110
+ parsed: false,
111
+ failures: [`the scenario would overwrite "${file.at}", which is a real file in this repository — a trial may not stand on a path somebody authored`],
112
+ };
113
+ }
114
+ const written = [];
115
+ const created = [];
116
+ let parsed = false;
117
+ let satisfied = false;
118
+ try {
119
+ for (const file of trial.scenario.files) {
120
+ const path = join(ROOT, file.at);
121
+ const made = mkdirSync(dirname(path), { recursive: true });
122
+ if (made !== undefined)
123
+ created.push(made);
124
+ writeFileSync(path, file.source);
125
+ written.push(path);
126
+ }
127
+ forget();
128
+ const path = join(ROOT, trial.scenario.at);
129
+ if (slopnator !== undefined && !reads(slopnator)) {
130
+ parsed = true;
131
+ satisfied = slopnator.checks(ROOT).length > 0 === trial.verdict.reports;
132
+ }
133
+ else if (slopnator !== undefined && sweeps(slopnator)) {
134
+ const held = slopnator.holds(undefined, {
135
+ filename: trial.scenario.at,
136
+ text: target.source,
137
+ ancestors: () => [],
138
+ comments: () => [],
139
+ });
140
+ parsed = true;
141
+ satisfied = (held !== false && held !== NOT_WRITTEN) === trial.verdict.reports;
142
+ }
143
+ else {
144
+ for (const wrap of WRAPPINGS) {
145
+ const text = wrap(target.source);
146
+ writeFileSync(path, text);
147
+ const messages = linter.verify(text, config(rule, module, parser), trial.scenario.at);
148
+ if (messages.some((message) => message.fatal === true))
149
+ continue;
150
+ parsed = true;
151
+ satisfied = messages.some((message) => message.ruleId === `rule/`) === trial.verdict.reports;
152
+ break;
153
+ }
154
+ }
155
+ }
156
+ finally {
157
+ for (const path of written)
158
+ if (existsSync(path))
159
+ rmSync(path);
160
+ for (const path of created)
161
+ if (existsSync(path))
162
+ rmSync(path, { recursive: true });
163
+ forget();
164
+ }
165
+ if (!parsed) {
166
+ failures.push('no wrapping of this source parsed, so nothing was measured — a trial that cannot run is not a trial that passed');
167
+ }
168
+ else if (!satisfied) {
169
+ failures.push(trial.verdict.reports
170
+ ? `expected to report on this source and it stayed silent — the rule documents a form its own slopnator does not refuse`
171
+ : `expected to stay silent on this source and it reported — the rule refuses the fix it recommends`);
172
+ }
173
+ return { id: trial.id, rule: rule.id, passed: failures.length === 0, parsed, failures };
174
+ }
175
+ export function runTrials(trials, all, rules, parser, slopnators) {
176
+ const byName = new Map(all.map((rule) => [rule.id, rule]));
177
+ const outcomes = trials.map((trial) => {
178
+ const rule = byName.get(trial.rule);
179
+ const module = rules[trial.rule];
180
+ if (rule === undefined || module === undefined) {
181
+ return {
182
+ id: trial.id,
183
+ rule: trial.rule,
184
+ passed: false,
185
+ parsed: false,
186
+ failures: [`no rule named "${trial.rule}" is registered, so this trial is about nothing — regenerate with \`thread trials\``],
187
+ };
188
+ }
189
+ return runTrial(trial, rule, module, parser, slopnators?.get(slopnatorName(rule.id)));
190
+ });
191
+ return {
192
+ outcomes,
193
+ passed: outcomes.filter((outcome) => outcome.passed).length,
194
+ failed: outcomes.filter((outcome) => !outcome.passed).length,
195
+ measured: outcomes.filter((outcome) => outcome.parsed).length,
196
+ };
197
+ }
198
+ if (invoked(import.meta.url)) {
199
+ const own = await rules();
200
+ const governing = own ?? CONSTRAINTS;
201
+ const source = own === undefined
202
+ ? `the ${String(CONSTRAINTS.length)} constraint(s) this tree authors`
203
+ : `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`;
204
+ const slopnators = await load(ROOT, governing);
205
+ const derivation = deriveTrials(governing, slopnators);
206
+ const byproduct = trialsByproduct(derivation);
207
+ if (process.argv.includes('--check')) {
208
+ const path = join(ROOT, byproduct.path);
209
+ const current = existsSync(path) ? readFileSync(path, 'utf8') : '';
210
+ const drifted = current !== byproduct.content;
211
+ if (drifted)
212
+ console.error(`trial: ${byproduct.path} is not what the rules derive. Run \`thread trials\` and commit the result.`);
213
+ console.log(`trial: ${String(derivation.trials.length)} derived from ${source}, ${String(drifted ? 1 : 0)} findings.`);
214
+ exit(drifted ? 1 : 0);
215
+ }
216
+ if (process.argv.includes('--try')) {
217
+ const at = process.argv.indexOf('--rule');
218
+ const only = at === -1 ? undefined : (process.argv[at + 1] ?? '').split(',').map((one) => one.trim()).filter(Boolean);
219
+ const unknown = (only ?? []).filter((id) => !governing.some((rule) => rule.id === id));
220
+ if (unknown.length > 0) {
221
+ console.error(`trial: no rule is called ${unknown.join(', ')} — a filter naming nothing would measure nothing and exit 0`);
222
+ exit(1);
223
+ }
224
+ const chosen = only === undefined ? derivation.trials : derivation.trials.filter((trial) => only.includes(trial.rule));
225
+ if (chosen.length === 0) {
226
+ if (only !== undefined) {
227
+ console.error('trial: the filter selected no trials, and measuring nothing must not exit 0');
228
+ exit(1);
229
+ }
230
+ if (governing.length > 0) {
231
+ console.error(`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`);
232
+ exit(1);
233
+ }
234
+ console.log(`trial: nothing to prove — ${source}.`);
235
+ exit(0);
236
+ }
237
+ const { default: plugin } = await import("../lint.js");
238
+ const parser = (await import('typescript-eslint')).parser;
239
+ const run = runTrials(chosen, governing, plugin.rules, parser, slopnators);
240
+ for (const outcome of run.outcomes) {
241
+ if (outcome.passed)
242
+ continue;
243
+ for (const failure of outcome.failures)
244
+ console.error(`trial: ${outcome.id} — ${failure}`);
245
+ }
246
+ console.log(`trial: ${String(run.passed)} of ${String(run.outcomes.length)} passed, ${String(run.measured)} measured.`);
247
+ if (only !== undefined) {
248
+ console.log(`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.`);
249
+ }
250
+ exit(run.failed === 0 ? 0 : 1);
251
+ }
252
+ write(ROOT, byproduct);
253
+ console.log(`trial: wrote ${byproduct.path} — ${String(derivation.trials.length)} trials from ${source}.`);
254
+ }
@@ -0,0 +1,50 @@
1
+ import { type Rule } from 'eslint';
2
+ import { type Constraint } from './constraint.ts';
3
+ import { type Slopnator } from './slopnator.ts';
4
+ export declare function ruleMessage(rule: Constraint): string;
5
+ export declare function agentInstructions(): readonly string[];
6
+ export declare function defineRule(rule: Constraint, slopnators: ReadonlyMap<string, Slopnator>): Constraint;
7
+ export declare function defineRules(slopnators: ReadonlyMap<string, Slopnator>): readonly Constraint[];
8
+ export declare function ruleRule(rule: Constraint, slopnators: ReadonlyMap<string, Slopnator>): Rule.RuleModule;
9
+ export declare function ruleRules(slopnators: ReadonlyMap<string, Slopnator>): Readonly<Record<string, Rule.RuleModule>>;
10
+ export declare function ruleSeverities(): Readonly<Record<string, 'error' | 'warn'>>;
11
+ export declare const plugin: {
12
+ rules: Readonly<Record<string, Rule.RuleModule>>;
13
+ };
14
+ export default plugin;
15
+ export declare const rules: {
16
+ files: string[];
17
+ ignores: readonly string[];
18
+ languageOptions: {
19
+ parser: import("typescript-eslint").CompatibleParser;
20
+ parserOptions: {
21
+ ecmaVersion: string;
22
+ sourceType: string;
23
+ ecmaFeatures: {
24
+ jsx: boolean;
25
+ };
26
+ };
27
+ };
28
+ plugins: {
29
+ lint: {
30
+ rules: Readonly<Record<string, Rule.RuleModule>>;
31
+ };
32
+ };
33
+ rules: Readonly<Record<string, "error" | "warn">>;
34
+ };
35
+ export declare function ruleDeclarations(): readonly (Constraint & {
36
+ readonly name: string;
37
+ })[];
38
+ export interface RuleViolation {
39
+ readonly file: string;
40
+ readonly line: number;
41
+ readonly column: number;
42
+ readonly rule: string;
43
+ readonly message: string;
44
+ readonly bans?: string;
45
+ readonly instead?: string;
46
+ readonly wrong?: string;
47
+ readonly right?: string;
48
+ }
49
+ export type RuleRunner = (file: string, source: string) => readonly RuleViolation[];
50
+ export declare function theRules(): RuleRunner;