@databricks/appkit 0.18.0 → 0.19.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 (91) hide show
  1. package/CLAUDE.md +8 -1
  2. package/dist/appkit/package.js +1 -1
  3. package/dist/cache/index.d.ts.map +1 -1
  4. package/dist/cache/index.js +2 -2
  5. package/dist/cache/index.js.map +1 -1
  6. package/dist/cli/commands/plugin/create/scaffold.js +2 -8
  7. package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
  8. package/dist/connectors/files/client.js +223 -0
  9. package/dist/connectors/files/client.js.map +1 -0
  10. package/dist/connectors/files/defaults.js +131 -0
  11. package/dist/connectors/files/defaults.js.map +1 -0
  12. package/dist/connectors/files/index.js +4 -0
  13. package/dist/connectors/index.js +3 -0
  14. package/dist/context/execution-context.js +7 -1
  15. package/dist/context/execution-context.js.map +1 -1
  16. package/dist/context/index.js +1 -1
  17. package/dist/core/appkit.d.ts.map +1 -1
  18. package/dist/core/appkit.js +24 -4
  19. package/dist/core/appkit.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/plugin/plugin.d.ts +24 -5
  24. package/dist/plugin/plugin.d.ts.map +1 -1
  25. package/dist/plugin/plugin.js +43 -10
  26. package/dist/plugin/plugin.js.map +1 -1
  27. package/dist/plugin/to-plugin.d.ts +5 -2
  28. package/dist/plugin/to-plugin.d.ts.map +1 -1
  29. package/dist/plugin/to-plugin.js +5 -2
  30. package/dist/plugin/to-plugin.js.map +1 -1
  31. package/dist/plugins/analytics/analytics.d.ts +1 -2
  32. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  33. package/dist/plugins/analytics/analytics.js +1 -2
  34. package/dist/plugins/analytics/analytics.js.map +1 -1
  35. package/dist/plugins/files/defaults.d.ts +1 -0
  36. package/dist/plugins/files/defaults.js +56 -0
  37. package/dist/plugins/files/defaults.js.map +1 -0
  38. package/dist/plugins/files/helpers.js +30 -0
  39. package/dist/plugins/files/helpers.js.map +1 -0
  40. package/dist/plugins/files/index.d.ts +3 -0
  41. package/dist/plugins/files/index.js +5 -0
  42. package/dist/plugins/files/manifest.js +40 -0
  43. package/dist/plugins/files/manifest.js.map +1 -0
  44. package/dist/plugins/files/plugin.d.ts +105 -0
  45. package/dist/plugins/files/plugin.d.ts.map +1 -0
  46. package/dist/plugins/files/plugin.js +714 -0
  47. package/dist/plugins/files/plugin.js.map +1 -0
  48. package/dist/plugins/files/types.d.ts +105 -0
  49. package/dist/plugins/files/types.d.ts.map +1 -0
  50. package/dist/plugins/genie/genie.d.ts +1 -2
  51. package/dist/plugins/genie/genie.d.ts.map +1 -1
  52. package/dist/plugins/genie/genie.js +1 -2
  53. package/dist/plugins/genie/genie.js.map +1 -1
  54. package/dist/plugins/index.d.ts +3 -0
  55. package/dist/plugins/index.js +4 -0
  56. package/dist/plugins/lakebase/lakebase.d.ts +1 -2
  57. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  58. package/dist/plugins/lakebase/lakebase.js +1 -2
  59. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  60. package/dist/plugins/server/index.d.ts +2 -2
  61. package/dist/plugins/server/index.d.ts.map +1 -1
  62. package/dist/plugins/server/index.js +9 -4
  63. package/dist/plugins/server/index.js.map +1 -1
  64. package/dist/registry/manifest-loader.js +1 -1
  65. package/dist/registry/manifest-loader.js.map +1 -1
  66. package/dist/registry/types.d.ts +3 -3
  67. package/dist/registry/types.d.ts.map +1 -1
  68. package/dist/registry/types.js.map +1 -1
  69. package/dist/shared/src/plugin.d.ts +12 -4
  70. package/dist/shared/src/plugin.d.ts.map +1 -1
  71. package/docs/api/appkit/Class.Plugin.md +60 -12
  72. package/docs/api/appkit/Class.ResourceRegistry.md +3 -3
  73. package/docs/api/appkit/Function.createApp.md +3 -3
  74. package/docs/api/appkit/Interface.PluginManifest.md +9 -3
  75. package/docs/api/appkit/TypeAlias.PluginData.md +45 -0
  76. package/docs/api/appkit/TypeAlias.ToPlugin.md +1 -1
  77. package/docs/api/appkit-ui/files/DirectoryList.md +36 -0
  78. package/docs/api/appkit-ui/files/FileBreadcrumb.md +27 -0
  79. package/docs/api/appkit-ui/files/FileEntry.md +27 -0
  80. package/docs/api/appkit-ui/files/FilePreviewPanel.md +32 -0
  81. package/docs/api/appkit-ui/files/NewFolderInput.md +30 -0
  82. package/docs/api/appkit.md +1 -0
  83. package/docs/configuration.md +15 -0
  84. package/docs/plugins/custom-plugins.md +4 -13
  85. package/docs/plugins/files.md +350 -0
  86. package/docs/plugins.md +2 -1
  87. package/llms.txt +8 -1
  88. package/package.json +1 -1
  89. package/dist/plugins/server/remote-tunnel/denied.html/denied.html +0 -68
  90. package/dist/plugins/server/remote-tunnel/index.html/index.html +0 -165
  91. package/dist/plugins/server/remote-tunnel/wait.html/wait.html +0 -158
