@dbx-tools/genie 0.9.187 → 0.9.189

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.md CHANGED
@@ -183,6 +183,42 @@ thrown.
183
183
  - `pollIntervalMs` - fallback polling cadence, default `500`.
184
184
  - `context` - SDK `Context` or `AbortSignal` for cancellation.
185
185
 
186
+ ## Download a chart image
187
+
188
+ After requesting visualizations, select the message attachment with a `viz`
189
+ field and download its PNG using the same conversation and message IDs:
190
+
191
+ ```ts
192
+ import { downloadGenieVisualization } from "@dbx-tools/genie";
193
+
194
+ const visualization = message.attachments?.find((attachment) => attachment.viz);
195
+ if (visualization?.attachment_id) {
196
+ const png = await downloadGenieVisualization(
197
+ {
198
+ space_id: message.space_id,
199
+ conversation_id: message.conversation_id,
200
+ message_id: message.message_id,
201
+ attachment_id: visualization.attachment_id,
202
+ },
203
+ { workspaceClient },
204
+ );
205
+ }
206
+ ```
207
+
208
+ Use the visualization's attachment ID, not its linked query attachment ID.
209
+ The SDK's `raw: true` transport returns the binary body and keeps normal SDK
210
+ authentication and cancellation. The helper validates the PNG response, then
211
+ uses the optional `sharp` dependency to resize it to four times its original
212
+ width and height with Lanczos3 and apply a mild unsharp mask. If `sharp` is not
213
+ installed or cannot process the image, the helper returns the validated
214
+ original bytes. Processing happens before the bytes return to callers, so a
215
+ downstream cache stores the enhanced image when enhancement succeeds.
216
+
217
+ This is a raster cleanup fallback. If the source ECharts instance is available,
218
+ prefer `getDataURL({ pixelRatio: 3 })` or SVG so chart text is re-rendered rather
219
+ than enlarged from a screenshot. The endpoint also accepts visualization
220
+ attachments from Agent Mode conversations.
221
+
186
222
  ## Modules
187
223
 
188
224
  - `chat` - `genieChat()` raw snapshot stream and `genieEventChat()` typed event
@@ -190,6 +226,7 @@ thrown.
190
226
  - `agentMode` - `genieAgentModeChat()`, availability classification, and Agent
191
227
  Mode transport options plus Agent Mode conversation history.
192
228
  - `space` - `getGenieSpace()` and `genieSampleQuestions()`.
229
+ - `visualization` - `downloadGenieVisualization()` for validated and optionally enhanced PNG downloads.
193
230
 
194
231
  The AppKit-Mastra package builds its Genie tools on top of this driver. See
195
232
  [@dbx-tools/appkit-mastra](../appkit-mastra) for the agent-level
package/index.ts CHANGED
@@ -3,11 +3,12 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/genie";
6
- export const PACKAGE_VERSION = "0.9.187";
6
+ export const PACKAGE_VERSION = "0.9.189";
7
7
  export * as agentMode from "./src/agent-mode.ts";
8
8
  export * as chat from "./src/chat.ts";
9
9
  export * as feedback from "./src/feedback.ts";
10
10
  export * as space from "./src/space.ts";
11
+ export * as visualization from "./src/visualization.ts";
11
12
  export { genieAgentModeChat, listAgentModeConversation, isAgentModeUnavailable } from "./src/agent-mode.ts";
12
13
  export type { GenieAgentModeOptions } from "./src/agent-mode.ts";
13
14
  export { genieChat, genieEventChat } from "./src/chat.ts";
@@ -16,3 +17,5 @@ export { sendGenieMessageFeedback } from "./src/feedback.ts";
16
17
  export type { SendGenieMessageFeedbackOptions } from "./src/feedback.ts";
17
18
  export { createGenieSpace, getGenieSpace, genieSampleQuestions } from "./src/space.ts";
18
19
  export type { GetGenieSpaceOptions, CreateGenieSpaceOptions } from "./src/space.ts";
20
+ export { upscaleGenieVisualizationPng, downloadGenieVisualization } from "./src/visualization.ts";
21
+ export type { DownloadGenieVisualizationOptions } from "./src/visualization.ts";
package/lib/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  export declare const PACKAGE_IDENTIFIER = "@dbx-tools/genie";
2
- export declare const PACKAGE_VERSION = "0.9.187";
2
+ export declare const PACKAGE_VERSION = "0.9.189";
3
3
  export * as agentMode from "./src/agent-mode.ts";
4
4
  export * as chat from "./src/chat.ts";
5
5
  export * as feedback from "./src/feedback.ts";
6
6
  export * as space from "./src/space.ts";
7
+ export * as visualization from "./src/visualization.ts";
7
8
  export { genieAgentModeChat, listAgentModeConversation, isAgentModeUnavailable } from "./src/agent-mode.ts";
8
9
  export type { GenieAgentModeOptions } from "./src/agent-mode.ts";
9
10
  export { genieChat, genieEventChat } from "./src/chat.ts";
