@memberjunction/metadata-sync 2.50.0 → 2.52.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 +423 -2
- package/dist/commands/file-reset/index.d.ts +15 -0
- package/dist/commands/file-reset/index.js +221 -0
- package/dist/commands/file-reset/index.js.map +1 -0
- package/dist/commands/pull/index.d.ts +1 -0
- package/dist/commands/pull/index.js +82 -10
- package/dist/commands/pull/index.js.map +1 -1
- package/dist/commands/push/index.d.ts +21 -0
- package/dist/commands/push/index.js +589 -45
- package/dist/commands/push/index.js.map +1 -1
- package/dist/commands/validate/index.d.ts +15 -0
- package/dist/commands/validate/index.js +149 -0
- package/dist/commands/validate/index.js.map +1 -0
- package/dist/commands/watch/index.js +39 -1
- package/dist/commands/watch/index.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/file-backup-manager.d.ts +90 -0
- package/dist/lib/file-backup-manager.js +186 -0
- package/dist/lib/file-backup-manager.js.map +1 -0
- package/dist/lib/provider-utils.d.ts +2 -2
- package/dist/lib/provider-utils.js +3 -4
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/sync-engine.js +29 -3
- package/dist/lib/sync-engine.js.map +1 -1
- package/dist/services/FormattingService.d.ts +45 -0
- package/dist/services/FormattingService.js +564 -0
- package/dist/services/FormattingService.js.map +1 -0
- package/dist/services/ValidationService.d.ts +110 -0
- package/dist/services/ValidationService.js +737 -0
- package/dist/services/ValidationService.js.map +1 -0
- package/dist/types/validation.d.ts +98 -0
- package/dist/types/validation.js +97 -0
- package/dist/types/validation.js.map +1 -0
- package/oclif.manifest.json +205 -39
- package/package.json +7 -7
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FormattingService = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
class FormattingService {
|
|
9
|
+
/**
|
|
10
|
+
* Format validation result as JSON
|
|
11
|
+
*/
|
|
12
|
+
formatValidationResultAsJson(result) {
|
|
13
|
+
const output = {
|
|
14
|
+
isValid: result.isValid,
|
|
15
|
+
summary: {
|
|
16
|
+
totalFiles: result.summary.totalFiles,
|
|
17
|
+
totalEntities: result.summary.totalEntities,
|
|
18
|
+
totalErrors: result.summary.totalErrors,
|
|
19
|
+
totalWarnings: result.summary.totalWarnings,
|
|
20
|
+
errorsByType: this.getErrorsByType(result.errors),
|
|
21
|
+
warningsByType: this.getWarningsByType(result.warnings)
|
|
22
|
+
},
|
|
23
|
+
errors: result.errors.map(e => ({
|
|
24
|
+
type: e.type,
|
|
25
|
+
entity: e.entity,
|
|
26
|
+
field: e.field,
|
|
27
|
+
file: e.file,
|
|
28
|
+
message: e.message,
|
|
29
|
+
suggestion: e.suggestion
|
|
30
|
+
})),
|
|
31
|
+
warnings: result.warnings.map(w => ({
|
|
32
|
+
type: w.type,
|
|
33
|
+
entity: w.entity,
|
|
34
|
+
field: w.field,
|
|
35
|
+
file: w.file,
|
|
36
|
+
message: w.message,
|
|
37
|
+
suggestion: w.suggestion
|
|
38
|
+
}))
|
|
39
|
+
};
|
|
40
|
+
return JSON.stringify(output, null, 2);
|
|
41
|
+
}
|
|
42
|
+
symbols = {
|
|
43
|
+
success: '✓',
|
|
44
|
+
error: '✗',
|
|
45
|
+
warning: '⚠',
|
|
46
|
+
info: 'ℹ',
|
|
47
|
+
arrow: '→',
|
|
48
|
+
bullet: '•',
|
|
49
|
+
box: {
|
|
50
|
+
topLeft: '┌',
|
|
51
|
+
topRight: '┐',
|
|
52
|
+
bottomLeft: '└',
|
|
53
|
+
bottomRight: '┘',
|
|
54
|
+
horizontal: '─',
|
|
55
|
+
vertical: '│',
|
|
56
|
+
cross: '┼'
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Format validation result for terminal output
|
|
61
|
+
*/
|
|
62
|
+
formatValidationResult(result, verbose = false) {
|
|
63
|
+
const lines = [];
|
|
64
|
+
// Header
|
|
65
|
+
lines.push(this.formatHeader('Validation Report'));
|
|
66
|
+
lines.push('');
|
|
67
|
+
// Summary box
|
|
68
|
+
lines.push(this.formatSummaryBox(result));
|
|
69
|
+
lines.push('');
|
|
70
|
+
// File results
|
|
71
|
+
if (result.summary.fileResults.size > 0) {
|
|
72
|
+
lines.push(this.formatSectionHeader('File Results'));
|
|
73
|
+
lines.push('');
|
|
74
|
+
for (const [file, fileResult] of result.summary.fileResults) {
|
|
75
|
+
const hasIssues = fileResult.errors.length > 0 || fileResult.warnings.length > 0;
|
|
76
|
+
if (!verbose && !hasIssues)
|
|
77
|
+
continue;
|
|
78
|
+
lines.push(this.formatFileResult(file, fileResult, verbose));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Detailed errors
|
|
82
|
+
if (result.errors.length > 0) {
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push(this.formatSectionHeader('Errors'));
|
|
85
|
+
lines.push('');
|
|
86
|
+
result.errors.forEach((error, index) => {
|
|
87
|
+
lines.push(this.formatError(error, index + 1));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Detailed warnings
|
|
91
|
+
if (result.warnings.length > 0) {
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push(this.formatSectionHeader('Warnings'));
|
|
94
|
+
lines.push('');
|
|
95
|
+
result.warnings.forEach((warning, index) => {
|
|
96
|
+
lines.push(this.formatWarning(warning, index + 1));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// Footer
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push(this.formatFooter(result));
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Format push/pull summary report
|
|
106
|
+
*/
|
|
107
|
+
formatSyncSummary(operation, stats) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
lines.push(this.formatHeader(`${operation.charAt(0).toUpperCase() + operation.slice(1)} Summary`));
|
|
110
|
+
lines.push('');
|
|
111
|
+
const total = stats.created + stats.updated + stats.deleted + stats.skipped + (stats.unchanged || 0);
|
|
112
|
+
lines.push(chalk_1.default.bold('Operation Statistics:'));
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push(` ${chalk_1.default.green(this.symbols.success)} Created: ${chalk_1.default.green(stats.created)}`);
|
|
115
|
+
lines.push(` ${chalk_1.default.blue(this.symbols.info)} Updated: ${chalk_1.default.blue(stats.updated)}`);
|
|
116
|
+
if (stats.unchanged !== undefined) {
|
|
117
|
+
lines.push(` ${chalk_1.default.gray('-')} Unchanged: ${chalk_1.default.gray(stats.unchanged)}`);
|
|
118
|
+
}
|
|
119
|
+
lines.push(` ${chalk_1.default.red(this.symbols.error)} Deleted: ${chalk_1.default.red(stats.deleted)}`);
|
|
120
|
+
lines.push(` ${chalk_1.default.gray('-')} Skipped: ${chalk_1.default.gray(stats.skipped)}`);
|
|
121
|
+
lines.push('');
|
|
122
|
+
lines.push(` Total Records: ${chalk_1.default.bold(total)}`);
|
|
123
|
+
lines.push(` Duration: ${chalk_1.default.cyan(this.formatDuration(stats.duration))}`);
|
|
124
|
+
if (stats.errors > 0) {
|
|
125
|
+
lines.push('');
|
|
126
|
+
lines.push(chalk_1.default.red(` ${this.symbols.error} Errors: ${stats.errors}`));
|
|
127
|
+
}
|
|
128
|
+
return lines.join('\n');
|
|
129
|
+
}
|
|
130
|
+
formatHeader(title) {
|
|
131
|
+
const width = 60;
|
|
132
|
+
const line = '═'.repeat(width);
|
|
133
|
+
// MemberJunction branding
|
|
134
|
+
const brandingText = 'MemberJunction Metadata Sync';
|
|
135
|
+
const brandingPadding = Math.floor((width - brandingText.length - 2) / 2);
|
|
136
|
+
// Title
|
|
137
|
+
const titlePadding = Math.floor((width - title.length - 2) / 2);
|
|
138
|
+
return chalk_1.default.blue([
|
|
139
|
+
line,
|
|
140
|
+
'║' + ' '.repeat(brandingPadding) + brandingText + ' '.repeat(width - brandingPadding - brandingText.length - 2) + '║',
|
|
141
|
+
'║' + ' '.repeat(titlePadding) + title + ' '.repeat(width - titlePadding - title.length - 2) + '║',
|
|
142
|
+
line
|
|
143
|
+
].join('\n'));
|
|
144
|
+
}
|
|
145
|
+
formatSectionHeader(title) {
|
|
146
|
+
return chalk_1.default.bold.underline(title);
|
|
147
|
+
}
|
|
148
|
+
formatSummaryBox(result) {
|
|
149
|
+
const lines = [];
|
|
150
|
+
const width = 50;
|
|
151
|
+
lines.push(chalk_1.default.gray('┌' + '─'.repeat(width - 2) + '┐'));
|
|
152
|
+
// Basic stats
|
|
153
|
+
const items = [
|
|
154
|
+
['Files:', result.summary.totalFiles],
|
|
155
|
+
['Entities:', result.summary.totalEntities],
|
|
156
|
+
['Errors:', result.summary.totalErrors],
|
|
157
|
+
['Warnings:', result.summary.totalWarnings]
|
|
158
|
+
];
|
|
159
|
+
items.forEach(([label, value]) => {
|
|
160
|
+
const numValue = Number(value);
|
|
161
|
+
const color = label === 'Errors:' && numValue > 0 ? chalk_1.default.red :
|
|
162
|
+
label === 'Warnings:' && numValue > 0 ? chalk_1.default.yellow :
|
|
163
|
+
chalk_1.default.white;
|
|
164
|
+
const line = `${String(label).padEnd(15)} ${color(String(value))}`;
|
|
165
|
+
lines.push(chalk_1.default.gray('│ ') + line.padEnd(width - 4) + chalk_1.default.gray(' │'));
|
|
166
|
+
});
|
|
167
|
+
// Add separator
|
|
168
|
+
if (result.errors.length > 0 || result.warnings.length > 0) {
|
|
169
|
+
lines.push(chalk_1.default.gray('├' + '─'.repeat(width - 2) + '┤'));
|
|
170
|
+
}
|
|
171
|
+
// Error breakdown by type
|
|
172
|
+
if (result.errors.length > 0) {
|
|
173
|
+
lines.push(chalk_1.default.gray('│ ') + chalk_1.default.bold('Errors by Type:').padEnd(width - 4) + chalk_1.default.gray(' │'));
|
|
174
|
+
const errorsByType = this.getErrorsByType(result.errors);
|
|
175
|
+
for (const [type, count] of Object.entries(errorsByType)) {
|
|
176
|
+
const typeText = ` ${type}:`;
|
|
177
|
+
const countText = chalk_1.default.red(count.toString());
|
|
178
|
+
const spaceBetween = width - 4 - typeText.length - count.toString().length;
|
|
179
|
+
const line = typeText + ' '.repeat(spaceBetween) + countText;
|
|
180
|
+
lines.push(chalk_1.default.gray('│ ') + line + chalk_1.default.gray(' │'));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Warning breakdown by type
|
|
184
|
+
if (result.warnings.length > 0) {
|
|
185
|
+
if (result.errors.length > 0) {
|
|
186
|
+
lines.push(chalk_1.default.gray('├' + '─'.repeat(width - 2) + '┤'));
|
|
187
|
+
}
|
|
188
|
+
lines.push(chalk_1.default.gray('│ ') + chalk_1.default.bold('Warnings by Type:').padEnd(width - 4) + chalk_1.default.gray(' │'));
|
|
189
|
+
const warningsByType = this.getWarningsByType(result.warnings);
|
|
190
|
+
for (const [type, count] of Object.entries(warningsByType)) {
|
|
191
|
+
const typeText = ` ${type}:`;
|
|
192
|
+
const countText = chalk_1.default.yellow(count.toString());
|
|
193
|
+
const spaceBetween = width - 4 - typeText.length - count.toString().length;
|
|
194
|
+
const line = typeText + ' '.repeat(spaceBetween) + countText;
|
|
195
|
+
lines.push(chalk_1.default.gray('│ ') + line + chalk_1.default.gray(' │'));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
lines.push(chalk_1.default.gray('└' + '─'.repeat(width - 2) + '┘'));
|
|
199
|
+
return lines.join('\n');
|
|
200
|
+
}
|
|
201
|
+
formatFileResult(file, result, verbose) {
|
|
202
|
+
const lines = [];
|
|
203
|
+
const hasErrors = result.errors.length > 0;
|
|
204
|
+
const hasWarnings = result.warnings.length > 0;
|
|
205
|
+
const icon = hasErrors ? chalk_1.default.red(this.symbols.error) :
|
|
206
|
+
hasWarnings ? chalk_1.default.yellow(this.symbols.warning) :
|
|
207
|
+
chalk_1.default.green(this.symbols.success);
|
|
208
|
+
const shortPath = this.shortenPath(file);
|
|
209
|
+
lines.push(`${icon} ${chalk_1.default.bold(shortPath)}`);
|
|
210
|
+
if (verbose || hasErrors || hasWarnings) {
|
|
211
|
+
lines.push(` ${chalk_1.default.gray(`Entities: ${result.entityCount}`)}`);
|
|
212
|
+
if (hasErrors) {
|
|
213
|
+
lines.push(` ${chalk_1.default.red(`Errors: ${result.errors.length}`)}`);
|
|
214
|
+
}
|
|
215
|
+
if (hasWarnings) {
|
|
216
|
+
lines.push(` ${chalk_1.default.yellow(`Warnings: ${result.warnings.length}`)}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
lines.push('');
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
formatError(error, index) {
|
|
223
|
+
const lines = [];
|
|
224
|
+
lines.push(chalk_1.default.red(`${index}. ${error.message}`));
|
|
225
|
+
if (error.entity) {
|
|
226
|
+
lines.push(chalk_1.default.gray(` Entity: ${error.entity}`));
|
|
227
|
+
}
|
|
228
|
+
if (error.field) {
|
|
229
|
+
lines.push(chalk_1.default.gray(` Field: ${error.field}`));
|
|
230
|
+
}
|
|
231
|
+
lines.push(chalk_1.default.gray(` File: ${this.shortenPath(error.file)}`));
|
|
232
|
+
if (error.suggestion) {
|
|
233
|
+
lines.push(chalk_1.default.cyan(` ${this.symbols.arrow} Suggestion: ${error.suggestion}`));
|
|
234
|
+
}
|
|
235
|
+
lines.push('');
|
|
236
|
+
return lines.join('\n');
|
|
237
|
+
}
|
|
238
|
+
formatWarning(warning, index) {
|
|
239
|
+
const lines = [];
|
|
240
|
+
lines.push(chalk_1.default.yellow(`${index}. ${warning.message}`));
|
|
241
|
+
if (warning.entity) {
|
|
242
|
+
lines.push(chalk_1.default.gray(` Entity: ${warning.entity}`));
|
|
243
|
+
}
|
|
244
|
+
if (warning.field) {
|
|
245
|
+
lines.push(chalk_1.default.gray(` Field: ${warning.field}`));
|
|
246
|
+
}
|
|
247
|
+
lines.push(chalk_1.default.gray(` File: ${this.shortenPath(warning.file)}`));
|
|
248
|
+
if (warning.suggestion) {
|
|
249
|
+
lines.push(chalk_1.default.cyan(` ${this.symbols.arrow} Suggestion: ${warning.suggestion}`));
|
|
250
|
+
}
|
|
251
|
+
lines.push('');
|
|
252
|
+
return lines.join('\n');
|
|
253
|
+
}
|
|
254
|
+
formatFooter(result) {
|
|
255
|
+
const lines = [];
|
|
256
|
+
if (result.isValid) {
|
|
257
|
+
lines.push(chalk_1.default.green.bold(`${this.symbols.success} Validation passed!`));
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
lines.push(chalk_1.default.red.bold(`${this.symbols.error} Validation failed with ${result.errors.length} error(s)`));
|
|
261
|
+
}
|
|
262
|
+
// Add documentation link if there are any issues
|
|
263
|
+
if (result.errors.length > 0 || result.warnings.length > 0) {
|
|
264
|
+
lines.push('');
|
|
265
|
+
lines.push(chalk_1.default.gray('For help resolving issues, see:'));
|
|
266
|
+
lines.push(chalk_1.default.cyan('https://github.com/MemberJunction/MJ/tree/next/packages/MetadataSync'));
|
|
267
|
+
}
|
|
268
|
+
return lines.join('\n');
|
|
269
|
+
}
|
|
270
|
+
shortenPath(filePath) {
|
|
271
|
+
const cwd = process.cwd();
|
|
272
|
+
if (filePath.startsWith(cwd)) {
|
|
273
|
+
return '.' + filePath.slice(cwd.length);
|
|
274
|
+
}
|
|
275
|
+
return filePath;
|
|
276
|
+
}
|
|
277
|
+
formatDuration(ms) {
|
|
278
|
+
if (ms < 1000)
|
|
279
|
+
return `${ms}ms`;
|
|
280
|
+
if (ms < 60000)
|
|
281
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
282
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get count of errors by type
|
|
286
|
+
*/
|
|
287
|
+
getErrorsByType(errors) {
|
|
288
|
+
const counts = {};
|
|
289
|
+
for (const error of errors) {
|
|
290
|
+
counts[error.type] = (counts[error.type] || 0) + 1;
|
|
291
|
+
}
|
|
292
|
+
return counts;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get count of warnings by type
|
|
296
|
+
*/
|
|
297
|
+
getWarningsByType(warnings) {
|
|
298
|
+
const counts = {};
|
|
299
|
+
for (const warning of warnings) {
|
|
300
|
+
counts[warning.type] = (counts[warning.type] || 0) + 1;
|
|
301
|
+
}
|
|
302
|
+
return counts;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Format validation result as markdown
|
|
306
|
+
*/
|
|
307
|
+
formatValidationResultAsMarkdown(result) {
|
|
308
|
+
const lines = [];
|
|
309
|
+
const timestamp = new Date();
|
|
310
|
+
const dateStr = timestamp.toLocaleDateString('en-US', {
|
|
311
|
+
weekday: 'long',
|
|
312
|
+
year: 'numeric',
|
|
313
|
+
month: 'long',
|
|
314
|
+
day: 'numeric'
|
|
315
|
+
});
|
|
316
|
+
const timeStr = timestamp.toLocaleTimeString('en-US', {
|
|
317
|
+
hour: '2-digit',
|
|
318
|
+
minute: '2-digit',
|
|
319
|
+
second: '2-digit'
|
|
320
|
+
});
|
|
321
|
+
// Header with branding
|
|
322
|
+
lines.push('# 🚀 MemberJunction Metadata Sync');
|
|
323
|
+
lines.push('## Validation Report');
|
|
324
|
+
lines.push('');
|
|
325
|
+
lines.push(`📅 **Date:** ${dateStr} `);
|
|
326
|
+
lines.push(`🕐 **Time:** ${timeStr} `);
|
|
327
|
+
lines.push(`📍 **Directory:** \`${process.cwd()}\` `);
|
|
328
|
+
lines.push('');
|
|
329
|
+
// Table of Contents
|
|
330
|
+
lines.push('## 📑 Table of Contents');
|
|
331
|
+
lines.push('');
|
|
332
|
+
lines.push('- [Executive Summary](#executive-summary)');
|
|
333
|
+
lines.push('- [Validation Results](#validation-results)');
|
|
334
|
+
if (result.errors.length > 0 || result.warnings.length > 0) {
|
|
335
|
+
lines.push('- [Issue Analysis](#issue-analysis)');
|
|
336
|
+
}
|
|
337
|
+
lines.push('- [File-by-File Breakdown](#file-by-file-breakdown)');
|
|
338
|
+
if (result.errors.length > 0) {
|
|
339
|
+
lines.push('- [Error Details](#error-details)');
|
|
340
|
+
}
|
|
341
|
+
if (result.warnings.length > 0) {
|
|
342
|
+
lines.push('- [Warning Details](#warning-details)');
|
|
343
|
+
}
|
|
344
|
+
lines.push('- [Next Steps](#next-steps)');
|
|
345
|
+
lines.push('- [Resources](#resources)');
|
|
346
|
+
lines.push('');
|
|
347
|
+
// Executive Summary
|
|
348
|
+
lines.push('## 📊 Executive Summary');
|
|
349
|
+
lines.push('');
|
|
350
|
+
const statusEmoji = result.isValid ? '✅' : '❌';
|
|
351
|
+
const statusText = result.isValid ? 'PASSED' : 'FAILED';
|
|
352
|
+
const statusColor = result.isValid ? 'green' : 'red';
|
|
353
|
+
lines.push(`### Overall Status: ${statusEmoji} **${statusText}**`);
|
|
354
|
+
lines.push('');
|
|
355
|
+
if (result.isValid) {
|
|
356
|
+
lines.push('> 🎉 **Congratulations!** Your metadata validation passed with no errors.');
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
lines.push(`> ⚠️ **Action Required:** ${result.errors.length} error(s) need to be resolved before proceeding.`);
|
|
360
|
+
}
|
|
361
|
+
lines.push('');
|
|
362
|
+
// Quick Stats
|
|
363
|
+
lines.push('### 📈 Quick Statistics');
|
|
364
|
+
lines.push('');
|
|
365
|
+
lines.push(`**📁 Files Validated:** ${result.summary.totalFiles} `);
|
|
366
|
+
lines.push(`**📦 Entities Processed:** ${result.summary.totalEntities} `);
|
|
367
|
+
lines.push(`**❌ Errors Found:** ${result.summary.totalErrors} `);
|
|
368
|
+
lines.push(`**⚠️ Warnings Found:** ${result.summary.totalWarnings} `);
|
|
369
|
+
lines.push('');
|
|
370
|
+
// Validation Results
|
|
371
|
+
lines.push('## 🔍 Validation Results');
|
|
372
|
+
lines.push('');
|
|
373
|
+
// Issue Analysis
|
|
374
|
+
if (result.errors.length > 0 || result.warnings.length > 0) {
|
|
375
|
+
lines.push('## 📊 Issue Analysis');
|
|
376
|
+
lines.push('');
|
|
377
|
+
if (result.errors.length > 0) {
|
|
378
|
+
lines.push('### ❌ Error Distribution');
|
|
379
|
+
lines.push('');
|
|
380
|
+
const errorsByType = this.getErrorsByType(result.errors);
|
|
381
|
+
lines.push('<details>');
|
|
382
|
+
lines.push('<summary>Click to expand error breakdown</summary>');
|
|
383
|
+
lines.push('');
|
|
384
|
+
for (const [type, count] of Object.entries(errorsByType)) {
|
|
385
|
+
const percentage = ((count / result.errors.length) * 100).toFixed(1);
|
|
386
|
+
lines.push(`- **${type}**: ${count} errors (${percentage}%)`);
|
|
387
|
+
}
|
|
388
|
+
lines.push('');
|
|
389
|
+
lines.push('</details>');
|
|
390
|
+
lines.push('');
|
|
391
|
+
}
|
|
392
|
+
if (result.warnings.length > 0) {
|
|
393
|
+
lines.push('### ⚠️ Warning Distribution');
|
|
394
|
+
lines.push('');
|
|
395
|
+
const warningsByType = this.getWarningsByType(result.warnings);
|
|
396
|
+
lines.push('<details>');
|
|
397
|
+
lines.push('<summary>Click to expand warning breakdown</summary>');
|
|
398
|
+
lines.push('');
|
|
399
|
+
for (const [type, count] of Object.entries(warningsByType)) {
|
|
400
|
+
const percentage = ((count / result.warnings.length) * 100).toFixed(1);
|
|
401
|
+
lines.push(`- **${type}**: ${count} warnings (${percentage}%)`);
|
|
402
|
+
}
|
|
403
|
+
lines.push('');
|
|
404
|
+
lines.push('</details>');
|
|
405
|
+
lines.push('');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// File Results
|
|
409
|
+
lines.push('## 📁 File-by-File Breakdown');
|
|
410
|
+
lines.push('');
|
|
411
|
+
const sortedFiles = Array.from(result.summary.fileResults.entries())
|
|
412
|
+
.sort(([a], [b]) => {
|
|
413
|
+
// Sort by error count (descending), then warning count, then name
|
|
414
|
+
const aResult = result.summary.fileResults.get(a);
|
|
415
|
+
const bResult = result.summary.fileResults.get(b);
|
|
416
|
+
if (aResult.errors.length !== bResult.errors.length) {
|
|
417
|
+
return bResult.errors.length - aResult.errors.length;
|
|
418
|
+
}
|
|
419
|
+
if (aResult.warnings.length !== bResult.warnings.length) {
|
|
420
|
+
return bResult.warnings.length - aResult.warnings.length;
|
|
421
|
+
}
|
|
422
|
+
return a.localeCompare(b);
|
|
423
|
+
});
|
|
424
|
+
for (const [file, fileResult] of sortedFiles) {
|
|
425
|
+
const shortPath = this.shortenPath(file);
|
|
426
|
+
const hasErrors = fileResult.errors.length > 0;
|
|
427
|
+
const hasWarnings = fileResult.warnings.length > 0;
|
|
428
|
+
const icon = hasErrors ? '❌' : hasWarnings ? '⚠️' : '✅';
|
|
429
|
+
const status = hasErrors ? 'Has Errors' : hasWarnings ? 'Has Warnings' : 'Clean';
|
|
430
|
+
lines.push(`<details>`);
|
|
431
|
+
lines.push(`<summary>${icon} <strong>${shortPath}</strong> - ${status}</summary>`);
|
|
432
|
+
lines.push('');
|
|
433
|
+
lines.push('#### File Statistics');
|
|
434
|
+
lines.push(`- **Entities:** ${fileResult.entityCount}`);
|
|
435
|
+
lines.push(`- **Errors:** ${fileResult.errors.length}`);
|
|
436
|
+
lines.push(`- **Warnings:** ${fileResult.warnings.length}`);
|
|
437
|
+
if (hasErrors) {
|
|
438
|
+
lines.push('');
|
|
439
|
+
lines.push('#### Errors in this file:');
|
|
440
|
+
fileResult.errors.forEach((error, idx) => {
|
|
441
|
+
lines.push(`${idx + 1}. ${error.message}`);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (hasWarnings) {
|
|
445
|
+
lines.push('');
|
|
446
|
+
lines.push('#### Warnings in this file:');
|
|
447
|
+
fileResult.warnings.forEach((warning, idx) => {
|
|
448
|
+
lines.push(`${idx + 1}. ${warning.message}`);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
lines.push('');
|
|
452
|
+
lines.push('</details>');
|
|
453
|
+
lines.push('');
|
|
454
|
+
}
|
|
455
|
+
// Detailed Errors
|
|
456
|
+
if (result.errors.length > 0) {
|
|
457
|
+
lines.push('## ❌ Error Details');
|
|
458
|
+
lines.push('');
|
|
459
|
+
lines.push(`> Found ${result.errors.length} error(s) that must be fixed.`);
|
|
460
|
+
lines.push('');
|
|
461
|
+
result.errors.forEach((error, index) => {
|
|
462
|
+
lines.push(`### Error ${index + 1}: ${error.message}`);
|
|
463
|
+
lines.push('');
|
|
464
|
+
lines.push(`**Type:** \`${error.type}\` `);
|
|
465
|
+
if (error.entity)
|
|
466
|
+
lines.push(`**Entity:** ${error.entity} `);
|
|
467
|
+
if (error.field)
|
|
468
|
+
lines.push(`**Field:** \`${error.field}\` `);
|
|
469
|
+
lines.push(`**File:** \`${this.shortenPath(error.file)}\` `);
|
|
470
|
+
lines.push(`**Severity:** ${error.severity} `);
|
|
471
|
+
if (error.suggestion) {
|
|
472
|
+
lines.push('');
|
|
473
|
+
lines.push('> 💡 **Suggestion:** ' + error.suggestion);
|
|
474
|
+
}
|
|
475
|
+
lines.push('');
|
|
476
|
+
lines.push('---');
|
|
477
|
+
lines.push('');
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
// Detailed Warnings
|
|
481
|
+
if (result.warnings.length > 0) {
|
|
482
|
+
lines.push('## ⚠️ Warning Details');
|
|
483
|
+
lines.push('');
|
|
484
|
+
lines.push(`> Found ${result.warnings.length} warning(s) for your review.`);
|
|
485
|
+
lines.push('');
|
|
486
|
+
// Group warnings by type for better organization
|
|
487
|
+
const warningsByType = new Map();
|
|
488
|
+
result.warnings.forEach(warning => {
|
|
489
|
+
if (!warningsByType.has(warning.type)) {
|
|
490
|
+
warningsByType.set(warning.type, []);
|
|
491
|
+
}
|
|
492
|
+
warningsByType.get(warning.type).push(warning);
|
|
493
|
+
});
|
|
494
|
+
for (const [type, warnings] of warningsByType) {
|
|
495
|
+
lines.push(`### Warning Type: \`${type}\``);
|
|
496
|
+
lines.push('');
|
|
497
|
+
warnings.forEach((warning, index) => {
|
|
498
|
+
lines.push(`#### ${index + 1}. ${warning.message}`);
|
|
499
|
+
lines.push('');
|
|
500
|
+
if (warning.entity)
|
|
501
|
+
lines.push(`**Entity:** ${warning.entity} `);
|
|
502
|
+
if (warning.field)
|
|
503
|
+
lines.push(`**Field:** \`${warning.field}\` `);
|
|
504
|
+
lines.push(`**File:** \`${this.shortenPath(warning.file)}\` `);
|
|
505
|
+
if (warning.suggestion) {
|
|
506
|
+
lines.push('');
|
|
507
|
+
lines.push('> 💡 **Suggestion:** ' + warning.suggestion);
|
|
508
|
+
}
|
|
509
|
+
lines.push('');
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// Next Steps
|
|
514
|
+
lines.push('## 🚀 Next Steps');
|
|
515
|
+
lines.push('');
|
|
516
|
+
if (result.errors.length > 0) {
|
|
517
|
+
lines.push('### To fix errors:');
|
|
518
|
+
lines.push('');
|
|
519
|
+
lines.push('1. Review each error in the [Error Details](#error-details) section');
|
|
520
|
+
lines.push('2. Follow the suggestions provided for each error');
|
|
521
|
+
lines.push('3. Run validation again after making changes');
|
|
522
|
+
lines.push('4. Repeat until all errors are resolved');
|
|
523
|
+
lines.push('');
|
|
524
|
+
}
|
|
525
|
+
if (result.warnings.length > 0) {
|
|
526
|
+
lines.push('### To address warnings:');
|
|
527
|
+
lines.push('');
|
|
528
|
+
lines.push('1. Review warnings in the [Warning Details](#warning-details) section');
|
|
529
|
+
lines.push('2. Determine which warnings are relevant to your use case');
|
|
530
|
+
lines.push('3. Apply suggested fixes where appropriate');
|
|
531
|
+
lines.push('');
|
|
532
|
+
}
|
|
533
|
+
if (result.isValid) {
|
|
534
|
+
lines.push('Your metadata is valid and ready to sync! 🎉');
|
|
535
|
+
lines.push('');
|
|
536
|
+
lines.push('```bash');
|
|
537
|
+
lines.push('# Push your metadata to the database');
|
|
538
|
+
lines.push('mj-sync push');
|
|
539
|
+
lines.push('```');
|
|
540
|
+
}
|
|
541
|
+
// Resources
|
|
542
|
+
lines.push('');
|
|
543
|
+
lines.push('## 📚 Resources');
|
|
544
|
+
lines.push('');
|
|
545
|
+
lines.push('- 📖 [MetadataSync Documentation](https://github.com/MemberJunction/MJ/tree/next/packages/MetadataSync)');
|
|
546
|
+
lines.push('- 🐛 [Report Issues](https://github.com/MemberJunction/MJ/issues)');
|
|
547
|
+
lines.push('- 💬 [MemberJunction Community](https://memberjunction.org)');
|
|
548
|
+
lines.push('- 📝 [Validation Rules Guide](https://github.com/MemberJunction/MJ/tree/next/packages/MetadataSync#validation-features)');
|
|
549
|
+
lines.push('');
|
|
550
|
+
// Footer
|
|
551
|
+
lines.push('---');
|
|
552
|
+
lines.push('');
|
|
553
|
+
lines.push('<div align="center">');
|
|
554
|
+
lines.push('');
|
|
555
|
+
lines.push('**Generated by [MemberJunction](https://memberjunction.org) Metadata Sync**');
|
|
556
|
+
lines.push('');
|
|
557
|
+
lines.push(`<sub>${timestamp.toISOString()}</sub>`);
|
|
558
|
+
lines.push('');
|
|
559
|
+
lines.push('</div>');
|
|
560
|
+
return lines.join('\n');
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
exports.FormattingService = FormattingService;
|
|
564
|
+
//# sourceMappingURL=FormattingService.js.map
|