Setting the file. One moment. Orchestration Router · Replatform · wix/skills · Skills Docslib/orchestration-router.js
lib/orchestration-router.js
JavaScript·122 lines·5 KB
{
9 await fs.access(filePath);
10 return true;
11 } catch {
12 return false;
13 }
14}
15
16async function readJson(filePath) {
17 try {
18 return JSON.parse(await fs.readFile(filePath, 'utf8'));
19 } catch {
20 return null;
21 }
22}
23
24async function determineNextStep(projectDir, artifacts) {
25 const validation = validateArtifacts(artifacts);
26 if (!validation.ok) {
27 return {
28 ok: false,
29 nextState: 'failed',
30 nextResource: null,
31 reason: 'invalid_orchestration_artifacts',
32 errors: validation.errors,
33 };
34 }
35
36 const decisions = artifacts.decisions || {};
37 const approvals = artifacts.approvals || {};
38 const sourceUrl = decisions.sourceUrl && decisions.sourceUrl.value;
39 const sourceMode = decisions.sourceMode && decisions.sourceMode.value;
40 const fileInputPaths = decisions.fileInputPaths && decisions.fileInputPaths.value;
41 const hasFileInputs = Array.isArray(fileInputPaths) && fileInputPaths.length > 0;
42 const deliveryMode = decisions.deliveryMode && decisions.deliveryMode.value;
43 const targetSiteStrategy = decisions.targetSiteStrategy && decisions.targetSiteStrategy.value;
44 const preflight = artifacts.preflight || null;
45
46 // A file-provided run (e.g. platform=csv) has no source URL to probe; its
47 // input files stand in for one. URL-based runs are unaffected: they never
48 // carry sourceMode=files_only, so they still fall through to the checks below
49 // in the original order.
50 if (sourceMode === 'files_only') {
51 if (!hasFileInputs) {
52 return { ok: true, nextState: 'awaiting_files', nextResource: null, reason: 'missing_file_inputs' };
53 }
54 } else if (!sourceUrl && !hasFileInputs) {
55 return { ok: true, nextState: 'awaiting_source', nextResource: null, reason: 'missing_source_url' };
56 }
57 if (!sourceMode) {
58 return { ok: true, nextState: 'awaiting_import_scope', nextResource: null, reason: 'missing_source_mode' };
59 }
60 if (!deliveryMode || !targetSiteStrategy) {
61 return { ok: true, nextState: 'awaiting_destination_strategy', nextResource: null, reason: 'missing_destination_strategy' };
62 }
63
64 if (!preflight || !preflight.status || preflight.status === 'not_started') {
65 return { ok: true, nextState: 'preflight_running', nextResource: null, reason: 'preflight_missing' };
66 }
67 if (preflight.status === 'blocked') {
68 return { ok: true, nextState: 'preflight_blocked', nextResource: null, reason: 'preflight_blocked' };
69 }
70 if (preflight.status === 'failed') {
71 return { ok: false, nextState: 'failed', nextResource: null, reason: 'preflight_failed', errors: [] };
72 }
73
74 if (!(await exists(path.join(projectDir, 'source-schema.json')))) {
75 return { ok: true, nextState: 'discovery_running', nextResource: 'resources/rp-discovery/', reason: 'missing_source_schema' };
76 }
77
78 if (!(await exists(path.join(projectDir, 'mapping', 'mapping-plan.json')))) {
79 return { ok: true, nextState: 'mapping_running', nextResource: 'resources/rp-mapper/', reason: 'missing_mapping_plan' };
80 }
81
82 if (approvals.mapping && approvals.mapping.status === 'pending') {
83 return { ok: true, nextState: 'awaiting_mapping_approval', nextResource: null, reason: 'mapping_approval_pending' };
84 }
85
86 if (!(await exists(path.join(projectDir, 'setup', 'setup-plan.json')))) {
87 return { ok: true, nextState: 'setup_discovery_running', nextResource: 'resources/rp-setup-discovery/', reason: 'missing_setup_plan' };
88 }
89
90 if (!(await exists(path.join(projectDir, 'execution', 'execution-manifest.json')))) {
91 return { ok: true, nextState: 'codegen_running', nextResource: 'resources/rp-import-codegen/', reason: 'missing_execution_manifest' };
92 }
93
94 // Sample-preview sub-gate: codegen emits the extractor, runs it in sample
95 // mode, and records the user's validation in preview/preview-result.json.
96 // Routing back into codegen while that is missing or pending is what enforces
97 // the gate — the artifact is stage-local, so no orchestration phase or
98 // schema change is involved. Projects that do not use the gate simply never
99 // create the file and are unaffected.
100 const previewResult = await readJson(path.join(projectDir, 'preview', 'preview-result.json'));
101 if (previewResult && previewResult.status === 'pending') {
102 return { ok: true, nextState: 'codegen_running', nextResource: 'resources/rp-import-codegen/', reason: 'sample_preview_pending' };
103 }
104
105 if (approvals.execution && approvals.execution.status === 'pending') {
106 return { ok: true, nextState: 'awaiting_execution_approval', nextResource: null, reason: 'execution_approval_pending' };
107 }
108
109 if (!(await exists(path.join(projectDir, 'setup', 'setup-verification.json')))) {
110 return { ok: true, nextState: 'executing_setup', nextResource: 'resources/rp-execute-setup/', reason: 'missing_setup_verification' };
111 }
112
113 if (!(await exists(path.join(projectDir, 'execution', 'completion-report.json')))) {
114 return { ok: true, nextState: 'executing_import', nextResource: 'resources/rp-execute-import/', reason: 'missing_completion_report' };
115 }
116
117 return { ok: true, nextState: 'completed', nextResource: null, reason: 'completion_report_present' };
118}
119
120module.exports = {
121 determineNextStep,
122};