@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.
Files changed (163) hide show
  1. package/AGENTS.md +89 -12
  2. package/CLAUDE.md +89 -12
  3. package/NOTICE.md +4 -0
  4. package/README.md +21 -15
  5. package/bin/appkit-lint.js +129 -0
  6. package/dist/analytics/analytics.d.ts +33 -8
  7. package/dist/analytics/analytics.d.ts.map +1 -1
  8. package/dist/analytics/analytics.js +67 -27
  9. package/dist/analytics/analytics.js.map +1 -1
  10. package/dist/analytics/defaults.js.map +1 -1
  11. package/dist/analytics/query.js +12 -6
  12. package/dist/analytics/query.js.map +1 -1
  13. package/dist/app/index.d.ts.map +1 -1
  14. package/dist/app/index.js +7 -5
  15. package/dist/app/index.js.map +1 -1
  16. package/dist/appkit/package.js +1 -1
  17. package/dist/cache/defaults.js.map +1 -1
  18. package/dist/cache/index.d.ts +1 -0
  19. package/dist/cache/index.d.ts.map +1 -1
  20. package/dist/cache/index.js +25 -5
  21. package/dist/cache/index.js.map +1 -1
  22. package/dist/cache/storage/memory.js.map +1 -1
  23. package/dist/cache/storage/persistent.js +12 -6
  24. package/dist/cache/storage/persistent.js.map +1 -1
  25. package/dist/connectors/lakebase/client.js +31 -21
  26. package/dist/connectors/lakebase/client.js.map +1 -1
  27. package/dist/connectors/lakebase/defaults.js.map +1 -1
  28. package/dist/connectors/sql-warehouse/client.js +68 -28
  29. package/dist/connectors/sql-warehouse/client.js.map +1 -1
  30. package/dist/connectors/sql-warehouse/defaults.js.map +1 -1
  31. package/dist/context/execution-context.js +75 -0
  32. package/dist/context/execution-context.js.map +1 -0
  33. package/dist/context/index.js +27 -0
  34. package/dist/context/index.js.map +1 -0
  35. package/dist/context/service-context.js +154 -0
  36. package/dist/context/service-context.js.map +1 -0
  37. package/dist/context/user-context.js +15 -0
  38. package/dist/context/user-context.js.map +1 -0
  39. package/dist/core/appkit.d.ts +3 -0
  40. package/dist/core/appkit.d.ts.map +1 -1
  41. package/dist/core/appkit.js +7 -0
  42. package/dist/core/appkit.js.map +1 -1
  43. package/dist/errors/authentication.d.ts +38 -0
  44. package/dist/errors/authentication.d.ts.map +1 -0
  45. package/dist/errors/authentication.js +48 -0
  46. package/dist/errors/authentication.js.map +1 -0
  47. package/dist/errors/base.d.ts +58 -0
  48. package/dist/errors/base.d.ts.map +1 -0
  49. package/dist/errors/base.js +70 -0
  50. package/dist/errors/base.js.map +1 -0
  51. package/dist/errors/configuration.d.ts +38 -0
  52. package/dist/errors/configuration.d.ts.map +1 -0
  53. package/dist/errors/configuration.js +45 -0
  54. package/dist/errors/configuration.js.map +1 -0
  55. package/dist/errors/connection.d.ts +42 -0
  56. package/dist/errors/connection.d.ts.map +1 -0
  57. package/dist/errors/connection.js +54 -0
  58. package/dist/errors/connection.js.map +1 -0
  59. package/dist/errors/execution.d.ts +42 -0
  60. package/dist/errors/execution.d.ts.map +1 -0
  61. package/dist/errors/execution.js +51 -0
  62. package/dist/errors/execution.js.map +1 -0
  63. package/dist/errors/index.js +28 -0
  64. package/dist/errors/index.js.map +1 -0
  65. package/dist/errors/initialization.d.ts +34 -0
  66. package/dist/errors/initialization.d.ts.map +1 -0
  67. package/dist/errors/initialization.js +42 -0
  68. package/dist/errors/initialization.js.map +1 -0
  69. package/dist/errors/server.d.ts +38 -0
  70. package/dist/errors/server.d.ts.map +1 -0
  71. package/dist/errors/server.js +45 -0
  72. package/dist/errors/server.js.map +1 -0
  73. package/dist/errors/tunnel.d.ts +38 -0
  74. package/dist/errors/tunnel.d.ts.map +1 -0
  75. package/dist/errors/tunnel.js +51 -0
  76. package/dist/errors/tunnel.js.map +1 -0
  77. package/dist/errors/validation.d.ts +36 -0
  78. package/dist/errors/validation.d.ts.map +1 -0
  79. package/dist/errors/validation.js +45 -0
  80. package/dist/errors/validation.js.map +1 -0
  81. package/dist/index.d.ts +12 -4
  82. package/dist/index.js +12 -4
  83. package/dist/index.js.map +1 -1
  84. package/dist/logging/logger.js +179 -0
  85. package/dist/logging/logger.js.map +1 -0
  86. package/dist/logging/sampling.js +56 -0
  87. package/dist/logging/sampling.js.map +1 -0
  88. package/dist/logging/wide-event-emitter.js +108 -0
  89. package/dist/logging/wide-event-emitter.js.map +1 -0
  90. package/dist/logging/wide-event.js +167 -0
  91. package/dist/logging/wide-event.js.map +1 -0
  92. package/dist/plugin/dev-reader.d.ts.map +1 -1
  93. package/dist/plugin/dev-reader.js +8 -3
  94. package/dist/plugin/dev-reader.js.map +1 -1
  95. package/dist/plugin/interceptors/cache.js.map +1 -1
  96. package/dist/plugin/interceptors/retry.js +10 -2
  97. package/dist/plugin/interceptors/retry.js.map +1 -1
  98. package/dist/plugin/interceptors/telemetry.js +24 -9
  99. package/dist/plugin/interceptors/telemetry.js.map +1 -1
  100. package/dist/plugin/interceptors/timeout.js +4 -0
  101. package/dist/plugin/interceptors/timeout.js.map +1 -1
  102. package/dist/plugin/plugin.d.ts +38 -4
  103. package/dist/plugin/plugin.d.ts.map +1 -1
  104. package/dist/plugin/plugin.js +86 -5
  105. package/dist/plugin/plugin.js.map +1 -1
  106. package/dist/plugin/to-plugin.d.ts +4 -0
  107. package/dist/plugin/to-plugin.d.ts.map +1 -1
  108. package/dist/plugin/to-plugin.js +3 -0
  109. package/dist/plugin/to-plugin.js.map +1 -1
  110. package/dist/server/index.d.ts +3 -0
  111. package/dist/server/index.d.ts.map +1 -1
  112. package/dist/server/index.js +25 -21
  113. package/dist/server/index.js.map +1 -1
  114. package/dist/server/remote-tunnel/remote-tunnel-controller.js +4 -2
  115. package/dist/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
  116. package/dist/server/remote-tunnel/remote-tunnel-manager.js +10 -8
  117. package/dist/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  118. package/dist/server/utils.js.map +1 -1
  119. package/dist/server/vite-dev-server.js +8 -5
  120. package/dist/server/vite-dev-server.js.map +1 -1
  121. package/dist/shared/src/sql/helpers.js.map +1 -1
  122. package/dist/stream/arrow-stream-processor.js +13 -6
  123. package/dist/stream/arrow-stream-processor.js.map +1 -1
  124. package/dist/stream/buffers.js +5 -1
  125. package/dist/stream/buffers.js.map +1 -1
  126. package/dist/stream/sse-writer.js.map +1 -1
  127. package/dist/stream/stream-manager.d.ts.map +1 -1
  128. package/dist/stream/stream-manager.js +47 -36
  129. package/dist/stream/stream-manager.js.map +1 -1
  130. package/dist/stream/stream-registry.js.map +1 -1
  131. package/dist/stream/types.js.map +1 -1
  132. package/dist/telemetry/index.d.ts +2 -2
  133. package/dist/telemetry/index.js +2 -2
  134. package/dist/telemetry/instrumentations.js +14 -10
  135. package/dist/telemetry/instrumentations.js.map +1 -1
  136. package/dist/telemetry/telemetry-manager.js +8 -6
  137. package/dist/telemetry/telemetry-manager.js.map +1 -1
  138. package/dist/telemetry/trace-sampler.js +33 -0
  139. package/dist/telemetry/trace-sampler.js.map +1 -0
  140. package/dist/type-generator/index.js +4 -2
  141. package/dist/type-generator/index.js.map +1 -1
  142. package/dist/type-generator/query-registry.js +4 -2
  143. package/dist/type-generator/query-registry.js.map +1 -1
  144. package/dist/type-generator/types.js.map +1 -1
  145. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  146. package/dist/type-generator/vite-plugin.js +5 -3
  147. package/dist/type-generator/vite-plugin.js.map +1 -1
  148. package/dist/utils/env-validator.js +5 -5
  149. package/dist/utils/env-validator.js.map +1 -1
  150. package/dist/utils/merge.js +1 -5
  151. package/dist/utils/merge.js.map +1 -1
  152. package/dist/utils/path-exclusions.js +66 -0
  153. package/dist/utils/path-exclusions.js.map +1 -0
  154. package/dist/utils/vite-config-merge.js +1 -5
  155. package/dist/utils/vite-config-merge.js.map +1 -1
  156. package/llms.txt +89 -12
  157. package/package.json +6 -1
  158. package/dist/utils/databricks-client-middleware.d.ts +0 -17
  159. package/dist/utils/databricks-client-middleware.d.ts.map +0 -1
  160. package/dist/utils/databricks-client-middleware.js +0 -117
  161. package/dist/utils/databricks-client-middleware.js.map +0 -1
  162. package/dist/utils/index.js +0 -26
  163. 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
