Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
840a67b
refactor: rename AnalyticsFormat values to API enum names
jamesbroadhead Apr 29, 2026
09392bb
refactor(analytics): accept legacy "JSON"/"ARROW" format aliases
jamesbroadhead May 11, 2026
08c5486
feat: decode inline Arrow IPC + warehouse-compat fallback
jamesbroadhead Apr 29, 2026
2ef0c65
fix: address ACE multi-model review findings
jamesbroadhead May 12, 2026
2b22d56
chore(shared): align zod with appkit's 4.3.6
jamesbroadhead May 12, 2026
a37c100
Merge remote-tracking branch 'origin/main' into rebase/329-on-new-main
jamesbroadhead May 12, 2026
90ecd8a
chore: regenerate pnpm-lock.yaml after zod restoration
jamesbroadhead May 12, 2026
3d54009
style: consolidate normalizeAnalyticsFormat into the types import block
jamesbroadhead May 12, 2026
e6c2aae
fix: restore logger.error in executeStatement catch block
jamesbroadhead May 12, 2026
a7434f6
refactor(analytics): stash inline Arrow server-side, drop arrow_inlin…
jamesbroadhead May 12, 2026
f34e18e
fix: address ACE multi-model review on the inline-stash redesign
jamesbroadhead May 12, 2026
09f801f
docs(stash): correct maxBytes comment after switch to reject-on-full
jamesbroadhead May 12, 2026
698a264
style: drop unused imports and tidy stash test types
jamesbroadhead May 12, 2026
f5b9604
Merge origin/main into stack/arrow-3-inline-arrow-fix
jamesbroadhead May 15, 2026
8f0e31c
test(analytics): cover stash-full fallback to EXTERNAL_LINKS
jamesbroadhead May 15, 2026
0f5022f
feat(appkit): retry JSON_ARRAY as ARROW_STREAM on inline-arrow-only w…
jamesbroadhead May 15, 2026
4afce1f
fix(appkit): address Xavier v3 review on inline-Arrow path
jamesbroadhead May 21, 2026
28fdb92
fix(appkit): address Xavier v4 self-review on inline-Arrow path
jamesbroadhead May 21, 2026
f69e577
refactor(appkit): stash telemetry, type cleanup, format-path extraction
jamesbroadhead May 21, 2026
ed3dfeb
docs: regenerate api docs for clientMessage field
jamesbroadhead May 28, 2026
7e3c240
Merge origin/main into arrow-3-inline-arrow-fix; fix Arrow IPC decode
MarioCadenas Jun 13, 2026
a03f69c
Merge remote-tracking branch 'origin/main' into arrow329-merge-main
MarioCadenas Jul 2, 2026
b1fa190
Merge remote-tracking branch 'origin/main' into arrow329-merge-main
MarioCadenas Jul 6, 2026
f1ef36b
refactor(appkit): direct Arrow IPC streaming + hardened result delive…
MarioCadenas Jul 7, 2026
b7dbe35
Merge branch 'main' into stack/arrow-3-inline-arrow-fix
MarioCadenas Jul 7, 2026
525a9bd
test(playground): serve Arrow IPC from the e2e mock for ARROW_STREAM …
MarioCadenas Jul 7, 2026
f3b6c37
fix(appkit-ui): keep zod out of the browser bundle in useAnalyticsQuery
MarioCadenas Jul 7, 2026
3541524
feat(appkit): harden arrow delivery — caching, capability memo, link …
MarioCadenas Jul 8, 2026
4588579
refactor(appkit): extract shared statement-error rethrow helper
MarioCadenas Jul 8, 2026
6f91df3
chore(appkit): bump bundle-size baseline for inline-arrow feature
MarioCadenas Jul 8, 2026
490c05d
Merge main (v0.44.0) into inline-arrow branch
MarioCadenas Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 73 additions & 47 deletions apps/dev-playground/tests/utils/test-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Page, Request } from "@playwright/test";
import { tableFromJSON, tableToIPC } from "apache-arrow";
import {
mockAnalyticsData,
mockReconnectMessages,
Expand Down Expand Up @@ -27,65 +28,90 @@ function getSSEHeaders() {
};
}

