@code-pushup/models 0.1.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 +66 -0
- package/index.js +543 -0
- package/package.json +11 -0
- package/src/index.d.ts +12 -0
- package/src/lib/category-config.d.ts +78 -0
- package/src/lib/core-config.d.ts +2246 -0
- package/src/lib/implementation/schemas.d.ts +119 -0
- package/src/lib/implementation/utils.d.ts +29 -0
- package/src/lib/persist-config.d.ts +17 -0
- package/src/lib/plugin-config-audits.d.ts +44 -0
- package/src/lib/plugin-config-groups.d.ts +111 -0
- package/src/lib/plugin-config-runner.d.ts +1172 -0
- package/src/lib/plugin-config.d.ts +2978 -0
- package/src/lib/plugin-process-output-audit-issue.d.ts +67 -0
- package/src/lib/plugin-process-output.d.ts +321 -0
- package/src/lib/report.d.ts +915 -0
- package/src/lib/upload-config.d.ts +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @code-pushup/models
|
|
2
|
+
|
|
3
|
+
**Model definitions and validators** for the [Code PushUp CLI](../cli/README.md).
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
If you've already installed another `@code-pushup/*` package, then you may have already installed `@code-pushup/models` indirectly.
|
|
8
|
+
|
|
9
|
+
If not, you can always install it separately:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install --save-dev @code-pushup/models
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
yarn add --dev @code-pushup/models
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm add --save-dev @code-pushup/models
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Import the type definitions if using TypeScript:
|
|
26
|
+
|
|
27
|
+
- in `code-pushup.config.ts`:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import type { CoreConfig } from '@code-pushup/models';
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
// ... this is type-checked ...
|
|
34
|
+
} satisfies CoreConfig;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- in custom plugin:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import type { PluginConfig } from '@code-pushup/models';
|
|
41
|
+
|
|
42
|
+
export default function myCustomPlugin(): PluginConfig {
|
|
43
|
+
return {
|
|
44
|
+
// ... this is type-checked ...
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import type { AuditOutput } from '@code-pushup/models';
|
|
51
|
+
|
|
52
|
+
async function myCustomPluginRunner() {
|
|
53
|
+
const audits: AuditOutput[] = await collectAudits();
|
|
54
|
+
|
|
55
|
+
await writeFile(RUNNER_OUTPUT_FILE, JSON.strinfigy(audits));
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If you need runtime validation, use the underlying Zod schemas:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { coreConfigSchema } from '@code-pushup/models';
|
|
63
|
+
|
|
64
|
+
const json = JSON.parse(readFileSync('code-pushup.config.json'));
|
|
65
|
+
const config = coreConfigSchema.parse(json); // throws ZodError if invalid
|
|
66
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
// packages/models/src/lib/category-config.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// packages/models/src/lib/implementation/schemas.ts
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { MATERIAL_ICONS } from "@code-pushup/portal-client";
|
|
7
|
+
|
|
8
|
+
// packages/models/src/lib/implementation/utils.ts
|
|
9
|
+
var slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
10
|
+
var filenameRegex = /^(?!.*[ \\/:*?"<>|]).+$/;
|
|
11
|
+
function hasDuplicateStrings(strings) {
|
|
12
|
+
const uniqueStrings = Array.from(new Set(strings));
|
|
13
|
+
const duplicatedStrings = strings.filter(
|
|
14
|
+
/* @__PURE__ */ ((i) => (v) => uniqueStrings[i] !== v || !++i)(0)
|
|
15
|
+
);
|
|
16
|
+
return duplicatedStrings.length === 0 ? false : duplicatedStrings;
|
|
17
|
+
}
|
|
18
|
+
function hasMissingStrings(toCheck, existing) {
|
|
19
|
+
const nonExisting = toCheck.filter((s) => !existing.includes(s));
|
|
20
|
+
return nonExisting.length === 0 ? false : nonExisting;
|
|
21
|
+
}
|
|
22
|
+
function errorItems(items, transform = (items2) => items2.join(", ")) {
|
|
23
|
+
const paredItems = items ? items : [];
|
|
24
|
+
return transform(paredItems);
|
|
25
|
+
}
|
|
26
|
+
function exists(value) {
|
|
27
|
+
return value != null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// packages/models/src/lib/implementation/schemas.ts
|
|
31
|
+
function executionMetaSchema(options = {
|
|
32
|
+
descriptionDate: "Execution start date and time",
|
|
33
|
+
descriptionDuration: "Execution duration in ms"
|
|
34
|
+
}) {
|
|
35
|
+
return z.object({
|
|
36
|
+
date: z.string({ description: options.descriptionDate }),
|
|
37
|
+
duration: z.number({ description: options.descriptionDuration })
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function slugSchema(description = "Unique ID (human-readable, URL-safe)") {
|
|
41
|
+
return z.string({ description }).regex(slugRegex, {
|
|
42
|
+
message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
|
|
43
|
+
}).max(128, {
|
|
44
|
+
message: "slug can be max 128 characters long"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function descriptionSchema(description = "Description (markdown)") {
|
|
48
|
+
return z.string({ description }).max(65536).optional();
|
|
49
|
+
}
|
|
50
|
+
function docsUrlSchema(description = "Documentation site") {
|
|
51
|
+
return urlSchema(description).optional().or(z.string().max(0));
|
|
52
|
+
}
|
|
53
|
+
function urlSchema(description) {
|
|
54
|
+
return z.string({ description }).url();
|
|
55
|
+
}
|
|
56
|
+
function titleSchema(description = "Descriptive name") {
|
|
57
|
+
return z.string({ description }).max(256);
|
|
58
|
+
}
|
|
59
|
+
function metaSchema(options) {
|
|
60
|
+
const {
|
|
61
|
+
descriptionDescription,
|
|
62
|
+
titleDescription,
|
|
63
|
+
docsUrlDescription,
|
|
64
|
+
description
|
|
65
|
+
} = options || {};
|
|
66
|
+
return z.object(
|
|
67
|
+
{
|
|
68
|
+
title: titleSchema(titleDescription),
|
|
69
|
+
description: descriptionSchema(descriptionDescription),
|
|
70
|
+
docsUrl: docsUrlSchema(docsUrlDescription)
|
|
71
|
+
},
|
|
72
|
+
{ description }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function filePathSchema(description) {
|
|
76
|
+
return z.string({ description }).trim().min(1, { message: "path is invalid" });
|
|
77
|
+
}
|
|
78
|
+
function fileNameSchema(description) {
|
|
79
|
+
return z.string({ description }).trim().regex(filenameRegex, {
|
|
80
|
+
message: `The filename has to be valid`
|
|
81
|
+
}).min(1, { message: "file name is invalid" });
|
|
82
|
+
}
|
|
83
|
+
function positiveIntSchema(description) {
|
|
84
|
+
return z.number({ description }).int().nonnegative();
|
|
85
|
+
}
|
|
86
|
+
function packageVersionSchema(options) {
|
|
87
|
+
let { versionDescription, optional } = options || {};
|
|
88
|
+
versionDescription = versionDescription || "NPM version of the package";
|
|
89
|
+
optional = !!optional;
|
|
90
|
+
const packageSchema = z.string({ description: "NPM package name" });
|
|
91
|
+
const versionSchema = z.string({ description: versionDescription });
|
|
92
|
+
return z.object(
|
|
93
|
+
{
|
|
94
|
+
packageName: optional ? packageSchema.optional() : packageSchema,
|
|
95
|
+
version: optional ? versionSchema.optional() : versionSchema
|
|
96
|
+
},
|
|
97
|
+
{ description: "NPM package name and version of a published package" }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function weightSchema(description = "Coefficient for the given score (use weight 0 if only for display)") {
|
|
101
|
+
return positiveIntSchema(description);
|
|
102
|
+
}
|
|
103
|
+
function weightedRefSchema(description, slugDescription) {
|
|
104
|
+
return z.object(
|
|
105
|
+
{
|
|
106
|
+
slug: slugSchema(slugDescription),
|
|
107
|
+
weight: weightSchema("Weight used to calculate score")
|
|
108
|
+
},
|
|
109
|
+
{ description }
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
|
|
113
|
+
return z.object(
|
|
114
|
+
{
|
|
115
|
+
slug: slugSchema('Human-readable unique ID, e.g. "performance"'),
|
|
116
|
+
refs: z.array(refSchema).refine(
|
|
117
|
+
(refs) => !duplicateCheckFn(refs),
|
|
118
|
+
(refs) => ({
|
|
119
|
+
message: duplicateMessageFn(refs)
|
|
120
|
+
})
|
|
121
|
+
)
|
|
122
|
+
},
|
|
123
|
+
{ description }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
var materialIconSchema = z.enum(
|
|
127
|
+
MATERIAL_ICONS,
|
|
128
|
+
{ description: "Icon from VSCode Material Icons extension" }
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// packages/models/src/lib/category-config.ts
|
|
132
|
+
var categoryRefSchema = weightedRefSchema(
|
|
133
|
+
"Weighted references to audits and/or groups for the category",
|
|
134
|
+
"Slug of an audit or group (depending on `type`)"
|
|
135
|
+
).merge(
|
|
136
|
+
z2.object({
|
|
137
|
+
type: z2.enum(["audit", "group"], {
|
|
138
|
+
description: "Discriminant for reference kind, affects where `slug` is looked up"
|
|
139
|
+
}),
|
|
140
|
+
plugin: slugSchema(
|
|
141
|
+
"Plugin slug (plugin should contain referenced audit or group)"
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
var categoryConfigSchema = scorableSchema(
|
|
146
|
+
"Category with a score calculated from audits and groups from various plugins",
|
|
147
|
+
categoryRefSchema,
|
|
148
|
+
getDuplicateRefsInCategoryMetrics,
|
|
149
|
+
duplicateRefsInCategoryMetricsErrorMsg
|
|
150
|
+
).merge(
|
|
151
|
+
metaSchema({
|
|
152
|
+
titleDescription: "Category Title",
|
|
153
|
+
docsUrlDescription: "Category docs URL",
|
|
154
|
+
descriptionDescription: "Category description",
|
|
155
|
+
description: "Meta info for category"
|
|
156
|
+
})
|
|
157
|
+
).merge(
|
|
158
|
+
z2.object({
|
|
159
|
+
isBinary: z2.boolean({
|
|
160
|
+
description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
|
|
161
|
+
}).optional()
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
function duplicateRefsInCategoryMetricsErrorMsg(metrics) {
|
|
165
|
+
const duplicateRefs = getDuplicateRefsInCategoryMetrics(metrics);
|
|
166
|
+
return `In the categories, the following audit or group refs are duplicates: ${errorItems(
|
|
167
|
+
duplicateRefs
|
|
168
|
+
)}`;
|
|
169
|
+
}
|
|
170
|
+
function getDuplicateRefsInCategoryMetrics(metrics) {
|
|
171
|
+
return hasDuplicateStrings(
|
|
172
|
+
metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// packages/models/src/lib/core-config.ts
|
|
177
|
+
import { z as z11 } from "zod";
|
|
178
|
+
|
|
179
|
+
// packages/models/src/lib/persist-config.ts
|
|
180
|
+
import { z as z3 } from "zod";
|
|
181
|
+
var formatSchema = z3.enum(["json", "md"]);
|
|
182
|
+
var persistConfigSchema = z3.object({
|
|
183
|
+
outputDir: filePathSchema("Artifacts folder"),
|
|
184
|
+
filename: fileNameSchema("Artifacts file name (without extension)").default(
|
|
185
|
+
"report"
|
|
186
|
+
),
|
|
187
|
+
format: z3.array(formatSchema).default(["json"]).optional()
|
|
188
|
+
// @TODO remove default or optional value and otherwise it will not set defaults.
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// packages/models/src/lib/plugin-config.ts
|
|
192
|
+
import { z as z9 } from "zod";
|
|
193
|
+
|
|
194
|
+
// packages/models/src/lib/plugin-config-audits.ts
|
|
195
|
+
import { z as z4 } from "zod";
|
|
196
|
+
var auditSchema = z4.object({
|
|
197
|
+
slug: slugSchema("ID (unique within plugin)")
|
|
198
|
+
}).merge(
|
|
199
|
+
metaSchema({
|
|
200
|
+
titleDescription: "Descriptive name",
|
|
201
|
+
descriptionDescription: "Description (markdown)",
|
|
202
|
+
docsUrlDescription: "Link to documentation (rationale)",
|
|
203
|
+
description: "List of scorable metrics for the given plugin"
|
|
204
|
+
})
|
|
205
|
+
);
|
|
206
|
+
var pluginAuditsSchema = z4.array(auditSchema, {
|
|
207
|
+
description: "List of audits maintained in a plugin"
|
|
208
|
+
}).refine(
|
|
209
|
+
(auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
|
|
210
|
+
(auditMetadata) => ({
|
|
211
|
+
message: duplicateSlugsInAuditsErrorMsg(auditMetadata)
|
|
212
|
+
})
|
|
213
|
+
);
|
|
214
|
+
function duplicateSlugsInAuditsErrorMsg(audits) {
|
|
215
|
+
const duplicateRefs = getDuplicateSlugsInAudits(audits);
|
|
216
|
+
return `In plugin audits the slugs are not unique: ${errorItems(
|
|
217
|
+
duplicateRefs
|
|
218
|
+
)}`;
|
|
219
|
+
}
|
|
220
|
+
function getDuplicateSlugsInAudits(audits) {
|
|
221
|
+
return hasDuplicateStrings(audits.map(({ slug }) => slug));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// packages/models/src/lib/plugin-config-groups.ts
|
|
225
|
+
import { z as z5 } from "zod";
|
|
226
|
+
var auditGroupRefSchema = weightedRefSchema(
|
|
227
|
+
"Weighted references to audits",
|
|
228
|
+
"Reference slug to an audit within this plugin (e.g. 'max-lines')"
|
|
229
|
+
);
|
|
230
|
+
var auditGroupSchema = scorableSchema(
|
|
231
|
+
'An audit group aggregates a set of audits into a single score which can be referenced from a category. E.g. the group slug "performance" groups audits and can be referenced in a category',
|
|
232
|
+
auditGroupRefSchema,
|
|
233
|
+
getDuplicateRefsInGroups,
|
|
234
|
+
duplicateRefsInGroupsErrorMsg
|
|
235
|
+
).merge(
|
|
236
|
+
metaSchema({
|
|
237
|
+
titleDescription: "Descriptive name for the group",
|
|
238
|
+
descriptionDescription: "Description of the group (markdown)",
|
|
239
|
+
docsUrlDescription: "Group documentation site",
|
|
240
|
+
description: "Group metadata"
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
var auditGroupsSchema = z5.array(auditGroupSchema, {
|
|
244
|
+
description: "List of groups"
|
|
245
|
+
}).optional().refine(
|
|
246
|
+
(groups) => !getDuplicateSlugsInGroups(groups),
|
|
247
|
+
(groups) => ({
|
|
248
|
+
message: duplicateSlugsInGroupsErrorMsg(groups)
|
|
249
|
+
})
|
|
250
|
+
);
|
|
251
|
+
function duplicateRefsInGroupsErrorMsg(groupAudits) {
|
|
252
|
+
const duplicateRefs = getDuplicateRefsInGroups(groupAudits);
|
|
253
|
+
return `In plugin groups the audit refs are not unique: ${errorItems(
|
|
254
|
+
duplicateRefs
|
|
255
|
+
)}`;
|
|
256
|
+
}
|
|
257
|
+
function getDuplicateRefsInGroups(groupAudits) {
|
|
258
|
+
return hasDuplicateStrings(
|
|
259
|
+
groupAudits.map(({ slug: ref }) => ref).filter(exists)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
function duplicateSlugsInGroupsErrorMsg(groups) {
|
|
263
|
+
const duplicateRefs = getDuplicateSlugsInGroups(groups);
|
|
264
|
+
return `In groups the slugs are not unique: ${errorItems(duplicateRefs)}`;
|
|
265
|
+
}
|
|
266
|
+
function getDuplicateSlugsInGroups(groups) {
|
|
267
|
+
return Array.isArray(groups) ? hasDuplicateStrings(groups.map(({ slug }) => slug)) : false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// packages/models/src/lib/plugin-config-runner.ts
|
|
271
|
+
import { z as z8 } from "zod";
|
|
272
|
+
|
|
273
|
+
// packages/models/src/lib/plugin-process-output.ts
|
|
274
|
+
import { z as z7 } from "zod";
|
|
275
|
+
|
|
276
|
+
// packages/models/src/lib/plugin-process-output-audit-issue.ts
|
|
277
|
+
import { z as z6 } from "zod";
|
|
278
|
+
var sourceFileLocationSchema = z6.object(
|
|
279
|
+
{
|
|
280
|
+
file: filePathSchema("Relative path to source file in Git repo"),
|
|
281
|
+
position: z6.object(
|
|
282
|
+
{
|
|
283
|
+
startLine: positiveIntSchema("Start line"),
|
|
284
|
+
startColumn: positiveIntSchema("Start column").optional(),
|
|
285
|
+
endLine: positiveIntSchema("End line").optional(),
|
|
286
|
+
endColumn: positiveIntSchema("End column").optional()
|
|
287
|
+
},
|
|
288
|
+
{ description: "Location in file" }
|
|
289
|
+
).optional()
|
|
290
|
+
},
|
|
291
|
+
{ description: "Source file location" }
|
|
292
|
+
);
|
|
293
|
+
var issueSeveritySchema = z6.enum(["info", "warning", "error"], {
|
|
294
|
+
description: "Severity level"
|
|
295
|
+
});
|
|
296
|
+
var issueSchema = z6.object(
|
|
297
|
+
{
|
|
298
|
+
message: z6.string({ description: "Descriptive error message" }).max(512),
|
|
299
|
+
severity: issueSeveritySchema,
|
|
300
|
+
source: sourceFileLocationSchema.optional()
|
|
301
|
+
},
|
|
302
|
+
{ description: "Issue information" }
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// packages/models/src/lib/plugin-process-output.ts
|
|
306
|
+
var auditOutputSchema = z7.object(
|
|
307
|
+
{
|
|
308
|
+
slug: slugSchema("Reference to audit"),
|
|
309
|
+
displayValue: z7.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional(),
|
|
310
|
+
value: positiveIntSchema("Raw numeric value"),
|
|
311
|
+
score: z7.number({
|
|
312
|
+
description: "Value between 0 and 1"
|
|
313
|
+
}).min(0).max(1),
|
|
314
|
+
details: z7.object(
|
|
315
|
+
{
|
|
316
|
+
issues: z7.array(issueSchema, { description: "List of findings" })
|
|
317
|
+
},
|
|
318
|
+
{ description: "Detailed information" }
|
|
319
|
+
).optional()
|
|
320
|
+
},
|
|
321
|
+
{ description: "Audit information" }
|
|
322
|
+
);
|
|
323
|
+
var auditOutputsSchema = z7.array(auditOutputSchema, {
|
|
324
|
+
description: "List of JSON formatted audit output emitted by the runner process of a plugin"
|
|
325
|
+
}).refine(
|
|
326
|
+
(audits) => !getDuplicateSlugsInAudits2(audits),
|
|
327
|
+
(audits) => ({ message: duplicateSlugsInAuditsErrorMsg2(audits) })
|
|
328
|
+
);
|
|
329
|
+
function duplicateSlugsInAuditsErrorMsg2(audits) {
|
|
330
|
+
const duplicateRefs = getDuplicateSlugsInAudits2(audits);
|
|
331
|
+
return `In plugin audits the slugs are not unique: ${errorItems(
|
|
332
|
+
duplicateRefs
|
|
333
|
+
)}`;
|
|
334
|
+
}
|
|
335
|
+
function getDuplicateSlugsInAudits2(audits) {
|
|
336
|
+
return hasDuplicateStrings(audits.map(({ slug }) => slug));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// packages/models/src/lib/plugin-config-runner.ts
|
|
340
|
+
var outputTransformSchema = z8.function().args(z8.unknown()).returns(z8.union([auditOutputsSchema, z8.promise(auditOutputsSchema)]));
|
|
341
|
+
var runnerConfigSchema = z8.object(
|
|
342
|
+
{
|
|
343
|
+
command: z8.string({
|
|
344
|
+
description: "Shell command to execute"
|
|
345
|
+
}),
|
|
346
|
+
args: z8.array(z8.string({ description: "Command arguments" })).optional(),
|
|
347
|
+
outputFile: filePathSchema("Output path"),
|
|
348
|
+
outputTransform: outputTransformSchema.optional()
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
description: "How to execute runner"
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
var onProgressSchema = z8.function().args(z8.unknown()).returns(z8.void());
|
|
355
|
+
var runnerFunctionSchema = z8.function().args(onProgressSchema.optional()).returns(z8.union([auditOutputsSchema, z8.promise(auditOutputsSchema)]));
|
|
356
|
+
|
|
357
|
+
// packages/models/src/lib/plugin-config.ts
|
|
358
|
+
var pluginMetaSchema = packageVersionSchema({
|
|
359
|
+
optional: true
|
|
360
|
+
}).merge(
|
|
361
|
+
metaSchema({
|
|
362
|
+
titleDescription: "Descriptive name",
|
|
363
|
+
descriptionDescription: "Description (markdown)",
|
|
364
|
+
docsUrlDescription: "Plugin documentation site",
|
|
365
|
+
description: "Plugin metadata"
|
|
366
|
+
})
|
|
367
|
+
).merge(
|
|
368
|
+
z9.object({
|
|
369
|
+
slug: slugSchema("References plugin. ID (unique within core config)"),
|
|
370
|
+
icon: materialIconSchema
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
var pluginDataSchema = z9.object({
|
|
374
|
+
runner: z9.union([runnerConfigSchema, runnerFunctionSchema]),
|
|
375
|
+
audits: pluginAuditsSchema,
|
|
376
|
+
groups: auditGroupsSchema
|
|
377
|
+
});
|
|
378
|
+
var pluginConfigSchema = pluginMetaSchema.merge(pluginDataSchema).refine(
|
|
379
|
+
(pluginCfg) => !getMissingRefsFromGroups(pluginCfg),
|
|
380
|
+
(pluginCfg) => ({
|
|
381
|
+
message: missingRefsFromGroupsErrorMsg(pluginCfg)
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
function missingRefsFromGroupsErrorMsg(pluginCfg) {
|
|
385
|
+
const missingRefs = getMissingRefsFromGroups(pluginCfg);
|
|
386
|
+
return `In the groups, the following audit ref's needs to point to a audit in this plugin config: ${errorItems(
|
|
387
|
+
missingRefs
|
|
388
|
+
)}`;
|
|
389
|
+
}
|
|
390
|
+
function getMissingRefsFromGroups(pluginCfg) {
|
|
391
|
+
if (pluginCfg?.groups?.length && pluginCfg?.audits?.length) {
|
|
392
|
+
const groups = pluginCfg?.groups || [];
|
|
393
|
+
const audits = pluginCfg?.audits || [];
|
|
394
|
+
return hasMissingStrings(
|
|
395
|
+
groups.flatMap(({ refs: audits2 }) => audits2.map(({ slug: ref }) => ref)),
|
|
396
|
+
audits.map(({ slug }) => slug)
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// packages/models/src/lib/upload-config.ts
|
|
403
|
+
import { z as z10 } from "zod";
|
|
404
|
+
var uploadConfigSchema = z10.object({
|
|
405
|
+
server: urlSchema("URL of deployed portal API"),
|
|
406
|
+
apiKey: z10.string({
|
|
407
|
+
description: "API key with write access to portal (use `process.env` for security)"
|
|
408
|
+
}),
|
|
409
|
+
organization: z10.string({
|
|
410
|
+
description: "Organization in code versioning system"
|
|
411
|
+
}),
|
|
412
|
+
project: z10.string({
|
|
413
|
+
description: "Project in code versioning system"
|
|
414
|
+
})
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// packages/models/src/lib/core-config.ts
|
|
418
|
+
var unrefinedCoreConfigSchema = z11.object({
|
|
419
|
+
plugins: z11.array(pluginConfigSchema, {
|
|
420
|
+
description: "List of plugins to be used (official, community-provided, or custom)"
|
|
421
|
+
}),
|
|
422
|
+
/** portal configuration for persisting results */
|
|
423
|
+
persist: persistConfigSchema,
|
|
424
|
+
/** portal configuration for uploading results */
|
|
425
|
+
upload: uploadConfigSchema.optional(),
|
|
426
|
+
categories: z11.array(categoryConfigSchema, {
|
|
427
|
+
description: "Categorization of individual audits"
|
|
428
|
+
}).refine(
|
|
429
|
+
(categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
|
|
430
|
+
(categoryCfg) => ({
|
|
431
|
+
message: duplicateSlugCategoriesErrorMsg(categoryCfg)
|
|
432
|
+
})
|
|
433
|
+
)
|
|
434
|
+
});
|
|
435
|
+
var coreConfigSchema = refineCoreConfig(unrefinedCoreConfigSchema);
|
|
436
|
+
function refineCoreConfig(schema) {
|
|
437
|
+
return schema.refine(
|
|
438
|
+
(coreCfg) => !getMissingRefsForCategories(coreCfg),
|
|
439
|
+
(coreCfg) => ({
|
|
440
|
+
message: missingRefsForCategoriesErrorMsg(coreCfg)
|
|
441
|
+
})
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function missingRefsForCategoriesErrorMsg(coreCfg) {
|
|
445
|
+
const missingRefs = getMissingRefsForCategories(coreCfg);
|
|
446
|
+
return `In the categories, the following plugin refs do not exist in the provided plugins: ${errorItems(
|
|
447
|
+
missingRefs
|
|
448
|
+
)}`;
|
|
449
|
+
}
|
|
450
|
+
function getMissingRefsForCategories(coreCfg) {
|
|
451
|
+
const missingRefs = [];
|
|
452
|
+
const auditRefsFromCategory = coreCfg.categories.flatMap(
|
|
453
|
+
({ refs }) => refs.filter(({ type }) => type === "audit").map(({ plugin, slug }) => `${plugin}/${slug}`)
|
|
454
|
+
);
|
|
455
|
+
const auditRefsFromPlugins = coreCfg.plugins.flatMap(
|
|
456
|
+
({ audits, slug: pluginSlug }) => {
|
|
457
|
+
return audits.map(({ slug }) => `${pluginSlug}/${slug}`);
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
const missingAuditRefs = hasMissingStrings(
|
|
461
|
+
auditRefsFromCategory,
|
|
462
|
+
auditRefsFromPlugins
|
|
463
|
+
);
|
|
464
|
+
if (Array.isArray(missingAuditRefs) && missingAuditRefs.length > 0) {
|
|
465
|
+
missingRefs.push(...missingAuditRefs);
|
|
466
|
+
}
|
|
467
|
+
const groupRefsFromCategory = coreCfg.categories.flatMap(
|
|
468
|
+
({ refs }) => refs.filter(({ type }) => type === "group").map(({ plugin, slug }) => `${plugin}#${slug} (group)`)
|
|
469
|
+
);
|
|
470
|
+
const groupRefsFromPlugins = coreCfg.plugins.flatMap(
|
|
471
|
+
({ groups, slug: pluginSlug }) => {
|
|
472
|
+
return Array.isArray(groups) ? groups.map(({ slug }) => `${pluginSlug}#${slug} (group)`) : [];
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
const missingGroupRefs = hasMissingStrings(
|
|
476
|
+
groupRefsFromCategory,
|
|
477
|
+
groupRefsFromPlugins
|
|
478
|
+
);
|
|
479
|
+
if (Array.isArray(missingGroupRefs) && missingGroupRefs.length > 0) {
|
|
480
|
+
missingRefs.push(...missingGroupRefs);
|
|
481
|
+
}
|
|
482
|
+
return missingRefs.length ? missingRefs : false;
|
|
483
|
+
}
|
|
484
|
+
function duplicateSlugCategoriesErrorMsg(categories) {
|
|
485
|
+
const duplicateStringSlugs = getDuplicateSlugCategories(categories);
|
|
486
|
+
return `In the categories, the following slugs are duplicated: ${errorItems(
|
|
487
|
+
duplicateStringSlugs
|
|
488
|
+
)}`;
|
|
489
|
+
}
|
|
490
|
+
function getDuplicateSlugCategories(categories) {
|
|
491
|
+
return hasDuplicateStrings(categories.map(({ slug }) => slug));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// packages/models/src/lib/report.ts
|
|
495
|
+
import { z as z12 } from "zod";
|
|
496
|
+
var auditReportSchema = auditSchema.merge(auditOutputSchema);
|
|
497
|
+
var pluginReportSchema = pluginMetaSchema.merge(
|
|
498
|
+
executionMetaSchema({
|
|
499
|
+
descriptionDate: "Start date and time of plugin run",
|
|
500
|
+
descriptionDuration: "Duration of the plugin run in ms"
|
|
501
|
+
})
|
|
502
|
+
).merge(
|
|
503
|
+
z12.object({
|
|
504
|
+
audits: z12.array(auditReportSchema),
|
|
505
|
+
groups: z12.array(auditGroupSchema).optional()
|
|
506
|
+
})
|
|
507
|
+
);
|
|
508
|
+
var reportSchema = packageVersionSchema({
|
|
509
|
+
versionDescription: "NPM version of the CLI"
|
|
510
|
+
}).merge(
|
|
511
|
+
executionMetaSchema({
|
|
512
|
+
descriptionDate: "Start date and time of the collect run",
|
|
513
|
+
descriptionDuration: "Duration of the collect run in ms"
|
|
514
|
+
})
|
|
515
|
+
).merge(
|
|
516
|
+
z12.object(
|
|
517
|
+
{
|
|
518
|
+
categories: z12.array(categoryConfigSchema),
|
|
519
|
+
plugins: z12.array(pluginReportSchema)
|
|
520
|
+
},
|
|
521
|
+
{ description: "Collect output data" }
|
|
522
|
+
)
|
|
523
|
+
);
|
|
524
|
+
export {
|
|
525
|
+
auditGroupSchema,
|
|
526
|
+
auditOutputsSchema,
|
|
527
|
+
auditReportSchema,
|
|
528
|
+
auditSchema,
|
|
529
|
+
categoryConfigSchema,
|
|
530
|
+
coreConfigSchema,
|
|
531
|
+
formatSchema,
|
|
532
|
+
materialIconSchema,
|
|
533
|
+
onProgressSchema,
|
|
534
|
+
persistConfigSchema,
|
|
535
|
+
pluginAuditsSchema,
|
|
536
|
+
pluginConfigSchema,
|
|
537
|
+
pluginReportSchema,
|
|
538
|
+
refineCoreConfig,
|
|
539
|
+
reportSchema,
|
|
540
|
+
runnerConfigSchema,
|
|
541
|
+
unrefinedCoreConfigSchema,
|
|
542
|
+
uploadConfigSchema
|
|
543
|
+
};
|
package/package.json
ADDED
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { CategoryConfig, CategoryRef, categoryConfigSchema, } from './lib/category-config';
|
|
2
|
+
export { CoreConfig, coreConfigSchema, refineCoreConfig, unrefinedCoreConfigSchema, } from './lib/core-config';
|
|
3
|
+
export { Format, PersistConfig, formatSchema, persistConfigSchema, } from './lib/persist-config';
|
|
4
|
+
export { PluginConfig, pluginConfigSchema } from './lib/plugin-config';
|
|
5
|
+
export { auditSchema, Audit, pluginAuditsSchema, } from './lib/plugin-config-audits';
|
|
6
|
+
export { AuditGroupRef, AuditGroup, auditGroupSchema, } from './lib/plugin-config-groups';
|
|
7
|
+
export { AuditOutput, AuditOutputs, auditOutputsSchema, } from './lib/plugin-process-output';
|
|
8
|
+
export { Issue, IssueSeverity } from './lib/plugin-process-output-audit-issue';
|
|
9
|
+
export { AuditReport, auditReportSchema, PluginReport, Report, pluginReportSchema, reportSchema, } from './lib/report';
|
|
10
|
+
export { UploadConfig, uploadConfigSchema } from './lib/upload-config';
|
|
11
|
+
export { materialIconSchema } from './lib/implementation/schemas';
|
|
12
|
+
export { onProgressSchema, OnProgress, RunnerFunction, runnerConfigSchema, RunnerConfig, } from './lib/plugin-config-runner';
|