@@ -12,3 +13,5 @@ export { sendGenieMessageFeedback } from "./src/feedback.ts";
12
13
  export type { SendGenieMessageFeedbackOptions } from "./src/feedback.ts";
13
14
  export { createGenieSpace, getGenieSpace, genieSampleQuestions } from "./src/space.ts";
14
15
  export type { GetGenieSpaceOptions, CreateGenieSpaceOptions } from "./src/space.ts";
16
+ export { upscaleGenieVisualizationPng, downloadGenieVisualization } from "./src/visualization.ts";
17
+ export type { DownloadGenieVisualizationOptions } from "./src/visualization.ts";
package/lib/index.js CHANGED
@@ -2,13 +2,15 @@
2
2
  // Regenerated from the exporting modules in ./src.
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
  export const PACKAGE_IDENTIFIER = "@dbx-tools/genie";
5
- export const PACKAGE_VERSION = "0.9.187";
5
+ export const PACKAGE_VERSION = "0.9.189";
6
6
  export * as agentMode from "./src/agent-mode.js";
7
7
  export * as chat from "./src/chat.js";
8
8
  export * as feedback from "./src/feedback.js";
9
9
  export * as space from "./src/space.js";
10
+ export * as visualization from "./src/visualization.js";
10
11
  export { genieAgentModeChat, listAgentModeConversation, isAgentModeUnavailable } from "./src/agent-mode.js";
11
12
  export { genieChat, genieEventChat } from "./src/chat.js";
12
13
  export { sendGenieMessageFeedback } from "./src/feedback.js";