/** Maps a query URL to its mock rows (matched by query-key substring). */
const ANALYTICS_MOCK: Array<readonly [string, readonly unknown[]]> = [
["spend_summary", mockAnalyticsData.spendSummary],
["apps_list", mockAnalyticsData.appsList],
["untagged_apps", mockAnalyticsData.untaggedApps],
["spend_data", mockAnalyticsData.spendData],
["top_contributors", mockAnalyticsData.topContributors],
["app_activity_heatmap", mockAnalyticsData.appActivityHeatmap],
["sql_helpers_test", mockAnalyticsData.sqlHelpersTest],
];

function resolveAnalyticsMock(url: string): readonly unknown[] {
return ANALYTICS_MOCK.find(([key]) => url.includes(key))?.[1] ?? [];
}

/**
* Build raw Arrow IPC stream bytes from mock rows — the format `fetchArrowDirect`
* expects for `format: "ARROW_STREAM"` (raw bytes on the response body, not SSE).
*
* Mirrors a real Databricks warehouse: the Arrow schema is encoded
* *positionally* (`col_0`, `col_1`, …) and the real names are returned
* separately for the `X-Appkit-Arrow-Columns` header, so the client's relabel
* path (positional schema → real names) is exercised end-to-end — that path
* was the live bug and is otherwise only unit-tested.
*
* Array/object cells are stringified so Arrow can infer a primitive column
* type, matching how the warehouse serializes complex types; chart-relevant
* columns (names, numbers) are unaffected.
*/
function toArrowIPC(rows: readonly unknown[]): {
body: Buffer;
columnNames: string[];
} {
const columnNames = Object.keys(rows[0] as Record<string, unknown>);
const positional = rows.map((row) => {
const values = Object.values(row as Record<string, unknown>);
return Object.fromEntries(
values.map((v, i) => [
`col_${i}`,
v !== null && typeof v === "object" ? JSON.stringify(v) : v,
]),
);
});
return {
body: Buffer.from(tableToIPC(tableFromJSON(positional), "stream")),
columnNames,
};
}

export async function setupMockAPI(page: Page) {
await page.route("**/api/analytics/query/**", async (route) => {
const url = route.request().url();
const data = resolveAnalyticsMock(route.request().url());

if (url.includes("spend_summary")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.spendSummary),
});
}
if (url.includes("apps_list")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.appsList),
});
}
if (url.includes("untagged_apps")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.untaggedApps),
});
}
if (url.includes("spend_data")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.spendData),
});
// ARROW_STREAM requests read raw Arrow IPC bytes off the response body
// (see fetchArrowDirect); everything else consumes the SSE/JSON result.
let requestFormat: string | undefined;
try {
requestFormat = route.request().postDataJSON()?.format;
} catch {
// Non-JSON body (e.g. a GET) — treat as the SSE/JSON path.
}
if (url.includes("top_contributors")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.topContributors),
});
}
if (url.includes("app_activity_heatmap")) {
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.appActivityHeatmap),
});
}
if (url.includes("sql_helpers_test")) {

if (requestFormat === "ARROW_STREAM" && data.length > 0) {
const { body, columnNames } = toArrowIPC(data);
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse(mockAnalyticsData.sqlHelpersTest),
headers: {
"Content-Type": "application/vnd.apache.arrow.stream",
"Cache-Control": "no-store",
// Real warehouses encode the schema positionally (col_N) and send
// the aliased names in this header; the client relabels the decoded
// Table. Mirror that so the relabel path is covered.
"X-Appkit-Arrow-Columns": encodeURIComponent(
JSON.stringify(columnNames),
),
},
body,
});
}

// Default empty response for unknown queries
return route.fulfill({
status: 200,
headers: getSSEHeaders(),
body: createSSEResponse([]),
body: createSSEResponse(data),
});
});

