archcheck 0.2.0

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 (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +536 -0
  3. package/dist/archcheck.d.ts +99 -0
  4. package/dist/archcheck.d.ts.map +1 -0
  5. package/dist/archcheck.js +339 -0
  6. package/dist/archcheck.js.map +1 -0
  7. package/dist/baseline/index.d.ts +29 -0
  8. package/dist/baseline/index.d.ts.map +1 -0
  9. package/dist/baseline/index.js +116 -0
  10. package/dist/baseline/index.js.map +1 -0
  11. package/dist/cli/args.d.ts +36 -0
  12. package/dist/cli/args.d.ts.map +1 -0
  13. package/dist/cli/args.js +233 -0
  14. package/dist/cli/args.js.map +1 -0
  15. package/dist/cli/index.d.ts +4 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +219 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/version.d.ts +2 -0
  20. package/dist/cli/version.d.ts.map +1 -0
  21. package/dist/cli/version.js +29 -0
  22. package/dist/cli/version.js.map +1 -0
  23. package/dist/contracts/banned-syntax.d.ts +48 -0
  24. package/dist/contracts/banned-syntax.d.ts.map +1 -0
  25. package/dist/contracts/banned-syntax.js +201 -0
  26. package/dist/contracts/banned-syntax.js.map +1 -0
  27. package/dist/contracts/declared-field.d.ts +39 -0
  28. package/dist/contracts/declared-field.d.ts.map +1 -0
  29. package/dist/contracts/declared-field.js +142 -0
  30. package/dist/contracts/declared-field.js.map +1 -0
  31. package/dist/contracts/encapsulation.d.ts +28 -0
  32. package/dist/contracts/encapsulation.d.ts.map +1 -0
  33. package/dist/contracts/encapsulation.js +130 -0
  34. package/dist/contracts/encapsulation.js.map +1 -0
  35. package/dist/contracts/index.d.ts +10 -0
  36. package/dist/contracts/index.d.ts.map +1 -0
  37. package/dist/contracts/index.js +9 -0
  38. package/dist/contracts/index.js.map +1 -0
  39. package/dist/contracts/judgement.d.ts +21 -0
  40. package/dist/contracts/judgement.d.ts.map +1 -0
  41. package/dist/contracts/judgement.js +109 -0
  42. package/dist/contracts/judgement.js.map +1 -0
  43. package/dist/contracts/layers.d.ts +75 -0
  44. package/dist/contracts/layers.d.ts.map +1 -0
  45. package/dist/contracts/layers.js +244 -0
  46. package/dist/contracts/layers.js.map +1 -0
  47. package/dist/contracts/naming.d.ts +21 -0
  48. package/dist/contracts/naming.d.ts.map +1 -0
  49. package/dist/contracts/naming.js +120 -0
  50. package/dist/contracts/naming.js.map +1 -0
  51. package/dist/contracts/registry.d.ts +23 -0
  52. package/dist/contracts/registry.d.ts.map +1 -0
  53. package/dist/contracts/registry.js +48 -0
  54. package/dist/contracts/registry.js.map +1 -0
  55. package/dist/contracts/required-file.d.ts +29 -0
  56. package/dist/contracts/required-file.d.ts.map +1 -0
  57. package/dist/contracts/required-file.js +131 -0
  58. package/dist/contracts/required-file.js.map +1 -0
  59. package/dist/contracts/types.d.ts +61 -0
  60. package/dist/contracts/types.d.ts.map +1 -0
  61. package/dist/contracts/types.js +2 -0
  62. package/dist/contracts/types.js.map +1 -0
  63. package/dist/coverage/index.d.ts +55 -0
  64. package/dist/coverage/index.d.ts.map +1 -0
  65. package/dist/coverage/index.js +68 -0
  66. package/dist/coverage/index.js.map +1 -0
  67. package/dist/emit/collect.d.ts +20 -0
  68. package/dist/emit/collect.d.ts.map +1 -0
  69. package/dist/emit/collect.js +17 -0
  70. package/dist/emit/collect.js.map +1 -0
  71. package/dist/emit/header.d.ts +21 -0
  72. package/dist/emit/header.d.ts.map +1 -0
  73. package/dist/emit/header.js +20 -0
  74. package/dist/emit/header.js.map +1 -0
  75. package/dist/emit/index.d.ts +57 -0
  76. package/dist/emit/index.d.ts.map +1 -0
  77. package/dist/emit/index.js +128 -0
  78. package/dist/emit/index.js.map +1 -0
  79. package/dist/emit/merge.d.ts +23 -0
  80. package/dist/emit/merge.d.ts.map +1 -0
  81. package/dist/emit/merge.js +71 -0
  82. package/dist/emit/merge.js.map +1 -0
  83. package/dist/emit/write.d.ts +20 -0
  84. package/dist/emit/write.d.ts.map +1 -0
  85. package/dist/emit/write.js +61 -0
  86. package/dist/emit/write.js.map +1 -0
  87. package/dist/exceptions/index.d.ts +46 -0
  88. package/dist/exceptions/index.d.ts.map +1 -0
  89. package/dist/exceptions/index.js +117 -0
  90. package/dist/exceptions/index.js.map +1 -0
  91. package/dist/fs/index.d.ts +12 -0
  92. package/dist/fs/index.d.ts.map +1 -0
  93. package/dist/fs/index.js +78 -0
  94. package/dist/fs/index.js.map +1 -0
  95. package/dist/graph/cache.d.ts +12 -0
  96. package/dist/graph/cache.d.ts.map +1 -0
  97. package/dist/graph/cache.js +76 -0
  98. package/dist/graph/cache.js.map +1 -0
  99. package/dist/graph/edges.d.ts +16 -0
  100. package/dist/graph/edges.d.ts.map +1 -0
  101. package/dist/graph/edges.js +161 -0
  102. package/dist/graph/edges.js.map +1 -0
  103. package/dist/graph/index.d.ts +15 -0
  104. package/dist/graph/index.d.ts.map +1 -0
  105. package/dist/graph/index.js +90 -0
  106. package/dist/graph/index.js.map +1 -0
  107. package/dist/graph/match.d.ts +12 -0
  108. package/dist/graph/match.d.ts.map +1 -0
  109. package/dist/graph/match.js +35 -0
  110. package/dist/graph/match.js.map +1 -0
  111. package/dist/graph/program.d.ts +12 -0
  112. package/dist/graph/program.d.ts.map +1 -0
  113. package/dist/graph/program.js +54 -0
  114. package/dist/graph/program.js.map +1 -0
  115. package/dist/graph/types.d.ts +55 -0
  116. package/dist/graph/types.d.ts.map +1 -0
  117. package/dist/graph/types.js +2 -0
  118. package/dist/graph/types.js.map +1 -0
  119. package/dist/index.d.ts +21 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +20 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/init/index.d.ts +23 -0
  124. package/dist/init/index.d.ts.map +1 -0
  125. package/dist/init/index.js +218 -0
  126. package/dist/init/index.js.map +1 -0
  127. package/dist/normalize/identity.d.ts +4 -0
  128. package/dist/normalize/identity.d.ts.map +1 -0
  129. package/dist/normalize/identity.js +33 -0
  130. package/dist/normalize/identity.js.map +1 -0
  131. package/dist/normalize/index.d.ts +3 -0
  132. package/dist/normalize/index.d.ts.map +1 -0
  133. package/dist/normalize/index.js +3 -0
  134. package/dist/normalize/index.js.map +1 -0
  135. package/dist/normalize/normalize.d.ts +34 -0
  136. package/dist/normalize/normalize.d.ts.map +1 -0
  137. package/dist/normalize/normalize.js +91 -0
  138. package/dist/normalize/normalize.js.map +1 -0
  139. package/dist/probes/graph.d.ts +19 -0
  140. package/dist/probes/graph.d.ts.map +1 -0
  141. package/dist/probes/graph.js +181 -0
  142. package/dist/probes/graph.js.map +1 -0
  143. package/dist/probes/index.d.ts +5 -0
  144. package/dist/probes/index.d.ts.map +1 -0
  145. package/dist/probes/index.js +5 -0
  146. package/dist/probes/index.js.map +1 -0
  147. package/dist/probes/lint.d.ts +17 -0
  148. package/dist/probes/lint.d.ts.map +1 -0
  149. package/dist/probes/lint.js +121 -0
  150. package/dist/probes/lint.js.map +1 -0
  151. package/dist/probes/model/anthropic.d.ts +47 -0
  152. package/dist/probes/model/anthropic.d.ts.map +1 -0
  153. package/dist/probes/model/anthropic.js +100 -0
  154. package/dist/probes/model/anthropic.js.map +1 -0
  155. package/dist/probes/model/budget.d.ts +22 -0
  156. package/dist/probes/model/budget.d.ts.map +1 -0
  157. package/dist/probes/model/budget.js +27 -0
  158. package/dist/probes/model/budget.js.map +1 -0
  159. package/dist/probes/model/cache.d.ts +13 -0
  160. package/dist/probes/model/cache.d.ts.map +1 -0
  161. package/dist/probes/model/cache.js +74 -0
  162. package/dist/probes/model/cache.js.map +1 -0
  163. package/dist/probes/model/index.d.ts +20 -0
  164. package/dist/probes/model/index.d.ts.map +1 -0
  165. package/dist/probes/model/index.js +160 -0
  166. package/dist/probes/model/index.js.map +1 -0
  167. package/dist/probes/model/prompt.d.ts +16 -0
  168. package/dist/probes/model/prompt.d.ts.map +1 -0
  169. package/dist/probes/model/prompt.js +57 -0
  170. package/dist/probes/model/prompt.js.map +1 -0
  171. package/dist/probes/model/provider.d.ts +88 -0
  172. package/dist/probes/model/provider.d.ts.map +1 -0
  173. package/dist/probes/model/provider.js +48 -0
  174. package/dist/probes/model/provider.js.map +1 -0
  175. package/dist/probes/structure.d.ts +3 -0
  176. package/dist/probes/structure.d.ts.map +1 -0
  177. package/dist/probes/structure.js +95 -0
  178. package/dist/probes/structure.js.map +1 -0
  179. package/dist/probes/types.d.ts +46 -0
  180. package/dist/probes/types.d.ts.map +1 -0
  181. package/dist/probes/types.js +15 -0
  182. package/dist/probes/types.js.map +1 -0
  183. package/dist/registry/define.d.ts +40 -0
  184. package/dist/registry/define.d.ts.map +1 -0
  185. package/dist/registry/define.js +4 -0
  186. package/dist/registry/define.js.map +1 -0
  187. package/dist/registry/ids.d.ts +19 -0
  188. package/dist/registry/ids.d.ts.map +1 -0
  189. package/dist/registry/ids.js +86 -0
  190. package/dist/registry/ids.js.map +1 -0
  191. package/dist/registry/index.d.ts +45 -0
  192. package/dist/registry/index.d.ts.map +1 -0
  193. package/dist/registry/index.js +71 -0
  194. package/dist/registry/index.js.map +1 -0
  195. package/dist/registry/load.d.ts +14 -0
  196. package/dist/registry/load.d.ts.map +1 -0
  197. package/dist/registry/load.js +108 -0
  198. package/dist/registry/load.js.map +1 -0
  199. package/dist/registry/validate.d.ts +24 -0
  200. package/dist/registry/validate.d.ts.map +1 -0
  201. package/dist/registry/validate.js +160 -0
  202. package/dist/registry/validate.js.map +1 -0
  203. package/dist/report/index.d.ts +10 -0
  204. package/dist/report/index.d.ts.map +1 -0
  205. package/dist/report/index.js +30 -0
  206. package/dist/report/index.js.map +1 -0
  207. package/dist/report/json.d.ts +20 -0
  208. package/dist/report/json.d.ts.map +1 -0
  209. package/dist/report/json.js +34 -0
  210. package/dist/report/json.js.map +1 -0
  211. package/dist/report/sarif.d.ts +21 -0
  212. package/dist/report/sarif.d.ts.map +1 -0
  213. package/dist/report/sarif.js +138 -0
  214. package/dist/report/sarif.js.map +1 -0
  215. package/dist/report/terminal.d.ts +3 -0
  216. package/dist/report/terminal.d.ts.map +1 -0
  217. package/dist/report/terminal.js +117 -0
  218. package/dist/report/terminal.js.map +1 -0
  219. package/dist/report/types.d.ts +28 -0
  220. package/dist/report/types.d.ts.map +1 -0
  221. package/dist/report/types.js +11 -0
  222. package/dist/report/types.js.map +1 -0
  223. package/dist/report/verdict.d.ts +21 -0
  224. package/dist/report/verdict.d.ts.map +1 -0
  225. package/dist/report/verdict.js +31 -0
  226. package/dist/report/verdict.js.map +1 -0
  227. package/dist/result.d.ts +22 -0
  228. package/dist/result.d.ts.map +1 -0
  229. package/dist/result.js +19 -0
  230. package/dist/result.js.map +1 -0
  231. package/dist/types.d.ts +178 -0
  232. package/dist/types.d.ts.map +1 -0
  233. package/dist/types.js +11 -0
  234. package/dist/types.js.map +1 -0
  235. package/dist/util/compilerApi.d.ts +2 -0
  236. package/dist/util/compilerApi.d.ts.map +1 -0
  237. package/dist/util/compilerApi.js +28 -0
  238. package/dist/util/compilerApi.js.map +1 -0
  239. package/dist/util/glob.d.ts +43 -0
  240. package/dist/util/glob.d.ts.map +1 -0
  241. package/dist/util/glob.js +195 -0
  242. package/dist/util/glob.js.map +1 -0
  243. package/dist/util/paths.d.ts +6 -0
  244. package/dist/util/paths.d.ts.map +1 -0
  245. package/dist/util/paths.js +22 -0
  246. package/dist/util/paths.js.map +1 -0
  247. package/dist/version.d.ts +13 -0
  248. package/dist/version.d.ts.map +1 -0
  249. package/dist/version.js +12 -0
  250. package/dist/version.js.map +1 -0
  251. package/package.json +72 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The `banned-syntax` kind. Constructs that are wrong wherever they appear in a given scope.
3
+ *
4
+ * What rule this enforces, and why the selector is a CLOSED SET rather than a selector language:
5
+ * the six rule families this kind exists for are
6
+ * no clock in a pure rule, no computed dynamic import, no console outside an entry point,
7
+ * no inline suppression comment, no branching on a literal name, no empty catch.
8
+ * Each is one named construct. A CSS-ish selector string would have to be parsed, would be
9
+ * half-implemented for years, and would let a manifest declare a selector that silently matches
10
+ * nothing — which looks exactly like compliance. A closed enum cannot do that: an unknown
11
+ * construct fails validation and exits 2.
12
+ *
13
+ * The inline-suppression ban is self-referential and deliberate. Exceptions live in the manifest
14
+ * with an owner and an expiry (plan 15); making the inline escape hatch itself a violation is what
15
+ * stops the manifest being bypassed.
16
+ *
17
+ * What a caller must know: these are evaluated over OUR OWN AST. We already hold the program from
18
+ * the graph reader, so a second parse would be waste. `remedy.autofix` stays false.
19
+ */
20
+ import ts from 'typescript';
21
+ import { err, ok } from '../result.js';
22
+ import { matchesAny } from '../util/glob.js';
23
+ /** The closed set. Adding one is a deliberate change with a fixture, not a config value. */
24
+ export const BANNED_CONSTRUCTS = [
25
+ 'member-access',
26
+ 'new-expression',
27
+ 'computed-dynamic-import',
28
+ 'suppression-comment',
29
+ 'string-literal-comparison',
30
+ 'empty-catch',
31
+ ];
32
+ /** Constructs that name what they ban, and are meaningless without it. */
33
+ const NEEDS_TARGET = new Set(['member-access', 'new-expression']);
34
+ export function asBannedSyntaxParams(rule) {
35
+ return rule.params;
36
+ }
37
+ export function bannedSyntaxApplies(params, relativePath) {
38
+ const globs = typeof params.appliesTo === 'string' ? [params.appliesTo] : params.appliesTo;
39
+ return matchesAny(globs, relativePath);
40
+ }
41
+ function positionOf(source, node) {
42
+ const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
43
+ return { line: line + 1, col: character + 1 };
44
+ }
45
+ /** `console` matches `console.log`; `Date.now` matches only `Date.now`. */
46
+ function dottedMatches(target, text) {
47
+ return text === target || text.startsWith(`${target}.`);
48
+ }
49
+ const SUPPRESSION = /(eslint-disable|biome-ignore|@ts-ignore|@ts-expect-error|archcheck-ignore)/;
50
+ function scanAst(params, source, found) {
51
+ const record = (node, snippet) => {
52
+ found.push({ ...positionOf(source, node), snippet });
53
+ };
54
+ const visit = (node) => {
55
+ switch (params.selector) {
56
+ case 'member-access':
57
+ if (ts.isPropertyAccessExpression(node) &&
58
+ dottedMatches(params.target ?? '', node.getText(source))) {
59
+ record(node, node.getText(source));
60
+ }
61
+ else if (ts.isIdentifier(node) &&
62
+ node.text === params.target &&
63
+ !ts.isPropertyAccessExpression(node.parent)) {
64
+ record(node, node.text);
65
+ }
66
+ break;
67
+ case 'new-expression':
68
+ if (ts.isNewExpression(node) && node.expression.getText(source) === params.target) {
69
+ record(node, `new ${params.target}()`);
70
+ }
71
+ break;
72
+ case 'string-literal-comparison':
73
+ if (ts.isBinaryExpression(node) &&
74
+ (node.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken ||
75
+ node.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) &&
76
+ (ts.isStringLiteralLike(node.left) || ts.isStringLiteralLike(node.right))) {
77
+ record(node, node.getText(source).slice(0, 60));
78
+ }
79
+ break;
80
+ case 'empty-catch':
81
+ // A block holding only a comment is an EXPLAINED silence, which the rule allows. Counting
82
+ // statements alone flags it, and found two false positives in archcheck's own source the
83
+ // first time it checked itself.
84
+ if (ts.isCatchClause(node) &&
85
+ node.block.statements.length === 0 &&
86
+ node.block.getText(source).replace(/[{}\s]/g, '').length === 0) {
87
+ record(node, 'catch {}');
88
+ }
89
+ break;
90
+ case 'computed-dynamic-import':
91
+ case 'suppression-comment':
92
+ // Handled outside the AST walk: one from the graph, one from the comment scanner.
93
+ break;
94
+ }
95
+ ts.forEachChild(node, visit);
96
+ };
97
+ ts.forEachChild(source, visit);
98
+ }
99
+ /**
100
+ * Comment trivia only, via the compiler's own scanner.
101
+ *
102
+ * Reading raw lines instead looks equivalent and is not: it matches the words wherever they appear,
103
+ * including inside the very pattern above. That false positive is how this function was found — by
104
+ * archcheck flagging this file on its first self-host run.
105
+ */
106
+ function scanComments(source, found) {
107
+ const text = source.getFullText();
108
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.Standard, text);
109
+ for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
110
+ if (token !== ts.SyntaxKind.SingleLineCommentTrivia &&
111
+ token !== ts.SyntaxKind.MultiLineCommentTrivia) {
112
+ continue;
113
+ }
114
+ const comment = scanner.getTokenText();
115
+ if (!SUPPRESSION.test(comment))
116
+ continue;
117
+ const { line, character } = source.getLineAndCharacterOfPosition(scanner.getTokenStart());
118
+ found.push({ line: line + 1, col: character + 1, snippet: comment.trim().slice(0, 60) });
119
+ }
120
+ }
121
+ /**
122
+ * Occurrences of the banned construct in one file.
123
+ *
124
+ * `computedImports` comes from the graph reader, which already detects a computed dynamic import —
125
+ * re-detecting it here would be a second implementation of the one thing that is hard to get right.
126
+ */
127
+ export function findBannedOccurrences(params, source, computedImports) {
128
+ const found = [];
129
+ if (params.selector === 'computed-dynamic-import') {
130
+ for (const edge of computedImports) {
131
+ found.push({ line: edge.line, col: edge.col, snippet: `import(${edge.specifier})` });
132
+ }
133
+ return found;
134
+ }
135
+ if (source === undefined)
136
+ return found;
137
+ if (params.selector === 'suppression-comment') {
138
+ scanComments(source, found);
139
+ return found;
140
+ }
141
+ scanAst(params, source, found);
142
+ return found;
143
+ }
144
+ function validateParams(rule, _ctx) {
145
+ const params = rule.params;
146
+ const errors = [];
147
+ const appliesTo = params.appliesTo;
148
+ const globs = typeof appliesTo === 'string' ? [appliesTo] : appliesTo;
149
+ if (!Array.isArray(globs) || globs.length === 0 || !globs.every((g) => typeof g === 'string')) {
150
+ errors.push('params.appliesTo is required — a path glob, or a list of them');
151
+ }
152
+ const selector = params.selector;
153
+ if (!BANNED_CONSTRUCTS.includes(selector)) {
154
+ errors.push(`params.selector must be one of ${BANNED_CONSTRUCTS.join(', ')} ` +
155
+ `(got ${JSON.stringify(params.selector)}) — the set is closed so a rule cannot silently match nothing`);
156
+ }
157
+ else if (NEEDS_TARGET.has(selector) &&
158
+ (typeof params.target !== 'string' || params.target.length === 0)) {
159
+ errors.push(`params.target is required for selector '${selector}' — name what is banned`);
160
+ }
161
+ else if (!NEEDS_TARGET.has(selector) && params.target !== undefined) {
162
+ errors.push(`params.target means nothing for selector '${selector}' — remove it`);
163
+ }
164
+ return errors.length > 0 ? err(errors) : ok(undefined);
165
+ }
166
+ function emitFragments(rule, _ctx) {
167
+ const params = asBannedSyntaxParams(rule);
168
+ return [
169
+ {
170
+ target: 'lint',
171
+ ruleId: rule.id,
172
+ content: {
173
+ files: typeof params.appliesTo === 'string' ? [params.appliesTo] : params.appliesTo,
174
+ forbid: params.selector,
175
+ ...(params.target === undefined ? {} : { target: params.target }),
176
+ message: `${rule.statement} — ${rule.remedy.summary}`,
177
+ autofix: false,
178
+ },
179
+ },
180
+ ];
181
+ }
182
+ function explainFinding(finding, rule, _ctx) {
183
+ const evidence = finding.evidence;
184
+ return [
185
+ rule.statement,
186
+ ` ${finding.file}${finding.line === undefined ? '' : `:${finding.line}`}`,
187
+ ` found: ${String(evidence.snippet)}`,
188
+ ` → ${rule.remedy.summary}`,
189
+ ].join('\n');
190
+ }
191
+ function refineFinding(_finding, rule) {
192
+ return { remedy: { ...rule.remedy, autofix: false } };
193
+ }
194
+ export const bannedSyntaxContract = {
195
+ kind: 'banned-syntax',
196
+ validate: validateParams,
197
+ emit: emitFragments,
198
+ explain: explainFinding,
199
+ refine: refineFinding,
200
+ };
201
+ //# sourceMappingURL=banned-syntax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banned-syntax.js","sourceRoot":"","sources":["../../src/contracts/banned-syntax.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The `declared-field` kind — rules about what must be DECLARED at a call site.
3
+ *
4
+ * What this covers: "every endpoint declares a permission and an operation id". A global middleware
5
+ * chain knows who the caller is; only the endpoint knows what is being asked, so the declaration
6
+ * has to be at the call site — and a call site that forgot it is invisible to every other kind.
7
+ *
8
+ * This is AST work, not filesystem work. It means finding the call expression and reading its
9
+ * arguments, which needs the program the graph reader already built.
10
+ *
11
+ * What a caller must know: the check is that the field is PRESENT (and, optionally, one of a set
12
+ * of allowed values). Whether the value is the right permission for that endpoint is a judgement
13
+ * rule, not this one.
14
+ */
15
+ import ts from 'typescript';
16
+ import type { Rule } from '../types.js';
17
+ import type { ContractType } from './types.js';
18
+ export type DeclaredFieldParams = {
19
+ appliesTo: string | string[];
20
+ /** The callee, e.g. `registerRoute` or `router.get`. */
21
+ on: string;
22
+ /** The property that must appear in one of the call's object-literal arguments. */
23
+ field: string;
24
+ /** When given, the value must also be one of these string literals. */
25
+ allowed?: string[];
26
+ };
27
+ export declare function asDeclaredFieldParams(rule: Rule): DeclaredFieldParams;
28
+ export declare function declaredFieldApplies(params: DeclaredFieldParams, relativePath: string): boolean;
29
+ export type MissingDeclaration = {
30
+ line: number;
31
+ col: number;
32
+ call: string;
33
+ /** 'absent' when the field is missing entirely; 'not-allowed' when its value is out of range. */
34
+ problem: 'absent' | 'not-allowed';
35
+ found?: string;
36
+ };
37
+ export declare function findMissingDeclarations(params: DeclaredFieldParams, source: ts.SourceFile | undefined): MissingDeclaration[];
38
+ export declare const declaredFieldContract: ContractType;
39
+ //# sourceMappingURL=declared-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-field.d.ts","sourceRoot":"","sources":["../../src/contracts/declared-field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAwB,IAAI,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAgD,MAAM,YAAY,CAAC;AAE7F,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,CAErE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAG/F;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,OAAO,EAAE,QAAQ,GAAG,aAAa,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAgBF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,SAAS,GAChC,kBAAkB,EAAE,CAmCtB;AAsED,eAAO,MAAM,qBAAqB,EAAE,YAMnC,CAAC"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The `declared-field` kind — rules about what must be DECLARED at a call site.
3
+ *
4
+ * What this covers: "every endpoint declares a permission and an operation id". A global middleware
5
+ * chain knows who the caller is; only the endpoint knows what is being asked, so the declaration
6
+ * has to be at the call site — and a call site that forgot it is invisible to every other kind.
7
+ *
8
+ * This is AST work, not filesystem work. It means finding the call expression and reading its
9
+ * arguments, which needs the program the graph reader already built.
10
+ *
11
+ * What a caller must know: the check is that the field is PRESENT (and, optionally, one of a set
12
+ * of allowed values). Whether the value is the right permission for that endpoint is a judgement
13
+ * rule, not this one.
14
+ */
15
+ import ts from 'typescript';
16
+ import { err, ok } from '../result.js';
17
+ import { matchesAny } from '../util/glob.js';
18
+ export function asDeclaredFieldParams(rule) {
19
+ return rule.params;
20
+ }
21
+ export function declaredFieldApplies(params, relativePath) {
22
+ const globs = typeof params.appliesTo === 'string' ? [params.appliesTo] : params.appliesTo;
23
+ return matchesAny(globs, relativePath);
24
+ }
25
+ function propertyNamed(object, field) {
26
+ return object.properties.find((property) => {
27
+ const name = property.name;
28
+ if (!name)
29
+ return false;
30
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name))
31
+ return name.text === field;
32
+ return false;
33
+ });
34
+ }
35
+ function literalValueOf(property) {
36
+ if (!ts.isPropertyAssignment(property))
37
+ return undefined;
38
+ return ts.isStringLiteralLike(property.initializer) ? property.initializer.text : undefined;
39
+ }
40
+ export function findMissingDeclarations(params, source) {
41
+ if (source === undefined)
42
+ return [];
43
+ const found = [];
44
+ const visit = (node) => {
45
+ if (ts.isCallExpression(node) && node.expression.getText(source) === params.on) {
46
+ const objects = node.arguments.filter(ts.isObjectLiteralExpression);
47
+ const property = objects
48
+ .map((object) => propertyNamed(object, params.field))
49
+ .find((match) => match !== undefined);
50
+ const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
51
+ const call = `${params.on}(…)`;
52
+ if (property === undefined) {
53
+ found.push({ line: line + 1, col: character + 1, call, problem: 'absent' });
54
+ }
55
+ else if (params.allowed && params.allowed.length > 0) {
56
+ const value = literalValueOf(property);
57
+ // A non-literal value is accepted: it may be a constant, and guessing would produce a
58
+ // false positive on correct code — which is worse than missing one.
59
+ if (value !== undefined && !params.allowed.includes(value)) {
60
+ found.push({
61
+ line: line + 1,
62
+ col: character + 1,
63
+ call,
64
+ problem: 'not-allowed',
65
+ found: value,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ ts.forEachChild(node, visit);
71
+ };
72
+ ts.forEachChild(source, visit);
73
+ return found;
74
+ }
75
+ function validateParams(rule, _ctx) {
76
+ const params = rule.params;
77
+ const errors = [];
78
+ const appliesTo = params.appliesTo;
79
+ const globs = typeof appliesTo === 'string' ? [appliesTo] : appliesTo;
80
+ if (!Array.isArray(globs) || globs.length === 0 || !globs.every((g) => typeof g === 'string')) {
81
+ errors.push('params.appliesTo is required — a path glob, or a list of them');
82
+ }
83
+ if (typeof params.on !== 'string' || params.on.length === 0) {
84
+ errors.push("params.on is required — the call being checked, e.g. 'registerRoute'");
85
+ }
86
+ if (typeof params.field !== 'string' || params.field.length === 0) {
87
+ errors.push("params.field is required — the property that must be declared, e.g. 'permission'");
88
+ }
89
+ if (params.allowed !== undefined) {
90
+ if (!Array.isArray(params.allowed) || !params.allowed.every((v) => typeof v === 'string')) {
91
+ errors.push('params.allowed must be a list of strings when given');
92
+ }
93
+ }
94
+ return errors.length > 0 ? err(errors) : ok(undefined);
95
+ }
96
+ function emitFragments(rule, _ctx) {
97
+ const params = asDeclaredFieldParams(rule);
98
+ return [
99
+ {
100
+ target: 'test',
101
+ ruleId: rule.id,
102
+ content: {
103
+ files: typeof params.appliesTo === 'string' ? [params.appliesTo] : params.appliesTo,
104
+ on: params.on,
105
+ field: params.field,
106
+ ...(params.allowed === undefined ? {} : { allowed: params.allowed }),
107
+ message: `${rule.statement} — ${rule.remedy.summary}`,
108
+ },
109
+ },
110
+ ];
111
+ }
112
+ function explainFinding(finding, rule, _ctx) {
113
+ const evidence = finding.evidence;
114
+ const params = asDeclaredFieldParams(rule);
115
+ const detail = evidence.problem === 'absent'
116
+ ? `declares no '${params.field}'`
117
+ : `declares '${params.field}' as '${String(evidence.found)}', which is not allowed`;
118
+ return [
119
+ `${String(evidence.call)} ${detail}`,
120
+ ` ${finding.file}${finding.line === undefined ? '' : `:${finding.line}`}`,
121
+ ...(params.allowed ? [` allowed: ${params.allowed.join(', ')}`] : []),
122
+ ` → ${rule.remedy.summary}`,
123
+ ].join('\n');
124
+ }
125
+ function refineFinding(finding, rule) {
126
+ const evidence = finding.evidence;
127
+ const params = asDeclaredFieldParams(rule);
128
+ return {
129
+ statement: evidence.problem === 'absent'
130
+ ? `${String(evidence.call)} must declare '${params.field}'`
131
+ : `'${String(evidence.found)}' is not an allowed '${params.field}'`,
132
+ remedy: { ...rule.remedy, ...(params.allowed ? { allowed: params.allowed } : {}) },
133
+ };
134
+ }
135
+ export const declaredFieldContract = {
136
+ kind: 'declared-field',
137
+ validate: validateParams,
138
+ emit: emitFragments,
139
+ explain: explainFinding,
140
+ refine: refineFinding,
141
+ };
142
+ //# sourceMappingURL=declared-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-field.js","sourceRoot":"","sources":["../../src/contracts/declared-field.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Rule } from '../types.js';
2
+ import type { ContractType } from './types.js';
3
+ export type EncapsulationParams = {
4
+ /** A glob matching module ROOT directories, e.g. `modules/*`. */
5
+ module: string;
6
+ /** Paths relative to a module root that may be imported from outside, e.g. `index.ts`. */
7
+ publicSurface: string | string[];
8
+ };
9
+ export declare function asEncapsulationParams(rule: Rule): EncapsulationParams;
10
+ export declare function publicSurfacesOf(rule: Rule): string[];
11
+ /**
12
+ * The module a repo-relative path belongs to, or undefined. Walks the path's ancestors and returns
13
+ * the first that the module glob matches — so `modules/*` claims `modules/ledger` for every file
14
+ * beneath it.
15
+ */
16
+ export declare function moduleOf(moduleGlob: string, relativePath: string): string | undefined;
17
+ export type EncapsulationVerdict = {
18
+ allowed: true;
19
+ } | {
20
+ allowed: false;
21
+ module: string;
22
+ surfaces: string[];
23
+ deepPath: string;
24
+ };
25
+ /** The whole decision, pure and path-only, so every case is testable without a fixture repo. */
26
+ export declare function resolveEncapsulation(params: EncapsulationParams, fromRelative: string, toRelative: string): EncapsulationVerdict;
27
+ export declare const encapsulationContract: ContractType;
28
+ //# sourceMappingURL=encapsulation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encapsulation.d.ts","sourceRoot":"","sources":["../../src/contracts/encapsulation.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAwB,IAAI,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAgD,MAAM,YAAY,CAAC;AAE7F,MAAM,MAAM,mBAAmB,GAAG;IAChC,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAClC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,CAErE;AAQD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAErD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOrF;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,oBAAoB,CAatB;AA2ED,eAAO,MAAM,qBAAqB,EAAE,YAMnC,CAAC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The `encapsulation` kind — the module barrel, and the deep-import ban generated from it.
3
+ *
4
+ * What rule this enforces: another module imports `modules/ledger`, never
5
+ * `modules/ledger/services/write`. This is the rule most often broken by accident, and the one
6
+ * where the generated exports map moves it from DETECTED to IMPOSSIBLE.
7
+ *
8
+ * Two sinks, one rule — the case that forced `emit()` to return an array (LLD §1.2):
9
+ * 1. graph — catches deep imports inside the repo, where module resolution does not apply
10
+ * 2. exports — makes the deep path unresolvable for anything outside the package
11
+ * The second costs nothing extra once the first exists, and it is the promotion from a rule people
12
+ * break to a path that does not exist.
13
+ *
14
+ * What a caller must know: only edges that CROSS a module boundary are checked.
15
+ * `modules/ledger/services/write.ts` importing `modules/ledger/domain/supersede.ts` is a normal
16
+ * internal import, not a barrel violation.
17
+ */
18
+ import { err, ok } from '../result.js';
19
+ import { matches } from '../util/glob.js';
20
+ export function asEncapsulationParams(rule) {
21
+ return rule.params;
22
+ }
23
+ const asParams = asEncapsulationParams;
24
+ function surfacesOf(params) {
25
+ return typeof params.publicSurface === 'string' ? [params.publicSurface] : params.publicSurface;
26
+ }
27
+ export function publicSurfacesOf(rule) {
28
+ return surfacesOf(asParams(rule));
29
+ }
30
+ /**
31
+ * The module a repo-relative path belongs to, or undefined. Walks the path's ancestors and returns
32
+ * the first that the module glob matches — so `modules/*` claims `modules/ledger` for every file
33
+ * beneath it.
34
+ */
35
+ export function moduleOf(moduleGlob, relativePath) {
36
+ const segments = relativePath.split('/');
37
+ for (let depth = segments.length - 1; depth > 0; depth -= 1) {
38
+ const candidate = segments.slice(0, depth).join('/');
39
+ if (matches(moduleGlob, candidate))
40
+ return candidate;
41
+ }
42
+ return undefined;
43
+ }
44
+ /** The whole decision, pure and path-only, so every case is testable without a fixture repo. */
45
+ export function resolveEncapsulation(params, fromRelative, toRelative) {
46
+ const target = moduleOf(params.module, toRelative);
47
+ if (target === undefined)
48
+ return { allowed: true };
49
+ if (moduleOf(params.module, fromRelative) === target)
50
+ return { allowed: true };
51
+ const inside = toRelative.slice(target.length + 1);
52
+ const surfaces = surfacesOf(params);
53
+ // `index.ts` in the manifest must accept `index.ts`, and a resolver that landed on the file
54
+ // itself rather than the directory. Both are the barrel.
55
+ if (surfaces.some((surface) => inside === surface || inside === surface.replace(/\.tsx?$/, ''))) {
56
+ return { allowed: true };
57
+ }
58
+ return { allowed: false, module: target, surfaces, deepPath: inside };
59
+ }
60
+ function validateParams(rule, _ctx) {
61
+ const params = rule.params;
62
+ const errors = [];
63
+ if (typeof params.module !== 'string' || params.module.length === 0) {
64
+ errors.push("params.module is required — a glob matching module roots, e.g. 'modules/*'");
65
+ }
66
+ const surface = params.publicSurface;
67
+ const list = typeof surface === 'string' ? [surface] : surface;
68
+ if (!Array.isArray(list) ||
69
+ list.length === 0 ||
70
+ !list.every((s) => typeof s === 'string' && s.length > 0)) {
71
+ errors.push("params.publicSurface is required — a path inside a module, e.g. 'index.ts'");
72
+ }
73
+ else {
74
+ for (const entry of list) {
75
+ if (entry.startsWith('/') || entry.startsWith('.')) {
76
+ errors.push(`params.publicSurface '${entry}' must be relative to the module root`);
77
+ }
78
+ }
79
+ }
80
+ return errors.length > 0 ? err(errors) : ok(undefined);
81
+ }
82
+ function emitFragments(rule, _ctx) {
83
+ const params = asParams(rule);
84
+ const surfaces = surfacesOf(params);
85
+ return [
86
+ {
87
+ target: 'graph',
88
+ ruleId: rule.id,
89
+ content: { module: params.module, publicSurface: surfaces, statement: rule.statement },
90
+ },
91
+ {
92
+ // The exports map: the deep path stops resolving at all for anything outside the package.
93
+ // We emit it; the consumer commits it into their own package.json.
94
+ target: 'exports',
95
+ ruleId: rule.id,
96
+ content: {
97
+ module: params.module,
98
+ exports: Object.fromEntries(surfaces.map((surface) => [
99
+ surface === 'index.ts' ? '.' : `./${surface.replace(/\.tsx?$/, '')}`,
100
+ `./${surface.replace(/\.tsx?$/, '.js')}`,
101
+ ])),
102
+ },
103
+ },
104
+ ];
105
+ }
106
+ function explainFinding(finding, rule, _ctx) {
107
+ const evidence = finding.evidence;
108
+ const surfaces = publicSurfacesOf(rule).join(', ');
109
+ return [
110
+ `${String(evidence.module)} is reached through its public surface only`,
111
+ ` ${finding.file} → ${String(evidence.to)}`,
112
+ ` public surface: ${surfaces}`,
113
+ ` → ${rule.remedy.summary}`,
114
+ ].join('\n');
115
+ }
116
+ function refineFinding(finding, rule) {
117
+ const evidence = finding.evidence;
118
+ return {
119
+ statement: `${String(evidence.module)} may only be imported through ${publicSurfacesOf(rule).join(' or ')}`,
120
+ remedy: { ...rule.remedy, allowed: publicSurfacesOf(rule) },
121
+ };
122
+ }
123
+ export const encapsulationContract = {
124
+ kind: 'encapsulation',
125
+ validate: validateParams,
126
+ emit: emitFragments,
127
+ explain: explainFinding,
128
+ refine: refineFinding,
129
+ };
130
+ //# sourceMappingURL=encapsulation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encapsulation.js","sourceRoot":"","sources":["../../src/contracts/encapsulation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { asBannedSyntaxParams, BANNED_CONSTRUCTS, type BannedConstruct, type BannedOccurrence, type BannedSyntaxParams, bannedSyntaxApplies, bannedSyntaxContract, findBannedOccurrences, } from './banned-syntax.js';
2
+ export { asDeclaredFieldParams, type DeclaredFieldParams, declaredFieldApplies, declaredFieldContract, findMissingDeclarations, type MissingDeclaration, } from './declared-field.js';
3
+ export { asEncapsulationParams, type EncapsulationParams, type EncapsulationVerdict, encapsulationContract, moduleOf, publicSurfacesOf, resolveEncapsulation, } from './encapsulation.js';
4
+ export { asJudgementParams, effectiveConfidence, type JudgementParams, judgementApplies, judgementContract, } from './judgement.js';
5
+ export { asLayersParams, buildLayerTable, type EdgeVerdict, type Layer, type LayersParams, type LayerTable, layersContract, resolveEdge, } from './layers.js';
6
+ export { asNamingParams, describeExpectation, type NamingParams, type NamingTarget, nameIsWellFormed, namingApplies, namingContract, } from './naming.js';
7
+ export { contractFor, implementedKinds, validateParamsByKind, warningsFor } from './registry.js';
8
+ export { asRequiredFileParams, isRuleSelector, type RequiredFileParams, type RequiredSubject, requiredFileContract, subjectsFor, } from './required-file.js';
9
+ export type { ContractType, EmitCtx, ExplainCtx, Refinement, ValidateCtx } from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,eAAe,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,oBAAoB,EACpB,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { asBannedSyntaxParams, BANNED_CONSTRUCTS, bannedSyntaxApplies, bannedSyntaxContract, findBannedOccurrences, } from './banned-syntax.js';
2
+ export { asDeclaredFieldParams, declaredFieldApplies, declaredFieldContract, findMissingDeclarations, } from './declared-field.js';
3
+ export { asEncapsulationParams, encapsulationContract, moduleOf, publicSurfacesOf, resolveEncapsulation, } from './encapsulation.js';
4
+ export { asJudgementParams, effectiveConfidence, judgementApplies, judgementContract, } from './judgement.js';
5
+ export { asLayersParams, buildLayerTable, layersContract, resolveEdge, } from './layers.js';
6
+ export { asNamingParams, describeExpectation, nameIsWellFormed, namingApplies, namingContract, } from './naming.js';
7
+ export { contractFor, implementedKinds, validateParamsByKind, warningsFor } from './registry.js';
8
+ export { asRequiredFileParams, isRuleSelector, requiredFileContract, subjectsFor, } from './required-file.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EAIjB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EAErB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,qBAAqB,EAGrB,qBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EAEnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,eAAe,EAKf,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,mBAAmB,EAGnB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,oBAAoB,EACpB,cAAc,EAGd,oBAAoB,EACpB,WAAW,GACZ,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Rule } from '../types.js';
2
+ import type { ContractType } from './types.js';
3
+ export type JudgementParams = {
4
+ /** One sentence, in plain English, that a reader and a model both understand. */
5
+ statement: string;
6
+ appliesTo: string | string[];
7
+ examples: {
8
+ /** Code that must NOT be flagged. At least one. */
9
+ pass: string[];
10
+ /** Code that MUST be flagged. At least one. */
11
+ fail: string[];
12
+ };
13
+ /** What the model must quote back to justify a verdict, e.g. 'the offending statement'. */
14
+ cite: string;
15
+ };
16
+ export declare function asJudgementParams(rule: Rule): JudgementParams;
17
+ export declare function judgementApplies(params: JudgementParams, relativePath: string): boolean;
18
+ /** `confidence` is not the manifest's to decide for this kind. */
19
+ export declare function effectiveConfidence(): 'model';
20
+ export declare const judgementContract: ContractType;
21
+ //# sourceMappingURL=judgement.d.ts.map