13
14
  export { createGenieSpace, getGenieSpace, genieSampleQuestions } from "./src/space.js";
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUM5QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTVHLE9BQU8sRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTFELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRTdELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxhQUFhLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCBjb25zdCBQQUNLQUdFX0lERU5USUZJRVIgPSBcIkBkYngtdG9vbHMvZ2VuaWVcIjtcbmV4cG9ydCBjb25zdCBQQUNLQUdFX1ZFUlNJT04gPSBcIjAuOS4xODdcIjtcbmV4cG9ydCAqIGFzIGFnZW50TW9kZSBmcm9tIFwiLi9zcmMvYWdlbnQtbW9kZS50c1wiO1xuZXhwb3J0ICogYXMgY2hhdCBmcm9tIFwiLi9zcmMvY2hhdC50c1wiO1xuZXhwb3J0ICogYXMgZmVlZGJhY2sgZnJvbSBcIi4vc3JjL2ZlZWRiYWNrLnRzXCI7XG5leHBvcnQgKiBhcyBzcGFjZSBmcm9tIFwiLi9zcmMvc3BhY2UudHNcIjtcbmV4cG9ydCB7IGdlbmllQWdlbnRNb2RlQ2hhdCwgbGlzdEFnZW50TW9kZUNvbnZlcnNhdGlvbiwgaXNBZ2VudE1vZGVVbmF2YWlsYWJsZSB9IGZyb20gXCIuL3NyYy9hZ2VudC1tb2RlLnRzXCI7XG5leHBvcnQgdHlwZSB7IEdlbmllQWdlbnRNb2RlT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9hZ2VudC1tb2RlLnRzXCI7XG5leHBvcnQgeyBnZW5pZUNoYXQsIGdlbmllRXZlbnRDaGF0IH0gZnJvbSBcIi4vc3JjL2NoYXQudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2VuaWVDaGF0T3B0aW9ucyB9IGZyb20gXCIuL3NyYy9jaGF0LnRzXCI7XG5leHBvcnQgeyBzZW5kR2VuaWVNZXNzYWdlRmVlZGJhY2sgfSBmcm9tIFwiLi9zcmMvZmVlZGJhY2sudHNcIjtcbmV4cG9ydCB0eXBlIHsgU2VuZEdlbmllTWVzc2FnZUZlZWRiYWNrT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9mZWVkYmFjay50c1wiO1xuZXhwb3J0IHsgY3JlYXRlR2VuaWVTcGFjZSwgZ2V0R2VuaWVTcGFjZSwgZ2VuaWVTYW1wbGVRdWVzdGlvbnMgfSBmcm9tIFwiLi9zcmMvc3BhY2UudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2V0R2VuaWVTcGFjZU9wdGlvbnMsIENyZWF0ZUdlbmllU3BhY2VPcHRpb25zIH0gZnJvbSBcIi4vc3JjL3NwYWNlLnRzXCI7XG4iXX0=
15
+ export { upscaleGenieVisualizationPng, downloadGenieVisualization } from "./src/visualization.js";
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztBQUNyRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUM5QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sS0FBSyxhQUFhLE1BQU0sd0JBQXdCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLHlCQUF5QixFQUFFLHNCQUFzQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFNUcsT0FBTyxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFMUQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFN0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLGFBQWEsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXZGLE9BQU8sRUFBRSw0QkFBNEIsRUFBRSwwQkFBMEIsRUFBRSxNQUFNLHdCQUF3QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9nZW5pZVwiO1xuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfVkVSU0lPTiA9IFwiMC45LjE4OVwiO1xuZXhwb3J0ICogYXMgYWdlbnRNb2RlIGZyb20gXCIuL3NyYy9hZ2VudC1tb2RlLnRzXCI7XG5leHBvcnQgKiBhcyBjaGF0IGZyb20gXCIuL3NyYy9jaGF0LnRzXCI7XG5leHBvcnQgKiBhcyBmZWVkYmFjayBmcm9tIFwiLi9zcmMvZmVlZGJhY2sudHNcIjtcbmV4cG9ydCAqIGFzIHNwYWNlIGZyb20gXCIuL3NyYy9zcGFjZS50c1wiO1xuZXhwb3J0ICogYXMgdmlzdWFsaXphdGlvbiBmcm9tIFwiLi9zcmMvdmlzdWFsaXphdGlvbi50c1wiO1xuZXhwb3J0IHsgZ2VuaWVBZ2VudE1vZGVDaGF0LCBsaXN0QWdlbnRNb2RlQ29udmVyc2F0aW9uLCBpc0FnZW50TW9kZVVuYXZhaWxhYmxlIH0gZnJvbSBcIi4vc3JjL2FnZW50LW1vZGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2VuaWVBZ2VudE1vZGVPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2FnZW50LW1vZGUudHNcIjtcbmV4cG9ydCB7IGdlbmllQ2hhdCwgZ2VuaWVFdmVudENoYXQgfSBmcm9tIFwiLi9zcmMvY2hhdC50c1wiO1xuZXhwb3J0IHR5cGUgeyBHZW5pZUNoYXRPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2NoYXQudHNcIjtcbmV4cG9ydCB7IHNlbmRHZW5pZU1lc3NhZ2VGZWVkYmFjayB9IGZyb20gXCIuL3NyYy9mZWVkYmFjay50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZW5kR2VuaWVNZXNzYWdlRmVlZGJhY2tPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2ZlZWRiYWNrLnRzXCI7XG5leHBvcnQgeyBjcmVhdGVHZW5pZVNwYWNlLCBnZXRHZW5pZVNwYWNlLCBnZW5pZVNhbXBsZVF1ZXN0aW9ucyB9IGZyb20gXCIuL3NyYy9zcGFjZS50c1wiO1xuZXhwb3J0IHR5cGUgeyBHZXRHZW5pZVNwYWNlT3B0aW9ucywgQ3JlYXRlR2VuaWVTcGFjZU9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvc3BhY2UudHNcIjtcbmV4cG9ydCB7IHVwc2NhbGVHZW5pZVZpc3VhbGl6YXRpb25QbmcsIGRvd25sb2FkR2VuaWVWaXN1YWxpemF0aW9uIH0gZnJvbSBcIi4vc3JjL3Zpc3VhbGl6YXRpb24udHNcIjtcbmV4cG9ydCB0eXBlIHsgRG93bmxvYWRHZW5pZVZpc3VhbGl6YXRpb25PcHRpb25zIH0gZnJvbSBcIi4vc3JjL3Zpc3VhbGl6YXRpb24udHNcIjtcbiJdfQ==
@@ -0,0 +1,22 @@
1
+ import { type WorkspaceClient } from "@databricks/appkit";
2
+ import { type ContextLike } from "@dbx-tools/appkit";
3
+ import { type GenieVisualizationRequest } from "@dbx-tools/shared-genie";
4
+ /**
5
+ * Resize a Genie screenshot to four times its source dimensions with Lanczos3,
6
+ * then apply a mild unsharp mask. Returns the original bytes when the optional
7
+ * Sharp runtime is unavailable or rejects the image.
8
+ */
9
+ export declare function upscaleGenieVisualizationPng(inputBytes: Uint8Array): Promise<Uint8Array>;
10
+ /** Identity and cancellation for a Genie visualization download. */
11
+ export interface DownloadGenieVisualizationOptions {
12
+ /** Workspace client whose identity owns the conversation. */
13
+ workspaceClient?: WorkspaceClient;
14
+ /** External cancellation for the request and image body. */
15
+ context?: ContextLike;
16
+ }
17
+ /**
18
+ * Download Genie's PNG and apply optional raster cleanup before returning it.
19
+ * The SDK's raw transport preserves binary responses and its normal authentication;
20
+ * source-chart exports should still be preferred when they are available.
21
+ */
22
+ export declare function downloadGenieVisualization(input: GenieVisualizationRequest, options?: DownloadGenieVisualizationOptions): Promise<Uint8Array>;
@@ -0,0 +1,94 @@
1
+ import { createWorkspaceClient } from "@databricks/appkit";
2
+ import { toContext } from "@dbx-tools/appkit";
3
+ import { isRecord, log, toError } from "@dbx-tools/shared-core";
4
+ import { genieVisualizationDownloadPath, } from "@dbx-tools/shared-genie";
5
+ const logger = log.logger("genie/visualization");
6
+ const GENIE_PNG_SCALE = 4;
7
+ const GENIE_PNG_SHARPEN = { sigma: 1.1, m1: 0.9, m2: 0.02 };
8
+ let sharpFactoryPromise;
9
+ function isByteStream(value) {
10
+ return (isRecord(value) && typeof value.getReader === "function" && typeof value.cancel === "function");
11
+ }
12
+ async function loadSharp() {
13
+ sharpFactoryPromise ??= import("sharp")
14
+ .then((module) => module.default)
15
+ .catch((error) => {
16
+ logger.debug("PNG enhancement unavailable; serving the original Genie image", {
17
+ error: toError(error),
18
+ });
19
+ return undefined;
20
+ });
21
+ return sharpFactoryPromise;
22
+ }
23
+ /**
24
+ * Resize a Genie screenshot to four times its source dimensions with Lanczos3,
25
+ * then apply a mild unsharp mask. Returns the original bytes when the optional
26
+ * Sharp runtime is unavailable or rejects the image.
27
+ */
28
+ export async function upscaleGenieVisualizationPng(inputBytes) {
29
+ const sharp = await loadSharp();
30
+ if (!sharp)
31
+ return inputBytes;
32
+ try {
33
+ const input = Buffer.from(inputBytes);
34
+ const metadata = await sharp(input).metadata();
35
+ if (!metadata.width || !metadata.height) {
36
+ throw new Error("Could not read Genie visualization dimensions");
37
+ }
38
+ const output = await sharp(input)
39
+ .resize({
40
+ width: metadata.width * GENIE_PNG_SCALE,
41
+ height: metadata.height * GENIE_PNG_SCALE,
42
+ kernel: sharp.kernel.lanczos3,
43
+ fit: "fill",
44
+ })
45
+ .sharpen(GENIE_PNG_SHARPEN)
46
+ .png()
47
+ .toBuffer();
48
+ return new Uint8Array(output);
49
+ }
50
+ catch (error) {
51
+ logger.warn("PNG enhancement failed; serving the original Genie image", {
52
+ error: toError(error),
53
+ });
54
+ return inputBytes;
55
+ }
56
+ }
57
+ /**
58
+ * Download Genie's PNG and apply optional raster cleanup before returning it.
59
+ * The SDK's raw transport preserves binary responses and its normal authentication;
60
+ * source-chart exports should still be preferred when they are available.
61
+ */
62
+ export async function downloadGenieVisualization(input, options = {}) {
63
+ const client = options.workspaceClient ?? createWorkspaceClient();
64
+ const response = await client.apiClient.request({
65
+ path: genieVisualizationDownloadPath(input),
66
+ method: "GET",
67
+ headers: new Headers({ accept: "image/png" }),
68
+ responseHeaders: ["content-type"],
69
+ raw: true,
70
+ }, options.context ? toContext(options.context) : undefined);
71
+ if (!isRecord(response) ||
72
+ typeof response["content-type"] !== "string" ||
73
+ response["content-type"].split(";")[0]?.trim().toLowerCase() !== "image/png" ||
74
+ !isByteStream(response.contents)) {
75
+ if (isRecord(response) && isByteStream(response.contents)) {
76
+ await response.contents.cancel().catch(() => undefined);
77
+ }
78
+ throw new TypeError("Genie did not return a PNG visualization");
79
+ }
80
+ const bytes = new Uint8Array(await new Response(response.contents).arrayBuffer());
81
+ if (bytes.length < 8 ||
82
+ bytes[0] !== 0x89 ||
83
+ bytes[1] !== 0x50 ||
84
+ bytes[2] !== 0x4e ||
85
+ bytes[3] !== 0x47 ||
86
+ bytes[4] !== 0x0d ||
87
+ bytes[5] !== 0x0a ||
88
+ bytes[6] !== 0x1a ||
89
+ bytes[7] !== 0x0a) {
90
+ throw new TypeError("Genie returned an invalid PNG visualization");
91
+ }
92
+ return upscaleGenieVisualizationPng(bytes);
93
+ }
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXphdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92aXN1YWxpemF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxxQkFBcUIsRUFBd0IsTUFBTSxvQkFBb0IsQ0FBQztBQUNqRixPQUFPLEVBQW9CLFNBQVMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2hFLE9BQU8sRUFFTCw4QkFBOEIsR0FDL0IsTUFBTSx5QkFBeUIsQ0FBQztBQUVqQyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLHFCQUFxQixDQUFDLENBQUM7QUFDakQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxDQUFDO0FBQzFCLE1BQU0saUJBQWlCLEdBQUcsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBVyxDQUFDO0FBSXJFLElBQUksbUJBQWtFLENBQUM7QUFFdkUsU0FBUyxZQUFZLENBQUMsS0FBYztJQUNsQyxPQUFPLENBQ0wsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLE9BQU8sS0FBSyxDQUFDLFNBQVMsS0FBSyxVQUFVLElBQUksT0FBTyxLQUFLLENBQUMsTUFBTSxLQUFLLFVBQVUsQ0FDL0YsQ0FBQztBQUNKLENBQUM7QUFFRCxLQUFLLFVBQVUsU0FBUztJQUN0QixtQkFBbUIsS0FBSyxNQUFNLENBQUMsT0FBTyxDQUFDO1NBQ3BDLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQztTQUNoQyxLQUFLLENBQUMsQ0FBQyxLQUFjLEVBQUUsRUFBRTtRQUN4QixNQUFNLENBQUMsS0FBSyxDQUFDLCtEQUErRCxFQUFFO1lBQzVFLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxD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RyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUNELE1BQU0sSUFBSSxTQUFTLENBQUMsMENBQTBDLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxVQUFVLENBQUMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUNsRixJQUNFLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUNoQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSTtRQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUNqQixDQUFDO1FBQ0QsTUFBTSxJQUFJLFNBQVMsQ0FBQyw2Q0FBNkMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFDRCxPQUFPLDRCQUE0QixDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzdDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGVXb3Jrc3BhY2VDbGllbnQsIHR5cGUgV29ya3NwYWNlQ2xpZW50IH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgdHlwZSBDb250ZXh0TGlrZSwgdG9Db250ZXh0IH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5pbXBvcnQgeyBpc1JlY29yZCwgbG9nLCB0b0Vycm9yIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7XG4gIHR5cGUgR2VuaWVWaXN1YWxpemF0aW9uUmVxdWVzdCxcbiAgZ2VuaWVWaXN1YWxpemF0aW9uRG93bmxvYWRQYXRoLFxufSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZ2VuaWVcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImdlbmllL3Zpc3VhbGl6YXRpb25cIik7XG5jb25zdCBHRU5JRV9QTkdfU0NBTEUgPSA0O1xuY29uc3QgR0VOSUVfUE5HX1NIQVJQRU4gPSB7IHNpZ21hOiAxLjEsIG0xOiAwLjksIG0yOiAwLjAyIH0gYXMgY29uc3Q7XG5cbnR5cGUgU2hhcnBGYWN0b3J5ID0gKHR5cGVvZiBpbXBvcnQoXCJzaGFycFwiKSlbXCJkZWZhdWx0XCJdO1xuXG5sZXQgc2hhcnBGYWN0b3J5UHJvbWlzZTogUHJvbWlzZTxTaGFycEZhY3RvcnkgfCB1bmRlZmluZWQ+IHwgdW5kZWZpbmVkO1xuXG5mdW5jdGlvbiBpc0J5dGVTdHJlYW0odmFsdWU6IHVua25vd24pOiB2YWx1ZSBpcyBSZWFkYWJsZVN0cmVhbTxVaW50OEFycmF5PiB7XG4gIHJldHVybiAoXG4gICAgaXNSZWNvcmQodmFsdWUpICYmIHR5cGVvZiB2YWx1ZS5nZXRSZWFkZXIgPT09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2YgdmFsdWUuY2FuY2VsID09PSBcImZ1bmN0aW9uXCJcbiAgKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbG9hZFNoYXJwKCk6IFByb21pc2U8U2hhcnBGYWN0b3J5IHwgdW5kZWZpbmVkPiB7XG4gIHNoYXJwRmFjdG9yeVByb21pc2UgPz89IGltcG9ydChcInNoYXJwXCIpXG4gICAgLnRoZW4oKG1vZHVsZSkgPT4gbW9kdWxlLmRlZmF1bHQpXG4gICAgLmNhdGNoKChlcnJvcjogdW5rbm93bikgPT4ge1xuICAgICAgbG9nZ2VyLmRlYnVnKFwiUE5HIGVuaGFuY2VtZW50IHVuYXZhaWxhYmxlOyBzZXJ2aW5nIHRoZSBvcmlnaW5hbCBHZW5pZSBpbWFnZVwiLCB7XG4gICAgICAgIGVycm9yOiB0b0Vycm9yKGVycm9yKSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9KTtcbiAgcmV0dXJuIHNoYXJwRmFjdG9yeVByb21pc2U7XG59XG5cbi8qKlxuICogUmVzaXplIGEgR2VuaWUgc2NyZWVuc2hvdCB0byBmb3VyIHRpbWVzIGl0cyBzb3VyY2UgZGltZW5zaW9ucyB3aXRoIExhbmN6b3MzLFxuICogdGhlbiBhcHBseSBhIG1pbGQgdW5zaGFycCBtYXNrLiBSZXR1cm5zIHRoZSBvcmlnaW5hbCBieXRlcyB3aGVuIHRoZSBvcHRpb25hbFxuICogU2hhcnAgcnVudGltZSBpcyB1bmF2YWlsYWJsZSBvciByZWplY3RzIHRoZSBpbWFnZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwc2NhbGVHZW5pZVZpc3VhbGl6YXRpb25QbmcoaW5wdXRCeXRlczogVWludDhBcnJheSk6IFByb21pc2U8VWludDhBcnJheT4ge1xuICBjb25zdCBzaGFycCA9IGF3YWl0IGxvYWRTaGFycCgpO1xuICBpZiAoIXNoYXJwKSByZXR1cm4gaW5wdXRCeXRlcztcbiAgdHJ5IHtcbiAgICBjb25zdCBpbnB1dCA9IEJ1ZmZlci5mcm9tKGlucHV0Qnl0ZXMpO1xuICAgIGNvbnN0IG1ldGFkYXRhID0gYXdhaXQgc2hhcnAoaW5wdXQpLm1ldGFkYXRhKCk7XG4gICAgaWYgKCFtZXRhZGF0YS53aWR0aCB8fCAhbWV0YWRhdGEuaGVpZ2h0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb3VsZCBub3QgcmVhZCBHZW5pZSB2aXN1YWxpemF0aW9uIGRpbWVuc2lvbnNcIik7XG4gICAgfVxuICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IHNoYXJwKGlucHV0KVxuICAgICAgLnJlc2l6ZSh7XG4gICAgICAgIHdpZHRoOiBtZXRhZGF0YS53aWR0aCAqIEdFTklFX1BOR19TQ0FMRSxcbiAgICAgICAgaGVpZ2h0OiBtZXRhZGF0YS5oZWlnaHQgKiBHRU5JRV9QTkdfU0NBTEUsXG4gICAgICAgIGtlcm5lbDogc2hhcnAua2VybmVsLmxhbmN6b3MzLFxuICAgICAgICBmaXQ6IFwiZmlsbFwiLFxuICAgICAgfSlcbiAgICAgIC5zaGFycGVuKEdFTklFX1BOR19TSEFSUEVOKVxuICAgICAgLnBuZygpXG4gICAgICAudG9CdWZmZXIoKTtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkob3V0cHV0KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dnZXIud2FybihcIlBORyBlbmhhbmNlbWVudCBmYWlsZWQ7IHNlcnZpbmcgdGhlIG9yaWdpbmFsIEdlbmllIGltYWdlXCIsIHtcbiAgICAgIGVycm9yOiB0b0Vycm9yKGVycm9yKSxcbiAgICB9KTtcbiAgICByZXR1cm4gaW5wdXRCeXRlcztcbiAgfVxufVxuXG4vKiogSWRlbnRpdHkgYW5kIGNhbmNlbGxhdGlvbiBmb3IgYSBHZW5pZSB2aXN1YWxpemF0aW9uIGRvd25sb2FkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBEb3dubG9hZEdlbmllVmlzdWFsaXphdGlvbk9wdGlvbnMge1xuICAvKiogV29ya3NwYWNlIGNsaWVudCB3aG9zZSBpZGVudGl0eSBvd25zIHRoZSBjb252ZXJzYXRpb24uICovXG4gIHdvcmtzcGFjZUNsaWVudD86IFdvcmtzcGFjZUNsaWVudDtcbiAgLyoqIEV4dGVybmFsIGNhbmNlbGxhdGlvbiBmb3IgdGhlIHJlcXVlc3QgYW5kIGltYWdlIGJvZHkuICovXG4gIGNvbnRleHQ/OiBDb250ZXh0TGlrZTtcbn1cblxuLyoqXG4gKiBEb3dubG9hZCBHZW5pZSdzIFBORyBhbmQgYXBwbHkgb3B0aW9uYWwgcmFzdGVyIGNsZWFudXAgYmVmb3JlIHJldHVybmluZyBpdC5cbiAqIFRoZSBTREsncyByYXcgdHJhbnNwb3J0IHByZXNlcnZlcyBiaW5hcnkgcmVzcG9uc2VzIGFuZCBpdHMgbm9ybWFsIGF1dGhlbnRpY2F0aW9uO1xuICogc291cmNlLWNoYXJ0IGV4cG9ydHMgc2hvdWxkIHN0aWxsIGJlIHByZWZlcnJlZCB3aGVuIHRoZXkgYXJlIGF2YWlsYWJsZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRvd25sb2FkR2VuaWVWaXN1YWxpemF0aW9uKFxuICBpbnB1dDogR2VuaWVWaXN1YWxpemF0aW9uUmVxdWVzdCxcbiAgb3B0aW9uczogRG93bmxvYWRHZW5pZVZpc3VhbGl6YXRpb25PcHRpb25zID0ge30sXG4pOiBQcm9taXNlPFVpbnQ4QXJyYXk+IHtcbiAgY29uc3QgY2xpZW50ID0gb3B0aW9ucy53b3Jrc3BhY2VDbGllbnQgPz8gY3JlYXRlV29ya3NwYWNlQ2xpZW50KCk7XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LmFwaUNsaWVudC5yZXF1ZXN0KFxuICAgIHtcbiAgICAgIHBhdGg6IGdlbmllVmlzdWFsaXphdGlvbkRvd25sb2FkUGF0aChpbnB1dCksXG4gICAgICBtZXRob2Q6IFwiR0VUXCIsXG4gICAgICBoZWFkZXJzOiBuZXcgSGVhZGVycyh7IGFjY2VwdDogXCJpbWFnZS9wbmdcIiB9KSxcbiAgICAgIHJlc3BvbnNlSGVhZGVyczogW1wiY29udGVudC10eXBlXCJdLFxuICAgICAgcmF3OiB0cnVlLFxuICAgIH0sXG4gICAgb3B0aW9ucy5jb250ZXh0ID8gdG9Db250ZXh0KG9wdGlvbnMuY29udGV4dCkgOiB1bmRlZmluZWQsXG4gICk7XG4gIGlmIChcbiAgICAhaXNSZWNvcmQocmVzcG9uc2UpIHx8XG4gICAgdHlwZW9mIHJlc3BvbnNlW1wiY29udGVudC10eXBlXCJdICE9PSBcInN0cmluZ1wiIHx8XG4gICAgcmVzcG9uc2VbXCJjb250ZW50LXR5cGVcIl0uc3BsaXQoXCI7XCIpWzBdPy50cmltKCkudG9Mb3dlckNhc2UoKSAhPT0gXCJpbWFnZS9wbmdcIiB8fFxuICAgICFpc0J5dGVTdHJlYW0ocmVzcG9uc2UuY29udGVudHMpXG4gICkge1xuICAgIGlmIChpc1JlY29yZChyZXNwb25zZSkgJiYgaXNCeXRlU3RyZWFtKHJlc3BvbnNlLmNvbnRlbnRzKSkge1xuICAgICAgYXdhaXQgcmVzcG9uc2UuY29udGVudHMuY2FuY2VsKCkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICB9XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkdlbmllIGRpZCBub3QgcmV0dXJuIGEgUE5HIHZpc3VhbGl6YXRpb25cIik7XG4gIH1cbiAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheShhd2FpdCBuZXcgUmVzcG9uc2UocmVzcG9uc2UuY29udGVudHMpLmFycmF5QnVmZmVyKCkpO1xuICBpZiAoXG4gICAgYnl0ZXMubGVuZ3RoIDwgOCB8fFxuICAgIGJ5dGVzWzBdICE9PSAweDg5IHx8XG4gICAgYnl0ZXNbMV0gIT09IDB4NTAgfHxcbiAgICBieXRlc1syXSAhPT0gMHg0ZSB8fFxuICAgIGJ5dGVzWzNdICE9PSAweDQ3IHx8XG4gICAgYnl0ZXNbNF0gIT09IDB4MGQgfHxcbiAgICBieXRlc1s1XSAhPT0gMHgwYSB8fFxuICAgIGJ5dGVzWzZdICE9PSAweDFhIHx8XG4gICAgYnl0ZXNbN10gIT09IDB4MGFcbiAgKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkdlbmllIHJldHVybmVkIGFuIGludmFsaWQgUE5HIHZpc3VhbGl6YXRpb25cIik7XG4gIH1cbiAgcmV0dXJuIHVwc2NhbGVHZW5pZVZpc3VhbGl6YXRpb25QbmcoYnl0ZXMpO1xufVxuIl19
@@ -1 +1 @@
1
- {"root":["../src/agent-mode.ts","../src/chat.ts","../src/feedback.ts","../src/space.ts","../index.ts"],"version":"5.9.3"}
1
+ {"root":["../src/agent-mode.ts","../src/chat.ts","../src/feedback.ts","../src/space.ts","../src/visualization.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -20,13 +20,17 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^24.6.0",
23
+ "sharp": "^0.35.5",
23
24
  "typescript": "^5.9.3"
24
25
  },
26
+ "peerDependencies": {
27
+ "sharp": "^0.35.5"
28
+ },
25
29
  "dependencies": {
26
30
  "@databricks/appkit": "0.81.0",
27
- "@dbx-tools/appkit": "^0.9.187",
28
- "@dbx-tools/shared-core": "^0.9.187",
29
- "@dbx-tools/shared-genie": "^0.9.187"
31
+ "@dbx-tools/appkit": "0.9.189",
32
+ "@dbx-tools/shared-core": "0.9.189",
33
+ "@dbx-tools/shared-genie": "0.9.189"
30
34
  },
31
35
  "main": "./lib/index.js",
32
36
  "license": "Apache-2.0",
@@ -42,7 +46,7 @@
42
46
  "./package.json": "./package.json"
43
47
  }