Expand Down
98 changes: 49 additions & 49 deletions bundle-size-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
{
"name": "@databricks/appkit",
"tarball": {
"packed": 674146,
"unpacked": 2386195
"packed": 727720,
"unpacked": 2550712
},
"dist": {
"total": {
"raw": 2381750,
"gzip": 797897
"raw": 2546267,
"gzip": 855702
},
"js": {
"raw": 702696,
"gzip": 245275
"raw": 750162,
"gzip": 262624
},
"types": {
"raw": 274230,
"gzip": 92925
"raw": 287585,
"gzip": 98642
},
"maps": {
"raw": 1394029,
"gzip": 455879
"raw": 1497725,
"gzip": 490618
},
"css": {
"raw": 0,
Expand All @@ -31,22 +31,22 @@
"raw": 10795,
"gzip": 3818
},
"fileCount": 518
"fileCount": 533
},
"entries": [
{
"id": ".",
"gzip": 78098,
"gzip": 83090,
"composition": {
"initialGzip": 75524,
"initialGzip": 80516,
"lazyGzip": 2574,
"totalGzip": 78098,
"own": 248907,
"totalGzip": 83090,
"own": 263971,
"nodeModules": null,
"chunks": [
{
"label": "index.js",
"gzip": 71426,
"gzip": 76418,
"kind": "initial"
},
{
Expand All @@ -64,17 +64,17 @@
},
{
"id": "./beta",
"gzip": 39886,
"gzip": 40063,
"composition": {
"initialGzip": 39655,
"initialGzip": 39832,
"lazyGzip": 231,
"totalGzip": 39886,
"own": 119365,
"totalGzip": 40063,
"own": 119920,
"nodeModules": null,
"chunks": [
{
"label": "beta.js",
"gzip": 30505,
"gzip": 30577,
"kind": "initial"
},
{
Expand All @@ -84,7 +84,7 @@
},
{
"label": "service-context.js",
"gzip": 3123,
"gzip": 3228,
"kind": "initial"
},
{
Expand All @@ -107,17 +107,17 @@
},
{
"id": "./type-generator",
"gzip": 18895,
"gzip": 19068,
"composition": {
"initialGzip": 18895,
"initialGzip": 19068,
"lazyGzip": 0,
"totalGzip": 18895,
"own": 54388,
"totalGzip": 19068,
"own": 55003,
"nodeModules": null,
"chunks": [
{
"label": "index.js",
"gzip": 18895,
"gzip": 19068,
"kind": "initial"
}
]
Expand All @@ -128,25 +128,25 @@
{
"name": "@databricks/appkit-ui",
"tarball": {
"packed": 296998,
"unpacked": 1254198
"packed": 313597,
"unpacked": 1314080
},
"dist": {
"total": {
"raw": 1250252,
"gzip": 413942
"raw": 1310134,
"gzip": 434210
},
"js": {
"raw": 354361,
"gzip": 117408
"raw": 377132,
"gzip": 125150
},
"types": {
"raw": 203935,
"gzip": 73265
"raw": 209874,
"gzip": 75430
},
"maps": {
"raw": 675096,
"gzip": 219923
"raw": 706268,
"gzip": 230284
},
"css": {
"raw": 16860,
Expand All @@ -156,22 +156,22 @@
"raw": 0,
"gzip": 0
},
"fileCount": 462
"fileCount": 478
},
"entries": [
{
"id": "./js",
"gzip": 4155,
"gzip": 4254,
"composition": {
"initialGzip": 4309,
"initialGzip": 4410,
"lazyGzip": 50587,
"totalGzip": 54896,
"own": 11585,
"totalGzip": 54997,
"own": 11865,
"nodeModules": 213288,
"chunks": [
{
"label": "index.js",
"gzip": 4189,
"gzip": 4290,
"kind": "initial"
},
{
Expand Down Expand Up @@ -207,17 +207,17 @@
},
{
"id": "./react",
"gzip": 45534,
"gzip": 45960,
"composition": {
"initialGzip": 437612,
"initialGzip": 438067,
"lazyGzip": 49772,
"totalGzip": 487384,
"own": 165847,
"nodeModules": 1402932,
"totalGzip": 487839,
"own": 167329,
"nodeModules": 1402934,
"chunks": [
{
"label": "index.js",
"gzip": 435462,
"gzip": 435917,
"kind": "initial"
},
{
Expand Down
Loading
Loading