@databricks/appkit 0.1.4 → 0.2.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/AGENTS.md +89 -12
- package/CLAUDE.md +89 -12
- package/NOTICE.md +4 -0
- package/README.md +21 -15
- package/bin/appkit-lint.js +129 -0
- package/dist/analytics/analytics.d.ts +33 -8
- package/dist/analytics/analytics.d.ts.map +1 -1
- package/dist/analytics/analytics.js +67 -27
- package/dist/analytics/analytics.js.map +1 -1
- package/dist/analytics/defaults.js.map +1 -1
- package/dist/analytics/query.js +12 -6
- package/dist/analytics/query.js.map +1 -1
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +7 -5
- package/dist/app/index.js.map +1 -1
- package/dist/appkit/package.js +1 -1
- package/dist/cache/defaults.js.map +1 -1
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +25 -5
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/storage/memory.js.map +1 -1
- package/dist/cache/storage/persistent.js +12 -6
- package/dist/cache/storage/persistent.js.map +1 -1
- package/dist/connectors/lakebase/client.js +31 -21
- package/dist/connectors/lakebase/client.js.map +1 -1
- package/dist/connectors/lakebase/defaults.js.map +1 -1
- package/dist/connectors/sql-warehouse/client.js +68 -28
- package/dist/connectors/sql-warehouse/client.js.map +1 -1
- package/dist/connectors/sql-warehouse/defaults.js.map +1 -1
- package/dist/context/execution-context.js +75 -0
- package/dist/context/execution-context.js.map +1 -0
- package/dist/context/index.js +27 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/service-context.js +154 -0
- package/dist/context/service-context.js.map +1 -0
- package/dist/context/user-context.js +15 -0
- package/dist/context/user-context.js.map +1 -0
- package/dist/core/appkit.d.ts +3 -0
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js +7 -0
- package/dist/core/appkit.js.map +1 -1
- package/dist/errors/authentication.d.ts +38 -0
- package/dist/errors/authentication.d.ts.map +1 -0
- package/dist/errors/authentication.js +48 -0
- package/dist/errors/authentication.js.map +1 -0
- package/dist/errors/base.d.ts +58 -0
- package/dist/errors/base.d.ts.map +1 -0
- package/dist/errors/base.js +70 -0
- package/dist/errors/base.js.map +1 -0
- package/dist/errors/configuration.d.ts +38 -0
- package/dist/errors/configuration.d.ts.map +1 -0
- package/dist/errors/configuration.js +45 -0
- package/dist/errors/configuration.js.map +1 -0
- package/dist/errors/connection.d.ts +42 -0
- package/dist/errors/connection.d.ts.map +1 -0
- package/dist/errors/connection.js +54 -0
- package/dist/errors/connection.js.map +1 -0
- package/dist/errors/execution.d.ts +42 -0
- package/dist/errors/execution.d.ts.map +1 -0
- package/dist/errors/execution.js +51 -0
- package/dist/errors/execution.js.map +1 -0
- package/dist/errors/index.js +28 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/initialization.d.ts +34 -0
- package/dist/errors/initialization.d.ts.map +1 -0
- package/dist/errors/initialization.js +42 -0
- package/dist/errors/initialization.js.map +1 -0
- package/dist/errors/server.d.ts +38 -0
- package/dist/errors/server.d.ts.map +1 -0
- package/dist/errors/server.js +45 -0
- package/dist/errors/server.js.map +1 -0
- package/dist/errors/tunnel.d.ts +38 -0
- package/dist/errors/tunnel.d.ts.map +1 -0
- package/dist/errors/tunnel.js +51 -0
- package/dist/errors/tunnel.js.map +1 -0
- package/dist/errors/validation.d.ts +36 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +45 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.js +179 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/sampling.js +56 -0
- package/dist/logging/sampling.js.map +1 -0
- package/dist/logging/wide-event-emitter.js +108 -0
- package/dist/logging/wide-event-emitter.js.map +1 -0
- package/dist/logging/wide-event.js +167 -0
- package/dist/logging/wide-event.js.map +1 -0
- package/dist/plugin/dev-reader.d.ts.map +1 -1
- package/dist/plugin/dev-reader.js +8 -3
- package/dist/plugin/dev-reader.js.map +1 -1
- package/dist/plugin/interceptors/cache.js.map +1 -1
- package/dist/plugin/interceptors/retry.js +10 -2
- package/dist/plugin/interceptors/retry.js.map +1 -1
- package/dist/plugin/interceptors/telemetry.js +24 -9
- package/dist/plugin/interceptors/telemetry.js.map +1 -1
- package/dist/plugin/interceptors/timeout.js +4 -0
- package/dist/plugin/interceptors/timeout.js.map +1 -1
- package/dist/plugin/plugin.d.ts +38 -4
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +86 -5
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/plugin/to-plugin.d.ts +4 -0
- package/dist/plugin/to-plugin.d.ts.map +1 -1
- package/dist/plugin/to-plugin.js +3 -0
- package/dist/plugin/to-plugin.js.map +1 -1
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +25 -21
- package/dist/server/index.js.map +1 -1
- package/dist/server/remote-tunnel/remote-tunnel-controller.js +4 -2
- package/dist/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
- package/dist/server/remote-tunnel/remote-tunnel-manager.js +10 -8
- package/dist/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
- package/dist/server/utils.js.map +1 -1
- package/dist/server/vite-dev-server.js +8 -5
- package/dist/server/vite-dev-server.js.map +1 -1
- package/dist/shared/src/sql/helpers.js.map +1 -1
- package/dist/stream/arrow-stream-processor.js +13 -6
- package/dist/stream/arrow-stream-processor.js.map +1 -1
- package/dist/stream/buffers.js +5 -1
- package/dist/stream/buffers.js.map +1 -1
- package/dist/stream/sse-writer.js.map +1 -1
- package/dist/stream/stream-manager.d.ts.map +1 -1
- package/dist/stream/stream-manager.js +47 -36
- package/dist/stream/stream-manager.js.map +1 -1
- package/dist/stream/stream-registry.js.map +1 -1
- package/dist/stream/types.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/instrumentations.js +14 -10
- package/dist/telemetry/instrumentations.js.map +1 -1
- package/dist/telemetry/telemetry-manager.js +8 -6
- package/dist/telemetry/telemetry-manager.js.map +1 -1
- package/dist/telemetry/trace-sampler.js +33 -0
- package/dist/telemetry/trace-sampler.js.map +1 -0
- package/dist/type-generator/index.js +4 -2
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/query-registry.js +4 -2
- package/dist/type-generator/query-registry.js.map +1 -1
- package/dist/type-generator/types.js.map +1 -1
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +5 -3
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/utils/env-validator.js +5 -5
- package/dist/utils/env-validator.js.map +1 -1
- package/dist/utils/merge.js +1 -5
- package/dist/utils/merge.js.map +1 -1
- package/dist/utils/path-exclusions.js +66 -0
- package/dist/utils/path-exclusions.js.map +1 -0
- package/dist/utils/vite-config-merge.js +1 -5
- package/dist/utils/vite-config-merge.js.map +1 -1
- package/llms.txt +89 -12
- package/package.json +6 -1
- package/dist/utils/databricks-client-middleware.d.ts +0 -17
- package/dist/utils/databricks-client-middleware.d.ts.map +0 -1
- package/dist/utils/databricks-client-middleware.js +0 -117
- package/dist/utils/databricks-client-middleware.js.map +0 -1
- package/dist/utils/index.js +0 -26
- package/dist/utils/index.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -440,23 +440,49 @@ Formats:
|
|
|
440
440
|
- `format: "JSON"` (default) returns JSON rows
|
|
441
441
|
- `format: "ARROW"` returns an Arrow “statement_id” payload over SSE, then the client fetches binary Arrow from `/api/analytics/arrow-result/:jobId`
|
|
442
442
|
|
|
443
|
-
###
|
|
443
|
+
### Execution context and `asUser(req)`
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
AppKit manages Databricks authentication via two contexts:
|
|
446
446
|
|
|
447
|
-
|
|
447
|
+
- **ServiceContext** (singleton): Initialized at app startup with service principal credentials
|
|
448
|
+
- **ExecutionContext**: Determined at runtime - either service principal or user context
|
|
449
|
+
|
|
450
|
+
**Headers used for user context:**
|
|
448
451
|
|
|
449
452
|
- `x-forwarded-user`: required in production; identifies the user
|
|
450
|
-
- `x-forwarded-access-token`:
|
|
453
|
+
- `x-forwarded-access-token`: required for user token passthrough
|
|
454
|
+
|
|
455
|
+
**Using `asUser(req)` for user-scoped operations:**
|
|
456
|
+
|
|
457
|
+
The `asUser(req)` pattern allows plugins to execute operations using the requesting user's credentials:
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
// In a custom plugin route handler
|
|
461
|
+
router.post("/users/me/data", async (req, res) => {
|
|
462
|
+
// Execute as the user (uses their Databricks permissions)
|
|
463
|
+
const result = await this.asUser(req).query("SELECT ...");
|
|
464
|
+
res.json(result);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Service principal execution (default)
|
|
468
|
+
router.post("/system/data", async (req, res) => {
|
|
469
|
+
const result = await this.query("SELECT ...");
|
|
470
|
+
res.json(result);
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Context helper functions (exported from `@databricks/appkit`):**
|
|
451
475
|
|
|
452
|
-
|
|
476
|
+
- `getExecutionContext()`: Returns current context (user or service)
|
|
477
|
+
- `getCurrentUserId()`: Returns user ID in user context, service user ID otherwise
|
|
478
|
+
- `getWorkspaceClient()`: Returns the appropriate WorkspaceClient for current context
|
|
479
|
+
- `getWarehouseId()`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID` or auto-selected in dev)
|
|
480
|
+
- `getWorkspaceId()`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
|
|
481
|
+
- `isInUserContext()`: Returns `true` if currently executing in user context
|
|
453
482
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
- `workspaceId`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
|
|
458
|
-
- `userDatabricksClient`: present only when passthrough is available (or in dev it equals service client)
|
|
459
|
-
- `serviceDatabricksClient`: always present
|
|
483
|
+
**Development mode behavior:**
|
|
484
|
+
|
|
485
|
+
In local development (`NODE_ENV=development`), if `asUser(req)` is called without a user token, it logs a warning and falls back to the service principal.
|
|
460
486
|
|
|
461
487
|
### Custom plugins (backend)
|
|
462
488
|
|
|
@@ -469,7 +495,6 @@ import type express from "express";
|
|
|
469
495
|
class MyPlugin extends Plugin {
|
|
470
496
|
name = "my-plugin";
|
|
471
497
|
envVars = []; // list required env vars here
|
|
472
|
-
requiresDatabricksClient = false; // set true if you need getRequestContext()
|
|
473
498
|
|
|
474
499
|
injectRoutes(router: express.Router) {
|
|
475
500
|
this.route(router, {
|
|
@@ -643,6 +668,56 @@ export function SpendChart() {
|
|
|
643
668
|
}
|
|
644
669
|
```
|
|
645
670
|
|
|
671
|
+
**Chart props reference (important):**
|
|
672
|
+
|
|
673
|
+
Charts are **self-contained ECharts components**. Configure via props, NOT children:
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
// ✅ Correct: use props for customization
|
|
677
|
+
<BarChart
|
|
678
|
+
queryKey="sales_by_region"
|
|
679
|
+
parameters={{}}
|
|
680
|
+
xKey="region" // X-axis field
|
|
681
|
+
yKey={["revenue", "expenses"]} // Y-axis field(s) - string or string[]
|
|
682
|
+
colors={['#40d1f5', '#4462c9']} // Custom colors
|
|
683
|
+
stacked // Stack bars (BarChart, AreaChart)
|
|
684
|
+
orientation="horizontal" // "vertical" (default) | "horizontal"
|
|
685
|
+
showLegend // Show legend
|
|
686
|
+
height={400} // Height in pixels (default: 300)
|
|
687
|
+
/>
|
|
688
|
+
|
|
689
|
+
<LineChart
|
|
690
|
+
queryKey="trend_data"
|
|
691
|
+
parameters={{}}
|
|
692
|
+
xKey="date"
|
|
693
|
+
yKey="value"
|
|
694
|
+
smooth // Smooth curves (default: true)
|
|
695
|
+
showSymbol={false} // Hide data point markers
|
|
696
|
+
/>
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**❌ CRITICAL: Charts do NOT accept Recharts children**
|
|
700
|
+
|
|
701
|
+
```tsx
|
|
702
|
+
// ❌ WRONG - AppKit charts are NOT Recharts wrappers
|
|
703
|
+
import { BarChart } from "@databricks/appkit-ui/react";
|
|
704
|
+
import { Bar, XAxis, YAxis, CartesianGrid } from "recharts";
|
|
705
|
+
|
|
706
|
+
<BarChart queryKey="data" parameters={{}}>
|
|
707
|
+
<CartesianGrid /> // ❌ This will cause TypeScript errors
|
|
708
|
+
<XAxis dataKey="x" /> // ❌ Not supported
|
|
709
|
+
<Bar dataKey="y" /> // ❌ Not supported
|
|
710
|
+
</BarChart>
|
|
711
|
+
|
|
712
|
+
// ✅ CORRECT - use props instead
|
|
713
|
+
<BarChart
|
|
714
|
+
queryKey="data"
|
|
715
|
+
parameters={{}}
|
|
716
|
+
xKey="x"
|
|
717
|
+
yKey="y"
|
|
718
|
+
/>
|
|
719
|
+
```
|
|
720
|
+
|
|
646
721
|
### SQL helpers (`sql.*`)
|
|
647
722
|
|
|
648
723
|
Use these to build typed parameters (they return marker objects: `{ __sql_type, value }`):
|
|
@@ -1144,6 +1219,7 @@ env:
|
|
|
1144
1219
|
- `useMemo` wraps parameters objects
|
|
1145
1220
|
- Loading/error/empty states are explicit
|
|
1146
1221
|
- Charts use `format="auto"` unless you have a reason to force `"json"`/`"arrow"`
|
|
1222
|
+
- Charts use props (`xKey`, `yKey`, `colors`) NOT children (they're ECharts-based, not Recharts)
|
|
1147
1223
|
- If using tooltips: root is wrapped with `<TooltipProvider>`
|
|
1148
1224
|
|
|
1149
1225
|
- **Never**
|
|
@@ -1151,4 +1227,5 @@ env:
|
|
|
1151
1227
|
- Don't pass untyped raw params for annotated queries
|
|
1152
1228
|
- Don't ignore `createApp()`'s promise
|
|
1153
1229
|
- Don't invent UI components not listed in this file
|
|
1230
|
+
- Don't pass Recharts children (`<Bar>`, `<XAxis>`, etc.) to AppKit chart components
|
|
1154
1231
|
|
package/CLAUDE.md
CHANGED
|
@@ -440,23 +440,49 @@ Formats:
|
|
|
440
440
|
- `format: "JSON"` (default) returns JSON rows
|
|
441
441
|
- `format: "ARROW"` returns an Arrow “statement_id” payload over SSE, then the client fetches binary Arrow from `/api/analytics/arrow-result/:jobId`
|
|
442
442
|
|
|
443
|
-
###
|
|
443
|
+
### Execution context and `asUser(req)`
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
AppKit manages Databricks authentication via two contexts:
|
|
446
446
|
|
|
447
|
-
|
|
447
|
+
- **ServiceContext** (singleton): Initialized at app startup with service principal credentials
|
|
448
|
+
- **ExecutionContext**: Determined at runtime - either service principal or user context
|
|
449
|
+
|
|
450
|
+
**Headers used for user context:**
|
|
448
451
|
|
|
449
452
|
- `x-forwarded-user`: required in production; identifies the user
|
|
450
|
-
- `x-forwarded-access-token`:
|
|
453
|
+
- `x-forwarded-access-token`: required for user token passthrough
|
|
454
|
+
|
|
455
|
+
**Using `asUser(req)` for user-scoped operations:**
|
|
456
|
+
|
|
457
|
+
The `asUser(req)` pattern allows plugins to execute operations using the requesting user's credentials:
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
// In a custom plugin route handler
|
|
461
|
+
router.post("/users/me/data", async (req, res) => {
|
|
462
|
+
// Execute as the user (uses their Databricks permissions)
|
|
463
|
+
const result = await this.asUser(req).query("SELECT ...");
|
|
464
|
+
res.json(result);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Service principal execution (default)
|
|
468
|
+
router.post("/system/data", async (req, res) => {
|
|
469
|
+
const result = await this.query("SELECT ...");
|
|
470
|
+
res.json(result);
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Context helper functions (exported from `@databricks/appkit`):**
|
|
451
475
|
|
|
452
|
-
|
|
476
|
+
- `getExecutionContext()`: Returns current context (user or service)
|
|
477
|
+
- `getCurrentUserId()`: Returns user ID in user context, service user ID otherwise
|
|
478
|
+
- `getWorkspaceClient()`: Returns the appropriate WorkspaceClient for current context
|
|
479
|
+
- `getWarehouseId()`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID` or auto-selected in dev)
|
|
480
|
+
- `getWorkspaceId()`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
|
|
481
|
+
- `isInUserContext()`: Returns `true` if currently executing in user context
|
|
453
482
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
- `workspaceId`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
|
|
458
|
-
- `userDatabricksClient`: present only when passthrough is available (or in dev it equals service client)
|
|
459
|
-
- `serviceDatabricksClient`: always present
|
|
483
|
+
**Development mode behavior:**
|
|
484
|
+
|
|
485
|
+
In local development (`NODE_ENV=development`), if `asUser(req)` is called without a user token, it logs a warning and falls back to the service principal.
|
|
460
486
|
|
|
461
487
|
### Custom plugins (backend)
|
|
462
488
|
|
|
@@ -469,7 +495,6 @@ import type express from "express";
|
|
|
469
495
|
class MyPlugin extends Plugin {
|
|
470
496
|
name = "my-plugin";
|
|
471
497
|
envVars = []; // list required env vars here
|
|
472
|
-
requiresDatabricksClient = false; // set true if you need getRequestContext()
|
|
473
498
|
|
|
474
499
|
injectRoutes(router: express.Router) {
|
|
475
500
|
this.route(router, {
|
|
@@ -643,6 +668,56 @@ export function SpendChart() {
|
|
|
643
668
|
}
|
|
644
669
|
```
|
|
645
670
|
|
|
671
|
+
**Chart props reference (important):**
|
|
672
|
+
|
|
673
|
+
Charts are **self-contained ECharts components**. Configure via props, NOT children:
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
// ✅ Correct: use props for customization
|
|
677
|
+
<BarChart
|
|
678
|
+
queryKey="sales_by_region"
|
|
679
|
+
parameters={{}}
|
|
680
|
+
xKey="region" // X-axis field
|
|
681
|
+
yKey={["revenue", "expenses"]} // Y-axis field(s) - string or string[]
|
|
682
|
+
colors={['#40d1f5', '#4462c9']} // Custom colors
|
|
683
|
+
stacked // Stack bars (BarChart, AreaChart)
|
|
684
|
+
orientation="horizontal" // "vertical" (default) | "horizontal"
|
|
685
|
+
showLegend // Show legend
|
|
686
|
+
height={400} // Height in pixels (default: 300)
|
|
687
|
+
/>
|
|
688
|
+
|
|
689
|
+
<LineChart
|
|
690
|
+
queryKey="trend_data"
|
|
691
|
+
parameters={{}}
|
|
692
|
+
xKey="date"
|
|
693
|
+
yKey="value"
|
|
694
|
+
smooth // Smooth curves (default: true)
|
|
695
|
+
showSymbol={false} // Hide data point markers
|
|
696
|
+
/>
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**❌ CRITICAL: Charts do NOT accept Recharts children**
|
|
700
|
+
|
|
701
|
+
```tsx
|
|
702
|
+
// ❌ WRONG - AppKit charts are NOT Recharts wrappers
|
|
703
|
+
import { BarChart } from "@databricks/appkit-ui/react";
|
|
704
|
+
import { Bar, XAxis, YAxis, CartesianGrid } from "recharts";
|
|
705
|
+
|
|
706
|
+
<BarChart queryKey="data" parameters={{}}>
|
|
707
|
+
<CartesianGrid /> // ❌ This will cause TypeScript errors
|
|
708
|
+
<XAxis dataKey="x" /> // ❌ Not supported
|
|
709
|
+
<Bar dataKey="y" /> // ❌ Not supported
|
|
710
|
+
</BarChart>
|
|
711
|
+
|
|
712
|
+
// ✅ CORRECT - use props instead
|
|
713
|
+
<BarChart
|
|
714
|
+
queryKey="data"
|
|
715
|
+
parameters={{}}
|
|
716
|
+
xKey="x"
|
|
717
|
+
yKey="y"
|
|
718
|
+
/>
|
|
719
|
+
```
|
|
720
|
+
|
|
646
721
|
### SQL helpers (`sql.*`)
|
|
647
722
|
|
|
648
723
|
Use these to build typed parameters (they return marker objects: `{ __sql_type, value }`):
|
|
@@ -1144,6 +1219,7 @@ env:
|
|
|
1144
1219
|
- `useMemo` wraps parameters objects
|
|
1145
1220
|
- Loading/error/empty states are explicit
|
|
1146
1221
|
- Charts use `format="auto"` unless you have a reason to force `"json"`/`"arrow"`
|
|
1222
|
+
- Charts use props (`xKey`, `yKey`, `colors`) NOT children (they're ECharts-based, not Recharts)
|
|
1147
1223
|
- If using tooltips: root is wrapped with `<TooltipProvider>`
|
|
1148
1224
|
|
|
1149
1225
|
- **Never**
|
|
@@ -1151,4 +1227,5 @@ env:
|
|
|
1151
1227
|
- Don't pass untyped raw params for annotated queries
|
|
1152
1228
|
- Don't ignore `createApp()`'s promise
|
|
1153
1229
|
- Don't invent UI components not listed in this file
|
|
1230
|
+
- Don't pass Recharts children (`<Bar>`, `<XAxis>`, etc.) to AppKit chart components
|
|
1154
1231
|
|
package/NOTICE.md
CHANGED
|
@@ -6,6 +6,7 @@ This Software contains code from the following open source projects:
|
|
|
6
6
|
|
|
7
7
|
| Name | Installed version | License | Code |
|
|
8
8
|
| :--------------- | :---------------- | :----------- | :--------------------------------------------------- |
|
|
9
|
+
| [@ast-grep/napi](https://www.npmjs.com/package/@ast-grep/napi) | 0.37.0 | MIT | https://ast-grep.github.io |
|
|
9
10
|
| [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers) | 5.2.2 | MIT | https://react-hook-form.com |
|
|
10
11
|
| [@opentelemetry/api](https://www.npmjs.com/package/@opentelemetry/api) | 1.9.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/api |
|
|
11
12
|
| [@opentelemetry/api-logs](https://www.npmjs.com/package/@opentelemetry/api-logs) | 0.208.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/api-logs |
|
|
@@ -49,6 +50,7 @@ This Software contains code from the following open source projects:
|
|
|
49
50
|
| [@radix-ui/react-tooltip](https://www.npmjs.com/package/@radix-ui/react-tooltip) | 1.2.8 | MIT | https://radix-ui.com/primitives |
|
|
50
51
|
| [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) | 8.21.3 | MIT | https://tanstack.com/table |
|
|
51
52
|
| [@tanstack/react-virtual](https://www.npmjs.com/package/@tanstack/react-virtual) | 3.13.12 | MIT | https://tanstack.com/virtual |
|
|
53
|
+
| [@types/semver](https://www.npmjs.com/package/@types/semver) | 7.7.1 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/semver |
|
|
52
54
|
| [apache-arrow](https://www.npmjs.com/package/apache-arrow) | 21.1.0 | Apache-2.0 | https://arrow.apache.org/js/ |
|
|
53
55
|
| [class-variance-authority](https://www.npmjs.com/package/class-variance-authority) | 0.7.1 | Apache-2.0 | https://github.com/joe-bell/cva#readme |
|
|
54
56
|
| [clsx](https://www.npmjs.com/package/clsx) | 2.1.1 | MIT | https://github.com/lukeed/clsx#readme |
|
|
@@ -62,10 +64,12 @@ This Software contains code from the following open source projects:
|
|
|
62
64
|
| [input-otp](https://www.npmjs.com/package/input-otp) | 1.4.2 | MIT | https://input-otp.rodz.dev/ |
|
|
63
65
|
| [lucide-react](https://www.npmjs.com/package/lucide-react) | 0.554.0 | ISC | https://lucide.dev |
|
|
64
66
|
| [next-themes](https://www.npmjs.com/package/next-themes) | 0.4.6 | MIT | https://github.com/pacocoursey/next-themes#readme |
|
|
67
|
+
| [obug](https://www.npmjs.com/package/obug) | 2.1.1 | MIT | https://github.com/sxzz/obug#readme |
|
|
65
68
|
| [pg](https://www.npmjs.com/package/pg) | 8.16.3 | MIT | https://github.com/brianc/node-postgres |
|
|
66
69
|
| [react-day-picker](https://www.npmjs.com/package/react-day-picker) | 9.12.0 | MIT | https://daypicker.dev |
|
|
67
70
|
| [react-hook-form](https://www.npmjs.com/package/react-hook-form) | 7.68.0 | MIT | https://react-hook-form.com |
|
|
68
71
|
| [react-resizable-panels](https://www.npmjs.com/package/react-resizable-panels) | 3.0.6 | MIT | https://github.com/bvaughn/react-resizable-panels#readme |
|
|
72
|
+
| [semver](https://www.npmjs.com/package/semver) | 6.3.1, 7.7.3 | ISC | https://github.com/npm/node-semver#readme |
|
|
69
73
|
| [sonner](https://www.npmjs.com/package/sonner) | 2.0.7 | MIT | https://sonner.emilkowal.ski/ |
|
|
70
74
|
| [tailwind-merge](https://www.npmjs.com/package/tailwind-merge) | 3.4.0 | MIT | https://github.com/dcastil/tailwind-merge |
|
|
71
75
|
| [vaul](https://www.npmjs.com/package/vaul) | 1.1.2 | MIT | https://vaul.emilkowal.ski/ |
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# AppKit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Build Databricks Apps faster with our brand-new Node.js + React SDK. Built for humans and AI.
|
|
4
|
+
|
|
5
|
+
> [!WARNING] PREVIEW - NOT FOR PRODUCTION USE
|
|
5
6
|
>
|
|
6
7
|
> **This SDK is in preview and is subject to change without notice.**
|
|
7
8
|
>
|
|
@@ -11,25 +12,30 @@
|
|
|
11
12
|
> - 📝 **Use for development and testing only**
|
|
12
13
|
>
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## Introduction
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
AppKit is a TypeScript SDK for building production-ready Databricks applications with a plugin-based architecture. It provides opinionated defaults, built-in observability, and seamless integration with Databricks services.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
AppKit simplifies building data applications on Databricks by providing:
|
|
20
|
+
|
|
21
|
+
- **Plugin architecture**: Modular design with built-in server and analytics plugins
|
|
22
|
+
- **Type safety**: End-to-end TypeScript with automatic query type generation
|
|
23
|
+
- **Production-ready features**: Built-in caching, telemetry, retry logic, and error handling
|
|
24
|
+
- **Developer experience**: Remote hot reload, file-based queries, optimized for AI-assisted development
|
|
25
|
+
- **Databricks native**: Seamless integration with SQL Warehouses, Unity Catalog, and other workspace resources
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
## Getting started
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
Follow the [Getting Started](https://databricks.github.io/appkit/docs/) guide to get started with AppKit.
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
# From root
|
|
26
|
-
pnpm docs:dev # Start dev server
|
|
27
|
-
pnpm docs:build # Build docs
|
|
28
|
-
pnpm docs:serve # Serve built docs
|
|
29
|
-
```
|
|
31
|
+
## Documentation
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
📖 For full AppKit documentation, visit the [AppKit Documentation](https://databricks.github.io/appkit/) website.
|
|
32
34
|
|
|
33
35
|
👉 For AI/code assistants:
|
|
34
36
|
- Use [llms-compact.txt](./llms-compact.txt) for quick usage patterns.
|
|
35
|
-
- See [llms.txt](./llms.txt) for full guidance and anti-patterns.
|
|
37
|
+
- See [llms.txt](./llms.txt) for full guidance and anti-patterns.
|
|
38
|
+
|
|
39
|
+
## Contributing
|
|
40
|
+
|
|
41
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AST-based linting using ast-grep.
|
|
4
|
+
* Catches patterns that ESLint/TypeScript miss or handle poorly.
|
|
5
|
+
* Usage: npx appkit-lint
|
|
6
|
+
*/
|
|
7
|
+
import { parse, Lang } from "@ast-grep/napi";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
const rules = [
|
|
12
|
+
{
|
|
13
|
+
id: "no-double-type-assertion",
|
|
14
|
+
pattern: "$X as unknown as $Y",
|
|
15
|
+
message:
|
|
16
|
+
"Avoid double type assertion (as unknown as). Use proper type guards or fix the source type.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "no-as-any",
|
|
20
|
+
pattern: "$X as any",
|
|
21
|
+
message:
|
|
22
|
+
'Avoid "as any" type assertion. Use proper typing or unknown with type guards.',
|
|
23
|
+
includeTests: false, // acceptable in test mocks
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "no-array-index-key",
|
|
27
|
+
pattern: "key={$IDX}",
|
|
28
|
+
message:
|
|
29
|
+
"Avoid using array index as React key. Use a stable unique identifier.",
|
|
30
|
+
filter: (code) => /key=\{(idx|index|i)\}/.test(code),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "no-parse-float-without-validation",
|
|
34
|
+
pattern: "parseFloat($X).toFixed($Y)",
|
|
35
|
+
message:
|
|
36
|
+
"parseFloat can return NaN. Validate input or use toNumber() helper from shared/types.ts.",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function isTestFile(filePath) {
|
|
41
|
+
return (
|
|
42
|
+
/\.(test|spec)\.(ts|tsx)$/.test(filePath) || filePath.includes("/tests/")
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function findTsFiles(dir, files = []) {
|
|
47
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
48
|
+
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const fullPath = path.join(dir, entry.name);
|
|
51
|
+
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
if (["node_modules", "dist", "build", ".git"].includes(entry.name))
|
|
54
|
+
continue;
|
|
55
|
+
findTsFiles(fullPath, files);
|
|
56
|
+
} else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name)) {
|
|
57
|
+
files.push(fullPath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return files;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function lintFile(filePath, rules) {
|
|
65
|
+
const violations = [];
|
|
66
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
67
|
+
const lang = filePath.endsWith(".tsx") ? Lang.Tsx : Lang.TypeScript;
|
|
68
|
+
const testFile = isTestFile(filePath);
|
|
69
|
+
|
|
70
|
+
const ast = parse(lang, content);
|
|
71
|
+
const root = ast.root();
|
|
72
|
+
|
|
73
|
+
for (const rule of rules) {
|
|
74
|
+
// skip rules that don't apply to test files
|
|
75
|
+
if (testFile && rule.includeTests === false) continue;
|
|
76
|
+
|
|
77
|
+
const matches = root.findAll(rule.pattern);
|
|
78
|
+
|
|
79
|
+
for (const match of matches) {
|
|
80
|
+
const code = match.text();
|
|
81
|
+
|
|
82
|
+
if (rule.filter && !rule.filter(code)) continue;
|
|
83
|
+
|
|
84
|
+
const range = match.range();
|
|
85
|
+
violations.push({
|
|
86
|
+
file: filePath,
|
|
87
|
+
line: range.start.line + 1,
|
|
88
|
+
column: range.start.column + 1,
|
|
89
|
+
rule: rule.id,
|
|
90
|
+
message: rule.message,
|
|
91
|
+
code: code.length > 80 ? `${code.slice(0, 77)}...` : code,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return violations;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function main() {
|
|
100
|
+
const rootDir = process.cwd();
|
|
101
|
+
const files = findTsFiles(rootDir);
|
|
102
|
+
|
|
103
|
+
console.log(`Scanning ${files.length} TypeScript files...\n`);
|
|
104
|
+
|
|
105
|
+
const allViolations = [];
|
|
106
|
+
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
const violations = lintFile(file, rules);
|
|
109
|
+
allViolations.push(...violations);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (allViolations.length === 0) {
|
|
113
|
+
console.log("No ast-grep lint violations found.");
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`Found ${allViolations.length} violation(s):\n`);
|
|
118
|
+
|
|
119
|
+
for (const v of allViolations) {
|
|
120
|
+
const relPath = path.relative(rootDir, v.file);
|
|
121
|
+
console.log(`${relPath}:${v.line}:${v.column}`);
|
|
122
|
+
console.log(` ${v.rule}: ${v.message}`);
|
|
123
|
+
console.log(` > ${v.code}\n`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main();
|
|
@@ -3,28 +3,53 @@ import { SQLTypeMarker } from "../shared/src/sql/types.js";
|
|
|
3
3
|
import { Plugin } from "../plugin/plugin.js";
|
|
4
4
|
import { IAnalyticsConfig } from "./types.js";
|
|
5
5
|
import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
6
|
+
import express from "express";
|
|
6
7
|
|
|
7
8
|
//#region src/analytics/analytics.d.ts
|
|
8
9
|
declare class AnalyticsPlugin extends Plugin {
|
|
9
10
|
name: string;
|
|
10
11
|
envVars: never[];
|
|
11
|
-
requiresDatabricksClient: boolean;
|
|
12
12
|
protected static description: string;
|
|
13
13
|
protected config: IAnalyticsConfig;
|
|
14
14
|
private SQLClient;
|
|
15
15
|
private queryProcessor;
|
|
16
16
|
constructor(config: IAnalyticsConfig);
|
|
17
17
|
injectRoutes(router: IAppRouter): void;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Handle Arrow data download requests.
|
|
20
|
+
* When called via asUser(req), uses the user's Databricks credentials.
|
|
21
|
+
*/
|
|
22
|
+
_handleArrowRoute(req: express.Request, res: express.Response): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Handle SQL query execution requests.
|
|
25
|
+
* When called via asUser(req), uses the user's Databricks credentials.
|
|
26
|
+
*/
|
|
27
|
+
_handleQueryRoute(req: express.Request, res: express.Response): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Execute a SQL query using the current execution context.
|
|
30
|
+
*
|
|
31
|
+
* When called directly: uses service principal credentials.
|
|
32
|
+
* When called via asUser(req).query(...): uses user's credentials.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Service principal execution
|
|
37
|
+
* const result = await analytics.query("SELECT * FROM table")
|
|
38
|
+
*
|
|
39
|
+
* // User context execution (in route handler)
|
|
40
|
+
* const result = await this.asUser(req).query("SELECT * FROM table")
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
query(query: string, parameters?: Record<string, SQLTypeMarker | null | undefined>, formatParameters?: Record<string, any>, signal?: AbortSignal): Promise<any>;
|
|
44
|
+
/**
|
|
45
|
+
* Get Arrow-formatted data for a completed query job.
|
|
46
|
+
*/
|
|
25
47
|
protected getArrowData(workspaceClient: WorkspaceClient, jobId: string, signal?: AbortSignal): Promise<ReturnType<typeof this.SQLClient.getArrowData>>;
|
|
26
48
|
shutdown(): Promise<void>;
|
|
27
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
28
53
|
declare const analytics: ToPlugin<typeof AnalyticsPlugin, IAnalyticsConfig, "analytics">;
|
|
29
54
|
//#endregion
|
|
30
55
|
export { analytics };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.d.ts","names":[],"sources":["../../src/analytics/analytics.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","names":[],"sources":["../../src/analytics/analytics.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0Ba,eAAA,SAAwB,MAAA;;;EAAxB,iBAAA,WAAgB,EAAA,MAAA;EAAA,UAAA,MAAA,EAKD,gBALC;UAKD,SAAA;UAMN,cAAA;aAWC,CAAA,MAAA,EAXD,gBAWC;cA6CN,CAAA,MAAA,EA7CM,UA6CN,CAAA,EAAA,IAAA;;;;;mBA2CZ,CAAA,GAAA,EA3CI,OAAA,CAAQ,OA2CZ,EAAA,GAAA,EA1CI,OAAA,CAAQ,QA0CZ,CAAA,EAzCA,OAyCA,CAAA,IAAA,CAAA;;;;;mBA8GA,CAAA,GAAA,EAhHI,OAAA,CAAQ,OAgHZ,EAAA,GAAA,EA/GI,OAAA,CAAQ,QA+GZ,CAAA,EA9GA,OA8GA,CAAA,IAAA,CAAA;;;;;;;;AAwCL;;;;;;;;oCA3CiB,eAAe,sDACT,8BACV,cACR;;;;0CAyBgB,yCAER,cACR,QAAQ;cAIO;;;;;cAQP,WAAS,gBAAA,iBAAA"}
|