@terra-symposium/standards-quality 0.8.0 → 0.9.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.
- package/README.md +8 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/node-reporter.d.ts +79 -0
- package/dist/node-reporter.d.ts.map +1 -0
- package/dist/node-reporter.js +217 -0
- package/dist/node-reporter.js.map +1 -0
- package/dist/playwright-reporter.d.ts +107 -0
- package/dist/playwright-reporter.d.ts.map +1 -0
- package/dist/playwright-reporter.js +138 -0
- package/dist/playwright-reporter.js.map +1 -0
- package/dist/progress.d.ts +5 -2
- package/dist/progress.d.ts.map +1 -1
- package/dist/progress.js +6 -4
- package/dist/progress.js.map +1 -1
- package/dist/runner.d.ts +3 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +580 -86
- package/dist/runner.js.map +1 -1
- package/dist/single-use.d.ts +2 -1
- package/dist/single-use.d.ts.map +1 -1
- package/dist/single-use.js +42 -15
- package/dist/single-use.js.map +1 -1
- package/dist/test-workspace.d.ts +6 -0
- package/dist/test-workspace.d.ts.map +1 -0
- package/dist/test-workspace.js +48 -0
- package/dist/test-workspace.js.map +1 -0
- package/dist/ui-recommendations.d.ts +43 -0
- package/dist/ui-recommendations.d.ts.map +1 -0
- package/dist/ui-recommendations.js +182 -0
- package/dist/ui-recommendations.js.map +1 -0
- package/dist/workspace-test-runner.d.ts +6 -0
- package/dist/workspace-test-runner.d.ts.map +1 -0
- package/dist/workspace-test-runner.js +48 -0
- package/dist/workspace-test-runner.js.map +1 -0
- package/package.json +2 -2
- package/quality.schema.json +195 -130
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/quality/src/ui-recommendations.ts
|
|
3
|
+
* On-demand recommendations for native JSX controls with shared component equivalents.
|
|
4
|
+
*/
|
|
5
|
+
/*//////////////////////////////////////////////////
|
|
6
|
+
IMPORTS
|
|
7
|
+
//////////////////////////////////////////////////*/
|
|
8
|
+
// API
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { displayPath, srcFiles } from './files.js';
|
|
12
|
+
const DEFAULT_RULES = [
|
|
13
|
+
{
|
|
14
|
+
element: 'button',
|
|
15
|
+
component: 'Button',
|
|
16
|
+
import: '@/components/ui/button',
|
|
17
|
+
reason: 'Use the shared button primitive to keep action variants and interaction states consistent.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
element: 'input',
|
|
21
|
+
component: 'Input',
|
|
22
|
+
import: '@/components/ui/input',
|
|
23
|
+
types: ['date', 'datetime-local', 'email', 'file', 'month', 'number', 'password', 'search', 'tel', 'text', 'time', 'url', 'week'],
|
|
24
|
+
reason: 'Use the shared input primitive to keep control sizing, focus, and validation states consistent.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
element: 'input',
|
|
28
|
+
component: 'Button',
|
|
29
|
+
import: '@/components/ui/button',
|
|
30
|
+
types: ['button', 'reset', 'submit'],
|
|
31
|
+
reason: 'Use the shared button primitive for this input-based action while preserving its type.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
element: 'a',
|
|
35
|
+
component: 'ButtonLink',
|
|
36
|
+
import: '@/components/ui/button',
|
|
37
|
+
when: 'button-like',
|
|
38
|
+
reason: 'This action-styled link can use ButtonLink while retaining anchor navigation semantics.',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
const DEFAULT_EXTENSIONS = ['.jsx', '.tsx'];
|
|
42
|
+
/*//////////////////////////////////////////////////
|
|
43
|
+
HELPERS
|
|
44
|
+
//////////////////////////////////////////////////*/
|
|
45
|
+
/**
|
|
46
|
+
* Reads a literal JSX attribute value without evaluating runtime expressions.
|
|
47
|
+
*
|
|
48
|
+
* @param attribute JSX attribute to inspect.
|
|
49
|
+
*
|
|
50
|
+
* @return Static string value, or undefined when the value is dynamic or absent.
|
|
51
|
+
*/
|
|
52
|
+
function staticAttributeValue(attribute) {
|
|
53
|
+
const initializer = attribute?.initializer;
|
|
54
|
+
if (!initializer)
|
|
55
|
+
return attribute ? '' : undefined;
|
|
56
|
+
if (ts.isStringLiteral(initializer) || ts.isNoSubstitutionTemplateLiteral(initializer))
|
|
57
|
+
return initializer.text;
|
|
58
|
+
if (!ts.isJsxExpression(initializer) || !initializer.expression)
|
|
59
|
+
return undefined;
|
|
60
|
+
let expression = initializer.expression;
|
|
61
|
+
while (ts.isParenthesizedExpression(expression))
|
|
62
|
+
expression = expression.expression;
|
|
63
|
+
return ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression) ? expression.text : undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns one JSX attribute by its exact lower-case name.
|
|
67
|
+
*
|
|
68
|
+
* @param element JSX opening element to inspect.
|
|
69
|
+
* @param name Attribute name to find.
|
|
70
|
+
*
|
|
71
|
+
* @return Matching attribute when present.
|
|
72
|
+
*/
|
|
73
|
+
function jsxAttribute(element, name) {
|
|
74
|
+
return element.attributes.properties.find((property) => ts.isJsxAttribute(property) && ts.isIdentifier(property.name) && property.name.text === name);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks whether static anchor classes resemble an action control.
|
|
78
|
+
*
|
|
79
|
+
* @param value Static class attribute value.
|
|
80
|
+
*
|
|
81
|
+
* @return `true` when the link has button-like spacing and shape.
|
|
82
|
+
*/
|
|
83
|
+
function isButtonLike(value) {
|
|
84
|
+
const classes = new Set((value ?? '').split(/\s+/u).filter(Boolean));
|
|
85
|
+
const hasHorizontalPadding = [...classes].some((className) => /^px-/u.test(className));
|
|
86
|
+
const hasVerticalPadding = [...classes].some((className) => /^py-/u.test(className));
|
|
87
|
+
const hasShape = [...classes].some((className) => className === 'border' || className === 'inline-flex' || className === 'flex' || className.startsWith('rounded'));
|
|
88
|
+
const hasActionStyle = [...classes].some((className) => className === 'font-medium' || className === 'no-underline' || className.startsWith('bg-'));
|
|
89
|
+
return hasHorizontalPadding && hasVerticalPadding && hasShape && hasActionStyle;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Checks whether a recommendation rule applies to one intrinsic JSX element.
|
|
93
|
+
*
|
|
94
|
+
* @param rule Configured replacement and conditions.
|
|
95
|
+
* @param element JSX opening element to inspect.
|
|
96
|
+
*
|
|
97
|
+
* @return `true` when the rule's element, input type, and style conditions match.
|
|
98
|
+
*/
|
|
99
|
+
function ruleMatches(rule, element) {
|
|
100
|
+
const type = jsxAttribute(element, 'type');
|
|
101
|
+
if (rule.types) {
|
|
102
|
+
const typeValue = type ? staticAttributeValue(type) : 'text';
|
|
103
|
+
if (typeValue === undefined || !rule.types.includes(typeValue.toLowerCase()))
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
if (rule.when === 'button-like') {
|
|
107
|
+
const classes = staticAttributeValue(jsxAttribute(element, 'className')) ?? staticAttributeValue(jsxAttribute(element, 'class'));
|
|
108
|
+
return isButtonLike(classes);
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Checks whether a source file belongs to a UI primitive implementation directory.
|
|
114
|
+
*
|
|
115
|
+
* @param file Repository-relative source path.
|
|
116
|
+
* @param ignoredDirectories Explicit repository-relative directories to skip.
|
|
117
|
+
*
|
|
118
|
+
* @return `true` when the file is an implementation or an explicitly skipped path.
|
|
119
|
+
*/
|
|
120
|
+
function isIgnoredFile(file, ignoredDirectories, ignoredFiles) {
|
|
121
|
+
const normalized = file.replaceAll('\\', '/');
|
|
122
|
+
const segments = normalized.split('/');
|
|
123
|
+
const primitiveDirectory = segments.some((segment, index) => segment === 'components' && segments[index + 1] === 'ui');
|
|
124
|
+
return (primitiveDirectory ||
|
|
125
|
+
ignoredFiles.has(normalized) ||
|
|
126
|
+
ignoredDirectories.some((directory) => {
|
|
127
|
+
const prefix = directory.replaceAll('\\', '/').replace(/^\/+|\/+$/gu, '');
|
|
128
|
+
return prefix && (normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
/*//////////////////////////////////////////////////
|
|
132
|
+
RECOMMENDATIONS
|
|
133
|
+
//////////////////////////////////////////////////*/
|
|
134
|
+
/**
|
|
135
|
+
* Finds JSX controls that have a configured or conventional shared-component equivalent.
|
|
136
|
+
*
|
|
137
|
+
* The scan is advisory and does not alter files or participate in normal quality/test runs.
|
|
138
|
+
* JSX and TSX are parsed so comments, text, and already-shared components are not reported.
|
|
139
|
+
*
|
|
140
|
+
* @param cfg Repository quality configuration and optional UI recommendation policy.
|
|
141
|
+
*
|
|
142
|
+
* @return Sorted native-element recommendations with source positions and import guidance.
|
|
143
|
+
*/
|
|
144
|
+
export function uiComponentRecommendations(cfg) {
|
|
145
|
+
const settings = cfg.uiRecommendations;
|
|
146
|
+
const rules = settings?.rules ?? DEFAULT_RULES;
|
|
147
|
+
const extensions = settings?.extensions ?? DEFAULT_EXTENSIONS;
|
|
148
|
+
const ignoredFiles = new Set((settings?.ignoredFiles ?? []).map((file) => file.replaceAll('\\', '/')));
|
|
149
|
+
const findings = [];
|
|
150
|
+
for (const file of srcFiles({ ...cfg, extensions })) {
|
|
151
|
+
const relativeFile = displayPath(cfg, file).replaceAll('\\', '/');
|
|
152
|
+
if (isIgnoredFile(relativeFile, settings?.ignoredDirectories ?? [], ignoredFiles))
|
|
153
|
+
continue;
|
|
154
|
+
const source = readFileSync(file, 'utf8');
|
|
155
|
+
const scriptKind = file.endsWith('.jsx') ? ts.ScriptKind.JSX : ts.ScriptKind.TSX;
|
|
156
|
+
const parsed = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, scriptKind);
|
|
157
|
+
const visit = (node) => {
|
|
158
|
+
if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && ts.isIdentifier(node.tagName)) {
|
|
159
|
+
const element = node.tagName.text;
|
|
160
|
+
if (element === element.toLowerCase()) {
|
|
161
|
+
const rule = rules.find((candidate) => candidate.element === element && ruleMatches(candidate, node));
|
|
162
|
+
if (rule) {
|
|
163
|
+
const { line, character } = parsed.getLineAndCharacterOfPosition(node.getStart(parsed));
|
|
164
|
+
findings.push({
|
|
165
|
+
file: relativeFile,
|
|
166
|
+
line: line + 1,
|
|
167
|
+
column: character + 1,
|
|
168
|
+
element,
|
|
169
|
+
component: rule.component,
|
|
170
|
+
import: rule.import,
|
|
171
|
+
reason: rule.reason ?? `Review whether this native <${element}> should use the shared ${rule.component} component.`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
ts.forEachChild(node, visit);
|
|
177
|
+
};
|
|
178
|
+
visit(parsed);
|
|
179
|
+
}
|
|
180
|
+
return findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=ui-recommendations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-recommendations.js","sourceRoot":"","sources":["../src/ui-recommendations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;oDAEoD;AAEpD,MAAM;AACN,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAsCnD,MAAM,aAAa,GAA2B;IAC7C;QACC,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,QAAQ;QACnB,MAAM,EAAE,wBAAwB;QAChC,MAAM,EAAE,4FAA4F;KACpG;IACD;QACC,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,OAAO;QAClB,MAAM,EAAE,uBAAuB;QAC/B,KAAK,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;QACjI,MAAM,EAAE,iGAAiG;KACzG;IACD;QACC,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,QAAQ;QACnB,MAAM,EAAE,wBAAwB;QAChC,KAAK,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;QACpC,MAAM,EAAE,wFAAwF;KAChG;IACD;QACC,OAAO,EAAE,GAAG;QACZ,SAAS,EAAE,YAAY;QACvB,MAAM,EAAE,wBAAwB;QAChC,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,yFAAyF;KACjG;CACD,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5C;;oDAEoD;AAEpD;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,SAAsC;IACnE,MAAM,WAAW,GAAG,SAAS,EAAE,WAAW,CAAC;IAC3C,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,IAAI,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC,IAAI,CAAC;IAChH,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClF,IAAI,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC;IACxC,OAAO,EAAE,CAAC,yBAAyB,CAAC,UAAU,CAAC;QAAE,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;IACpF,OAAO,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACvH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,OAAiC,EAAE,IAAY;IACpE,OAAO,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,EAA+B,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACpL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,KAAyB;IAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,oBAAoB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACvF,MAAM,kBAAkB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,aAAa,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IACpK,MAAM,cAAc,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,aAAa,IAAI,SAAS,KAAK,cAAc,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IACpJ,OAAO,oBAAoB,IAAI,kBAAkB,IAAI,QAAQ,IAAI,cAAc,CAAC;AACjF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,IAA0B,EAAE,OAAiC;IACjF,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC7D,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5F,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,oBAAoB,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,IAAI,oBAAoB,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACjI,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,kBAA4B,EAAE,YAAyB;IAC3F,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,YAAY,IAAI,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACvH,OAAO,CACN,kBAAkB;QAClB,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC;QAC5B,kBAAkB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YAC1E,OAAO,MAAM,IAAI,CAAC,UAAU,KAAK,MAAM,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;QACjF,CAAC,CAAC,CACF,CAAC;AACH,CAAC;AAED;;oDAEoD;AAEpD;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAe;IACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,iBAAiB,CAAC;IACvC,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,IAAI,aAAa,CAAC;IAC/C,MAAM,UAAU,GAAG,QAAQ,EAAE,UAAU,IAAI,kBAAkB,CAAC;IAC9D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACvG,MAAM,QAAQ,GAAgC,EAAE,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClE,IAAI,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,IAAI,EAAE,EAAE,YAAY,CAAC;YAAE,SAAS;QAC5F,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QACjF,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAE3F,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;YACrC,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClC,IAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;oBACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,OAAO,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;oBACtG,IAAI,IAAI,EAAE,CAAC;wBACV,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;wBACxF,QAAQ,CAAC,IAAI,CAAC;4BACb,IAAI,EAAE,YAAY;4BAClB,IAAI,EAAE,IAAI,GAAG,CAAC;4BACd,MAAM,EAAE,SAAS,GAAG,CAAC;4BACrB,OAAO;4BACP,SAAS,EAAE,IAAI,CAAC,SAAS;4BACzB,MAAM,EAAE,IAAI,CAAC,MAAM;4BACnB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,+BAA+B,OAAO,2BAA2B,IAAI,CAAC,SAAS,aAAa;yBACnH,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;YACF,CAAC;YACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC;QAEF,KAAK,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AACxG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-test-runner.d.ts","sourceRoot":"","sources":["../src/workspace-test-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/quality/src/workspace-test-runner.ts
|
|
3
|
+
* Runs the Standards workspace test matrix once with shared progress reporting.
|
|
4
|
+
*/
|
|
5
|
+
/*//////////////////////////////////////////////////
|
|
6
|
+
IMPORTS
|
|
7
|
+
//////////////////////////////////////////////////*/
|
|
8
|
+
// API
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { dirname, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { qualityNodeReporterOptions } from './node-reporter.js';
|
|
13
|
+
import { qualityProgress } from './progress.js';
|
|
14
|
+
/*//////////////////////////////////////////////////
|
|
15
|
+
RUNNER
|
|
16
|
+
//////////////////////////////////////////////////*/
|
|
17
|
+
/**
|
|
18
|
+
* Runs the three publishable package suites selected by the Standards workspace.
|
|
19
|
+
*
|
|
20
|
+
* @return Nothing; the recursive test command's status becomes this process status.
|
|
21
|
+
*/
|
|
22
|
+
function runWorkspaceTests() {
|
|
23
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../..');
|
|
24
|
+
const reporter = new URL('./node-reporter.js', import.meta.url).href;
|
|
25
|
+
const env = { ...process.env };
|
|
26
|
+
env.NODE_OPTIONS = qualityNodeReporterOptions(env.NODE_OPTIONS, reporter);
|
|
27
|
+
env.TERRA_QUALITY_NODE_REPORTER_OWNER = '1';
|
|
28
|
+
const suites = ['@terra-symposium/standards-error-boundaries', '@terra-symposium/standards-quality', '@terra-symposium/standards-reporting'];
|
|
29
|
+
let failed = false;
|
|
30
|
+
console.error('\nTESTS\n Unit / Contract · Standards workspace');
|
|
31
|
+
qualityProgress('Test suites', 0, suites.length);
|
|
32
|
+
for (const [idx, name] of suites.entries()) {
|
|
33
|
+
const result = spawnSync('pnpm', ['--filter', name, 'test'], { cwd: root, stdio: 'inherit', env });
|
|
34
|
+
if (result.error) {
|
|
35
|
+
console.error(` ${name} failed to start: ${result.error.message}`);
|
|
36
|
+
failed = true;
|
|
37
|
+
}
|
|
38
|
+
else if (result.status !== 0) {
|
|
39
|
+
failed = true;
|
|
40
|
+
}
|
|
41
|
+
qualityProgress('Test suites', idx + 1, suites.length);
|
|
42
|
+
}
|
|
43
|
+
console.error(` ${failed ? '✗ failed' : '✓ passed'}`);
|
|
44
|
+
if (failed)
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
runWorkspaceTests();
|
|
48
|
+
//# sourceMappingURL=workspace-test-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-test-runner.js","sourceRoot":"","sources":["../src/workspace-test-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;oDAEoD;AAEpD,MAAM;AACN,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;oDAEoD;AAEpD;;;;GAIG;AACH,SAAS,iBAAiB;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC1E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IACrE,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/B,GAAG,CAAC,YAAY,GAAG,0BAA0B,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC1E,GAAG,CAAC,iCAAiC,GAAG,GAAG,CAAC;IAC5C,MAAM,MAAM,GAAG,CAAC,6CAA6C,EAAE,oCAAoC,EAAE,sCAAsC,CAAC,CAAC;IAC7I,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClE,eAAe,CAAC,aAAa,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QACnG,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,qBAAqB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,MAAM,GAAG,IAAI,CAAC;QACf,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,IAAI,CAAC;QACf,CAAC;QACD,eAAe,CAAC,aAAa,EAAE,GAAG,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACvD,IAAI,MAAM;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,iBAAiB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terra-symposium/standards-quality",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Configurable quality checks and maintenance tooling for Terra Symposium repositories",
|
|
5
5
|
"homepage": "https://standards-docs.netlify.app/",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc -p tsconfig.json",
|
|
37
37
|
"prepare": "npm run build",
|
|
38
|
-
"test": "node --test
|
|
38
|
+
"test": "node --test"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"typescript": "^5.0.0"
|
package/quality.schema.json
CHANGED
|
@@ -1,132 +1,197 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Terra quality configuration",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["sourceRoots", "extensions"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": { "type": "string" },
|
|
9
|
+
"root": { "type": "string" },
|
|
10
|
+
"sourceRoots": { "type": "array", "items": { "type": "string" } },
|
|
11
|
+
"includeRoots": { "type": "array", "items": { "type": "string" } },
|
|
12
|
+
"extensions": { "type": "array", "items": { "type": "string", "pattern": "^\\." } },
|
|
13
|
+
"aliases": { "type": "object", "additionalProperties": { "type": "string" } },
|
|
14
|
+
"frameworkEntryExports": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } },
|
|
15
|
+
"externalPrefixes": { "type": "array", "items": { "type": "string" } },
|
|
16
|
+
"skippedDirectories": { "type": "array", "items": { "type": "string" } },
|
|
17
|
+
"skippedFiles": { "type": "array", "items": { "type": "string" } },
|
|
18
|
+
"excludeTests": { "type": "boolean", "description": "Exclude files ending in .test.* and .spec.* from configured quality source scans." },
|
|
19
|
+
"singleUse": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": { "skippedDirectories": { "type": "array", "items": { "type": "string" } } },
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"renameExports": { "type": "boolean" },
|
|
25
|
+
"protectedIdentifiers": { "type": "array", "items": { "type": "string" } },
|
|
26
|
+
"enabledChecks": { "$ref": "#/$defs/toggles" },
|
|
27
|
+
"autofix": { "$ref": "#/$defs/toggles" },
|
|
28
|
+
"documentation": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"required": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"types": { "type": "boolean" },
|
|
35
|
+
"functions": { "type": "boolean", "description": "Require TSDoc for named functions, including substantive descriptions for every parameter." },
|
|
36
|
+
"exportedFunctions": { "type": "boolean", "description": "Require TSDoc for exported named functions, including substantive descriptions for every parameter." },
|
|
37
|
+
"noInlineFunctions": { "type": "boolean" }
|
|
38
|
+
},
|
|
39
|
+
"additionalProperties": false
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
"tailwindCanonical": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"required": ["stylesheet"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"stylesheet": { "type": "string" },
|
|
49
|
+
"sourceRoots": { "type": "array", "items": { "type": "string" } }
|
|
50
|
+
},
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
},
|
|
53
|
+
"security": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"forbiddenTerms": { "type": "array", "items": { "type": "string" } },
|
|
57
|
+
"forbiddenImports": { "type": "array", "items": { "type": "string" } },
|
|
58
|
+
"clientRoots": { "type": "array", "items": { "type": "string" } },
|
|
59
|
+
"allowProcessEnvRoots": { "type": "array", "items": { "type": "string" } }
|
|
60
|
+
},
|
|
61
|
+
"additionalProperties": false
|
|
62
|
+
},
|
|
63
|
+
"repeatedClasses": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"minimumCount": { "type": "number" },
|
|
67
|
+
"extensions": { "type": "array", "items": { "type": "string" } },
|
|
68
|
+
"ignoredDirectories": { "type": "array", "items": { "type": "string" } }
|
|
69
|
+
},
|
|
70
|
+
"additionalProperties": false
|
|
71
|
+
},
|
|
72
|
+
"regexDedup": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"minimumCount": { "type": "number" },
|
|
76
|
+
"extensions": { "type": "array", "items": { "type": "string" } },
|
|
77
|
+
"ignoredFiles": { "type": "array", "items": { "type": "string" } },
|
|
78
|
+
"sharedModule": { "type": "string", "minLength": 1 }
|
|
79
|
+
},
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
},
|
|
82
|
+
"uiPatterns": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"properties": {
|
|
85
|
+
"forbidden": { "type": "array", "items": { "type": "string" } },
|
|
86
|
+
"ignoredFiles": { "type": "array", "items": { "type": "string" } },
|
|
87
|
+
"extensions": { "type": "array", "items": { "type": "string" } }
|
|
88
|
+
},
|
|
89
|
+
"additionalProperties": false
|
|
90
|
+
},
|
|
91
|
+
"uiRecommendations": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"rules": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["element", "component", "import"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"element": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
101
|
+
"component": { "type": "string", "minLength": 1 },
|
|
102
|
+
"import": { "type": "string", "minLength": 1 },
|
|
103
|
+
"types": { "type": "array", "items": { "type": "string" } },
|
|
104
|
+
"when": { "enum": ["always", "button-like"] },
|
|
105
|
+
"reason": { "type": "string" }
|
|
106
|
+
},
|
|
107
|
+
"additionalProperties": false
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"extensions": { "type": "array", "items": { "enum": [".jsx", ".tsx"] } },
|
|
111
|
+
"ignoredFiles": { "type": "array", "items": { "type": "string" } },
|
|
112
|
+
"ignoredDirectories": { "type": "array", "items": { "type": "string" } }
|
|
113
|
+
},
|
|
114
|
+
"additionalProperties": false
|
|
115
|
+
},
|
|
116
|
+
"github": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"properties": {
|
|
119
|
+
"repositories": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" } },
|
|
120
|
+
"requiredFeatures": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"uniqueItems": true,
|
|
123
|
+
"items": { "enum": ["dependencyGraph", "dependabotAlerts", "malwareAlerts", "securityUpdates", "versionUpdates", "groupedUpdates", "secretScanning", "pushProtection", "codeScanning"] }
|
|
124
|
+
},
|
|
125
|
+
"featureExemptions": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"propertyNames": { "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" },
|
|
128
|
+
"additionalProperties": {
|
|
129
|
+
"type": "array",
|
|
130
|
+
"uniqueItems": true,
|
|
131
|
+
"items": { "enum": ["dependencyGraph", "dependabotAlerts", "malwareAlerts", "securityUpdates", "versionUpdates", "groupedUpdates", "secretScanning", "pushProtection", "codeScanning"] }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"additionalProperties": false
|
|
136
|
+
},
|
|
137
|
+
"enabledCommands": { "type": "object", "additionalProperties": { "type": "boolean" } },
|
|
138
|
+
"enabledRecommendations": { "type": "object", "additionalProperties": { "type": "boolean" } },
|
|
139
|
+
"qualityCommands": {
|
|
140
|
+
"$ref": "#/$defs/commands",
|
|
141
|
+
"description": "Commands selected for the quality run. Terra Quality owns phase order and test grouping; array order is preserved within ordinary phases and within each test group."
|
|
142
|
+
},
|
|
143
|
+
"recommendationCommands": { "$ref": "#/$defs/commands" },
|
|
144
|
+
"manualWarnings": { "enum": ["error", "warning"], "default": "error", "description": "Whether dead-code findings that require manual review block quality. Defaults to error." },
|
|
145
|
+
"tsconfig": { "type": "string" },
|
|
146
|
+
"oneLineImports": { "type": "boolean" },
|
|
147
|
+
"prettierPrintWidth": { "type": "number" },
|
|
148
|
+
"lintRoots": { "type": "array", "items": { "type": "string" } },
|
|
149
|
+
"typecheckRoots": { "type": "array", "items": { "type": "string" } },
|
|
150
|
+
"typecheckExtensions": { "type": "array", "items": { "type": "string" } },
|
|
151
|
+
"typecheckCommand": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"required": ["command"],
|
|
154
|
+
"properties": {
|
|
155
|
+
"command": { "type": "string" },
|
|
156
|
+
"args": { "type": "array", "items": { "type": "string" } }
|
|
157
|
+
},
|
|
158
|
+
"additionalProperties": false
|
|
159
|
+
},
|
|
160
|
+
"loadTesting": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"properties": {
|
|
163
|
+
"enabled": { "type": "boolean" },
|
|
164
|
+
"defaultBudget": { "$ref": "#/$defs/loadBudget" },
|
|
165
|
+
"testBudgets": { "type": "object", "additionalProperties": { "$ref": "#/$defs/loadBudget" } },
|
|
166
|
+
"operationBudgets": { "type": "object", "additionalProperties": { "$ref": "#/$defs/loadBudget" } }
|
|
167
|
+
},
|
|
168
|
+
"additionalProperties": false
|
|
169
|
+
},
|
|
170
|
+
"tools": { "type": "object", "additionalProperties": { "type": "string" } }
|
|
171
|
+
},
|
|
172
|
+
"$defs": {
|
|
173
|
+
"toggles": { "type": "object", "additionalProperties": { "type": "boolean" } },
|
|
174
|
+
"loadBudget": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"properties": {
|
|
177
|
+
"warnMs": { "type": "number", "minimum": 0 },
|
|
178
|
+
"failMs": { "type": "number", "minimum": 0 }
|
|
179
|
+
},
|
|
180
|
+
"additionalProperties": false,
|
|
181
|
+
"anyOf": [{ "required": ["warnMs"] }, { "required": ["failMs"] }]
|
|
182
|
+
},
|
|
183
|
+
"commands": {
|
|
184
|
+
"type": "array",
|
|
185
|
+
"items": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"required": ["name", "command"],
|
|
188
|
+
"properties": {
|
|
189
|
+
"name": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
190
|
+
"command": { "type": "string" },
|
|
191
|
+
"args": { "type": "array", "items": { "type": "string" } }
|
|
192
|
+
},
|
|
193
|
+
"additionalProperties": false
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
132
197
|
}
|