opencroc 0.5.0 → 0.6.0
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/README.en.md +1 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli/index.js +368 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -294,7 +294,7 @@ export default defineConfig({
|
|
|
294
294
|
- [x] Plugin system
|
|
295
295
|
- [x] HTML / JSON / Markdown report generation
|
|
296
296
|
- [x] NestJS controller parser
|
|
297
|
-
- [
|
|
297
|
+
- [x] Visual dashboard (opencroc.com)
|
|
298
298
|
- [x] Drizzle ORM adapter
|
|
299
299
|
|
|
300
300
|
## Documentation
|
package/README.ja.md
CHANGED
|
@@ -294,7 +294,7 @@ export default defineConfig({
|
|
|
294
294
|
- [x] Plugin system
|
|
295
295
|
- [x] HTML / JSON / Markdown report generation
|
|
296
296
|
- [x] NestJS controller parser
|
|
297
|
-
- [
|
|
297
|
+
- [x] Visual dashboard (opencroc.com)
|
|
298
298
|
- [x] Drizzle ORM adapter
|
|
299
299
|
|
|
300
300
|
## ドキュメント
|
package/README.md
CHANGED
|
@@ -294,7 +294,7 @@ export default defineConfig({
|
|
|
294
294
|
- [x] Plugin system
|
|
295
295
|
- [x] HTML / JSON / Markdown report generation
|
|
296
296
|
- [x] NestJS controller parser
|
|
297
|
-
- [
|
|
297
|
+
- [x] Visual dashboard (opencroc.com)
|
|
298
298
|
- [x] Drizzle ORM adapter
|
|
299
299
|
|
|
300
300
|
## Documentation
|
package/README.zh-CN.md
CHANGED
|
@@ -294,7 +294,7 @@ export default defineConfig({
|
|
|
294
294
|
- [x] Plugin system
|
|
295
295
|
- [x] HTML / JSON / Markdown report generation
|
|
296
296
|
- [x] NestJS controller parser
|
|
297
|
-
- [
|
|
297
|
+
- [x] Visual dashboard (opencroc.com)
|
|
298
298
|
- [x] Drizzle ORM adapter
|
|
299
299
|
|
|
300
300
|
## 文档
|
package/dist/cli/index.js
CHANGED
|
@@ -853,20 +853,20 @@ function detectCycles(dag) {
|
|
|
853
853
|
const color = /* @__PURE__ */ new Map();
|
|
854
854
|
for (const node of dag.nodes) color.set(node, 0 /* WHITE */);
|
|
855
855
|
const warnings = [];
|
|
856
|
-
const
|
|
856
|
+
const path9 = [];
|
|
857
857
|
function dfs(node) {
|
|
858
858
|
color.set(node, 1 /* GRAY */);
|
|
859
|
-
|
|
859
|
+
path9.push(node);
|
|
860
860
|
for (const neighbor of adjacency.get(node) || []) {
|
|
861
861
|
const nc = color.get(neighbor);
|
|
862
862
|
if (nc === 1 /* GRAY */) {
|
|
863
|
-
const cycleStart =
|
|
864
|
-
warnings.push(`Cycle detected: ${
|
|
863
|
+
const cycleStart = path9.indexOf(neighbor);
|
|
864
|
+
warnings.push(`Cycle detected: ${path9.slice(cycleStart).concat(neighbor).join(" \u2192 ")}`);
|
|
865
865
|
} else if (nc === 0 /* WHITE */) {
|
|
866
866
|
dfs(neighbor);
|
|
867
867
|
}
|
|
868
868
|
}
|
|
869
|
-
|
|
869
|
+
path9.pop();
|
|
870
870
|
color.set(node, 2 /* BLACK */);
|
|
871
871
|
}
|
|
872
872
|
for (const node of dag.nodes) {
|
|
@@ -2127,11 +2127,369 @@ var init_report = __esm({
|
|
|
2127
2127
|
}
|
|
2128
2128
|
});
|
|
2129
2129
|
|
|
2130
|
+
// src/dashboard/index.ts
|
|
2131
|
+
function escapeHtml2(s) {
|
|
2132
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2133
|
+
}
|
|
2134
|
+
function number(v) {
|
|
2135
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2136
|
+
}
|
|
2137
|
+
function buildDashboardDataFromPipeline(result) {
|
|
2138
|
+
const moduleCards = result.modules.map((mod) => {
|
|
2139
|
+
const er = result.erDiagrams.get(mod);
|
|
2140
|
+
const plan = result.chainPlans.get(mod);
|
|
2141
|
+
return {
|
|
2142
|
+
module: mod,
|
|
2143
|
+
tables: er?.tables.length ?? 0,
|
|
2144
|
+
relations: er?.relations.length ?? 0,
|
|
2145
|
+
chains: plan?.chains.length ?? 0,
|
|
2146
|
+
steps: plan?.totalSteps ?? 0
|
|
2147
|
+
};
|
|
2148
|
+
});
|
|
2149
|
+
const totals = {
|
|
2150
|
+
modules: result.modules.length,
|
|
2151
|
+
tables: moduleCards.reduce((s, m) => s + m.tables, 0),
|
|
2152
|
+
relations: moduleCards.reduce((s, m) => s + m.relations, 0),
|
|
2153
|
+
chains: moduleCards.reduce((s, m) => s + m.chains, 0),
|
|
2154
|
+
steps: moduleCards.reduce((s, m) => s + m.steps, 0),
|
|
2155
|
+
files: result.generatedFiles.length,
|
|
2156
|
+
errors: result.validationErrors.filter((e) => e.severity === "error").length,
|
|
2157
|
+
warnings: result.validationErrors.filter((e) => e.severity === "warning").length
|
|
2158
|
+
};
|
|
2159
|
+
return {
|
|
2160
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2161
|
+
durationMs: result.duration,
|
|
2162
|
+
modules: [...result.modules],
|
|
2163
|
+
totals,
|
|
2164
|
+
moduleCards,
|
|
2165
|
+
files: result.generatedFiles.map((f) => ({ filePath: f.filePath, module: f.module, chain: f.chain })),
|
|
2166
|
+
issues: result.validationErrors.map((e) => ({
|
|
2167
|
+
severity: e.severity,
|
|
2168
|
+
module: e.module,
|
|
2169
|
+
field: e.field,
|
|
2170
|
+
message: e.message
|
|
2171
|
+
}))
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
function buildDashboardDataFromReportJson(input) {
|
|
2175
|
+
const src = input ?? {};
|
|
2176
|
+
const modules = Array.isArray(src.modules) ? src.modules.map((m) => String(m)) : [];
|
|
2177
|
+
const er = src.erDiagrams ?? {};
|
|
2178
|
+
const plans = src.chainPlans ?? {};
|
|
2179
|
+
const files = Array.isArray(src.generatedFiles) ? src.generatedFiles.map((f) => {
|
|
2180
|
+
const row = f;
|
|
2181
|
+
return {
|
|
2182
|
+
filePath: String(row.filePath ?? ""),
|
|
2183
|
+
module: String(row.module ?? ""),
|
|
2184
|
+
chain: String(row.chain ?? "")
|
|
2185
|
+
};
|
|
2186
|
+
}) : [];
|
|
2187
|
+
const rawIssues = Array.isArray(src.validationErrors) ? src.validationErrors : [];
|
|
2188
|
+
const issues = rawIssues.map((item) => {
|
|
2189
|
+
const row = item;
|
|
2190
|
+
return {
|
|
2191
|
+
severity: String(row.severity ?? "warning"),
|
|
2192
|
+
module: String(row.module ?? "unknown"),
|
|
2193
|
+
field: String(row.field ?? "unknown"),
|
|
2194
|
+
message: String(row.message ?? "")
|
|
2195
|
+
};
|
|
2196
|
+
});
|
|
2197
|
+
const moduleCards = modules.map((mod) => ({
|
|
2198
|
+
module: mod,
|
|
2199
|
+
tables: number(er[mod]?.tables),
|
|
2200
|
+
relations: number(er[mod]?.relations),
|
|
2201
|
+
chains: number(plans[mod]?.chains),
|
|
2202
|
+
steps: number(plans[mod]?.totalSteps)
|
|
2203
|
+
}));
|
|
2204
|
+
return {
|
|
2205
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2206
|
+
durationMs: number(src.duration),
|
|
2207
|
+
modules,
|
|
2208
|
+
totals: {
|
|
2209
|
+
modules: modules.length,
|
|
2210
|
+
tables: moduleCards.reduce((s, m) => s + m.tables, 0),
|
|
2211
|
+
relations: moduleCards.reduce((s, m) => s + m.relations, 0),
|
|
2212
|
+
chains: moduleCards.reduce((s, m) => s + m.chains, 0),
|
|
2213
|
+
steps: moduleCards.reduce((s, m) => s + m.steps, 0),
|
|
2214
|
+
files: files.length,
|
|
2215
|
+
errors: issues.filter((i) => i.severity === "error").length,
|
|
2216
|
+
warnings: issues.filter((i) => i.severity === "warning").length
|
|
2217
|
+
},
|
|
2218
|
+
moduleCards,
|
|
2219
|
+
files,
|
|
2220
|
+
issues
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
function generateVisualDashboardHtml(data) {
|
|
2224
|
+
const moduleCardHtml = data.moduleCards.map(
|
|
2225
|
+
(m) => `<article class="module-card reveal">
|
|
2226
|
+
<h3>${escapeHtml2(m.module)}</h3>
|
|
2227
|
+
<div class="meta">${m.tables} tables \xB7 ${m.relations} relations</div>
|
|
2228
|
+
<div class="bars">
|
|
2229
|
+
<div class="bar"><span>Chains</span><strong>${m.chains}</strong></div>
|
|
2230
|
+
<div class="bar"><span>Steps</span><strong>${m.steps}</strong></div>
|
|
2231
|
+
</div>
|
|
2232
|
+
</article>`
|
|
2233
|
+
).join("\n");
|
|
2234
|
+
const fileRows = data.files.slice(0, 20).map(
|
|
2235
|
+
(f) => `<tr><td><code>${escapeHtml2(f.filePath)}</code></td><td>${escapeHtml2(f.module)}</td><td>${escapeHtml2(f.chain)}</td></tr>`
|
|
2236
|
+
).join("\n");
|
|
2237
|
+
const issueRows = data.issues.map(
|
|
2238
|
+
(i) => `<tr class="${escapeHtml2(i.severity)}"><td>${escapeHtml2(i.severity)}</td><td>${escapeHtml2(i.module)}</td><td>${escapeHtml2(i.field)}</td><td>${escapeHtml2(i.message)}</td></tr>`
|
|
2239
|
+
).join("\n");
|
|
2240
|
+
return `<!DOCTYPE html>
|
|
2241
|
+
<html lang="en">
|
|
2242
|
+
<head>
|
|
2243
|
+
<meta charset="utf-8" />
|
|
2244
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
2245
|
+
<title>OpenCroc Visual Dashboard</title>
|
|
2246
|
+
<style>
|
|
2247
|
+
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap');
|
|
2248
|
+
:root {
|
|
2249
|
+
--bg: #f4efe6;
|
|
2250
|
+
--ink: #18222c;
|
|
2251
|
+
--card: #fff8ef;
|
|
2252
|
+
--line: #d8c7b4;
|
|
2253
|
+
--accent: #0b7a75;
|
|
2254
|
+
--accent-2: #f26a2e;
|
|
2255
|
+
--ok: #2f7d32;
|
|
2256
|
+
--warn: #b7791f;
|
|
2257
|
+
--err: #c0392b;
|
|
2258
|
+
}
|
|
2259
|
+
* { box-sizing: border-box; }
|
|
2260
|
+
body {
|
|
2261
|
+
margin: 0;
|
|
2262
|
+
font-family: 'Space Grotesk', 'Segoe UI', sans-serif;
|
|
2263
|
+
color: var(--ink);
|
|
2264
|
+
background:
|
|
2265
|
+
radial-gradient(circle at 15% 10%, #f9e2c5 0%, transparent 32%),
|
|
2266
|
+
radial-gradient(circle at 85% 0%, #d3efe4 0%, transparent 28%),
|
|
2267
|
+
var(--bg);
|
|
2268
|
+
min-height: 100vh;
|
|
2269
|
+
}
|
|
2270
|
+
.wrap { max-width: 1160px; margin: 0 auto; padding: 24px 18px 40px; }
|
|
2271
|
+
.hero {
|
|
2272
|
+
border: 2px solid var(--line);
|
|
2273
|
+
border-radius: 18px;
|
|
2274
|
+
background: linear-gradient(130deg, #fff8ef 0%, #f8f2ea 45%, #f3ece3 100%);
|
|
2275
|
+
padding: 22px;
|
|
2276
|
+
box-shadow: 0 12px 24px rgba(24, 34, 44, 0.08);
|
|
2277
|
+
position: relative;
|
|
2278
|
+
overflow: hidden;
|
|
2279
|
+
}
|
|
2280
|
+
.hero::after {
|
|
2281
|
+
content: '';
|
|
2282
|
+
position: absolute;
|
|
2283
|
+
right: -42px;
|
|
2284
|
+
top: -38px;
|
|
2285
|
+
width: 160px;
|
|
2286
|
+
height: 160px;
|
|
2287
|
+
border-radius: 50%;
|
|
2288
|
+
background: conic-gradient(from 50deg, #f26a2e 0deg, #f7a35a 90deg, #0b7a75 220deg, #f26a2e 360deg);
|
|
2289
|
+
opacity: 0.14;
|
|
2290
|
+
}
|
|
2291
|
+
h1 { margin: 0; font-size: clamp(1.6rem, 3vw, 2.5rem); letter-spacing: -0.03em; }
|
|
2292
|
+
.subtitle { margin-top: 8px; font-size: 0.95rem; opacity: 0.82; }
|
|
2293
|
+
.kpi-grid {
|
|
2294
|
+
margin-top: 16px;
|
|
2295
|
+
display: grid;
|
|
2296
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
2297
|
+
gap: 10px;
|
|
2298
|
+
}
|
|
2299
|
+
.kpi {
|
|
2300
|
+
border: 1px solid var(--line);
|
|
2301
|
+
border-radius: 12px;
|
|
2302
|
+
padding: 10px 12px;
|
|
2303
|
+
background: #fffdf9;
|
|
2304
|
+
}
|
|
2305
|
+
.kpi .label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.7; }
|
|
2306
|
+
.kpi .value { font-size: 1.45rem; font-weight: 700; margin-top: 4px; }
|
|
2307
|
+
.kpi.error .value { color: var(--err); }
|
|
2308
|
+
.kpi.warning .value { color: var(--warn); }
|
|
2309
|
+
.kpi.files .value { color: var(--accent); }
|
|
2310
|
+
|
|
2311
|
+
.section-title {
|
|
2312
|
+
margin: 24px 0 10px;
|
|
2313
|
+
font-size: 1rem;
|
|
2314
|
+
text-transform: uppercase;
|
|
2315
|
+
letter-spacing: 0.08em;
|
|
2316
|
+
color: #344250;
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
.module-grid {
|
|
2320
|
+
display: grid;
|
|
2321
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
2322
|
+
gap: 12px;
|
|
2323
|
+
}
|
|
2324
|
+
.module-card {
|
|
2325
|
+
border: 1px solid var(--line);
|
|
2326
|
+
border-radius: 14px;
|
|
2327
|
+
padding: 14px;
|
|
2328
|
+
background: var(--card);
|
|
2329
|
+
box-shadow: 0 8px 16px rgba(24, 34, 44, 0.06);
|
|
2330
|
+
}
|
|
2331
|
+
.module-card h3 { margin: 0 0 6px; font-size: 1.05rem; }
|
|
2332
|
+
.module-card .meta { font-size: 0.85rem; opacity: 0.78; }
|
|
2333
|
+
.bars { margin-top: 10px; display: grid; gap: 8px; }
|
|
2334
|
+
.bar { display: flex; justify-content: space-between; border-top: 1px dashed var(--line); padding-top: 7px; }
|
|
2335
|
+
|
|
2336
|
+
table {
|
|
2337
|
+
width: 100%;
|
|
2338
|
+
border-collapse: collapse;
|
|
2339
|
+
border: 1px solid var(--line);
|
|
2340
|
+
border-radius: 12px;
|
|
2341
|
+
overflow: hidden;
|
|
2342
|
+
background: #fffdf9;
|
|
2343
|
+
font-family: 'IBM Plex Mono', 'Consolas', monospace;
|
|
2344
|
+
font-size: 0.82rem;
|
|
2345
|
+
}
|
|
2346
|
+
thead { background: #efe5d8; }
|
|
2347
|
+
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eadfce; }
|
|
2348
|
+
tbody tr:last-child td { border-bottom: none; }
|
|
2349
|
+
tr.error td:first-child { color: var(--err); font-weight: 700; }
|
|
2350
|
+
tr.warning td:first-child { color: var(--warn); font-weight: 700; }
|
|
2351
|
+
|
|
2352
|
+
code {
|
|
2353
|
+
font-family: 'IBM Plex Mono', 'Consolas', monospace;
|
|
2354
|
+
background: #f5ece0;
|
|
2355
|
+
border: 1px solid #eadfce;
|
|
2356
|
+
border-radius: 6px;
|
|
2357
|
+
padding: 1px 5px;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
.reveal { opacity: 0; transform: translateY(12px); animation: rise .55s ease forwards; }
|
|
2361
|
+
.module-card.reveal:nth-child(2) { animation-delay: .06s; }
|
|
2362
|
+
.module-card.reveal:nth-child(3) { animation-delay: .12s; }
|
|
2363
|
+
.module-card.reveal:nth-child(4) { animation-delay: .18s; }
|
|
2364
|
+
.module-card.reveal:nth-child(5) { animation-delay: .24s; }
|
|
2365
|
+
@keyframes rise {
|
|
2366
|
+
to { opacity: 1; transform: translateY(0); }
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
@media (max-width: 700px) {
|
|
2370
|
+
.wrap { padding: 14px 12px 28px; }
|
|
2371
|
+
.hero { padding: 16px; }
|
|
2372
|
+
table { font-size: 0.76rem; }
|
|
2373
|
+
th, td { padding: 7px 8px; }
|
|
2374
|
+
}
|
|
2375
|
+
</style>
|
|
2376
|
+
</head>
|
|
2377
|
+
<body>
|
|
2378
|
+
<main class="wrap">
|
|
2379
|
+
<section class="hero reveal">
|
|
2380
|
+
<h1>OpenCroc Visual Dashboard</h1>
|
|
2381
|
+
<p class="subtitle">Pipeline finished in ${data.durationMs}ms \xB7 Generated ${escapeHtml2(data.generatedAt)}</p>
|
|
2382
|
+
<div class="kpi-grid">
|
|
2383
|
+
<div class="kpi"><div class="label">Modules</div><div class="value">${data.totals.modules}</div></div>
|
|
2384
|
+
<div class="kpi"><div class="label">Tables</div><div class="value">${data.totals.tables}</div></div>
|
|
2385
|
+
<div class="kpi"><div class="label">Relations</div><div class="value">${data.totals.relations}</div></div>
|
|
2386
|
+
<div class="kpi"><div class="label">Chains</div><div class="value">${data.totals.chains}</div></div>
|
|
2387
|
+
<div class="kpi"><div class="label">Steps</div><div class="value">${data.totals.steps}</div></div>
|
|
2388
|
+
<div class="kpi files"><div class="label">Files</div><div class="value">${data.totals.files}</div></div>
|
|
2389
|
+
<div class="kpi error"><div class="label">Errors</div><div class="value">${data.totals.errors}</div></div>
|
|
2390
|
+
<div class="kpi warning"><div class="label">Warnings</div><div class="value">${data.totals.warnings}</div></div>
|
|
2391
|
+
</div>
|
|
2392
|
+
</section>
|
|
2393
|
+
|
|
2394
|
+
<h2 class="section-title">Module Health</h2>
|
|
2395
|
+
<section class="module-grid">
|
|
2396
|
+
${moduleCardHtml || '<article class="module-card">No module data</article>'}
|
|
2397
|
+
</section>
|
|
2398
|
+
|
|
2399
|
+
<h2 class="section-title">Generated Files (Top 20)</h2>
|
|
2400
|
+
<section>
|
|
2401
|
+
<table>
|
|
2402
|
+
<thead><tr><th>File</th><th>Module</th><th>Chain</th></tr></thead>
|
|
2403
|
+
<tbody>${fileRows || '<tr><td colspan="3">No files generated</td></tr>'}</tbody>
|
|
2404
|
+
</table>
|
|
2405
|
+
</section>
|
|
2406
|
+
|
|
2407
|
+
<h2 class="section-title">Validation Issues</h2>
|
|
2408
|
+
<section>
|
|
2409
|
+
<table>
|
|
2410
|
+
<thead><tr><th>Severity</th><th>Module</th><th>Field</th><th>Message</th></tr></thead>
|
|
2411
|
+
<tbody>${issueRows || '<tr><td colspan="4">No validation issues</td></tr>'}</tbody>
|
|
2412
|
+
</table>
|
|
2413
|
+
</section>
|
|
2414
|
+
</main>
|
|
2415
|
+
</body>
|
|
2416
|
+
</html>`;
|
|
2417
|
+
}
|
|
2418
|
+
var init_dashboard = __esm({
|
|
2419
|
+
"src/dashboard/index.ts"() {
|
|
2420
|
+
"use strict";
|
|
2421
|
+
init_esm_shims();
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
|
|
2425
|
+
// src/cli/commands/dashboard.ts
|
|
2426
|
+
var dashboard_exports = {};
|
|
2427
|
+
__export(dashboard_exports, {
|
|
2428
|
+
dashboard: () => dashboard
|
|
2429
|
+
});
|
|
2430
|
+
import * as fs7 from "fs";
|
|
2431
|
+
import * as path8 from "path";
|
|
2432
|
+
import chalk8 from "chalk";
|
|
2433
|
+
async function dashboard(opts) {
|
|
2434
|
+
let dashboardHtml;
|
|
2435
|
+
if (opts.input) {
|
|
2436
|
+
const inputPath = path8.resolve(opts.input);
|
|
2437
|
+
if (!fs7.existsSync(inputPath)) {
|
|
2438
|
+
console.error(chalk8.red(`Input report not found: ${inputPath}`));
|
|
2439
|
+
process.exitCode = 1;
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
const raw = fs7.readFileSync(inputPath, "utf-8");
|
|
2443
|
+
let parsed;
|
|
2444
|
+
try {
|
|
2445
|
+
parsed = JSON.parse(raw);
|
|
2446
|
+
} catch {
|
|
2447
|
+
console.error(chalk8.red("Invalid JSON input. Please provide opencroc-report.json output."));
|
|
2448
|
+
process.exitCode = 1;
|
|
2449
|
+
return;
|
|
2450
|
+
}
|
|
2451
|
+
const data = buildDashboardDataFromReportJson(parsed);
|
|
2452
|
+
dashboardHtml = generateVisualDashboardHtml(data);
|
|
2453
|
+
console.log(chalk8.cyan(`Building visual dashboard from ${inputPath}...`));
|
|
2454
|
+
} else {
|
|
2455
|
+
let loaded;
|
|
2456
|
+
try {
|
|
2457
|
+
loaded = await loadConfig();
|
|
2458
|
+
} catch {
|
|
2459
|
+
console.error(chalk8.red("No opencroc config found. Run `opencroc init` first."));
|
|
2460
|
+
process.exitCode = 1;
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2463
|
+
const { config } = loaded;
|
|
2464
|
+
console.log(chalk8.cyan("Running pipeline to build visual dashboard..."));
|
|
2465
|
+
const pipeline = createPipeline(config);
|
|
2466
|
+
const result = await pipeline.run();
|
|
2467
|
+
const data = buildDashboardDataFromPipeline(result);
|
|
2468
|
+
dashboardHtml = generateVisualDashboardHtml(data);
|
|
2469
|
+
}
|
|
2470
|
+
const outDir = opts.output ? path8.resolve(opts.output) : path8.resolve("./opencroc-output");
|
|
2471
|
+
if (!fs7.existsSync(outDir)) {
|
|
2472
|
+
fs7.mkdirSync(outDir, { recursive: true });
|
|
2473
|
+
}
|
|
2474
|
+
const outPath = path8.join(outDir, "opencroc-dashboard.html");
|
|
2475
|
+
fs7.writeFileSync(outPath, dashboardHtml, "utf-8");
|
|
2476
|
+
console.log(chalk8.green(`\u2714 visual dashboard \u2192 ${outPath}`));
|
|
2477
|
+
}
|
|
2478
|
+
var init_dashboard2 = __esm({
|
|
2479
|
+
"src/cli/commands/dashboard.ts"() {
|
|
2480
|
+
"use strict";
|
|
2481
|
+
init_esm_shims();
|
|
2482
|
+
init_load_config();
|
|
2483
|
+
init_pipeline();
|
|
2484
|
+
init_dashboard();
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
|
|
2130
2488
|
// src/cli/index.ts
|
|
2131
2489
|
init_esm_shims();
|
|
2132
2490
|
import { Command } from "commander";
|
|
2133
2491
|
var program = new Command();
|
|
2134
|
-
program.name("opencroc").description("AI-native E2E testing framework").version("0.
|
|
2492
|
+
program.name("opencroc").description("AI-native E2E testing framework").version("0.6.0");
|
|
2135
2493
|
program.command("init").description("Initialize OpenCroc in the current project").option("-y, --yes", "Skip prompts and use defaults").action(async (opts) => {
|
|
2136
2494
|
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2137
2495
|
await initProject2(opts);
|
|
@@ -2160,5 +2518,9 @@ program.command("report").description("Generate pipeline report (HTML/JSON/Markd
|
|
|
2160
2518
|
const { report: report2 } = await Promise.resolve().then(() => (init_report(), report_exports));
|
|
2161
2519
|
await report2(opts);
|
|
2162
2520
|
});
|
|
2521
|
+
program.command("dashboard").description("Generate visual dashboard (opencroc-dashboard.html)").option("-i, --input <file>", "Build from existing opencroc-report.json file").option("-o, --output <dir>", "Output directory", "./opencroc-output").action(async (opts) => {
|
|
2522
|
+
const { dashboard: dashboard2 } = await Promise.resolve().then(() => (init_dashboard2(), dashboard_exports));
|
|
2523
|
+
await dashboard2(opts);
|
|
2524
|
+
});
|
|
2163
2525
|
program.parse();
|
|
2164
2526
|
//# sourceMappingURL=index.js.map
|