- ### Request context (`getRequestContext()`)
443
+ ### Execution context and `asUser(req)`
444
444
 
445
- If a plugin sets `requiresDatabricksClient = true`, AppKit adds middleware that provides request context.
445
+ AppKit manages Databricks authentication via two contexts:
446
446
 
447
- Headers used:
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`: optional; enables **user token passthrough** if `DATABRICKS_HOST` is set
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
- Context fields (real behavior):
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
- - `userId`: derived from `x-forwarded-user` (in development it falls back to `serviceUserId`)
455
- - `serviceUserId`: service principal/user ID
456
- - `warehouseId`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID`, or auto-selected in development)
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
- ### Request context (`getRequestContext()`)
443
+ ### Execution context and `asUser(req)`
444
444
 
445
- If a plugin sets `requiresDatabricksClient = true`, AppKit adds middleware that provides request context.
445
+ AppKit manages Databricks authentication via two contexts:
446
446
 
447
- Headers used:
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`: optional; enables **user token passthrough** if `DATABRICKS_HOST` is set
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
- Context fields (real behavior):
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
- - `userId`: derived from `x-forwarded-user` (in development it falls back to `serviceUserId`)
455
- - `serviceUserId`: service principal/user ID
456
- - `warehouseId`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID`, or auto-selected in development)
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
- > [!WARNING]
4
- > ## ⚠️ PREVIEW - NOT FOR PRODUCTION USE
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
- ## Contributing
15
+ ## Introduction
15
16
 
