@datashow/super-visual-cli 0.1.0-rc.5 → 0.1.0-rc.7

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.5",
3
+ "version": "0.1.0-rc.7",
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.5",
29
- "@datashow/super-visual-compiler": "0.1.0-rc.5",
30
- "@datashow/super-visual-contracts": "0.1.0-rc.5",
31
- "@datashow/super-visual-fabric": "0.1.0-rc.5",
28
+ "@datashow/super-visual-adapter-claude-dashboard": "0.1.0-rc.7",
29
+ "@datashow/super-visual-compiler": "0.1.0-rc.7",
30
+ "@datashow/super-visual-contracts": "0.1.0-rc.7",
31
+ "@datashow/super-visual-fabric": "0.1.0-rc.7",
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,11 +16,10 @@ 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.5";
19
+ const VERSION = "0.1.0-rc.7";
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;
23
- let activeProgress = null;
24
23
 
25
24
  function writeProgress(value) {
26
25
  try {
@@ -58,34 +57,48 @@ function output(value, json) {
58
57
  process.stdout.write(`${json ? JSON.stringify(value) : typeof value === "string" ? value : JSON.stringify(value, null, 2)}\n`);
59
58
  }
60
59
 
61
- export function stopActiveProgress() {
62
- if (!activeProgress) return;
63
- clearInterval(activeProgress.timer);
64
- writeProgress(`\r${" ".repeat(activeProgress.width)}\r`);
65
- activeProgress = null;
60
+ export function createCommandProgressRenderer({
61
+ write = writeProgress,
62
+ isTTY = () => Boolean(progressStream.isTTY),
63
+ startTimer = (callback, milliseconds) => setInterval(callback, milliseconds),
64
+ cancelTimer = (timer) => clearInterval(timer),
65
+ } = {}) {
66
+ let activeProgress = null;
67
+ const stop = () => {
68
+ if (!activeProgress) return;
69
+ cancelTimer(activeProgress.timer);
70
+ write(`\r${" ".repeat(activeProgress.width)}\r`);
71
+ activeProgress = null;
72
+ };
73
+ const handle = (event) => {
74
+ if (event.status === "running") {
75
+ stop();
76
+ write(`> ${event.message}\n`);
77
+ if (!isTTY()) return;
78
+ let frame = 0;
79
+ const render = () => {
80
+ const line = `${SPINNER_FRAMES[frame % SPINNER_FRAMES.length]} Working...`;
81
+ write(`\r${line}`);
82
+ activeProgress.width = Math.max(activeProgress.width, line.length);
83
+ frame += 1;
84
+ };
85
+ activeProgress = { timer: null, width: 12 };
86
+ render();
87
+ activeProgress.timer = startTimer(render, 120);
88
+ return;
89
+ }
90
+ stop();
91
+ const marker = event.status === "passed" ? "✓" : "!";
92
+ write(`${marker} ${event.message}\n`);
93
+ };
94
+ return { handle, stop };
66
95
  }
67
96
 
68
- export function writeCommandProgress(event) {
69
- if (event.status === "running") {
70
- stopActiveProgress();
71
- writeProgress(`> ${event.message}\n`);
72
- if (!progressStream.isTTY) return;
73
- let frame = 0;
74
- const render = () => {
75
- const line = `${SPINNER_FRAMES[frame % SPINNER_FRAMES.length]} Working...`;
76
- writeProgress(`\r${line}`);
77
- activeProgress.width = Math.max(activeProgress.width, line.length);
78
- frame += 1;
79
- };
80
- activeProgress = { timer: null, width: 12 };
81
- render();
82
- activeProgress.timer = setInterval(render, 120);
83
- return;
84
- }
85
- stopActiveProgress();
86
- const marker = event.status === "passed" ? "✓" : "!";
87
- writeProgress(`${marker} ${event.message}\n`);
88
- }
97
+ const commandProgressRenderer = createCommandProgressRenderer();
98
+
99
+ export function stopActiveProgress() { commandProgressRenderer.stop(); }
100
+
101
+ export function writeCommandProgress(event) { commandProgressRenderer.handle(event); }
89
102
 
90
103
  function printPreviewResult(result) {
91
104
  process.stdout.write(`\nLocal preview is ready.\n`);
@@ -124,12 +137,17 @@ function jsonEnabled(command) {
124
137
  return Boolean(command.optsWithGlobals().json);
125
138
  }
126
139
 
127
- async function packageVisual(result) {
140
+ async function packageVisual(result, onProgress = null) {
128
141
  const temporary = await mkdtemp(path.join(os.tmpdir(), "supervisual-package-"));
129
142
  const visual = path.join(temporary, "Visual");
143
+ onProgress?.({ status: "running", step: "visual-build", message: "Preparing the isolated Power BI visual build" });
130
144
  await cp(path.join(result.outputDirectory, "Visual"), visual, { recursive: true });
131
145
  await run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], { cwd: visual, maxBuffer: 10 * 1024 * 1024 });
146
+ onProgress?.({ status: "passed", step: "visual-build", message: "Prepared the isolated Power BI visual build" });
147
+ onProgress?.({ status: "running", step: "typecheck", message: "Type-checking the compiled React application and visual runtime" });
132
148
  await run("npm", ["run", "typecheck"], { cwd: visual, maxBuffer: 10 * 1024 * 1024 });
149
+ onProgress?.({ status: "passed", step: "typecheck", message: "Type-checked the compiled React application and visual runtime" });
150
+ onProgress?.({ status: "running", step: "pbiviz", message: "Building the deployable Power BI visual package" });
133
151
  await run("npm", ["run", "package"], { cwd: visual, maxBuffer: 20 * 1024 * 1024 });
134
152
  const name = (await readdir(path.join(visual, "dist"))).find((file) => file.endsWith(".pbiviz"));
135
153
  if (!name) throw new Error("pbiviz package did not produce a visual archive");
@@ -139,6 +157,7 @@ async function packageVisual(result) {
139
157
  await cp(path.join(visual, "dist", name), destination);
140
158
  await embedVisualPackage(destination, path.join(result.outputDirectory, "Report.Report"));
141
159
  await refreshSolutionManifest(result.outputDirectory);
160
+ onProgress?.({ status: "passed", step: "pbiviz", message: `Built and embedded ${name}` });
142
161
  return destination;
143
162
  }
144
163
 
@@ -152,12 +171,18 @@ async function semanticParameters(file) {
152
171
  return { server: profile.source.server.trim(), database: profile.source.database.trim() };
153
172
  }
154
173
 
155
- async function build(project, shouldPackage, parametersFile, semanticCatalogSource = null, semanticParameterOverride = null) {
174
+ async function build(project, shouldPackage, parametersFile, semanticCatalogSource = null, semanticParameterOverride = null, onProgress = null) {
175
+ onProgress?.({ status: "running", step: "source", message: "Synchronizing the React source and report contract" });
156
176
  await syncProjectSource(projectFile(project));
177
+ onProgress?.({ status: "passed", step: "source", message: "Synchronized the React source and report contract" });
157
178
  const parameters = semanticParameterOverride ?? await semanticParameters(parametersFile);
179
+ onProgress?.({ status: "running", step: "compile", message: "Compiling the application, semantic binding, and report wrapper" });
158
180
  const result = await buildSolution(projectFile(project), { semanticParameters: parameters, semanticCatalogSource });
159
- const artifact = shouldPackage ? await packageVisual(result) : null;
181
+ onProgress?.({ status: "passed", step: "compile", message: "Compiled the application, semantic binding, and report wrapper" });
182
+ const artifact = shouldPackage ? await packageVisual(result, onProgress) : null;
183
+ onProgress?.({ status: "running", step: "verify", message: "Verifying the generated solution manifest and file hashes" });
160
184
  const verified = await verifySolutionManifest(result.outputDirectory);
185
+ onProgress?.({ status: verified.ok ? "passed" : "failed", step: "verify", message: verified.ok ? `Verified ${verified.fileCount} generated files` : "Generated solution verification failed" });
161
186
  return {
162
187
  ok: verified.ok,
163
188
  outputDirectory: result.outputDirectory,
@@ -224,11 +249,45 @@ function printDeploymentPlan(plan) {
224
249
  process.stdout.write(`\nReady to publish to ${plan.workspace.displayName} (${plan.workspace.id}):\n - ${operations}\n\nPlan ID: ${plan.planId}\n`);
225
250
  }
226
251
 
227
- async function applySavedDeployment({ loaded, planPath, confirm, backup, receipt: receiptOption, resume, timeout }) {
252
+ export function formatPublishResult(result) {
253
+ const report = result.items?.report;
254
+ const semanticModel = result.items?.semanticModel;
255
+ const reportUrl = report?.id && result.workspace?.id
256
+ ? `https://app.powerbi.com/groups/${encodeURIComponent(result.workspace.id)}/reports/${encodeURIComponent(report.id)}`
257
+ : null;
258
+ const acceptance = result.verification?.acceptance;
259
+ const verification = [
260
+ result.verification?.binding ? "connection bound" : null,
261
+ result.verification?.refresh?.status ? `refresh ${result.verification.refresh.status}` : null,
262
+ acceptance ? `acceptance query passed (${acceptance.rowCount} row${acceptance.rowCount === 1 ? "" : "s"})` : null,
263
+ ].filter(Boolean).join(" · ") || "incomplete";
264
+ const lines = [
265
+ "",
266
+ result.accepted
267
+ ? `Publish complete — ${report?.displayName ?? "the report"} is live.`
268
+ : `Publish finished — ${report?.displayName ?? "the report"} was deployed, but live verification is incomplete.`,
269
+ ...(reportUrl ? ["", "Open the report:", reportUrl] : []),
270
+ "",
271
+ `Workspace: ${result.workspace?.displayName ?? "Unknown"} (${result.workspace?.id ?? "unknown"})`,
272
+ ...(report ? [`Report: ${report.displayName} (${report.id})`] : []),
273
+ ...(semanticModel ? [`Semantic model: ${semanticModel.displayName} (${semanticModel.id})`] : []),
274
+ `Verification: ${verification}`,
275
+ `Plan: ${result.planId}`,
276
+ ...(result.receiptPath ? [`Receipt: ${result.receiptPath}`] : []),
277
+ "",
278
+ ];
279
+ return lines.join("\n");
280
+ }
281
+
282
+ function printPublishResult(result) { process.stdout.write(formatPublishResult(result)); }
283
+
284
+ async function applySavedDeployment({ loaded, planPath, confirm, backup, receipt: receiptOption, resume, timeout, onProgress = null }) {
228
285
  const resolvedPlanPath = path.resolve(planPath);
286
+ onProgress?.({ status: "running", step: "deployment-plan", message: "Loading and validating the authorized deployment plan" });
229
287
  const plan = JSON.parse(await readFile(resolvedPlanPath, "utf8"));
230
288
  if (confirm !== plan.planId) throw new Error(`Deployment requires exact plan confirmation ${plan.planId}`);
231
289
  if (receiptOption && resume && path.resolve(receiptOption) !== path.resolve(resume)) throw new Error("--receipt and --resume must identify the same file when both are supplied");
290
+ onProgress?.({ status: "passed", step: "deployment-plan", message: `Validated authorized plan ${plan.planId}` });
232
291
  const receiptPath = path.resolve(receiptOption ?? resume ?? path.join(loaded.root, ".supervisual", "deploy-receipt.json"));
233
292
  const receipt = resume
234
293
  ? JSON.parse(await readFile(path.resolve(resume), "utf8"))
@@ -236,6 +295,7 @@ async function applySavedDeployment({ loaded, planPath, confirm, backup, receipt
236
295
  await writeJsonAtomic(receiptPath, receipt);
237
296
  const client = new FabricHttpClient({ timeoutSeconds: Number(timeout) });
238
297
  try {
298
+ onProgress?.({ status: "running", step: "deployment", message: backup ? "Revalidating the target and preparing the recoverable deployment" : "Revalidating the target and applying the authorized deployment" });
239
299
  const result = await applyDeploymentPlan({
240
300
  client,
241
301
  plan,
@@ -247,6 +307,12 @@ async function applySavedDeployment({ loaded, planPath, confirm, backup, receipt
247
307
  receipt.status = "in-progress";
248
308
  receipt.updatedAt = new Date().toISOString();
249
309
  await writeJsonAtomic(receiptPath, receipt);
310
+ onProgress?.({ status: "passed", step: `deploy-${action}`, message: `${item.resumed ? "Verified" : item.operation === "update" ? "Updated" : "Created"} ${item.displayName}` });
311
+ onProgress?.({
312
+ status: "running",
313
+ step: action === "semanticModel" ? "deploy-report" : "live-verification",
314
+ message: action === "semanticModel" ? "Deploying the bound Power BI report" : "Binding the connection, refreshing the semantic model, and running acceptance checks",
315
+ });
250
316
  },
251
317
  });
252
318
  receipt.items = result.items;
@@ -254,6 +320,7 @@ async function applySavedDeployment({ loaded, planPath, confirm, backup, receipt
254
320
  receipt.completedAt = new Date().toISOString();
255
321
  receipt.verification = result.verification;
256
322
  await writeJsonAtomic(receiptPath, receipt);
323
+ onProgress?.({ status: result.accepted ? "passed" : "failed", step: "live-verification", message: result.accepted ? "Deployment and live verification completed successfully" : "Deployment completed but live verification is incomplete" });
257
324
  return { ...result, receiptPath, planPath: resolvedPlanPath };
258
325
  } catch (error) {
259
326
  receipt.status = "interrupted";
@@ -566,11 +633,17 @@ function createProgram() {
566
633
  .option("--resume <file>", "resume from a matching interrupted receipt")
567
634
  .option("--timeout <seconds>", "Fabric operation timeout", "900")
568
635
  .action(async (project = ".", options, command) => {
636
+ const json = jsonEnabled(command);
637
+ const onProgress = json ? null : writeCommandProgress;
638
+ if (!json) writeProgress(`\nSuper Visual publish\n\n`);
639
+ onProgress?.({ status: "running", step: "project", message: "Loading and validating the Super Visual project" });
569
640
  const loaded = await loadProject(projectFile(project));
641
+ onProgress?.({ status: "passed", step: "project", message: "Loaded and validated the Super Visual project" });
570
642
  const planPath = path.resolve(options.plan ?? path.join(loaded.root, ".supervisual", "deploy-plan.json"));
571
643
  if (options.confirm && options.preAuthorize) throw new Error("--confirm and --pre-authorize are mutually exclusive");
572
644
  if (options.confirm) {
573
- const result = await applySavedDeployment({ loaded, planPath, confirm: options.confirm, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout });
645
+ const result = await applySavedDeployment({ loaded, planPath, confirm: options.confirm, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout, onProgress });
646
+ onProgress?.({ status: "running", step: "deployment-state", message: "Saving the deployed Fabric item identities" });
574
647
  await writeProjectTarget(loaded.root, targetUpdate({
575
648
  workspaceId: result.workspace.id,
576
649
  workspaceName: result.workspace.displayName,
@@ -579,31 +652,40 @@ function createProgram() {
579
652
  reportId: result.items.report?.id,
580
653
  reportName: result.items.report?.displayName,
581
654
  }));
582
- output({ ok: result.accepted, ...result }, jsonEnabled(command));
655
+ onProgress?.({ status: "passed", step: "deployment-state", message: "Saved the deployed Fabric item identities" });
656
+ if (json) output({ ok: result.accepted, ...result }, true);
657
+ else printPublishResult(result);
583
658
  if (!result.accepted) process.exitCode = 3;
584
659
  return result;
585
660
  }
586
661
 
662
+ onProgress?.({ status: "running", step: "binding", message: "Loading the saved Fabric workspace and semantic-model binding" });
587
663
  const target = await resolveProjectTarget(loaded.root, options, { requireSemanticModel: true });
664
+ onProgress?.({ status: "passed", step: "binding", message: "Loaded the Fabric workspace and semantic-model binding" });
588
665
  const reportName = target.reportName ?? loaded.project.application.title;
589
666
  const semanticModelName = target.managedSemanticModelName ?? `${reportName} — Super Visual data`;
590
667
  const client = new FabricHttpClient({ timeoutSeconds: Number(options.timeout) });
591
668
  const selectedSemanticModelId = target.semanticModelId ?? target.managedSemanticModelId;
592
- const inspected = await inspectSemanticModel({ client, workspaceId: target.workspaceId, workspaceName: target.workspaceName, semanticModelId: selectedSemanticModelId });
669
+ const inspected = await inspectSemanticModel({ client, workspaceId: target.workspaceId, workspaceName: target.workspaceName, semanticModelId: selectedSemanticModelId, onProgress });
670
+ onProgress?.({ status: "running", step: "compatibility", message: "Checking application queries against the selected semantic model" });
593
671
  const compatibility = usesSemanticCatalog(loaded.broker)
594
672
  ? { ok: true, summary: { required: 1, matched: 1, missing: 0, ambiguous: 0 }, diagnostics: [] }
595
673
  : checkSemanticCompatibility(inspected.semanticModel, loaded.semanticModel, loaded.broker);
596
674
  if (!compatibility.ok) throw new Error(`Selected semantic model is incompatible:\n- ${compatibility.diagnostics.map((item) => item.message).join("\n- ")}`);
675
+ onProgress?.({ status: "passed", step: "compatibility", message: `Confirmed ${compatibility.summary.matched || compatibility.summary.required} semantic binding${compatibility.summary.required === 1 ? "" : "s"}` });
676
+ onProgress?.({ status: "running", step: "connection", message: "Resolving the selected semantic model's Fabric connection and source path" });
597
677
  const selectedConnection = selectedConnectionDefaults(await client.getSemanticConnections(target.workspaceId, selectedSemanticModelId));
598
678
  const connectionId = target.connectionId ?? selectedConnection?.connectionId;
599
679
  const connectionMode = target.connectionId ? target.connectionMode : selectedConnection?.connectionMode ?? target.connectionMode;
600
680
  const automaticParameters = options.parametersFile ? null : selectedConnection?.semanticParameters;
601
681
  if (!connectionId) throw new Error("The selected semantic model does not expose one reusable Fabric connection. Use the advanced --connection-id override.");
602
682
  if (!options.parametersFile && !automaticParameters) throw new Error("The selected semantic model source path cannot be reused automatically. Use the advanced --parameters-file override.");
603
- const packaged = await build(project, true, options.parametersFile, inspected.semanticModel, automaticParameters);
683
+ onProgress?.({ status: "passed", step: "connection", message: "Resolved the reusable Fabric connection and semantic source path" });
684
+ const packaged = await build(project, true, options.parametersFile, inspected.semanticModel, automaticParameters, onProgress);
604
685
  if (!packaged.ok) throw new Error("Packaged solution manifest verification failed");
605
686
  const acceptanceDax = options.acceptanceQueryFile ? await readFile(path.resolve(options.acceptanceQueryFile), "utf8") : 'EVALUATE ROW("Super Visual", 1)';
606
687
  const postDeployment = { connectionId, connectivityType: connectionMode, timeoutSeconds: Number(options.timeout), acceptanceDax };
688
+ onProgress?.({ status: "running", step: "plan", message: "Inspecting the target workspace and creating the immutable deployment plan" });
607
689
  const plan = await createDeploymentPlan({
608
690
  client,
609
691
  workspaceId: target.workspaceId,
@@ -614,6 +696,8 @@ function createProgram() {
614
696
  allowUpdates: Boolean(options.allowUpdates),
615
697
  postDeployment,
616
698
  });
699
+ onProgress?.({ status: plan.blockers.length ? "failed" : "passed", step: "plan", message: plan.blockers.length ? `Created a blocked deployment plan with ${plan.blockers.length} issue${plan.blockers.length === 1 ? "" : "s"}` : `Created immutable deployment plan ${plan.planId}` });
700
+ onProgress?.({ status: "running", step: "plan-state", message: "Saving the deployment plan and resolved Fabric target" });
617
701
  await writeJsonAtomic(planPath, plan);
618
702
  await writeProjectTarget(loaded.root, targetUpdate({
619
703
  workspaceId: target.workspaceId,
@@ -625,6 +709,7 @@ function createProgram() {
625
709
  connectionId,
626
710
  connectionMode,
627
711
  }));
712
+ onProgress?.({ status: "passed", step: "plan-state", message: `Saved the deployment plan to ${planPath}` });
628
713
  const result = {
629
714
  ok: plan.blockers.length === 0,
630
715
  status: plan.blockers.length ? "blocked" : "planned",
@@ -635,11 +720,12 @@ function createProgram() {
635
720
  next: plan.blockers.length ? null : `supervisual publish --confirm ${plan.planId}`,
636
721
  };
637
722
  if (!plan.blockers.length && options.preAuthorize) {
638
- if (!jsonEnabled(command)) {
723
+ if (!json) {
639
724
  printDeploymentPlan(plan);
640
725
  process.stdout.write("Pre-authorization accepted; applying this generated plan without a second prompt.\n");
641
726
  }
642
- const applied = await applySavedDeployment({ loaded, planPath, confirm: plan.planId, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout });
727
+ const applied = await applySavedDeployment({ loaded, planPath, confirm: plan.planId, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout, onProgress });
728
+ onProgress?.({ status: "running", step: "deployment-state", message: "Saving the deployed Fabric item identities" });
643
729
  await writeProjectTarget(loaded.root, targetUpdate({
644
730
  workspaceId: applied.workspace.id,
645
731
  workspaceName: applied.workspace.displayName,
@@ -650,14 +736,17 @@ function createProgram() {
650
736
  reportId: applied.items.report?.id,
651
737
  reportName: applied.items.report?.displayName,
652
738
  }));
653
- output({ ok: applied.accepted, preAuthorized: true, ...applied }, jsonEnabled(command));
739
+ onProgress?.({ status: "passed", step: "deployment-state", message: "Saved the deployed Fabric item identities" });
740
+ if (json) output({ ok: applied.accepted, preAuthorized: true, ...applied }, true);
741
+ else printPublishResult(applied);
654
742
  if (!applied.accepted) process.exitCode = 3;
655
743
  return applied;
656
744
  }
657
- if (!plan.blockers.length && !jsonEnabled(command) && process.stdin.isTTY && process.stdout.isTTY) {
745
+ if (!plan.blockers.length && !json && process.stdin.isTTY && process.stdout.isTTY) {
658
746
  const confirmed = await confirmDeploymentPlan(plan);
659
747
  if (confirmed) {
660
- const applied = await applySavedDeployment({ loaded, planPath, confirm: plan.planId, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout });
748
+ const applied = await applySavedDeployment({ loaded, planPath, confirm: plan.planId, backup: options.backup, receipt: options.receipt, resume: options.resume, timeout: options.timeout, onProgress });
749
+ onProgress?.({ status: "running", step: "deployment-state", message: "Saving the deployed Fabric item identities" });
661
750
  await writeProjectTarget(loaded.root, targetUpdate({
662
751
  workspaceId: applied.workspace.id,
663
752
  workspaceName: applied.workspace.displayName,
@@ -668,14 +757,15 @@ function createProgram() {
668
757
  reportId: applied.items.report?.id,
669
758
  reportName: applied.items.report?.displayName,
670
759
  }));
671
- output({ ok: applied.accepted, ...applied }, false);
760
+ onProgress?.({ status: "passed", step: "deployment-state", message: "Saved the deployed Fabric item identities" });
761
+ printPublishResult(applied);
672
762
  if (!applied.accepted) process.exitCode = 3;
673
763
  return applied;
674
764
  }
675
765
  output(`Publish cancelled. The reviewed plan is saved at ${planPath}.`, false);
676
766
  return result;
677
767
  }
678
- output(result, jsonEnabled(command));
768
+ output(result, json);
679
769
  if (plan.blockers.length) process.exitCode = 2;
680
770
  return result;
681
771
  });
@@ -10,6 +10,6 @@
10
10
  "verify": "supervisual verify ."
11
11
  },
12
12
  "devDependencies": {
13
- "@datashow/super-visual-cli": "0.1.0-rc.5"
13
+ "@datashow/super-visual-cli": "0.1.0-rc.7"
14
14
  }
15
15
  }
@@ -10,6 +10,6 @@
10
10
  "verify": "supervisual verify ."
11
11
  },
12
12
  "devDependencies": {
13
- "@datashow/super-visual-cli": "0.1.0-rc.5"
13
+ "@datashow/super-visual-cli": "0.1.0-rc.7"
14
14
  }
15
15
  }
@@ -165,8 +165,12 @@ class SemanticPreviewHost {
165
165
  }
166
166
  }
167
167
 
168
- document.documentElement.style.height = "100%";
169
- document.body.style.cssText = "height:100%;margin:0";
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();