Setting the file. One moment. Migration Completion · Wix Replatform · wix/skills · Skills Docs47.10
Website Handoff Generate
lib/migration-completion.js
JavaScript·103 lines·4 KB
;
9const COMPLETE_STATUSES = new Set(['complete', 'complete_with_warnings']);
10
11async function readJsonIfExists(filePath) {
12 try {
13 return JSON.parse(await fs.readFile(filePath, 'utf8'));
14 } catch (error) {
15 if (error && error.code === 'ENOENT') return null;
16 throw error;
17 }
18}
19
20function validateFrontendCompletion(receipt, handoff, { requireHandoff = true } = {}) {
21 if (!receipt || typeof receipt !== 'object') {
22 return { ok: false, reason: 'frontend_completion_missing' };
23 }
24 if (receipt.schemaVersion !== 1) {
25 return { ok: false, reason: 'frontend_completion_schema_invalid' };
26 }
27 if (!COMPLETE_STATUSES.has(receipt.status)) {
28 return { ok: false, reason: receipt.status === 'blocked' ? 'frontend_blocked' : 'frontend_completion_incomplete', receipt };
29 }
30 if (requireHandoff && (!handoff || !handoff.handoffFingerprint || receipt.handoffFingerprint !== handoff.handoffFingerprint)) {
31 return { ok: false, reason: 'frontend_completion_stale', receipt };
32 }
33 const gapAnalysis = receipt.gapAnalysis;
34 if (!gapAnalysis || gapAnalysis.screenshotReview !== 'complete'
35 || gapAnalysis.unresolvedCritical !== 0 || gapAnalysis.unresolvedHigh !== 0) {
36 return { ok: false, reason: 'frontend_gap_analysis_incomplete', receipt };
37 }
38 if (!receipt.release || !['released', 'not_applicable'].includes(receipt.release.status)) {
39 return { ok: false, reason: 'frontend_release_incomplete', receipt };
40 }
41 return { ok: true, receipt };
42}
43
44function createMigrationCompletion({ deliveryMode, backendCompletion, frontendCompletion = null, handoff = null }) {
45 if (!backendCompletion || typeof backendCompletion !== 'object' || !backendCompletion.status) {
46 throw new Error('backend completion report is required');
47 }
48 if (!['management', 'management_and_website'].includes(deliveryMode)) {
49 throw new Error('aggregate migration completion requires management or management_and_website deliveryMode');
50 }
51 if (deliveryMode === 'management_and_website') {
52 const validation = validateFrontendCompletion(frontendCompletion, handoff);
53 if (!validation.ok) throw new Error(`website migration cannot complete: ${validation.reason}`);
54 }
55 return {
56 schemaVersion: 1,
57 status: backendCompletion.status,
58 deliveryMode,
59 backendCompletion: BACKEND_COMPLETION_FILE,
60 frontendCompletion: deliveryMode === 'management_and_website' ? FRONTEND_COMPLETION_FILE : null,
61 handoffFingerprint: deliveryMode === 'management_and_website' ? handoff.handoffFingerprint : null,
62 finalizedAt: new Date().toISOString(),
63 };
64}
65
66async function loadMigrationCompletionInputs(projectDir) {
67 const [backendCompletion, frontendCompletion, handoff, completion] = await Promise.all([
68 readJsonIfExists(path.join(projectDir, BACKEND_COMPLETION_FILE)),
69 readJsonIfExists(path.join(projectDir, FRONTEND_COMPLETION_FILE)),
70 readJsonIfExists(path.join(projectDir, 'website', 'handoff.json')),
71 readJsonIfExists(path.join(projectDir, COMPLETION_FILE)),
72 ]);
73 return { backendCompletion, frontendCompletion, handoff, completion };
74}
75
76function validateMigrationCompletion(completion, { deliveryMode, backendCompletion, frontendCompletion, handoff }) {
77 if (!completion || typeof completion !== 'object') return { ok: false, reason: 'migration_completion_missing' };
78 if (completion.schemaVersion !== 1 || completion.deliveryMode !== deliveryMode) {
79 return { ok: false, reason: 'migration_completion_invalid' };
80 }
81 if (!backendCompletion || completion.status !== backendCompletion.status) {
82 return { ok: false, reason: 'migration_completion_stale' };
83 }
84 if (deliveryMode === 'management_and_website') {
85 const frontend = validateFrontendCompletion(frontendCompletion, handoff);
86 if (!frontend.ok) return frontend;
87 if (completion.frontendCompletion !== FRONTEND_COMPLETION_FILE || completion.handoffFingerprint !== handoff.handoffFingerprint) {
88 return { ok: false, reason: 'migration_completion_stale' };
89 }
90 }
91 return { ok: true, completion };
92}
93
94module.exports = {
95 COMPLETION_FILE,
96 FRONTEND_COMPLETION_FILE,
97 BACKEND_COMPLETION_FILE,
98 readJsonIfExists,
99 validateFrontendCompletion,
100 createMigrationCompletion,
101 loadMigrationCompletionInputs,
102 validateMigrationCompletion,
103};