44
48
  },
45
- "version": "0.9.187",
49
+ "version": "0.9.189",
46
50
  "types": "./lib/index.d.ts",
47
51
  "dbxToolsConfig": {
48
52
  "tags": [
@@ -62,5 +66,10 @@
62
66
  "src",
63
67
  "lib"
64
68
  ],
69
+ "peerDependenciesMeta": {
70
+ "sharp": {
71
+ "optional": true
72
+ }
73
+ },
65
74
  "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
66
75
  }
@@ -0,0 +1,122 @@
1
+ import { createWorkspaceClient, type WorkspaceClient } from "@databricks/appkit";
2
+ import { type ContextLike, toContext } from "@dbx-tools/appkit";
3
+ import { isRecord, log, toError } from "@dbx-tools/shared-core";
4
+ import {
5
+ type GenieVisualizationRequest,
6
+ genieVisualizationDownloadPath,
7
+ } from "@dbx-tools/shared-genie";
8
+
9
+ const logger = log.logger("genie/visualization");
10
+ const GENIE_PNG_SCALE = 4;
11
+ const GENIE_PNG_SHARPEN = { sigma: 1.1, m1: 0.9, m2: 0.02 } as const;
12
+
13
+ type SharpFactory = (typeof import("sharp"))["default"];
14
+
15
+ let sharpFactoryPromise: Promise<SharpFactory | undefined> | undefined;
16
+
17
+ function isByteStream(value: unknown): value is ReadableStream<Uint8Array> {
18
+ return (
19
+ isRecord(value) && typeof value.getReader === "function" && typeof value.cancel === "function"
20
+ );
21
+ }
22
+
23
+ async function loadSharp(): Promise<SharpFactory | undefined> {
24
+ sharpFactoryPromise ??= import("sharp")
25
+ .then((module) => module.default)
26
+ .catch((error: unknown) => {
27
+ logger.debug("PNG enhancement unavailable; serving the original Genie image", {
28
+ error: toError(error),
29
+ });
30
+ return undefined;
31
+ });
32
+ return sharpFactoryPromise;
33
+ }
34
+
35
+ /**
36
+ * Resize a Genie screenshot to four times its source dimensions with Lanczos3,
37
+ * then apply a mild unsharp mask. Returns the original bytes when the optional
38
+ * Sharp runtime is unavailable or rejects the image.
39
+ */
40
+ export async function upscaleGenieVisualizationPng(inputBytes: Uint8Array): Promise<Uint8Array> {
41
+ const sharp = await loadSharp();
42
+ if (!sharp) return inputBytes;
43
+ try {
44
+ const input = Buffer.from(inputBytes);
45
+ const metadata = await sharp(input).metadata();
46
+ if (!metadata.width || !metadata.height) {
47
+ throw new Error("Could not read Genie visualization dimensions");
48
+ }
49
+ const output = await sharp(input)
50
+ .resize({
51
+ width: metadata.width * GENIE_PNG_SCALE,
52
+ height: metadata.height * GENIE_PNG_SCALE,
53
+ kernel: sharp.kernel.lanczos3,
54
+ fit: "fill",
55
+ })
56
+ .sharpen(GENIE_PNG_SHARPEN)
57
+ .png()
58
+ .toBuffer();
59
+ return new Uint8Array(output);
60
+ } catch (error) {
61
+ logger.warn("PNG enhancement failed; serving the original Genie image", {
62
+ error: toError(error),
63
+ });
64
+ return inputBytes;
65
+ }
66
+ }
67
+
68
+ /** Identity and cancellation for a Genie visualization download. */
69
+ export interface DownloadGenieVisualizationOptions {
70
+ /** Workspace client whose identity owns the conversation. */
71
+ workspaceClient?: WorkspaceClient;
72
+ /** External cancellation for the request and image body. */
73
+ context?: ContextLike;
74
+ }
75
+
76
+ /**
77
+ * Download Genie's PNG and apply optional raster cleanup before returning it.
78
+ * The SDK's raw transport preserves binary responses and its normal authentication;
79
+ * source-chart exports should still be preferred when they are available.
80
+ */
81
+ export async function downloadGenieVisualization(
82
+ input: GenieVisualizationRequest,
83
+ options: DownloadGenieVisualizationOptions = {},
84
+ ): Promise<Uint8Array> {
85
+ const client = options.workspaceClient ?? createWorkspaceClient();
86
+ const response = await client.apiClient.request(
87
+ {
88
+ path: genieVisualizationDownloadPath(input),
89
+ method: "GET",
90
+ headers: new Headers({ accept: "image/png" }),
91
+ responseHeaders: ["content-type"],
92
+ raw: true,
93
+ },
94
+ options.context ? toContext(options.context) : undefined,
95
+ );
96
+ if (
97
+ !isRecord(response) ||
98
+ typeof response["content-type"] !== "string" ||
99
+ response["content-type"].split(";")[0]?.trim().toLowerCase() !== "image/png" ||
100
+ !isByteStream(response.contents)
101
+ ) {
102
+ if (isRecord(response) && isByteStream(response.contents)) {
103
+ await response.contents.cancel().catch(() => undefined);
104
+ }
105
+ throw new TypeError("Genie did not return a PNG visualization");
106
+ }
107
+ const bytes = new Uint8Array(await new Response(response.contents).arrayBuffer());
108
+ if (
109
+ bytes.length < 8 ||
110
+ bytes[0] !== 0x89 ||
111
+ bytes[1] !== 0x50 ||
112
+ bytes[2] !== 0x4e ||
113
+ bytes[3] !== 0x47 ||
114
+ bytes[4] !== 0x0d ||
115
+ bytes[5] !== 0x0a ||
116
+ bytes[6] !== 0x1a ||
117
+ bytes[7] !== 0x0a
118
+ ) {
119
+ throw new TypeError("Genie returned an invalid PNG visualization");
120
+ }
121
+ return upscaleGenieVisualizationPng(bytes);
122
+ }