@deneb-ui/cli 2.0.87 → 2.0.89
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/bin/index.js +31 -5
- package/package.json +2 -2
- package/src/arc/__tests__/acceptance-gates.test.cjs +62 -0
- package/src/arc/__tests__/arc.test.cjs +922 -0
- package/src/arc/__tests__/canonical.test.cjs +57 -0
- package/src/arc/__tests__/classification.test.cjs +54 -0
- package/src/arc/__tests__/corpus.test.cjs +144 -0
- package/src/arc/__tests__/explain-blocked.test.cjs +192 -0
- package/src/arc/__tests__/fuzz.test.cjs +106 -0
- package/src/arc/__tests__/interaction.test.cjs +85 -0
- package/src/arc/__tests__/visual-regression.test.cjs +99 -0
- package/src/arc/acceptance-gates.cjs +119 -0
- package/src/arc/canonical-paths.cjs +142 -23
- package/src/arc/corpus-verifier.cjs +295 -0
- package/src/arc/data-classification.cjs +182 -0
- package/src/arc/data-flow.cjs +62 -2
- package/src/arc/explain-blocked.cjs +388 -0
- package/src/arc/fuzz-engine.cjs +604 -0
- package/src/arc/index.cjs +148 -2
- package/src/arc/interaction-verifier.cjs +185 -0
- package/src/arc/ir-builder.cjs +170 -12
- package/src/arc/planner.cjs +16 -1
- package/src/arc/residual.cjs +22 -0
- package/src/arc/rsc-boundary.cjs +105 -4
- package/src/arc/runtime-validator.cjs +65 -0
- package/src/arc/semantic.cjs +12 -1
- package/src/arc/transformer.cjs +14 -2745
- package/src/arc/transforms/assets/tailwind-bg.cjs +38 -0
- package/src/arc/transforms/collections/list.cjs +474 -0
- package/src/arc/transforms/components/child-card.cjs +268 -0
- package/src/arc/transforms/components/compound-content.cjs +49 -0
- package/src/arc/transforms/components/reusable-component.cjs +173 -0
- package/src/arc/transforms/element-transform.cjs +192 -0
- package/src/arc/transforms/healing/conditionals.cjs +295 -0
- package/src/arc/transforms/healing/sanitizer.cjs +401 -0
- package/src/arc/transforms/index.cjs +195 -0
- package/src/arc/transforms/primitives/action.cjs +76 -0
- package/src/arc/transforms/primitives/text.cjs +65 -0
- package/src/arc/transforms/routing/page-key.cjs +84 -0
- package/src/arc/transforms/routing/shared-layout.cjs +294 -0
- package/src/arc/transforms/runtime/provider.cjs +230 -0
- package/src/arc/transforms/transform-context.cjs +118 -0
- package/src/arc/types/acceptance.d.ts +50 -0
- package/src/arc/types/canonical.d.ts +50 -0
- package/src/arc/types/classification.d.ts +42 -0
- package/src/arc/types/corpus.d.ts +52 -0
- package/src/arc/types/explain-blocked.d.ts +41 -0
- package/src/arc/types/fuzz.d.ts +74 -0
- package/src/arc/types/interaction.d.ts +43 -0
- package/src/arc/types/ir.d.ts +114 -0
- package/src/arc/types/rsc.d.ts +39 -0
- package/src/arc/types/visual-regression.d.ts +49 -0
- package/src/arc/visual-regression.cjs +195 -0
- package/src/arc/workspace.cjs +233 -0
- package/src/tools/deneb-template-validator.cjs +17 -17
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { walkFiles, isIgnoredDirName, isSecretFile, copyFilePreserve, writeJson, rel } = require('./fs-utils.cjs');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates isolated run directory structure:
|
|
9
|
+
* .deneb/
|
|
10
|
+
* runs/
|
|
11
|
+
* <runId>/
|
|
12
|
+
* source-snapshot/
|
|
13
|
+
* workspace/
|
|
14
|
+
* reports/
|
|
15
|
+
* transformed/
|
|
16
|
+
* runtime-results.json
|
|
17
|
+
*/
|
|
18
|
+
function createRunWorkspace(projectDir, runId) {
|
|
19
|
+
const runsRoot = path.join(projectDir, '.deneb', 'runs', runId);
|
|
20
|
+
const snapshotDir = path.join(runsRoot, 'source-snapshot');
|
|
21
|
+
const workspaceDir = path.join(runsRoot, 'workspace');
|
|
22
|
+
const reportsDir = path.join(runsRoot, 'reports');
|
|
23
|
+
const transformedDir = path.join(runsRoot, 'transformed');
|
|
24
|
+
|
|
25
|
+
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
26
|
+
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
27
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
28
|
+
fs.mkdirSync(transformedDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
runsRoot,
|
|
32
|
+
snapshotDir,
|
|
33
|
+
workspaceDir,
|
|
34
|
+
reportsDir,
|
|
35
|
+
transformedDir,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Copies source project files into workspace sandbox, avoiding ignored directories.
|
|
41
|
+
* Links node_modules via junction/symlink so compilation and runtime dependencies resolve.
|
|
42
|
+
*/
|
|
43
|
+
function copyProjectToWorkspace(projectDir, workspaceDir, snapshotDir = null) {
|
|
44
|
+
const copiedFiles = [];
|
|
45
|
+
const entries = fs.readdirSync(projectDir, { withFileTypes: true });
|
|
46
|
+
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
if (isIgnoredDirName(entry.name) || isSecretFile(entry.name)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const srcPath = path.join(projectDir, entry.name);
|
|
52
|
+
const destPath = path.join(workspaceDir, entry.name);
|
|
53
|
+
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
copyDirRecursive(srcPath, destPath, (f) => !isIgnoredDirName(path.basename(f)));
|
|
56
|
+
} else if (entry.isFile()) {
|
|
57
|
+
copyFilePreserve(srcPath, destPath);
|
|
58
|
+
copiedFiles.push(entry.name);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Also snapshot all source files if snapshotDir is provided
|
|
63
|
+
if (snapshotDir) {
|
|
64
|
+
const allSources = walkFiles(projectDir, { include: () => true });
|
|
65
|
+
for (const file of allSources) {
|
|
66
|
+
const relative = rel(projectDir, file);
|
|
67
|
+
const snapDest = path.join(snapshotDir, relative);
|
|
68
|
+
copyFilePreserve(file, snapDest);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Handle node_modules linking if present in source project
|
|
73
|
+
const srcNodeModules = path.join(projectDir, 'node_modules');
|
|
74
|
+
const destNodeModules = path.join(workspaceDir, 'node_modules');
|
|
75
|
+
if (fs.existsSync(srcNodeModules) && !fs.existsSync(destNodeModules)) {
|
|
76
|
+
try {
|
|
77
|
+
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
|
78
|
+
fs.symlinkSync(srcNodeModules, destNodeModules, symlinkType);
|
|
79
|
+
} catch {
|
|
80
|
+
// If symlink/junction fails (e.g. cross-device or permission), continue without node_modules
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return copiedFiles;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function copyDirRecursive(srcDir, destDir, filter = () => true) {
|
|
88
|
+
if (!fs.existsSync(srcDir) || !filter(srcDir)) return;
|
|
89
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
90
|
+
const items = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
91
|
+
for (const item of items) {
|
|
92
|
+
if (isIgnoredDirName(item.name)) continue;
|
|
93
|
+
const srcItem = path.join(srcDir, item.name);
|
|
94
|
+
const destItem = path.join(destDir, item.name);
|
|
95
|
+
if (!filter(srcItem)) continue;
|
|
96
|
+
if (item.isDirectory()) {
|
|
97
|
+
copyDirRecursive(srcItem, destItem, filter);
|
|
98
|
+
} else if (item.isFile()) {
|
|
99
|
+
copyFilePreserve(srcItem, destItem);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Commits transformed files from workspace back to developer's project.
|
|
106
|
+
*/
|
|
107
|
+
function commitWorkspaceToProject(workspaceDir, projectDir, changedRelativeFiles = []) {
|
|
108
|
+
const committed = [];
|
|
109
|
+
const filesToCommit = changedRelativeFiles.length > 0
|
|
110
|
+
? changedRelativeFiles
|
|
111
|
+
: walkFiles(workspaceDir, { include: () => true }).map((f) => rel(workspaceDir, f));
|
|
112
|
+
|
|
113
|
+
for (const relative of filesToCommit) {
|
|
114
|
+
const srcFile = path.join(workspaceDir, relative);
|
|
115
|
+
const destFile = path.join(projectDir, relative);
|
|
116
|
+
if (fs.existsSync(srcFile)) {
|
|
117
|
+
copyFilePreserve(srcFile, destFile);
|
|
118
|
+
committed.push(relative);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return committed;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Destroys the staging workspace directory safely.
|
|
126
|
+
*/
|
|
127
|
+
function cleanupWorkspace(workspaceDir) {
|
|
128
|
+
if (!workspaceDir || !fs.existsSync(workspaceDir)) return;
|
|
129
|
+
try {
|
|
130
|
+
// Unlink node_modules junction first if present
|
|
131
|
+
const nodeModulesLink = path.join(workspaceDir, 'node_modules');
|
|
132
|
+
if (fs.existsSync(nodeModulesLink)) {
|
|
133
|
+
try {
|
|
134
|
+
const stat = fs.lstatSync(nodeModulesLink);
|
|
135
|
+
if (stat.isSymbolicLink()) {
|
|
136
|
+
fs.unlinkSync(nodeModulesLink);
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
// ignore
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
fs.rmSync(workspaceDir, { recursive: true, force: true });
|
|
143
|
+
} catch {
|
|
144
|
+
// ignore cleanup errors
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Saves execution report and diagnostics into .deneb/runs/<runId>/reports/
|
|
150
|
+
*/
|
|
151
|
+
function saveRunArtifacts(runDirs, data = {}) {
|
|
152
|
+
const { reportsDir, runsRoot, transformedDir, workspaceDir } = runDirs;
|
|
153
|
+
|
|
154
|
+
if (data.report) {
|
|
155
|
+
writeJson(path.join(reportsDir, 'conversion-report.json'), data.report);
|
|
156
|
+
}
|
|
157
|
+
if (data.runtimeResults) {
|
|
158
|
+
writeJson(path.join(runsRoot, 'runtime-results.json'), data.runtimeResults);
|
|
159
|
+
writeJson(path.join(reportsDir, 'runtime-results.json'), data.runtimeResults);
|
|
160
|
+
}
|
|
161
|
+
if (data.failureReasons) {
|
|
162
|
+
writeJson(path.join(reportsDir, 'rollback-reason.json'), {
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
reasons: data.failureReasons,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Snapshot transformed files to transformed/
|
|
169
|
+
if (workspaceDir && fs.existsSync(workspaceDir)) {
|
|
170
|
+
const files = walkFiles(workspaceDir, { include: () => true });
|
|
171
|
+
for (const f of files) {
|
|
172
|
+
const relPath = rel(workspaceDir, f);
|
|
173
|
+
copyFilePreserve(f, path.join(transformedDir, relPath));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Generates structured explanation when conversion is blocked.
|
|
180
|
+
*/
|
|
181
|
+
function formatFailureExplanation(validation, reasons = []) {
|
|
182
|
+
const lines = [
|
|
183
|
+
'────────────────────────────────────────────────────────────',
|
|
184
|
+
' DENEB CONVERSION BLOCKED — ZERO FILES MODIFIED',
|
|
185
|
+
'────────────────────────────────────────────────────────────',
|
|
186
|
+
'The template did not pass the 100% Fivora Success Contract.',
|
|
187
|
+
'Your original project files have been kept completely intact.',
|
|
188
|
+
'',
|
|
189
|
+
'Blocking issues detected:',
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
if (reasons.length > 0) {
|
|
193
|
+
for (let i = 0; i < reasons.length; i++) {
|
|
194
|
+
lines.push(` ${i + 1}. ${reasons[i]}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (validation?.fivoraContractErrors?.length) {
|
|
199
|
+
lines.push('');
|
|
200
|
+
lines.push('Fivora Contract Violations:');
|
|
201
|
+
for (const err of validation.fivoraContractErrors.slice(0, 10)) {
|
|
202
|
+
lines.push(` - ${err}`);
|
|
203
|
+
}
|
|
204
|
+
if (validation.fivoraContractErrors.length > 10) {
|
|
205
|
+
lines.push(` ... and ${validation.fivoraContractErrors.length - 10} more`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (validation?.uncoveredVisibleText > 0) {
|
|
210
|
+
lines.push('');
|
|
211
|
+
lines.push(`Uncovered Visible Text: ${validation.uncoveredVisibleText} node(s) without visual markers.`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (validation?.designPreservation < 98 && validation?.designPreservation > 0) {
|
|
215
|
+
lines.push('');
|
|
216
|
+
lines.push(`Design Preservation: ${validation.designPreservation}% (Minimum required: 98%).`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
lines.push('');
|
|
220
|
+
lines.push('Run "deneb explain <file>" or inspect ".deneb/runs/<runId>/reports/conversion-report.json"');
|
|
221
|
+
lines.push('────────────────────────────────────────────────────────────');
|
|
222
|
+
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
createRunWorkspace,
|
|
228
|
+
copyProjectToWorkspace,
|
|
229
|
+
commitWorkspaceToProject,
|
|
230
|
+
cleanupWorkspace,
|
|
231
|
+
saveRunArtifacts,
|
|
232
|
+
formatFailureExplanation,
|
|
233
|
+
};
|