@datashow/super-visual-cli 0.1.0-rc.4 → 0.1.0-rc.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datashow/super-visual-cli",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.6",
|
|
4
4
|
"description": "CLI for validating, compiling, packaging, verifying, and deploying Super Visual projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@datashow/super-visual-adapter-claude-dashboard": "0.1.0-rc.
|
|
29
|
-
"@datashow/super-visual-compiler": "0.1.0-rc.
|
|
30
|
-
"@datashow/super-visual-contracts": "0.1.0-rc.
|
|
31
|
-
"@datashow/super-visual-fabric": "0.1.0-rc.
|
|
28
|
+
"@datashow/super-visual-adapter-claude-dashboard": "0.1.0-rc.6",
|
|
29
|
+
"@datashow/super-visual-compiler": "0.1.0-rc.6",
|
|
30
|
+
"@datashow/super-visual-contracts": "0.1.0-rc.6",
|
|
31
|
+
"@datashow/super-visual-fabric": "0.1.0-rc.6",
|
|
32
32
|
"@fontsource/source-serif-4": "5.3.1",
|
|
33
33
|
"@nivo/bar": "0.99.0",
|
|
34
34
|
"@nivo/line": "0.99.0",
|
package/src/main.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import { resolveProjectTarget, targetUpdate, writeProjectTarget } from "./projec
|
|
|
16
16
|
import { runSemanticPreviewTest, startSemanticPreview } from "./semantic-preview.mjs";
|
|
17
17
|
|
|
18
18
|
const run = promisify(execFile);
|
|
19
|
-
const VERSION = "0.1.0-rc.
|
|
19
|
+
const VERSION = "0.1.0-rc.6";
|
|
20
20
|
const FABRIC_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
21
21
|
const SPINNER_FRAMES = ["|", "/", "-", "\\"];
|
|
22
22
|
const progressStream = process.stderr;
|
|
@@ -65,7 +65,7 @@ export function stopActiveProgress() {
|
|
|
65
65
|
activeProgress = null;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function
|
|
68
|
+
export function writeCommandProgress(event) {
|
|
69
69
|
if (event.status === "running") {
|
|
70
70
|
stopActiveProgress();
|
|
71
71
|
writeProgress(`> ${event.message}\n`);
|
|
@@ -87,6 +87,14 @@ export function writeInitProgress(event) {
|
|
|
87
87
|
writeProgress(`${marker} ${event.message}\n`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function printPreviewResult(result) {
|
|
91
|
+
process.stdout.write(`\nLocal preview is ready.\n`);
|
|
92
|
+
process.stdout.write(`URL: ${result.url}\n`);
|
|
93
|
+
process.stdout.write(`Workspace: ${result.workspace.displayName} (${result.workspace.id})\n`);
|
|
94
|
+
process.stdout.write(`Semantic model: ${result.semanticModel.displayName} (${result.semanticModel.id})\n`);
|
|
95
|
+
process.stdout.write("The preview is using live semantic-model data. Press Ctrl+C to stop it.\n");
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
function semanticInventory(model) {
|
|
91
99
|
const tables = model.tables ?? [];
|
|
92
100
|
return {
|
|
@@ -345,7 +353,7 @@ function createProgram() {
|
|
|
345
353
|
workspaceId: options.workspaceId,
|
|
346
354
|
workspaceName: options.workspaceName,
|
|
347
355
|
semanticModelId: options.semanticModelId,
|
|
348
|
-
onProgress: json ? null :
|
|
356
|
+
onProgress: json ? null : writeCommandProgress,
|
|
349
357
|
});
|
|
350
358
|
} catch (error) {
|
|
351
359
|
throw new CliUserError(error instanceof Error ? error.message : String(error), {
|
|
@@ -361,7 +369,7 @@ function createProgram() {
|
|
|
361
369
|
semanticModel: { id: inspected.semanticModel.id, displayName: inspected.semanticModel.displayName, format: inspected.semanticModel.format },
|
|
362
370
|
inventory: semanticInventory(inspected.semanticModel),
|
|
363
371
|
};
|
|
364
|
-
if (!json)
|
|
372
|
+
if (!json) writeCommandProgress({ status: "passed", message: `Verified workspace ${inspected.workspace.displayName} and semantic model ${inspected.semanticModel.displayName}` });
|
|
365
373
|
} else if (!json && options.skipLiveValidation) {
|
|
366
374
|
writeProgress("! Live Fabric validation skipped by request\n");
|
|
367
375
|
} else if (!json) {
|
|
@@ -374,10 +382,10 @@ function createProgram() {
|
|
|
374
382
|
title: options.title,
|
|
375
383
|
app: options.app,
|
|
376
384
|
force: options.force,
|
|
377
|
-
onProgress: json ? null :
|
|
385
|
+
onProgress: json ? null : writeCommandProgress,
|
|
378
386
|
});
|
|
379
387
|
if (options.workspaceId) {
|
|
380
|
-
if (!json)
|
|
388
|
+
if (!json) writeCommandProgress({ status: "running", message: "Saving the reusable Fabric project binding" });
|
|
381
389
|
const saved = await writeProjectTarget(result.directory, targetUpdate({
|
|
382
390
|
...options,
|
|
383
391
|
workspaceName: options.workspaceName ?? liveValidation?.workspace.displayName,
|
|
@@ -385,7 +393,7 @@ function createProgram() {
|
|
|
385
393
|
reportName: options.reportName ?? result.application.title,
|
|
386
394
|
}));
|
|
387
395
|
result.binding = { file: saved.file, ...saved.value };
|
|
388
|
-
if (!json)
|
|
396
|
+
if (!json) writeCommandProgress({ status: "passed", message: `Saved Fabric binding to ${saved.file}` });
|
|
389
397
|
}
|
|
390
398
|
result.validation = { local: result.validation, live: liveValidation ?? { ok: true, status: "not-run", scope: "fabric-read-only" } };
|
|
391
399
|
if (json) output(result, true);
|
|
@@ -434,23 +442,38 @@ function createProgram() {
|
|
|
434
442
|
.option("--open", "open the preview in the default browser")
|
|
435
443
|
.option("--timeout <seconds>", "Fabric operation timeout", "900")
|
|
436
444
|
.action(async (project = ".", options, command) => {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
445
|
+
const json = jsonEnabled(command);
|
|
446
|
+
if (!json) writeProgress(`\nSuper Visual dev\n\n`);
|
|
447
|
+
try {
|
|
448
|
+
if (!json) writeCommandProgress({ status: "running", message: "Synchronizing the React source and report contract" });
|
|
449
|
+
await syncProjectSource(projectFile(project));
|
|
450
|
+
if (!json) writeCommandProgress({ status: "passed", message: "Synchronized the React source and report contract" });
|
|
451
|
+
if (!json) writeCommandProgress({ status: "running", message: "Loading the saved Fabric project binding" });
|
|
452
|
+
const loaded = await loadProject(projectFile(project));
|
|
453
|
+
const target = await resolveProjectTarget(loaded.root, options, { requireSemanticModel: true });
|
|
454
|
+
if (!json) writeCommandProgress({ status: "passed", message: "Loaded the Fabric workspace and semantic-model binding" });
|
|
455
|
+
const client = new FabricHttpClient({ timeoutSeconds: Number(options.timeout) });
|
|
456
|
+
const preview = await startSemanticPreview({
|
|
457
|
+
client,
|
|
458
|
+
projectFile: projectFile(project),
|
|
459
|
+
workspaceId: target.workspaceId,
|
|
460
|
+
workspaceName: target.workspaceName,
|
|
461
|
+
semanticModelId: target.semanticModelId ?? target.managedSemanticModelId,
|
|
462
|
+
host: options.host,
|
|
463
|
+
port: Number(options.port),
|
|
464
|
+
open: Boolean(options.open),
|
|
465
|
+
onProgress: json ? null : writeCommandProgress,
|
|
466
|
+
});
|
|
467
|
+
const { close, ...result } = preview;
|
|
468
|
+
if (json) output(result, true);
|
|
469
|
+
else printPreviewResult(result);
|
|
470
|
+
await waitForPreviewShutdown({ close });
|
|
471
|
+
} catch (error) {
|
|
472
|
+
throw new CliUserError(error instanceof Error ? error.message : String(error), {
|
|
473
|
+
summary: "Super Visual could not start the local semantic preview.",
|
|
474
|
+
guidance: "Check the project binding and your Fabric access, then run supervisual dev --open again.",
|
|
475
|
+
});
|
|
476
|
+
}
|
|
454
477
|
});
|
|
455
478
|
|
|
456
479
|
const preview = program.command("preview").description("exercise the local React host against a live semantic model");
|
package/src/semantic-preview.mjs
CHANGED
|
@@ -396,15 +396,18 @@ async function writePreviewHost(root, token) {
|
|
|
396
396
|
await writeFile(path.join(root, "src", "config.ts"), `export const PREVIEW_TOKEN = ${JSON.stringify(token)};\n`);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
-
export async function startSemanticPreview({ client, projectFile, workspaceId, workspaceName, semanticModelId, host = "127.0.0.1", port = 5173, open = false }) {
|
|
399
|
+
export async function startSemanticPreview({ client, projectFile, workspaceId, workspaceName, semanticModelId, host = "127.0.0.1", port = 5173, open = false, onProgress = null }) {
|
|
400
400
|
if (!["127.0.0.1", "localhost", "::1"].includes(host)) throw new Error("Semantic preview may bind only to a loopback host");
|
|
401
|
+
onProgress?.({ status: "running", step: "project", message: "Loading and validating the Super Visual project" });
|
|
401
402
|
const loaded = await loadProject(projectFile);
|
|
402
403
|
const diagnostics = validateCompilation(loaded);
|
|
403
404
|
if (diagnostics.length) throw new Error(`Preview validation failed:\n- ${diagnostics.join("\n- ")}`);
|
|
404
|
-
|
|
405
|
+
onProgress?.({ status: "passed", step: "project", message: "Validated the project contracts and semantic bindings" });
|
|
406
|
+
const inspected = await inspectSemanticModel({ client, workspaceId, workspaceName, semanticModelId, onProgress });
|
|
405
407
|
const catalogMode = usesSemanticCatalog(loaded.broker);
|
|
406
408
|
const brokerDiagnostics = previewBrokerSurfaceDiagnostics(inspected.semanticModel);
|
|
407
409
|
const previewMode = catalogMode ? "catalog" : brokerDiagnostics.length ? "direct" : "managed";
|
|
410
|
+
onProgress?.({ status: "running", step: "compatibility", message: "Checking application queries against the selected semantic model" });
|
|
408
411
|
const compatibility = catalogMode
|
|
409
412
|
? { ok: true, summary: { required: 1, matched: 1, missing: 0, ambiguous: 0 }, checks: [], diagnostics: [] }
|
|
410
413
|
: checkSemanticCompatibility(inspected.semanticModel, loaded.semanticModel, loaded.broker);
|
|
@@ -412,19 +415,26 @@ export async function startSemanticPreview({ client, projectFile, workspaceId, w
|
|
|
412
415
|
const messages = compatibility.diagnostics.map((item) => item.message);
|
|
413
416
|
throw new Error(`Semantic preview compatibility failed:\n- ${messages.join("\n- ")}`);
|
|
414
417
|
}
|
|
418
|
+
onProgress?.({ status: "passed", step: "compatibility", message: `Confirmed ${compatibility.summary.matched || compatibility.summary.required} semantic binding${compatibility.summary.required === 1 ? "" : "s"}` });
|
|
415
419
|
loaded.previewMode = previewMode;
|
|
416
420
|
if (catalogMode) loaded.catalogRows = semanticCatalogRows(inspected.semanticModel);
|
|
417
421
|
|
|
418
422
|
const previewRoot = path.join(loaded.root, ".supervisual", "semantic-preview");
|
|
419
423
|
const solutionRoot = path.join(previewRoot, "solution");
|
|
420
424
|
const hostRoot = path.join(previewRoot, "host");
|
|
425
|
+
onProgress?.({ status: "running", step: "build", message: "Building the local preview runtime" });
|
|
421
426
|
await buildSolution(projectFile, { outputDirectory: solutionRoot });
|
|
427
|
+
onProgress?.({ status: "passed", step: "build", message: "Built the local preview runtime" });
|
|
428
|
+
onProgress?.({ status: "running", step: "host", message: "Preparing the secure loopback preview host" });
|
|
422
429
|
const token = randomBytes(24).toString("base64url");
|
|
423
430
|
await writePreviewHost(hostRoot, token);
|
|
431
|
+
const aliases = dependencyAliases();
|
|
432
|
+
onProgress?.({ status: "passed", step: "host", message: "Prepared the secure loopback preview host" });
|
|
433
|
+
onProgress?.({ status: "running", step: "server", message: `Starting the preview server on ${host}:${port}` });
|
|
424
434
|
const server = await createServer({
|
|
425
435
|
root: hostRoot,
|
|
426
436
|
logLevel: "silent",
|
|
427
|
-
resolve: { alias:
|
|
437
|
+
resolve: { alias: aliases, dedupe: ["react", "react-dom"] },
|
|
428
438
|
server: {
|
|
429
439
|
host,
|
|
430
440
|
port: Number(port),
|
|
@@ -441,11 +451,13 @@ export async function startSemanticPreview({ client, projectFile, workspaceId, w
|
|
|
441
451
|
const address = server.httpServer?.address();
|
|
442
452
|
const actualPort = typeof address === "object" && address ? address.port : Number(port);
|
|
443
453
|
const urlHost = host === "::1" ? "[::1]" : host;
|
|
454
|
+
const url = `http://${urlHost}:${actualPort}/`;
|
|
455
|
+
onProgress?.({ status: "passed", step: "server", message: `Preview server is ready at ${url}` });
|
|
444
456
|
return {
|
|
445
457
|
ok: true,
|
|
446
458
|
status: "ready",
|
|
447
459
|
mode: "live-semantic-model",
|
|
448
|
-
url
|
|
460
|
+
url,
|
|
449
461
|
workspace: inspected.workspace,
|
|
450
462
|
semanticModel: { id: inspected.semanticModel.id, displayName: inspected.semanticModel.displayName },
|
|
451
463
|
compatibility: compatibility.summary,
|
|
@@ -165,8 +165,12 @@ class SemanticPreviewHost {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
document
|
|
169
|
-
|
|
168
|
+
// The compiled visual disables document scrolling because Power BI owns its
|
|
169
|
+
// canvas viewport. The standalone preview owns the browser page instead, so it
|
|
170
|
+
// must explicitly restore document flow for applications taller than the
|
|
171
|
+
// viewport.
|
|
172
|
+
document.documentElement.style.cssText = "min-height:100%;height:auto;overflow:auto";
|
|
173
|
+
document.body.style.cssText = "min-height:100%;height:auto;margin:0;overflow:auto";
|
|
170
174
|
const banner = document.getElementById("supervisual-preview-status")!;
|
|
171
175
|
banner.style.cssText = "position:fixed;right:12px;bottom:12px;z-index:2147483647;padding:7px 10px;border-radius:5px;background:#173c36;color:white;font:12px/1.3 system-ui;box-shadow:0 2px 10px #0003";
|
|
172
176
|
new SemanticPreviewHost();
|