16
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.
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
- ## Documentation
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
- The `docs/` directory contains the AppKit documentation site, built with Docusaurus.
27
+ ## Getting started
21
28
 
22
- **Working with docs:**
29
+ Follow the [Getting Started](https://databricks.github.io/appkit/docs/) guide to get started with AppKit.
23
30
 
24
- ```bash
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
- See [docs/README.md](./docs/README.md) for more details.
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
- private _handleArrowRoute;
19
- private _handleQueryRoute;
20
- query(query: string, parameters?: Record<string, SQLTypeMarker | null | undefined>, formatParameters?: Record<string, any>, signal?: AbortSignal, {
21
- asUser
22
- }?: {
23
- asUser?: boolean;
24
- }): Promise<any>;
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":";;;;;;;cAmBa,eAAA,SAAwB,MAAA;;;EAAxB,wBAAgB,EAAA,OAAA;EAAA,iBAAA,WAAA,EAAA,MAAA;YAMD,MAAA,EAAA,gBAAA;UAMN,SAAA;UAWC,cAAA;aAgKS,CAAA,MAAA,EA3KV,gBA2KU;cAAf,CAAA,MAAA,EAhKM,UAgKN,CAAA,EAAA,IAAA;UACM,iBAAA;UACV,iBAAA;OACP,CAAA,KAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAHW,MAGX,CAAA,MAAA,EAH0B,aAG1B,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA,gBAAA,CAAA,EAFiB,MAEjB,CAAA,MAAA,EAAA,GAAA,CAAA,EAAA,MAAA,CAAA,EADO,WACP,EAAA;IAAA;IAAA,EAAA;IACD,MAAA,CAAA,EAAA,OAAA;MAAA,OAwBgB,CAAA,GAAA,CAAA;YAER,YAAA,CAAA,eAAA,EAFQ,eAER,EAAA,KAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,WAAA,CAAA,EACR,OADQ,CACA,UADA,CAAA,OAAA,IAAA,CAAA,SAAA,CAAA,YAAA,CAAA,CAAA;UACA,CAAA,CAAA,EAIO,OAJP,CAAA,IAAA,CAAA;;AAIO,cAKP,SALO,EAKE,QALF,CAAA,OAKE,eALF,EAKE,gBALF,EAAA,WAAA,CAAA"}
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"}