@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.
Files changed (55) hide show
  1. package/bin/index.js +31 -5
  2. package/package.json +2 -2
  3. package/src/arc/__tests__/acceptance-gates.test.cjs +62 -0
  4. package/src/arc/__tests__/arc.test.cjs +922 -0
  5. package/src/arc/__tests__/canonical.test.cjs +57 -0
  6. package/src/arc/__tests__/classification.test.cjs +54 -0
  7. package/src/arc/__tests__/corpus.test.cjs +144 -0
  8. package/src/arc/__tests__/explain-blocked.test.cjs +192 -0
  9. package/src/arc/__tests__/fuzz.test.cjs +106 -0
  10. package/src/arc/__tests__/interaction.test.cjs +85 -0
  11. package/src/arc/__tests__/visual-regression.test.cjs +99 -0
  12. package/src/arc/acceptance-gates.cjs +119 -0
  13. package/src/arc/canonical-paths.cjs +142 -23
  14. package/src/arc/corpus-verifier.cjs +295 -0
  15. package/src/arc/data-classification.cjs +182 -0
  16. package/src/arc/data-flow.cjs +62 -2
  17. package/src/arc/explain-blocked.cjs +388 -0
  18. package/src/arc/fuzz-engine.cjs +604 -0
  19. package/src/arc/index.cjs +148 -2
  20. package/src/arc/interaction-verifier.cjs +185 -0
  21. package/src/arc/ir-builder.cjs +170 -12
  22. package/src/arc/planner.cjs +16 -1
  23. package/src/arc/residual.cjs +22 -0
  24. package/src/arc/rsc-boundary.cjs +105 -4
  25. package/src/arc/runtime-validator.cjs +65 -0
  26. package/src/arc/semantic.cjs +12 -1
  27. package/src/arc/transformer.cjs +14 -2745
  28. package/src/arc/transforms/assets/tailwind-bg.cjs +38 -0
  29. package/src/arc/transforms/collections/list.cjs +474 -0
  30. package/src/arc/transforms/components/child-card.cjs +268 -0
  31. package/src/arc/transforms/components/compound-content.cjs +49 -0
  32. package/src/arc/transforms/components/reusable-component.cjs +173 -0
  33. package/src/arc/transforms/element-transform.cjs +192 -0
  34. package/src/arc/transforms/healing/conditionals.cjs +295 -0
  35. package/src/arc/transforms/healing/sanitizer.cjs +401 -0
  36. package/src/arc/transforms/index.cjs +195 -0
  37. package/src/arc/transforms/primitives/action.cjs +76 -0
  38. package/src/arc/transforms/primitives/text.cjs +65 -0
  39. package/src/arc/transforms/routing/page-key.cjs +84 -0
  40. package/src/arc/transforms/routing/shared-layout.cjs +294 -0
  41. package/src/arc/transforms/runtime/provider.cjs +230 -0
  42. package/src/arc/transforms/transform-context.cjs +118 -0
  43. package/src/arc/types/acceptance.d.ts +50 -0
  44. package/src/arc/types/canonical.d.ts +50 -0
  45. package/src/arc/types/classification.d.ts +42 -0
  46. package/src/arc/types/corpus.d.ts +52 -0
  47. package/src/arc/types/explain-blocked.d.ts +41 -0
  48. package/src/arc/types/fuzz.d.ts +74 -0
  49. package/src/arc/types/interaction.d.ts +43 -0
  50. package/src/arc/types/ir.d.ts +114 -0
  51. package/src/arc/types/rsc.d.ts +39 -0
  52. package/src/arc/types/visual-regression.d.ts +49 -0
  53. package/src/arc/visual-regression.cjs +195 -0
  54. package/src/arc/workspace.cjs +233 -0
  55. 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
+ };