@@ -11,9 +11,8 @@ import express from "express";
11
11
 
12
12
  //#region src/plugins/analytics/analytics.d.ts
13
13
  declare class AnalyticsPlugin extends Plugin {
14
- name: string;
15
14
  /** Plugin manifest declaring metadata and resource requirements */
16
- static manifest: PluginManifest;
15
+ static manifest: PluginManifest<"analytics">;
17
16
  protected static description: string;
18
17
  protected config: IAnalyticsConfig;
19
18
  private SQLClient;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;cA4Ba,eAAA,SAAwB,MAAA;EACnC,IAAA;;SAGO,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EAjBK;;;;EA0CpB,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAAA;;;;EAsCG,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EA+GY;;;;;;;;;;;;;;;EAFT,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EA/MsC;;;EAAA,UAuOzB,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EAzOD;;;;EAiPjB,OAAA,CAAA;;;;2BA5Ce,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAqDQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA"}
1
+ {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;cA4Ba,eAAA,SAAwB,MAAA;;SAE5B,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EApBS;;;;EA6CxB,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EADI;;;;EAuCD,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EA+G2B;;;;;;;;;;;;;;;EAFxB,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EA7MsC;;;EAAA,UAqOzB,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EAzOD;;;;EAiPjB,OAAA,CAAA;;;;2BA5Ce,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAqDQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA"}
@@ -14,7 +14,6 @@ import { QueryProcessor } from "./query.js";
14
14
  init_context();
15
15
  const logger = createLogger("analytics");
16
16
  var AnalyticsPlugin = class extends Plugin {
17
- name = "analytics";
18
17
  /** Plugin manifest declaring metadata and resource requirements */
19
18
  static manifest = manifest_default;
20
19
  static description = "Analytics plugin for data analysis";
@@ -178,7 +177,7 @@ var AnalyticsPlugin = class extends Plugin {
178
177
  /**
179
178
  * @internal
180
179
  */
181
- const analytics = toPlugin(AnalyticsPlugin, "analytics");
180
+ const analytics = toPlugin(AnalyticsPlugin);
182
181
 
183
182
  //#endregion
184
183
  export { AnalyticsPlugin, analytics };
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n} from \"shared\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport {\n getCurrentUserId,\n getWarehouseId,\n getWorkspaceClient,\n} from \"../../context\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin {\n name = \"analytics\";\n\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Service principal endpoints\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format = \"JSON\" } = req.body as IAnalyticsQueryRequest;\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const userKey = getCurrentUserId();\n const executorKey = isAsUser ? userKey : \"global\";\n\n const queryParameters =\n format === \"ARROW\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin<\n typeof AnalyticsPlugin,\n IAnalyticsConfig,\n \"analytics\"\n>(AnalyticsPlugin, \"analytics\");\n"],"mappings":"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"}
1
+ {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n} from \"shared\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport {\n getCurrentUserId,\n getWarehouseId,\n getWorkspaceClient,\n} from \"../../context\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin {\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest<\"analytics\">;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Service principal endpoints\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format = \"JSON\" } = req.body as IAnalyticsQueryRequest;\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const userKey = getCurrentUserId();\n const executorKey = isAsUser ? userKey : \"global\";\n\n const queryParameters =\n format === \"ARROW\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin(AnalyticsPlugin);\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import "../../shared/src/index.js";
@@ -0,0 +1,56 @@
1
+ import { EXTENSION_CONTENT_TYPES, FILES_MAX_READ_SIZE } from "../../connectors/files/defaults.js";
2
+
3
+ //#region src/plugins/files/defaults.ts
4
+ /**
5
+ * Execution defaults for read-tier operations (list, read, exists, metadata, preview).
6
+ * Cache 60s (ttl in seconds)
7
+ * Retry 3x with 1s backoff
8
+ * Timeout 30s
9
+ **/
10
+ const FILES_READ_DEFAULTS = {
11
+ cache: {
12
+ enabled: true,
13
+ ttl: 60
14
+ },
15
+ retry: {
16
+ enabled: true,
17
+ initialDelay: 1e3,
18
+ attempts: 3
19
+ },
20
+ timeout: 3e4
21
+ };
22
+ /**
23
+ * Execution defaults for download-tier operations (download, raw).
24
+ * No cache
25
+ * Retry 3x with 1s backoff
26
+ * Timeout 30s (stream start only)
27
+ **/
28
+ const FILES_DOWNLOAD_DEFAULTS = {
29
+ cache: { enabled: false },
30
+ retry: {
31
+ enabled: true,
32
+ initialDelay: 1e3,
33
+ attempts: 3
34
+ },
35
+ timeout: 3e4
36
+ };
37
+ /**
38
+ * Execution defaults for write-tier operations (upload, mkdir, delete).
39
+ * No cache
40
+ * No retry
41
+ * Timeout 600s.
42
+ **/
43
+ const FILES_WRITE_DEFAULTS = {
44
+ cache: { enabled: false },
45
+ retry: { enabled: false },
46
+ timeout: 6e5
47
+ };
48
+ /**
49
+ * Default maximum upload size in bytes (5 GB).
50
+ * This matches the Databricks Files API v2 per-file limit.
51
+ */
52
+ const FILES_MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
53
+
54
+ //#endregion
55
+ export { FILES_DOWNLOAD_DEFAULTS, FILES_MAX_UPLOAD_SIZE, FILES_READ_DEFAULTS, FILES_WRITE_DEFAULTS };
56
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","names":[],"sources":["../../../src/plugins/files/defaults.ts"],"sourcesContent":["import type { PluginExecuteConfig } from \"shared\";\n\n/**\n * Execution defaults for read-tier operations (list, read, exists, metadata, preview).\n * Cache 60s (ttl in seconds)\n * Retry 3x with 1s backoff\n * Timeout 30s\n **/\nexport const FILES_READ_DEFAULTS: PluginExecuteConfig = {\n cache: {\n enabled: true,\n ttl: 60,\n },\n retry: {\n enabled: true,\n initialDelay: 1000,\n attempts: 3,\n },\n timeout: 30_000,\n};\n\n/**\n * Execution defaults for download-tier operations (download, raw).\n * No cache\n * Retry 3x with 1s backoff\n * Timeout 30s (stream start only)\n **/\nexport const FILES_DOWNLOAD_DEFAULTS: PluginExecuteConfig = {\n cache: {\n enabled: false,\n },\n retry: {\n enabled: true,\n initialDelay: 1000,\n attempts: 3,\n },\n /**\n * @info this timeout is for the stream to start, not for the full download.\n */\n timeout: 30_000,\n};\n\n/**\n * Execution defaults for write-tier operations (upload, mkdir, delete).\n * No cache\n * No retry\n * Timeout 600s.\n **/\nexport const FILES_WRITE_DEFAULTS: PluginExecuteConfig = {\n cache: {\n enabled: false,\n },\n retry: {\n enabled: false,\n },\n timeout: 600_000,\n};\n\n/**\n * Default maximum upload size in bytes (5 GB).\n * This matches the Databricks Files API v2 per-file limit.\n */\nexport const FILES_MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024; // 5 GB\n\nexport {\n EXTENSION_CONTENT_TYPES,\n FILES_MAX_READ_SIZE,\n} from \"../../connectors/files/defaults\";\n"],"mappings":";;;;;;;;;AAQA,MAAa,sBAA2C;CACtD,OAAO;EACL,SAAS;EACT,KAAK;EACN;CACD,OAAO;EACL,SAAS;EACT,cAAc;EACd,UAAU;EACX;CACD,SAAS;CACV;;;;;;;AAQD,MAAa,0BAA+C;CAC1D,OAAO,EACL,SAAS,OACV;CACD,OAAO;EACL,SAAS;EACT,cAAc;EACd,UAAU;EACX;CAID,SAAS;CACV;;;;;;;AAQD,MAAa,uBAA4C;CACvD,OAAO,EACL,SAAS,OACV;CACD,OAAO,EACL,SAAS,OACV;CACD,SAAS;CACV;;;;;AAMD,MAAa,wBAAwB,IAAI,OAAO,OAAO"}
@@ -0,0 +1,30 @@
1
+ import { contentTypeFromPath, isTextContentType } from "../../connectors/files/defaults.js";
2
+
3
+ //#region src/plugins/files/helpers.ts
4
+ /**
5
+ * Extract the parent directory from a file or directory path.
6
+ *
7
+ * Handles edge cases such as root-level files (`"/file.txt"` → `"/"`),
8
+ * paths without slashes (`"file.txt"` → `""`), and trailing slashes.
9
+ */
10
+ function parentDirectory(path) {
11
+ const normalized = path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
12
+ const lastSlash = normalized.lastIndexOf("/");
13
+ if (lastSlash > 0) return normalized.substring(0, lastSlash);
14
+ if (normalized.startsWith("/")) return "/";
15
+ return "";
16
+ }
17
+ /**
18
+ * Sanitize a filename for use in a `Content-Disposition` HTTP header.
19
+ *
20
+ * Redundancy check – Unity Catalog is unlikely to allow filenames with
21
+ * quotes or control characters, but we sanitize defensively to prevent
22
+ * HTTP header injection if upstream constraints ever change.
23
+ */
24
+ function sanitizeFilename(raw) {
25
+ return raw.replace(/["\\]/g, "\\$&").replace(/[\x00-\x1f]/g, "");
26
+ }
27
+
28
+ //#endregion
29
+ export { parentDirectory, sanitizeFilename };
30
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","names":[],"sources":["../../../src/plugins/files/helpers.ts"],"sourcesContent":["export {\n contentTypeFromPath,\n isTextContentType,\n} from \"../../connectors/files/defaults\";\n\n/**\n * Extract the parent directory from a file or directory path.\n *\n * Handles edge cases such as root-level files (`\"/file.txt\"` → `\"/\"`),\n * paths without slashes (`\"file.txt\"` → `\"\"`), and trailing slashes.\n */\nexport function parentDirectory(path: string): string {\n const normalized =\n path.length > 1 && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n const lastSlash = normalized.lastIndexOf(\"/\");\n\n if (lastSlash > 0) return normalized.substring(0, lastSlash);\n if (normalized.startsWith(\"/\")) return \"/\";\n return \"\";\n}\n\n/**\n * Sanitize a filename for use in a `Content-Disposition` HTTP header.\n *\n * Redundancy check – Unity Catalog is unlikely to allow filenames with\n * quotes or control characters, but we sanitize defensively to prevent\n * HTTP header injection if upstream constraints ever change.\n */\nexport function sanitizeFilename(raw: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentionally stripping control chars for security\n return raw.replace(/[\"\\\\]/g, \"\\\\$&\").replace(/[\\x00-\\x1f]/g, \"\");\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,gBAAgB,MAAsB;CACpD,MAAM,aACJ,KAAK,SAAS,KAAK,KAAK,SAAS,IAAI,GAAG,KAAK,MAAM,GAAG,GAAG,GAAG;CAC9D,MAAM,YAAY,WAAW,YAAY,IAAI;AAE7C,KAAI,YAAY,EAAG,QAAO,WAAW,UAAU,GAAG,UAAU;AAC5D,KAAI,WAAW,WAAW,IAAI,CAAE,QAAO;AACvC,QAAO;;;;;;;;;AAUT,SAAgB,iBAAiB,KAAqB;AAEpD,QAAO,IAAI,QAAQ,UAAU,OAAO,CAAC,QAAQ,gBAAgB,GAAG"}
@@ -0,0 +1,3 @@
1
+ import "./defaults.js";
2
+ import { DirectoryEntry, DownloadResponse, FileMetadata, FilePreview, FilesExport, IFilesConfig, VolumeAPI, VolumeConfig, VolumeHandle } from "./types.js";
3
+ import { FilesPlugin, files } from "./plugin.js";
@@ -0,0 +1,5 @@
1
+ import { EXTENSION_CONTENT_TYPES, FILES_MAX_READ_SIZE } from "../../connectors/files/defaults.js";
2
+ import { FILES_DOWNLOAD_DEFAULTS, FILES_MAX_UPLOAD_SIZE, FILES_READ_DEFAULTS, FILES_WRITE_DEFAULTS } from "./defaults.js";
3
+ import { FilesPlugin, files } from "./plugin.js";
4
+
5
+ export { };
@@ -0,0 +1,40 @@
1
+ //#region src/plugins/files/manifest.json
2
+ var manifest_default = {
3
+ $schema: "https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json",
4
+ name: "files",
5
+ displayName: "Files Plugin",
6
+ description: "File operations against Databricks Volumes and Unity Catalog",
7
+ resources: {
8
+ "required": [{
9
+ "type": "volume",
10
+ "alias": "Files",
11
+ "resourceKey": "files",
12
+ "description": "Permission to write to volumes",
13
+ "permission": "WRITE_VOLUME",
14
+ "fields": { "path": {
15
+ "env": "DATABRICKS_VOLUME_FILES",
16
+ "description": "Volume path for file storage (e.g. /Volumes/catalog/schema/volume_name)"
17
+ } }
18
+ }],
19
+ "optional": []
20
+ },
21
+ config: { "schema": {
22
+ "type": "object",
23
+ "properties": {
24
+ "timeout": {
25
+ "type": "number",
26
+ "default": 3e4,
27
+ "description": "File operation timeout in milliseconds"
28
+ },
29
+ "maxUploadSize": {
30
+ "type": "number",
31
+ "default": 5368709120,
32
+ "description": "Maximum upload size in bytes. Defaults to 5 GB (Databricks Files API v2 limit)."
33
+ }
34
+ }
35
+ } }
36
+ };
37
+
38
+ //#endregion
39
+ export { manifest_default as default };
40
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","names":[],"sources":["../../../src/plugins/files/manifest.json"],"sourcesContent":[""],"mappings":""}
@@ -0,0 +1,105 @@
1
+ import { IAppRouter, ToPlugin } from "../../shared/src/plugin.js";
2
+ import "../../shared/src/index.js";
3
+ import { Plugin } from "../../plugin/plugin.js";
4
+ import "../../plugin/index.js";
5
+ import { PluginManifest, ResourceRequirement } from "../../registry/types.js";
6
+ import "../../registry/index.js";
7
+ import { FilesExport, IFilesConfig, VolumeAPI, VolumeConfig } from "./types.js";
8
+
9
+ //#region src/plugins/files/plugin.d.ts
10
+ declare class FilesPlugin extends Plugin {
11
+ name: string;
12
+ /** Plugin manifest declaring metadata and resource requirements. */
13
+ static manifest: PluginManifest;
14
+ protected static description: string;
15
+ protected config: IFilesConfig;
16
+ private volumeConnectors;
17
+ private volumeConfigs;
18
+ private volumeKeys;
19
+ /**
20
+ * Scans `process.env` for `DATABRICKS_VOLUME_*` keys and merges them with
21
+ * any explicitly configured volumes. Explicit config wins for per-volume
22
+ * overrides; auto-discovered volumes get default `{}` config.
23
+ */
24
+ static discoverVolumes(config: IFilesConfig): Record<string, VolumeConfig>;
25
+ /**
26
+ * Generates resource requirements dynamically from discovered + configured volumes.
27
+ * Each volume key maps to a `DATABRICKS_VOLUME_{KEY_UPPERCASE}` env var.
28
+ */
29
+ static getResourceRequirements(config: IFilesConfig): ResourceRequirement[];
30
+ /**
31
+ * Warns when a method is called without a user context (i.e. as service principal).
32
+ * OBO access via `asUser(req)` is strongly recommended.
33
+ */
34
+ private warnIfNoUserContext;
35
+ /**
36
+ * Throws when a method is called without a user context (i.e. as service principal).
37
+ * OBO access via `asUser(req)` is enforced for now.
38
+ */
39
+ private throwIfNoUserContext;
40
+ constructor(config: IFilesConfig);
41
+ /**
42
+ * Creates a VolumeAPI for a specific volume key.
43
+ * Each method warns if called outside a user context (service principal).
44
+ */
45
+ protected createVolumeAPI(volumeKey: string): VolumeAPI;
46
+ injectRoutes(router: IAppRouter): void;
47
+ /**
48
+ * Resolve `:volumeKey` from the request. Returns the connector and key,
49
+ * or sends a 404 and returns `{ connector: undefined }`.
50
+ */
51
+ private _resolveVolume;
52
+ /**
53
+ * Validate a file/directory path from user input.
54
+ * Returns `true` if valid, or an error message string if invalid.
55
+ */
56
+ private _isValidPath;
57
+ private _readSettings;
58
+ /**
59
+ * Invalidate cached list entries for a directory after a write operation.
60
+ * Uses the same cache-key format as `_handleList`: resolved path for
61
+ * subdirectories, `"__root__"` for the volume root.
62
+ */
63
+ private _invalidateListCache;
64
+ private _handleApiError;
65
+ private _handleList;
66
+ private _handleRead;
67
+ private _handleDownload;
68
+ private _handleRaw;
69
+ /**
70
+ * Shared handler for `/download` and `/raw` endpoints.
71
+ * - `download`: always forces `Content-Disposition: attachment`.
72
+ * - `raw`: adds CSP sandbox; forces attachment only for unsafe content types.
73
+ */
74
+ private _serveFile;
75
+ private _handleExists;
76
+ private _handleMetadata;
77
+ private _handlePreview;
78
+ private _handleUpload;
79
+ private _handleMkdir;
80
+ private _handleDelete;
81
+ private inflightWrites;
82
+ private trackWrite;
83
+ shutdown(): Promise<void>;
84
+ /**
85
+ * Returns the programmatic API for the Files plugin.
86
+ * Callable with a volume key to get a volume-scoped handle.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * // OBO access (recommended)
91
+ * appKit.files("uploads").asUser(req).list()
92
+ *
93
+ * // Service principal access (logs a warning)
94
+ * appKit.files("uploads").list()
95
+ * ```
96
+ */
97
+ exports(): FilesExport;
98
+ }
99
+ /**
100
+ * @internal
101
+ */
102
+ declare const files: ToPlugin<typeof FilesPlugin, IFilesConfig, string>;
103
+ //#endregion
104
+ export { FilesPlugin, files };
105
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../src/plugins/files/plugin.ts"],"mappings":";;;;;;;;;cAmCa,WAAA,SAAoB,MAAA;EAC/B,IAAA;;SAGO,QAAA,EAAuB,cAAA;EAAA,iBACb,WAAA;EAAA,UACC,MAAA,EAAQ,YAAA;EAAA,QAElB,gBAAA;EAAA,QACA,aAAA;EAAA,QACA,UAAA;EAJkB;;;;;EAAA,OAWnB,eAAA,CAAgB,MAAA,EAAQ,YAAA,GAAe,MAAA,SAAe,YAAA;EAkEzC;;;;EAAA,OA3Cb,uBAAA,CAAwB,MAAA,EAAQ,YAAA,GAAe,mBAAA;EAxCvB;;;;EAAA,QA8DvB,mBAAA;EA1DD;;;;EAAA,QAuEC,oBAAA;cAQI,MAAA,EAAQ,YAAA;EA1EZ;;;;EAAA,UA+GE,eAAA,CAAgB,SAAA,WAAoB,SAAA;EAmD9C,YAAA,CAAa,MAAA,EAAQ,UAAA;EA1JwC;;;;EAAA,QAwRrD,cAAA;EA3OA;;;;EAAA,QAkQA,YAAA;EAAA,QAQA,aAAA;EAhNkB;;;;;EAAA,QAgOlB,oBAAA;EAAA,QAiBA,eAAA;EAAA,QA8BM,WAAA;EAAA,QA+BA,WAAA;EAAA,QAoCA,eAAA;EAAA,QAWA,UAAA;EA/CA;;;;;EAAA,QA+DA,UAAA;EAAA,QAgFA,aAAA;EAAA,QAsCA,eAAA;EAAA,QAsCA,cAAA;EAAA,QAoCA,aAAA;EAAA,QA2GA,YAAA;EAAA,QA+CA,aAAA;EAAA,QA6CN,cAAA;EAAA,QAEA,UAAA;EAOF,QAAA,CAAA,GAAY,OAAA;EAgCP;;;AA+Bb;;;;;;;;;;EA/BE,OAAA,CAAA,GAAW,WAAA;AAAA;;;;cA+BA,KAAA,EAAK,QAAA,QAAA,WAAA,EAAA,YAAA"}