@reshotdev/screenshot 0.0.1-beta.2 → 0.0.1-beta.6
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 +2 -2
- package/package.json +9 -2
- package/src/commands/auth.js +1 -3
- package/src/commands/ci-setup.js +2 -2
- package/src/commands/drifts.js +5 -70
- package/src/commands/import-tests.js +13 -13
- package/src/commands/ingest.js +10 -10
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +3 -204
- package/src/commands/pull.js +2 -2
- package/src/commands/setup-wizard.js +123 -523
- package/src/commands/setup.js +7 -7
- package/src/commands/status.js +26 -43
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/index.js +9 -90
- package/src/lib/api-client.js +8 -8
- package/src/lib/capture-engine.js +3 -3
- package/src/lib/capture-script-runner.js +27 -37
- package/src/lib/config.js +8 -72
- package/src/lib/record-config.js +1 -1
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +4 -4
- package/src/lib/ui-api.js +3 -3
- package/web/manager/dist/assets/{index--ZgioErz.js → index-8H7P9ANi.js} +1 -1
- package/web/manager/dist/index.html +1 -1
- package/src/commands/validate-docs.js +0 -529
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-8H7P9ANi.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-n468W0Wr.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* validate-docs.js - Validate docsync configuration and documentation bindings
|
|
3
|
-
*
|
|
4
|
-
* Checks:
|
|
5
|
-
* 1. Configuration file syntax and schema
|
|
6
|
-
* 2. Scenario definitions and selectors
|
|
7
|
-
* 3. Documentation file bindings (zombie links)
|
|
8
|
-
* 4. Asset references and file integrity
|
|
9
|
-
* 5. Variant/dimension configuration
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const chalk = require("chalk");
|
|
13
|
-
const fs = require("fs-extra");
|
|
14
|
-
const path = require("path");
|
|
15
|
-
const config = require("../lib/config");
|
|
16
|
-
const glob = require("glob");
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Validation issue severity
|
|
20
|
-
*/
|
|
21
|
-
const Severity = {
|
|
22
|
-
ERROR: "error",
|
|
23
|
-
WARNING: "warning",
|
|
24
|
-
INFO: "info",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Validation result
|
|
29
|
-
*/
|
|
30
|
-
class ValidationResult {
|
|
31
|
-
constructor() {
|
|
32
|
-
this.issues = [];
|
|
33
|
-
this.stats = {
|
|
34
|
-
scenarios: 0,
|
|
35
|
-
steps: 0,
|
|
36
|
-
assets: 0,
|
|
37
|
-
bindings: 0,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
addIssue(severity, code, message, context = {}) {
|
|
42
|
-
this.issues.push({
|
|
43
|
-
severity,
|
|
44
|
-
code,
|
|
45
|
-
message,
|
|
46
|
-
...context,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
hasErrors() {
|
|
51
|
-
return this.issues.some((i) => i.severity === Severity.ERROR);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
hasWarnings() {
|
|
55
|
-
return this.issues.some((i) => i.severity === Severity.WARNING);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
getErrors() {
|
|
59
|
-
return this.issues.filter((i) => i.severity === Severity.ERROR);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
getWarnings() {
|
|
63
|
-
return this.issues.filter((i) => i.severity === Severity.WARNING);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Validate configuration schema
|
|
69
|
-
*/
|
|
70
|
-
function validateConfigSchema(docSyncConfig, result) {
|
|
71
|
-
// Check required fields
|
|
72
|
-
if (!docSyncConfig.project?.baseUrl) {
|
|
73
|
-
result.addIssue(
|
|
74
|
-
Severity.ERROR,
|
|
75
|
-
"MISSING_BASE_URL",
|
|
76
|
-
"Missing project.baseUrl in configuration"
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Validate scenarios
|
|
81
|
-
if (!docSyncConfig.scenarios || !Array.isArray(docSyncConfig.scenarios)) {
|
|
82
|
-
result.addIssue(
|
|
83
|
-
Severity.ERROR,
|
|
84
|
-
"MISSING_SCENARIOS",
|
|
85
|
-
"No scenarios array found in configuration"
|
|
86
|
-
);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (docSyncConfig.scenarios.length === 0) {
|
|
91
|
-
result.addIssue(
|
|
92
|
-
Severity.WARNING,
|
|
93
|
-
"EMPTY_SCENARIOS",
|
|
94
|
-
"Scenarios array is empty - nothing to capture"
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Validate each scenario
|
|
99
|
-
const seenKeys = new Set();
|
|
100
|
-
|
|
101
|
-
for (const scenario of docSyncConfig.scenarios) {
|
|
102
|
-
// Check for duplicate keys
|
|
103
|
-
if (seenKeys.has(scenario.key)) {
|
|
104
|
-
result.addIssue(
|
|
105
|
-
Severity.ERROR,
|
|
106
|
-
"DUPLICATE_SCENARIO_KEY",
|
|
107
|
-
`Duplicate scenario key: ${scenario.key}`,
|
|
108
|
-
{ scenario: scenario.key }
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
seenKeys.add(scenario.key);
|
|
112
|
-
|
|
113
|
-
// Validate scenario structure
|
|
114
|
-
if (!scenario.key) {
|
|
115
|
-
result.addIssue(
|
|
116
|
-
Severity.ERROR,
|
|
117
|
-
"MISSING_SCENARIO_KEY",
|
|
118
|
-
"Scenario missing required 'key' field"
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!scenario.name) {
|
|
123
|
-
result.addIssue(
|
|
124
|
-
Severity.WARNING,
|
|
125
|
-
"MISSING_SCENARIO_NAME",
|
|
126
|
-
`Scenario '${scenario.key}' missing 'name' field`,
|
|
127
|
-
{ scenario: scenario.key }
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Validate steps
|
|
132
|
-
if (!scenario.steps || !Array.isArray(scenario.steps)) {
|
|
133
|
-
result.addIssue(
|
|
134
|
-
Severity.WARNING,
|
|
135
|
-
"MISSING_STEPS",
|
|
136
|
-
`Scenario '${scenario.key}' has no steps defined`,
|
|
137
|
-
{ scenario: scenario.key }
|
|
138
|
-
);
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
result.stats.scenarios++;
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < scenario.steps.length; i++) {
|
|
145
|
-
const step = scenario.steps[i];
|
|
146
|
-
validateStep(step, scenario.key, i, result);
|
|
147
|
-
result.stats.steps++;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Validate dimensions
|
|
152
|
-
if (docSyncConfig.dimensions) {
|
|
153
|
-
validateDimensions(docSyncConfig.dimensions, result);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Validate a single step
|
|
159
|
-
*/
|
|
160
|
-
function validateStep(step, scenarioKey, stepIndex, result) {
|
|
161
|
-
// Check action
|
|
162
|
-
const validActions = ["navigate", "click", "type", "hover", "scroll", "wait", "capture"];
|
|
163
|
-
if (!step.action) {
|
|
164
|
-
result.addIssue(
|
|
165
|
-
Severity.ERROR,
|
|
166
|
-
"MISSING_STEP_ACTION",
|
|
167
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' missing 'action' field`,
|
|
168
|
-
{ scenario: scenarioKey, step: stepIndex + 1 }
|
|
169
|
-
);
|
|
170
|
-
} else if (!validActions.includes(step.action)) {
|
|
171
|
-
result.addIssue(
|
|
172
|
-
Severity.WARNING,
|
|
173
|
-
"UNKNOWN_STEP_ACTION",
|
|
174
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' has unknown action: ${step.action}`,
|
|
175
|
-
{ scenario: scenarioKey, step: stepIndex + 1, action: step.action }
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check selector for interactive actions
|
|
180
|
-
const actionsRequiringSelector = ["click", "type", "hover"];
|
|
181
|
-
if (actionsRequiringSelector.includes(step.action) && !step.selector) {
|
|
182
|
-
result.addIssue(
|
|
183
|
-
Severity.ERROR,
|
|
184
|
-
"MISSING_STEP_SELECTOR",
|
|
185
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' (${step.action}) missing 'selector'`,
|
|
186
|
-
{ scenario: scenarioKey, step: stepIndex + 1, action: step.action }
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Validate selector syntax (basic check)
|
|
191
|
-
if (step.selector) {
|
|
192
|
-
try {
|
|
193
|
-
// Check for common selector issues
|
|
194
|
-
if (step.selector.includes("{{") && !step.selector.includes("}}")) {
|
|
195
|
-
result.addIssue(
|
|
196
|
-
Severity.ERROR,
|
|
197
|
-
"MALFORMED_SELECTOR_TEMPLATE",
|
|
198
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' has unclosed template in selector`,
|
|
199
|
-
{ scenario: scenarioKey, step: stepIndex + 1, selector: step.selector }
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
} catch (e) {
|
|
203
|
-
result.addIssue(
|
|
204
|
-
Severity.ERROR,
|
|
205
|
-
"INVALID_SELECTOR",
|
|
206
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' has invalid selector: ${e.message}`,
|
|
207
|
-
{ scenario: scenarioKey, step: stepIndex + 1, selector: step.selector }
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Check for navigate without URL
|
|
213
|
-
if (step.action === "navigate" && !step.url) {
|
|
214
|
-
result.addIssue(
|
|
215
|
-
Severity.ERROR,
|
|
216
|
-
"MISSING_NAVIGATE_URL",
|
|
217
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' (navigate) missing 'url'`,
|
|
218
|
-
{ scenario: scenarioKey, step: stepIndex + 1 }
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Check for type without value
|
|
223
|
-
if (step.action === "type" && !step.value && step.value !== "") {
|
|
224
|
-
result.addIssue(
|
|
225
|
-
Severity.WARNING,
|
|
226
|
-
"MISSING_TYPE_VALUE",
|
|
227
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' (type) missing 'value'`,
|
|
228
|
-
{ scenario: scenarioKey, step: stepIndex + 1 }
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Check capture with name
|
|
233
|
-
if (step.action === "capture" && !step.name) {
|
|
234
|
-
result.addIssue(
|
|
235
|
-
Severity.INFO,
|
|
236
|
-
"UNNAMED_CAPTURE",
|
|
237
|
-
`Step ${stepIndex + 1} in '${scenarioKey}' capture without 'name' (will use step index)`,
|
|
238
|
-
{ scenario: scenarioKey, step: stepIndex + 1 }
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Validate dimensions configuration
|
|
245
|
-
*/
|
|
246
|
-
function validateDimensions(dimensions, result) {
|
|
247
|
-
for (const [key, dim] of Object.entries(dimensions)) {
|
|
248
|
-
if (!dim.options || Object.keys(dim.options).length === 0) {
|
|
249
|
-
result.addIssue(
|
|
250
|
-
Severity.WARNING,
|
|
251
|
-
"EMPTY_DIMENSION",
|
|
252
|
-
`Dimension '${key}' has no options defined`,
|
|
253
|
-
{ dimension: key }
|
|
254
|
-
);
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Check each option
|
|
259
|
-
for (const [optKey, optValue] of Object.entries(dim.options)) {
|
|
260
|
-
if (typeof optValue !== "object") {
|
|
261
|
-
result.addIssue(
|
|
262
|
-
Severity.WARNING,
|
|
263
|
-
"INVALID_DIMENSION_OPTION",
|
|
264
|
-
`Dimension '${key}' option '${optKey}' should be an object`,
|
|
265
|
-
{ dimension: key, option: optKey }
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Validate asset references and files
|
|
274
|
-
*/
|
|
275
|
-
async function validateAssets(docSyncConfig, result) {
|
|
276
|
-
const outputDir = config.getOutputDir();
|
|
277
|
-
|
|
278
|
-
if (!fs.existsSync(outputDir)) {
|
|
279
|
-
result.addIssue(
|
|
280
|
-
Severity.INFO,
|
|
281
|
-
"NO_OUTPUT_DIR",
|
|
282
|
-
`Output directory does not exist: ${outputDir}. Run 'reshot run' first.`
|
|
283
|
-
);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Find all generated assets
|
|
288
|
-
const assets = glob.sync("**/*.{png,jpg,jpeg,gif,mp4,webm}", {
|
|
289
|
-
cwd: outputDir,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
result.stats.assets = assets.length;
|
|
293
|
-
|
|
294
|
-
if (assets.length === 0) {
|
|
295
|
-
result.addIssue(
|
|
296
|
-
Severity.WARNING,
|
|
297
|
-
"NO_ASSETS",
|
|
298
|
-
`No visual assets found in ${outputDir}. Run 'reshot run' to generate.`
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Check for orphaned assets (assets without matching scenario)
|
|
303
|
-
const scenarioKeys = new Set(docSyncConfig.scenarios?.map((s) => s.key) || []);
|
|
304
|
-
|
|
305
|
-
for (const asset of assets) {
|
|
306
|
-
const parts = asset.split(path.sep);
|
|
307
|
-
if (parts.length > 0) {
|
|
308
|
-
const assetScenario = parts[0];
|
|
309
|
-
if (!scenarioKeys.has(assetScenario) && assetScenario !== "_shared") {
|
|
310
|
-
result.addIssue(
|
|
311
|
-
Severity.WARNING,
|
|
312
|
-
"ORPHANED_ASSET",
|
|
313
|
-
`Asset may be orphaned (scenario '${assetScenario}' not in config): ${asset}`,
|
|
314
|
-
{ asset, scenario: assetScenario }
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Validate documentation bindings
|
|
323
|
-
*/
|
|
324
|
-
async function validateBindings(docSyncConfig, result) {
|
|
325
|
-
// Check if bindings are configured
|
|
326
|
-
const bindings = docSyncConfig.bindings || {};
|
|
327
|
-
const bindingEntries = Object.entries(bindings);
|
|
328
|
-
|
|
329
|
-
if (bindingEntries.length === 0) {
|
|
330
|
-
result.addIssue(
|
|
331
|
-
Severity.INFO,
|
|
332
|
-
"NO_BINDINGS",
|
|
333
|
-
"No documentation bindings configured. Visual assets are standalone."
|
|
334
|
-
);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
result.stats.bindings = bindingEntries.length;
|
|
339
|
-
|
|
340
|
-
// Validate each binding
|
|
341
|
-
for (const [docPath, binding] of bindingEntries) {
|
|
342
|
-
// Check if documentation file exists
|
|
343
|
-
if (!fs.existsSync(docPath)) {
|
|
344
|
-
result.addIssue(
|
|
345
|
-
Severity.ERROR,
|
|
346
|
-
"MISSING_DOC_FILE",
|
|
347
|
-
`Binding target file does not exist: ${docPath}`,
|
|
348
|
-
{ binding: docPath }
|
|
349
|
-
);
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Check if bound scenarios exist
|
|
354
|
-
if (binding.scenarios) {
|
|
355
|
-
for (const scenarioKey of binding.scenarios) {
|
|
356
|
-
const scenario = docSyncConfig.scenarios?.find((s) => s.key === scenarioKey);
|
|
357
|
-
if (!scenario) {
|
|
358
|
-
result.addIssue(
|
|
359
|
-
Severity.ERROR,
|
|
360
|
-
"INVALID_BINDING_SCENARIO",
|
|
361
|
-
`Binding '${docPath}' references non-existent scenario: ${scenarioKey}`,
|
|
362
|
-
{ binding: docPath, scenario: scenarioKey }
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Attempt to auto-fix issues
|
|
372
|
-
*/
|
|
373
|
-
async function autoFix(docSyncConfig, result) {
|
|
374
|
-
let fixCount = 0;
|
|
375
|
-
const fixes = [];
|
|
376
|
-
|
|
377
|
-
// Auto-fix: Add missing scenario names
|
|
378
|
-
for (const issue of result.getWarnings()) {
|
|
379
|
-
if (issue.code === "MISSING_SCENARIO_NAME" && issue.scenario) {
|
|
380
|
-
const scenario = docSyncConfig.scenarios?.find((s) => s.key === issue.scenario);
|
|
381
|
-
if (scenario) {
|
|
382
|
-
scenario.name = scenario.key
|
|
383
|
-
.replace(/-/g, " ")
|
|
384
|
-
.replace(/\b\w/g, (l) => l.toUpperCase());
|
|
385
|
-
fixes.push(`Added name '${scenario.name}' to scenario '${scenario.key}'`);
|
|
386
|
-
fixCount++;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Save if fixes were made
|
|
392
|
-
if (fixCount > 0) {
|
|
393
|
-
config.writeConfig(docSyncConfig);
|
|
394
|
-
console.log(chalk.green(`\n✓ Applied ${fixCount} auto-fixes:`));
|
|
395
|
-
fixes.forEach((fix) => console.log(` - ${fix}`));
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return fixCount;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Format and print validation results
|
|
403
|
-
*/
|
|
404
|
-
function printResults(result, verbose) {
|
|
405
|
-
console.log(chalk.bold("\n📋 Validation Results\n"));
|
|
406
|
-
|
|
407
|
-
// Print stats
|
|
408
|
-
console.log(chalk.dim("Statistics:"));
|
|
409
|
-
console.log(` Scenarios: ${result.stats.scenarios}`);
|
|
410
|
-
console.log(` Steps: ${result.stats.steps}`);
|
|
411
|
-
console.log(` Assets: ${result.stats.assets}`);
|
|
412
|
-
console.log(` Bindings: ${result.stats.bindings}`);
|
|
413
|
-
console.log();
|
|
414
|
-
|
|
415
|
-
// Print errors
|
|
416
|
-
const errors = result.getErrors();
|
|
417
|
-
if (errors.length > 0) {
|
|
418
|
-
console.log(chalk.red(`✗ ${errors.length} Error(s):`));
|
|
419
|
-
errors.forEach((e) => {
|
|
420
|
-
console.log(chalk.red(` [${e.code}] ${e.message}`));
|
|
421
|
-
if (verbose && e.scenario) {
|
|
422
|
-
console.log(chalk.dim(` Scenario: ${e.scenario}`));
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
console.log();
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Print warnings
|
|
429
|
-
const warnings = result.getWarnings();
|
|
430
|
-
if (warnings.length > 0) {
|
|
431
|
-
console.log(chalk.yellow(`⚠ ${warnings.length} Warning(s):`));
|
|
432
|
-
warnings.forEach((w) => {
|
|
433
|
-
console.log(chalk.yellow(` [${w.code}] ${w.message}`));
|
|
434
|
-
if (verbose && w.scenario) {
|
|
435
|
-
console.log(chalk.dim(` Scenario: ${w.scenario}`));
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
console.log();
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Print info
|
|
442
|
-
if (verbose) {
|
|
443
|
-
const infos = result.issues.filter((i) => i.severity === Severity.INFO);
|
|
444
|
-
if (infos.length > 0) {
|
|
445
|
-
console.log(chalk.blue(`ℹ ${infos.length} Info:`));
|
|
446
|
-
infos.forEach((i) => {
|
|
447
|
-
console.log(chalk.blue(` [${i.code}] ${i.message}`));
|
|
448
|
-
});
|
|
449
|
-
console.log();
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Summary
|
|
454
|
-
if (!result.hasErrors() && !result.hasWarnings()) {
|
|
455
|
-
console.log(chalk.green("✓ All validations passed!"));
|
|
456
|
-
} else if (!result.hasErrors()) {
|
|
457
|
-
console.log(chalk.yellow("⚠ Validation completed with warnings"));
|
|
458
|
-
} else {
|
|
459
|
-
console.log(chalk.red("✗ Validation failed with errors"));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Main validation command
|
|
465
|
-
*/
|
|
466
|
-
async function validateDocs(options = {}) {
|
|
467
|
-
const { strict = false, fix = false, verbose = false } = options;
|
|
468
|
-
|
|
469
|
-
console.log(chalk.bold("🔍 Validating Reshot Configuration\n"));
|
|
470
|
-
|
|
471
|
-
// Read configuration
|
|
472
|
-
let docSyncConfig;
|
|
473
|
-
try {
|
|
474
|
-
docSyncConfig = config.readConfig();
|
|
475
|
-
} catch (error) {
|
|
476
|
-
console.log(chalk.red("✗ Failed to read configuration:"), error.message);
|
|
477
|
-
process.exit(1);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const configPath = config.getConfigPath();
|
|
481
|
-
console.log(chalk.dim(`Config file: ${configPath}\n`));
|
|
482
|
-
|
|
483
|
-
// Run validations
|
|
484
|
-
const result = new ValidationResult();
|
|
485
|
-
|
|
486
|
-
// 1. Validate schema
|
|
487
|
-
console.log(chalk.dim("Checking configuration schema..."));
|
|
488
|
-
validateConfigSchema(docSyncConfig, result);
|
|
489
|
-
|
|
490
|
-
// 2. Validate assets
|
|
491
|
-
console.log(chalk.dim("Checking generated assets..."));
|
|
492
|
-
await validateAssets(docSyncConfig, result);
|
|
493
|
-
|
|
494
|
-
// 3. Validate bindings
|
|
495
|
-
console.log(chalk.dim("Checking documentation bindings..."));
|
|
496
|
-
await validateBindings(docSyncConfig, result);
|
|
497
|
-
|
|
498
|
-
// Auto-fix if requested
|
|
499
|
-
if (fix) {
|
|
500
|
-
await autoFix(docSyncConfig, result);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Print results
|
|
504
|
-
printResults(result, verbose);
|
|
505
|
-
|
|
506
|
-
// Exit code
|
|
507
|
-
if (result.hasErrors()) {
|
|
508
|
-
process.exit(1);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (strict && result.hasWarnings()) {
|
|
512
|
-
console.log(chalk.yellow("\n--strict mode: Exiting with error due to warnings"));
|
|
513
|
-
process.exit(1);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Alias for validateDocs (for backward compatibility)
|
|
519
|
-
*/
|
|
520
|
-
async function validateDocSync(options = {}) {
|
|
521
|
-
return validateDocs(options);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
module.exports = {
|
|
525
|
-
validateDocs,
|
|
526
|
-
validateDocSync,
|
|
527
|
-
ValidationResult,
|
|
528
|
-
Severity,
|
|
529
|
-
};
|