Setting the file. One moment.
Frontend Automation State · Wix Headless Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page 28.10
Workflow
Previous
Script Font Contract
scripts/lib/ frontend-automation-state.mjs
JavaScript · 184 lines · 7 KB
(args.handoff))
:
null
;
8 if ( ! handoffPath) {
9 const sourceUrl = normalizeUrl (args._[ 0 ] || args.url). toString ();
10 const explicitUrls = parseExplicitUrls (args.urls, sourceUrl);
11 return {
12 mode: "standalone" ,
13 handoffPath: null ,
14 handoff: null ,
15 sourceUrl,
16 scope: String (args.scope || (explicitUrls. length ? "specific" : "home" )),
17 explicitUrls,
18 outputDir: resolveOutputDir (sourceUrl, args.out),
19 automationMode: "manual" ,
20 autoApprove: false ,
21 faceliftRequested: args.facelift === true || args.facelift === "true" || args.facelift === "requested" ,
22 frontendPhase: "build" ,
23 selectedScopeSource: args.scope ? "cli" : explicitUrls. length ? "cli-explicit-urls" : "default" ,
24 };
25 }
26
27 const handoff = await readJson (handoffPath);
28 const sourceUrl = normalizeUrl (args._[ 0 ] || args.url || handoff?.source?.url). toString ();
29 const explicitUrls = normalizeExplicitUrls (handoff?.websiteScope?.explicitUrls, sourceUrl);
30 const selectedScope = handoff?.websiteScope?.selectedScope || null ;
31 const scope = String (args.scope || (explicitUrls. length ? "specific" : selectedScope || "home" ));
32 const outputDir = args.out
33 ? path. normalize ( String (args.out))
34 : handoff?.destination?.frontendProjectDir
35 ? path. normalize ( String (handoff.destination.frontendProjectDir))
36 : resolveOutputDir (sourceUrl);
37 const automationMode = handoff?.automationMode === "one_click" ? "one_click" : "manual" ;
38
39 return {
40 mode: "migration_phase" ,
41 handoffPath,
42 handoff,
43 sourceUrl,
44 scope,
45 explicitUrls: explicitUrls. length && ! args.urls ? explicitUrls : parseExplicitUrls (args.urls, sourceUrl),
46 outputDir,
47 automationMode,
48 autoApprove: automationMode === "one_click" ,
49 faceliftRequested: handoff?.facelift?.requested === true || args.facelift === true || args.facelift === "true" || args.facelift === "requested" ,
50 frontendPhase: handoff?.frontendPhase?.allowedNow === "plan" ? "plan" : "build" ,
51 selectedScopeSource: args.scope ? "cli" : selectedScope ? "handoff" : explicitUrls. length ? "handoff-explicit-urls" : "default" ,
52 };
53 }
54
55 export function createFrontendAutomationState ( context , timestamp = new Date (). toISOString ()) {
56 const routineCheckpoint = context.autoApprove
57 ? {
58 status: "approved" ,
59 decidedAt: timestamp,
60 decidedBy: "agent" ,
61 notes: "Migration phase 1-click mode inherited from website/handoff.json; routine frontend checkpoints auto-progress unless a real blocker stops the run." ,
62 artifactRefs: context.handoffPath ? [context.handoffPath] : [],
63 }
64 : {
65 status: context.mode === "migration_phase" ? "pending" : "not_applicable" ,
66 decidedAt: null ,
67 decidedBy: null ,
68 notes: context.mode === "migration_phase"
69 ? "Routine frontend checkpoints stay manual unless website/handoff.json declares automationMode=one_click."
70 : "Standalone mode does not inherit migration automation." ,
71 artifactRefs: context.handoffPath ? [context.handoffPath] : [],
72 };
73
74 return {
75 schemaVersion: FRONTEND_AUTOMATION_SCHEMA_VERSION ,
76 generatedAt: timestamp,
77 mode: context.mode,
78 automationMode: context.automationMode,
79 handoffPath: context.handoffPath,
80 sourceUrl: context.sourceUrl,
81 outputDir: context.outputDir,
82 frontendPhase: context.frontendPhase,
83 scope: {
84 value: context.scope,
85 explicitUrls: context.explicitUrls,
86 selectedBy: context.selectedScopeSource,
87 },
88 checkpoints: {
89 scopeSummary: {
90 status: "not_started" ,
91 decidedAt: null ,
92 decidedBy: null ,
93 notes: null ,
94 artifactRefs: [],
95 },
96 routineAutoProgress: routineCheckpoint,
97 facelift: {
98 requested: context.faceliftRequested === true ,
99 status: context.faceliftRequested ? "waiting_for_clone_acceptance" : "not_requested" ,
100 decidedBy: context.faceliftRequested ? "user" : null ,
101 decidedAt: context.faceliftRequested ? timestamp : null ,
102 notes: context.faceliftRequested
103 ? "Run only after the clone's normal gap-analysis and fix loop is accepted."
104 : "UI/UX facelift was not explicitly requested." ,
105 artifactRefs: [],
106 },
107 },
108 };
109 }
110
111 export function updateScopeSummaryCheckpoint ( state , {
112 status ,
113 discovery ,
114 artifactRefs = [],
115 decidedBy = null ,
116 notes = null ,
117 timestamp = new Date (). toISOString (),
118 }) {
119 state.checkpoints.scopeSummary = {
120 status,
121 decidedAt: [ "approved" , "rejected" ]. includes (status) ? timestamp : null ,
122 decidedBy: [ "approved" , "rejected" ]. includes (status) ? decidedBy : null ,
123 notes,
124 artifactRefs,
125 summary: discovery ? {
126 scope: discovery.scope,
127 totalDiscovered: discovery.totalDiscovered,
128 countsByArea: discovery.countsByArea,
129 inScopeCount: discovery.inScopePages?. length || 0 ,
130 preservedCount: discovery.preservedPages?. length || 0 ,
131 } : undefined ,
132 };
133 return state;
134 }
135
136 export async function writeFrontendAutomationState ( outputDir , state ) {
137 const docs = docsDir (outputDir);
138 await ensureDir (docs);
139 const filePath = path. join (docs, "frontend-automation-state.json" );
140 await writeJson (filePath, state);
141 return filePath;
142 }
143
144 export function renderScopeSummary ( context , discovery ) {
145 const lines = [
146 "# Scope Summary" ,
147 "" ,
148 `- Mode: ${ context . mode }` ,
149 `- Automation: ${ context . automationMode }` ,
150 `- Frontend phase: ${ context . frontendPhase }` ,
151 `- Effective scope: ${ discovery . scope }` ,
152 `- Total discovered URLs: ${ discovery . totalDiscovered }` ,
153 `- In-scope implementation URLs: ${ discovery . inScopePages ?. length || 0 }` ,
154 `- Preserved fallback URLs: ${ discovery . preservedPages ?. length || 0 }` ,
155 "" ,
156 "## Counts By Area" ,
157 ];
158 for ( const [ area , count ] of Object. entries (discovery.countsByArea || {})) {
159 lines. push ( `- ${ area }: ${ count }` );
160 }
161 return `${ lines . join ( " \n " ) } \n ` ;
162 }
163
164 function parseExplicitUrls ( value , sourceUrl ) {
165 if ( ! value) return [];
166 return String (value)
167 . split ( "," )
168 . map (( item ) => item. trim ())
169 . filter (Boolean)
170 . map (( item ) => new URL (item, sourceUrl). toString ());
171 }
172
173 function normalizeExplicitUrls ( urls , sourceUrl ) {
174 if ( ! Array. isArray (urls)) return [];
175 return urls
176 . map (( item ) => {
177 try {
178 return new URL ( String (item), sourceUrl). toString ();
179 } catch {
180 return null ;
181 }
182 })
183 . filter (Boolean);
184 }