@databricks/appkit 0.37.0 → 0.38.1

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 (156) hide show
  1. package/CLAUDE.md +3 -2
  2. package/NOTICE.md +1 -2
  3. package/dist/appkit/package.js +1 -1
  4. package/dist/cache/index.d.ts +27 -5
  5. package/dist/cache/index.d.ts.map +1 -1
  6. package/dist/cache/index.js +88 -16
  7. package/dist/cache/index.js.map +1 -1
  8. package/dist/cache/storage/persistent.js +1 -2
  9. package/dist/cache/storage/persistent.js.map +1 -1
  10. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  11. package/dist/cli/commands/plugin/create/resource-defaults.js +2 -1
  12. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -1
  13. package/dist/cli/commands/plugin/schema-resources.js +34 -51
  14. package/dist/cli/commands/plugin/schema-resources.js.map +1 -1
  15. package/dist/cli/commands/plugin/sync/sync.js +20 -6
  16. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  17. package/dist/cli/commands/plugin/validate/validate-manifest.js +71 -157
  18. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  19. package/dist/cli/commands/plugin/validate/validate.js +2 -2
  20. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  21. package/dist/cli/commands/setup.js +2 -2
  22. package/dist/cli/commands/setup.js.map +1 -1
  23. package/dist/connectors/index.js +0 -1
  24. package/dist/connectors/jobs/client.js +1 -2
  25. package/dist/connectors/jobs/client.js.map +1 -1
  26. package/dist/connectors/lakebase/routing-pool.js +1 -2
  27. package/dist/connectors/lakebase/routing-pool.js.map +1 -1
  28. package/dist/connectors/sql-warehouse/client.js +1 -2
  29. package/dist/connectors/sql-warehouse/client.js.map +1 -1
  30. package/dist/context/execution-context.js +9 -13
  31. package/dist/context/execution-context.js.map +1 -1
  32. package/dist/context/index.js +3 -13
  33. package/dist/context/service-context.js +137 -136
  34. package/dist/context/service-context.js.map +1 -1
  35. package/dist/context/user-context.js +1 -5
  36. package/dist/context/user-context.js.map +1 -1
  37. package/dist/core/agent/create-agent.js +1 -2
  38. package/dist/core/agent/create-agent.js.map +1 -1
  39. package/dist/core/appkit.js +1 -2
  40. package/dist/core/appkit.js.map +1 -1
  41. package/dist/errors/authentication.js +44 -40
  42. package/dist/errors/authentication.js.map +1 -1
  43. package/dist/errors/base.js +86 -70
  44. package/dist/errors/base.js.map +1 -1
  45. package/dist/errors/configuration.js +70 -66
  46. package/dist/errors/configuration.js.map +1 -1
  47. package/dist/errors/connection.js +50 -46
  48. package/dist/errors/connection.js.map +1 -1
  49. package/dist/errors/execution.js +47 -43
  50. package/dist/errors/execution.js.map +1 -1
  51. package/dist/errors/index.js +10 -27
  52. package/dist/errors/initialization.js +38 -34
  53. package/dist/errors/initialization.js.map +1 -1
  54. package/dist/errors/server.js +34 -31
  55. package/dist/errors/server.js.map +1 -1
  56. package/dist/errors/tunnel.js +47 -43
  57. package/dist/errors/tunnel.js.map +1 -1
  58. package/dist/errors/validation.js +41 -37
  59. package/dist/errors/validation.js.map +1 -1
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.js +3 -9
  62. package/dist/plugin/dev-reader.js +1 -2
  63. package/dist/plugin/dev-reader.js.map +1 -1
  64. package/dist/plugin/interceptors/cache.js +13 -1
  65. package/dist/plugin/interceptors/cache.js.map +1 -1
  66. package/dist/plugin/interceptors/retry.js +1 -2
  67. package/dist/plugin/interceptors/retry.js.map +1 -1
  68. package/dist/plugin/interceptors/telemetry.js +1 -2
  69. package/dist/plugin/interceptors/telemetry.js.map +1 -1
  70. package/dist/plugin/plugin.js +2 -4
  71. package/dist/plugin/plugin.js.map +1 -1
  72. package/dist/plugins/analytics/analytics.js +1 -2
  73. package/dist/plugins/analytics/analytics.js.map +1 -1
  74. package/dist/plugins/analytics/manifest.js +9 -1
  75. package/dist/plugins/analytics/query.js +2 -4
  76. package/dist/plugins/analytics/query.js.map +1 -1
  77. package/dist/plugins/files/manifest.js +7 -1
  78. package/dist/plugins/files/plugin.js +3 -6
  79. package/dist/plugins/files/plugin.js.map +1 -1
  80. package/dist/plugins/genie/genie.js +1 -2
  81. package/dist/plugins/genie/genie.js.map +1 -1
  82. package/dist/plugins/genie/manifest.js +7 -1
  83. package/dist/plugins/jobs/plugin.js +2 -4
  84. package/dist/plugins/jobs/plugin.js.map +1 -1
  85. package/dist/plugins/lakebase/lakebase.js +1 -2
  86. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  87. package/dist/plugins/lakebase/manifest.js +26 -4
  88. package/dist/plugins/server/index.js +1 -2
  89. package/dist/plugins/server/index.js.map +1 -1
  90. package/dist/plugins/server/vite-dev-server.js +1 -2
  91. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  92. package/dist/plugins/serving/serving.js +1 -2
  93. package/dist/plugins/serving/serving.js.map +1 -1
  94. package/dist/registry/index.d.ts +1 -1
  95. package/dist/registry/manifest-loader.d.ts +4 -4
  96. package/dist/registry/manifest-loader.d.ts.map +1 -1
  97. package/dist/registry/manifest-loader.js +1 -2
  98. package/dist/registry/manifest-loader.js.map +1 -1
  99. package/dist/registry/resource-registry.js +1 -2
  100. package/dist/registry/resource-registry.js.map +1 -1
  101. package/dist/registry/types.d.ts +21 -7
  102. package/dist/registry/types.d.ts.map +1 -1
  103. package/dist/registry/types.generated.d.ts +1 -1
  104. package/dist/registry/types.generated.js +1 -1
  105. package/dist/registry/types.generated.js.map +1 -1
  106. package/dist/registry/types.js +3 -3
  107. package/dist/registry/types.js.map +1 -1
  108. package/dist/schemas/manifest.d.ts +1139 -0
  109. package/dist/schemas/manifest.d.ts.map +1 -0
  110. package/dist/schemas/manifest.js +524 -0
  111. package/dist/schemas/manifest.js.map +1 -0
  112. package/dist/shared/src/index.d.ts +1 -1
  113. package/dist/shared/src/plugin.d.ts +38 -7
  114. package/dist/shared/src/plugin.d.ts.map +1 -1
  115. package/dist/shared/src/schemas/manifest.d.ts +1110 -0
  116. package/dist/shared/src/schemas/manifest.d.ts.map +1 -0
  117. package/dist/stream/arrow-stream-processor.js +1 -2
  118. package/dist/stream/arrow-stream-processor.js.map +1 -1
  119. package/dist/stream/buffers.js +1 -2
  120. package/dist/stream/buffers.js.map +1 -1
  121. package/dist/stream/stream-manager.d.ts.map +1 -1
  122. package/dist/stream/stream-manager.js +3 -3
  123. package/dist/stream/stream-manager.js.map +1 -1
  124. package/docs/api/appkit/Enumeration.ResourceType.md +1 -1
  125. package/docs/api/appkit/Interface.PluginManifest.md +57 -3
  126. package/docs/api/appkit/Interface.ResourceEntry.md +6 -4
  127. package/docs/api/appkit/Interface.ResourceRequirement.md +7 -61
  128. package/docs/api/appkit/TypeAlias.ResourceFieldEntry.md +6 -0
  129. package/docs/api/appkit.md +6 -6
  130. package/docs/app-management.md +1 -1
  131. package/docs/development/ai-assisted-development.md +2 -2
  132. package/docs/development/local-development.md +1 -1
  133. package/docs/development/remote-bridge.md +1 -1
  134. package/docs/development/templates.md +118 -12
  135. package/docs/development.md +1 -1
  136. package/docs/plugins/custom-plugins.md +33 -23
  137. package/docs/plugins/lakebase.md +1 -1
  138. package/docs/plugins/manifest.md +293 -0
  139. package/docs.md +2 -2
  140. package/llms.txt +3 -2
  141. package/package.json +3 -3
  142. package/sbom.cdx.json +1 -1
  143. package/dist/_virtual/_rolldown/runtime.js +0 -7
  144. package/dist/connectors/lakebase-v1/client.js +0 -15
  145. package/dist/connectors/lakebase-v1/client.js.map +0 -1
  146. package/dist/connectors/lakebase-v1/index.js +0 -3
  147. package/dist/context/index.js.map +0 -1
  148. package/dist/errors/index.js.map +0 -1
  149. package/dist/index.js.map +0 -1
  150. package/dist/schemas/plugin-manifest.generated.d.ts +0 -182
  151. package/dist/schemas/plugin-manifest.generated.d.ts.map +0 -1
  152. package/dist/schemas/plugin-manifest.schema.json +0 -489
  153. package/dist/schemas/template-plugins.schema.json +0 -113
  154. package/dist/shared/src/schemas/plugin-manifest.generated.d.ts +0 -182
  155. package/dist/shared/src/schemas/plugin-manifest.generated.d.ts.map +0 -1
  156. package/docs/api/appkit/Interface.ResourceFieldEntry.md +0 -82
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"sources":["../../../src/connectors/sql-warehouse/client.ts"],"sourcesContent":["import {\n Context,\n type sql,\n type WorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport type { TelemetryOptions } from \"shared\";\nimport {\n AppKitError,\n ConnectionError,\n ExecutionError,\n ValidationError,\n} from \"../../errors\";\nimport { createLogger } from \"../../logging/logger\";\nimport { ArrowStreamProcessor } from \"../../stream/arrow-stream-processor\";\nimport type { TelemetryProvider } from \"../../telemetry\";\nimport {\n type Counter,\n type Histogram,\n type Span,\n SpanKind,\n SpanStatusCode,\n TelemetryManager,\n} from \"../../telemetry\";\nimport { executeStatementDefaults } from \"./defaults\";\n\nconst logger = createLogger(\"connectors:sql-warehouse\");\n\ninterface SQLWarehouseConfig {\n timeout?: number;\n telemetry?: TelemetryOptions;\n}\n\nexport class SQLWarehouseConnector {\n private readonly name = \"sql-warehouse\";\n\n private config: SQLWarehouseConfig;\n\n // Lazy-initialized: only created when Arrow format is used\n private _arrowProcessor: ArrowStreamProcessor | null = null;\n // telemetry\n private readonly telemetry: TelemetryProvider;\n private readonly telemetryMetrics: {\n queryCount: Counter;\n queryDuration: Histogram;\n };\n\n constructor(config: SQLWarehouseConfig) {\n this.config = config;\n\n this.telemetry = TelemetryManager.getProvider(\n this.name,\n this.config.telemetry,\n );\n this.telemetryMetrics = {\n queryCount: this.telemetry.getMeter().createCounter(\"query.count\", {\n description: \"Total number of queries executed\",\n unit: \"1\",\n }),\n queryDuration: this.telemetry\n .getMeter()\n .createHistogram(\"query.duration\", {\n description: \"Duration of queries executed\",\n unit: \"ms\",\n }),\n };\n }\n\n /**\n * Lazily initializes and returns the ArrowStreamProcessor.\n * Only created on first Arrow format query to avoid unnecessary allocation.\n */\n private get arrowProcessor(): ArrowStreamProcessor {\n if (!this._arrowProcessor) {\n this._arrowProcessor = new ArrowStreamProcessor({\n timeout: this.config.timeout || executeStatementDefaults.timeout,\n maxConcurrentDownloads:\n ArrowStreamProcessor.DEFAULT_MAX_CONCURRENT_DOWNLOADS,\n retries: ArrowStreamProcessor.DEFAULT_RETRIES,\n });\n }\n return this._arrowProcessor;\n }\n\n async executeStatement(\n workspaceClient: WorkspaceClient,\n input: sql.ExecuteStatementRequest,\n signal?: AbortSignal,\n ) {\n const startTime = Date.now();\n let success = false;\n\n // if signal is aborted, throw an error\n if (signal?.aborted) {\n throw ExecutionError.canceled();\n }\n\n return this.telemetry.startActiveSpan(\n \"sql.query\",\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"databricks\",\n \"db.warehouse_id\": input.warehouse_id || \"\",\n \"db.catalog\": input.catalog ?? \"\",\n \"db.schema\": input.schema ?? \"\",\n \"db.statement\": input.statement?.substring(0, 500) || \"\",\n \"db.has_parameters\": !!input.parameters,\n },\n },\n async (span: Span) => {\n let abortHandler: (() => void) | undefined;\n let isAborted = false;\n\n if (signal) {\n abortHandler = () => {\n // abort span if not recording\n if (!span.isRecording()) return;\n isAborted = true;\n span.setAttribute(\"cancelled\", true);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: \"Query cancelled by client\",\n });\n span.end();\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n try {\n // validate required fields\n if (!input.statement) {\n throw ValidationError.missingField(\"statement\");\n }\n\n if (!input.warehouse_id) {\n throw ValidationError.missingField(\"warehouse_id\");\n }\n\n const body: sql.ExecuteStatementRequest = {\n statement: input.statement,\n parameters: input.parameters,\n warehouse_id: input.warehouse_id,\n catalog: input.catalog,\n schema: input.schema,\n wait_timeout:\n input.wait_timeout || executeStatementDefaults.wait_timeout,\n disposition:\n input.disposition || executeStatementDefaults.disposition,\n format: input.format || executeStatementDefaults.format,\n byte_limit: input.byte_limit,\n row_limit: input.row_limit,\n on_wait_timeout:\n input.on_wait_timeout || executeStatementDefaults.on_wait_timeout,\n };\n\n span.addEvent(\"statement.submitting\", {\n \"db.warehouse_id\": input.warehouse_id,\n });\n\n const response =\n await workspaceClient.statementExecution.executeStatement(\n body,\n this._createContext(signal),\n );\n\n if (!response) {\n throw ConnectionError.apiFailure(\"SQL Warehouse\");\n }\n const status = response.status;\n const statementId = response.statement_id as string;\n\n span.setAttribute(\"db.statement_id\", statementId);\n span.addEvent(\"statement.submitted\", {\n \"db.statement_id\": response.statement_id,\n \"db.status\": status?.state,\n });\n\n let result:\n | sql.StatementResponse\n | { result: { statement_id: string; status: sql.StatementStatus } };\n\n switch (status?.state) {\n case \"RUNNING\":\n case \"PENDING\":\n span.addEvent(\"statement.polling_started\", {\n \"db.status\": response.status?.state,\n });\n result = await this._pollForStatementResult(\n workspaceClient,\n statementId,\n this.config.timeout,\n signal,\n );\n break;\n case \"SUCCEEDED\":\n result = this._transformDataArray(response);\n break;\n case \"FAILED\":\n throw ExecutionError.statementFailed(status.error?.message);\n case \"CANCELED\":\n throw ExecutionError.canceled();\n case \"CLOSED\":\n throw ExecutionError.resultsClosed();\n default:\n throw ExecutionError.unknownState(\n String(status?.state ?? \"unknown\"),\n );\n }\n\n const resultData = result.result as any;\n const rowCount =\n resultData?.data?.length ?? resultData?.data_array?.length ?? 0;\n\n if (rowCount > 0) {\n span.setAttribute(\"db.result.row_count\", rowCount);\n }\n\n const duration = Date.now() - startTime;\n logger.event()?.setContext(\"sql-warehouse\", {\n warehouse_id: input.warehouse_id,\n rows_returned: rowCount,\n query_duration_ms: duration,\n });\n\n success = true;\n // only set success status if not aborted\n if (!isAborted) {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n return result;\n } catch (error) {\n // only record error if not already handled by abort\n if (!isAborted) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n\n logger.error(\n \"Statement execution failed: %s\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n // remove abort handler\n if (abortHandler && signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n\n const duration = Date.now() - startTime;\n\n // end span if not already ended by abort handler\n if (!isAborted) {\n span.end();\n }\n\n const attributes = {\n \"db.warehouse_id\": input.warehouse_id,\n \"db.catalog\": input.catalog ?? \"\",\n \"db.schema\": input.schema ?? \"\",\n \"db.statement\": input.statement?.substring(0, 500) || \"\",\n success: success.toString(),\n };\n\n this.telemetryMetrics.queryCount.add(1, attributes);\n this.telemetryMetrics.queryDuration.record(duration, attributes);\n }\n },\n { name: this.name, includePrefix: true },\n );\n }\n\n private async _pollForStatementResult(\n workspaceClient: WorkspaceClient,\n statementId: string,\n timeout = executeStatementDefaults.timeout,\n signal?: AbortSignal,\n ) {\n return this.telemetry.startActiveSpan(\n \"sql.poll\",\n {\n attributes: {\n \"db.statement_id\": statementId,\n \"db.polling.timeout\": timeout,\n },\n },\n async (span: Span) => {\n try {\n const startTime = Date.now();\n let delay = 1000;\n const maxDelayBetweenPolls = 5000; // max 5 seconds between polls\n let pollCount = 0;\n\n while (true) {\n pollCount++;\n span.setAttribute(\"db.polling.current_attempt\", pollCount);\n\n // check if timeout exceeded\n const elapsedTime = Date.now() - startTime;\n if (elapsedTime > timeout) {\n const error = ExecutionError.statementFailed(\n `Polling timeout exceeded after ${timeout}ms (elapsed: ${elapsedTime}ms)`,\n );\n span.recordException(error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n }\n\n if (signal?.aborted) {\n const error = ExecutionError.canceled();\n span.recordException(error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n }\n\n span.addEvent(\"polling.attempt\", {\n \"poll.attempt\": pollCount,\n \"poll.delay_ms\": delay,\n \"poll.elapsed_ms\": elapsedTime,\n });\n\n const response =\n await workspaceClient.statementExecution.getStatement(\n {\n statement_id: statementId,\n },\n this._createContext(signal),\n );\n if (!response) {\n throw ConnectionError.apiFailure(\"SQL Warehouse\");\n }\n\n const status = response.status;\n\n span.addEvent(\"polling.status_check\", {\n \"db.status\": status?.state,\n \"poll.attempt\": pollCount,\n });\n\n switch (status?.state) {\n case \"PENDING\":\n case \"RUNNING\":\n // continue polling\n break;\n case \"SUCCEEDED\":\n span.setAttribute(\"db.polling.attempts\", pollCount);\n span.setAttribute(\"db.polling.total_duration_ms\", elapsedTime);\n span.addEvent(\"polling.completed\", {\n \"poll.attempts\": pollCount,\n \"poll.duration_ms\": elapsedTime,\n });\n span.setStatus({ code: SpanStatusCode.OK });\n return this._transformDataArray(response);\n case \"FAILED\":\n throw ExecutionError.statementFailed(status.error?.message);\n case \"CANCELED\":\n throw ExecutionError.canceled();\n case \"CLOSED\":\n throw ExecutionError.resultsClosed();\n default:\n throw ExecutionError.unknownState(\n String(status?.state ?? \"unknown\"),\n );\n }\n\n // continue polling after delay\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * 2, maxDelayBetweenPolls);\n }\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n\n // error logging is handled by executeStatement's catch block (gated on isAborted)\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n span.end();\n }\n },\n { name: this.name, includePrefix: true },\n );\n }\n\n private _transformDataArray(response: sql.StatementResponse) {\n if (response.manifest?.format === \"ARROW_STREAM\") {\n return this.updateWithArrowStatus(response);\n }\n\n if (!response.result?.data_array || !response.manifest?.schema?.columns) {\n return response;\n }\n\n const columns = response.manifest.schema.columns;\n\n const transformedData = response.result.data_array.map((row) => {\n const obj: Record<string, unknown> = {};\n row.forEach((value, index) => {\n const column = columns[index];\n const columnName = column?.name || `column_${index}`;\n\n // attempt to parse JSON strings for string columns\n if (\n column?.type_name === \"STRING\" &&\n typeof value === \"string\" &&\n value &&\n (value[0] === \"{\" || value[0] === \"[\")\n ) {\n try {\n obj[columnName] = JSON.parse(value);\n } catch {\n // if parsing fails, keep as string\n obj[columnName] = value;\n }\n } else {\n obj[columnName] = value;\n }\n });\n return obj;\n });\n\n // remove data_array\n const { data_array: _data_array, ...restResult } = response.result;\n return {\n ...response,\n result: {\n ...restResult,\n data: transformedData,\n },\n };\n }\n\n private updateWithArrowStatus(response: sql.StatementResponse): {\n result: { statement_id: string; status: sql.StatementStatus };\n } {\n return {\n result: {\n statement_id: response.statement_id as string,\n status: {\n state: response.status?.state,\n error: response.status?.error,\n } as sql.StatementStatus,\n },\n };\n }\n\n async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.arrowProcessor.processChunks>> {\n const startTime = Date.now();\n\n return this.telemetry.startActiveSpan(\n \"arrow.getData\",\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"databricks\",\n \"arrow.job_id\": jobId,\n },\n },\n async (span: Span) => {\n try {\n const response =\n await workspaceClient.statementExecution.getStatement(\n { statement_id: jobId },\n this._createContext(signal),\n );\n\n const chunks = response.result?.external_links;\n const schema = response.manifest?.schema;\n\n if (!chunks || !schema) {\n throw ExecutionError.missingData(\"chunks or schema\");\n }\n\n span.setAttribute(\"arrow.chunk_count\", chunks.length);\n\n const result = await this.arrowProcessor.processChunks(\n chunks,\n schema,\n signal,\n );\n\n span.setAttribute(\"arrow.data_size_bytes\", result.data.length);\n span.setStatus({ code: SpanStatusCode.OK });\n\n const duration = Date.now() - startTime;\n this.telemetryMetrics.queryDuration.record(duration, {\n operation: \"arrow.getData\",\n status: \"success\",\n });\n\n logger.event()?.setContext(\"sql-warehouse\", {\n arrow_data_size_bytes: result.data.length,\n arrow_job_id: jobId,\n });\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n span.recordException(error as Error);\n\n const duration = Date.now() - startTime;\n this.telemetryMetrics.queryDuration.record(duration, {\n operation: \"arrow.getData\",\n status: \"error\",\n });\n\n logger.error(\"Failed Arrow job: %s %O\", jobId, error);\n\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n }\n },\n );\n }\n\n // create context for cancellation token\n private _createContext(signal?: AbortSignal) {\n return new Context({\n cancellationToken: {\n isCancellationRequested: signal?.aborted ?? false,\n onCancellationRequested: (cb: () => void) => {\n signal?.addEventListener(\"abort\", cb, { once: true });\n },\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../../src/connectors/sql-warehouse/client.ts"],"sourcesContent":["import {\n Context,\n type sql,\n type WorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport type { TelemetryOptions } from \"shared\";\nimport {\n AppKitError,\n ConnectionError,\n ExecutionError,\n ValidationError,\n} from \"../../errors\";\nimport { createLogger } from \"../../logging/logger\";\nimport { ArrowStreamProcessor } from \"../../stream/arrow-stream-processor\";\nimport type { TelemetryProvider } from \"../../telemetry\";\nimport {\n type Counter,\n type Histogram,\n type Span,\n SpanKind,\n SpanStatusCode,\n TelemetryManager,\n} from \"../../telemetry\";\nimport { executeStatementDefaults } from \"./defaults\";\n\nconst logger = createLogger(\"connectors:sql-warehouse\");\n\ninterface SQLWarehouseConfig {\n timeout?: number;\n telemetry?: TelemetryOptions;\n}\n\nexport class SQLWarehouseConnector {\n private readonly name = \"sql-warehouse\";\n\n private config: SQLWarehouseConfig;\n\n // Lazy-initialized: only created when Arrow format is used\n private _arrowProcessor: ArrowStreamProcessor | null = null;\n // telemetry\n private readonly telemetry: TelemetryProvider;\n private readonly telemetryMetrics: {\n queryCount: Counter;\n queryDuration: Histogram;\n };\n\n constructor(config: SQLWarehouseConfig) {\n this.config = config;\n\n this.telemetry = TelemetryManager.getProvider(\n this.name,\n this.config.telemetry,\n );\n this.telemetryMetrics = {\n queryCount: this.telemetry.getMeter().createCounter(\"query.count\", {\n description: \"Total number of queries executed\",\n unit: \"1\",\n }),\n queryDuration: this.telemetry\n .getMeter()\n .createHistogram(\"query.duration\", {\n description: \"Duration of queries executed\",\n unit: \"ms\",\n }),\n };\n }\n\n /**\n * Lazily initializes and returns the ArrowStreamProcessor.\n * Only created on first Arrow format query to avoid unnecessary allocation.\n */\n private get arrowProcessor(): ArrowStreamProcessor {\n if (!this._arrowProcessor) {\n this._arrowProcessor = new ArrowStreamProcessor({\n timeout: this.config.timeout || executeStatementDefaults.timeout,\n maxConcurrentDownloads:\n ArrowStreamProcessor.DEFAULT_MAX_CONCURRENT_DOWNLOADS,\n retries: ArrowStreamProcessor.DEFAULT_RETRIES,\n });\n }\n return this._arrowProcessor;\n }\n\n async executeStatement(\n workspaceClient: WorkspaceClient,\n input: sql.ExecuteStatementRequest,\n signal?: AbortSignal,\n ) {\n const startTime = Date.now();\n let success = false;\n\n // if signal is aborted, throw an error\n if (signal?.aborted) {\n throw ExecutionError.canceled();\n }\n\n return this.telemetry.startActiveSpan(\n \"sql.query\",\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"databricks\",\n \"db.warehouse_id\": input.warehouse_id || \"\",\n \"db.catalog\": input.catalog ?? \"\",\n \"db.schema\": input.schema ?? \"\",\n \"db.statement\": input.statement?.substring(0, 500) || \"\",\n \"db.has_parameters\": !!input.parameters,\n },\n },\n async (span: Span) => {\n let abortHandler: (() => void) | undefined;\n let isAborted = false;\n\n if (signal) {\n abortHandler = () => {\n // abort span if not recording\n if (!span.isRecording()) return;\n isAborted = true;\n span.setAttribute(\"cancelled\", true);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: \"Query cancelled by client\",\n });\n span.end();\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n try {\n // validate required fields\n if (!input.statement) {\n throw ValidationError.missingField(\"statement\");\n }\n\n if (!input.warehouse_id) {\n throw ValidationError.missingField(\"warehouse_id\");\n }\n\n const body: sql.ExecuteStatementRequest = {\n statement: input.statement,\n parameters: input.parameters,\n warehouse_id: input.warehouse_id,\n catalog: input.catalog,\n schema: input.schema,\n wait_timeout:\n input.wait_timeout || executeStatementDefaults.wait_timeout,\n disposition:\n input.disposition || executeStatementDefaults.disposition,\n format: input.format || executeStatementDefaults.format,\n byte_limit: input.byte_limit,\n row_limit: input.row_limit,\n on_wait_timeout:\n input.on_wait_timeout || executeStatementDefaults.on_wait_timeout,\n };\n\n span.addEvent(\"statement.submitting\", {\n \"db.warehouse_id\": input.warehouse_id,\n });\n\n const response =\n await workspaceClient.statementExecution.executeStatement(\n body,\n this._createContext(signal),\n );\n\n if (!response) {\n throw ConnectionError.apiFailure(\"SQL Warehouse\");\n }\n const status = response.status;\n const statementId = response.statement_id as string;\n\n span.setAttribute(\"db.statement_id\", statementId);\n span.addEvent(\"statement.submitted\", {\n \"db.statement_id\": response.statement_id,\n \"db.status\": status?.state,\n });\n\n let result:\n | sql.StatementResponse\n | { result: { statement_id: string; status: sql.StatementStatus } };\n\n switch (status?.state) {\n case \"RUNNING\":\n case \"PENDING\":\n span.addEvent(\"statement.polling_started\", {\n \"db.status\": response.status?.state,\n });\n result = await this._pollForStatementResult(\n workspaceClient,\n statementId,\n this.config.timeout,\n signal,\n );\n break;\n case \"SUCCEEDED\":\n result = this._transformDataArray(response);\n break;\n case \"FAILED\":\n throw ExecutionError.statementFailed(status.error?.message);\n case \"CANCELED\":\n throw ExecutionError.canceled();\n case \"CLOSED\":\n throw ExecutionError.resultsClosed();\n default:\n throw ExecutionError.unknownState(\n String(status?.state ?? \"unknown\"),\n );\n }\n\n const resultData = result.result as any;\n const rowCount =\n resultData?.data?.length ?? resultData?.data_array?.length ?? 0;\n\n if (rowCount > 0) {\n span.setAttribute(\"db.result.row_count\", rowCount);\n }\n\n const duration = Date.now() - startTime;\n logger.event()?.setContext(\"sql-warehouse\", {\n warehouse_id: input.warehouse_id,\n rows_returned: rowCount,\n query_duration_ms: duration,\n });\n\n success = true;\n // only set success status if not aborted\n if (!isAborted) {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n return result;\n } catch (error) {\n // only record error if not already handled by abort\n if (!isAborted) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n\n logger.error(\n \"Statement execution failed: %s\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n // remove abort handler\n if (abortHandler && signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n\n const duration = Date.now() - startTime;\n\n // end span if not already ended by abort handler\n if (!isAborted) {\n span.end();\n }\n\n const attributes = {\n \"db.warehouse_id\": input.warehouse_id,\n \"db.catalog\": input.catalog ?? \"\",\n \"db.schema\": input.schema ?? \"\",\n \"db.statement\": input.statement?.substring(0, 500) || \"\",\n success: success.toString(),\n };\n\n this.telemetryMetrics.queryCount.add(1, attributes);\n this.telemetryMetrics.queryDuration.record(duration, attributes);\n }\n },\n { name: this.name, includePrefix: true },\n );\n }\n\n private async _pollForStatementResult(\n workspaceClient: WorkspaceClient,\n statementId: string,\n timeout = executeStatementDefaults.timeout,\n signal?: AbortSignal,\n ) {\n return this.telemetry.startActiveSpan(\n \"sql.poll\",\n {\n attributes: {\n \"db.statement_id\": statementId,\n \"db.polling.timeout\": timeout,\n },\n },\n async (span: Span) => {\n try {\n const startTime = Date.now();\n let delay = 1000;\n const maxDelayBetweenPolls = 5000; // max 5 seconds between polls\n let pollCount = 0;\n\n while (true) {\n pollCount++;\n span.setAttribute(\"db.polling.current_attempt\", pollCount);\n\n // check if timeout exceeded\n const elapsedTime = Date.now() - startTime;\n if (elapsedTime > timeout) {\n const error = ExecutionError.statementFailed(\n `Polling timeout exceeded after ${timeout}ms (elapsed: ${elapsedTime}ms)`,\n );\n span.recordException(error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n }\n\n if (signal?.aborted) {\n const error = ExecutionError.canceled();\n span.recordException(error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n }\n\n span.addEvent(\"polling.attempt\", {\n \"poll.attempt\": pollCount,\n \"poll.delay_ms\": delay,\n \"poll.elapsed_ms\": elapsedTime,\n });\n\n const response =\n await workspaceClient.statementExecution.getStatement(\n {\n statement_id: statementId,\n },\n this._createContext(signal),\n );\n if (!response) {\n throw ConnectionError.apiFailure(\"SQL Warehouse\");\n }\n\n const status = response.status;\n\n span.addEvent(\"polling.status_check\", {\n \"db.status\": status?.state,\n \"poll.attempt\": pollCount,\n });\n\n switch (status?.state) {\n case \"PENDING\":\n case \"RUNNING\":\n // continue polling\n break;\n case \"SUCCEEDED\":\n span.setAttribute(\"db.polling.attempts\", pollCount);\n span.setAttribute(\"db.polling.total_duration_ms\", elapsedTime);\n span.addEvent(\"polling.completed\", {\n \"poll.attempts\": pollCount,\n \"poll.duration_ms\": elapsedTime,\n });\n span.setStatus({ code: SpanStatusCode.OK });\n return this._transformDataArray(response);\n case \"FAILED\":\n throw ExecutionError.statementFailed(status.error?.message);\n case \"CANCELED\":\n throw ExecutionError.canceled();\n case \"CLOSED\":\n throw ExecutionError.resultsClosed();\n default:\n throw ExecutionError.unknownState(\n String(status?.state ?? \"unknown\"),\n );\n }\n\n // continue polling after delay\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * 2, maxDelayBetweenPolls);\n }\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n\n // error logging is handled by executeStatement's catch block (gated on isAborted)\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n span.end();\n }\n },\n { name: this.name, includePrefix: true },\n );\n }\n\n private _transformDataArray(response: sql.StatementResponse) {\n if (response.manifest?.format === \"ARROW_STREAM\") {\n return this.updateWithArrowStatus(response);\n }\n\n if (!response.result?.data_array || !response.manifest?.schema?.columns) {\n return response;\n }\n\n const columns = response.manifest.schema.columns;\n\n const transformedData = response.result.data_array.map((row) => {\n const obj: Record<string, unknown> = {};\n row.forEach((value, index) => {\n const column = columns[index];\n const columnName = column?.name || `column_${index}`;\n\n // attempt to parse JSON strings for string columns\n if (\n column?.type_name === \"STRING\" &&\n typeof value === \"string\" &&\n value &&\n (value[0] === \"{\" || value[0] === \"[\")\n ) {\n try {\n obj[columnName] = JSON.parse(value);\n } catch {\n // if parsing fails, keep as string\n obj[columnName] = value;\n }\n } else {\n obj[columnName] = value;\n }\n });\n return obj;\n });\n\n // remove data_array\n const { data_array: _data_array, ...restResult } = response.result;\n return {\n ...response,\n result: {\n ...restResult,\n data: transformedData,\n },\n };\n }\n\n private updateWithArrowStatus(response: sql.StatementResponse): {\n result: { statement_id: string; status: sql.StatementStatus };\n } {\n return {\n result: {\n statement_id: response.statement_id as string,\n status: {\n state: response.status?.state,\n error: response.status?.error,\n } as sql.StatementStatus,\n },\n };\n }\n\n async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.arrowProcessor.processChunks>> {\n const startTime = Date.now();\n\n return this.telemetry.startActiveSpan(\n \"arrow.getData\",\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"db.system\": \"databricks\",\n \"arrow.job_id\": jobId,\n },\n },\n async (span: Span) => {\n try {\n const response =\n await workspaceClient.statementExecution.getStatement(\n { statement_id: jobId },\n this._createContext(signal),\n );\n\n const chunks = response.result?.external_links;\n const schema = response.manifest?.schema;\n\n if (!chunks || !schema) {\n throw ExecutionError.missingData(\"chunks or schema\");\n }\n\n span.setAttribute(\"arrow.chunk_count\", chunks.length);\n\n const result = await this.arrowProcessor.processChunks(\n chunks,\n schema,\n signal,\n );\n\n span.setAttribute(\"arrow.data_size_bytes\", result.data.length);\n span.setStatus({ code: SpanStatusCode.OK });\n\n const duration = Date.now() - startTime;\n this.telemetryMetrics.queryDuration.record(duration, {\n operation: \"arrow.getData\",\n status: \"success\",\n });\n\n logger.event()?.setContext(\"sql-warehouse\", {\n arrow_data_size_bytes: result.data.length,\n arrow_job_id: jobId,\n });\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n span.recordException(error as Error);\n\n const duration = Date.now() - startTime;\n this.telemetryMetrics.queryDuration.record(duration, {\n operation: \"arrow.getData\",\n status: \"error\",\n });\n\n logger.error(\"Failed Arrow job: %s %O\", jobId, error);\n\n if (error instanceof AppKitError) {\n throw error;\n }\n throw ExecutionError.statementFailed(\n error instanceof Error ? error.message : String(error),\n );\n }\n },\n );\n }\n\n // create context for cancellation token\n private _createContext(signal?: AbortSignal) {\n return new Context({\n cancellationToken: {\n isCancellationRequested: signal?.aborted ?? false,\n onCancellationRequested: (cb: () => void) => {\n signal?.addEventListener(\"abort\", cb, { once: true });\n },\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -1,12 +1,16 @@
1
- import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
1
  import { ConfigurationError } from "../errors/configuration.js";
3
- import { init_errors } from "../errors/index.js";
4
- import { ServiceContext, init_service_context } from "./service-context.js";
5
- import { init_user_context, isUserContext } from "./user-context.js";
2
+ import "../errors/index.js";
3
+ import { ServiceContext } from "./service-context.js";
4
+ import { isUserContext } from "./user-context.js";
6
5
  import { AsyncLocalStorage } from "node:async_hooks";
7
6
 
8
7
  //#region src/context/execution-context.ts
9
8
  /**
9
+ * AsyncLocalStorage for execution context.
10
+ * Used to pass user context through the call stack without explicit parameters.
11
+ */
12
+ const executionContextStorage = new AsyncLocalStorage();
13
+ /**
10
14
  * Run a function in the context of a user.
11
15
  * All calls within the function will have access to the user context.
12
16
  *
@@ -74,15 +78,7 @@ function isInUserContext() {
74
78
  function getUserContext() {
75
79
  return executionContextStorage.getStore();
76
80
  }
77
- var executionContextStorage;
78
- var init_execution_context = __esmMin((() => {
79
- init_errors();
80
- init_service_context();
81
- init_user_context();
82
- executionContextStorage = new AsyncLocalStorage();
83
- }));
84
81
 
85
82
  //#endregion
86
- init_execution_context();
87
- export { getCurrentUserId, getExecutionContext, getUserContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, isInUserContext, runInUserContext };
83
+ export { getCurrentUserId, getExecutionContext, getUserContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, isInUserContext, runInUserContext };
88
84
  //# sourceMappingURL=execution-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.js","names":[],"sources":["../../src/context/execution-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { ConfigurationError } from \"../errors\";\nimport { ServiceContext } from \"./service-context\";\nimport {\n type ExecutionContext,\n isUserContext,\n type UserContext,\n} from \"./user-context\";\n\n/**\n * AsyncLocalStorage for execution context.\n * Used to pass user context through the call stack without explicit parameters.\n */\nconst executionContextStorage = new AsyncLocalStorage<UserContext>();\n\n/**\n * Run a function in the context of a user.\n * All calls within the function will have access to the user context.\n *\n * @param userContext - The user context to use\n * @param fn - The function to run\n * @returns The result of the function\n */\nexport function runInUserContext<T>(userContext: UserContext, fn: () => T): T {\n return executionContextStorage.run(userContext, fn);\n}\n\n/**\n * Get the current execution context.\n *\n * - If running inside a user context (via asUser), returns the user context\n * - Otherwise, returns the service context\n *\n * @throws Error if ServiceContext is not initialized\n */\nexport function getExecutionContext(): ExecutionContext {\n const userContext = executionContextStorage.getStore();\n if (userContext) {\n return userContext;\n }\n return ServiceContext.get();\n}\n\n/**\n * Get the current user ID for cache keying and telemetry.\n *\n * Returns the user ID if in user context, otherwise the service user ID.\n */\nexport function getCurrentUserId(): string {\n const ctx = getExecutionContext();\n if (isUserContext(ctx)) {\n return ctx.userId;\n }\n return ctx.serviceUserId;\n}\n\n/**\n * Get the WorkspaceClient for the current execution context.\n */\nexport function getWorkspaceClient() {\n return getExecutionContext().client;\n}\n\n/**\n * Get the warehouse ID promise.\n */\nexport function getWarehouseId(): Promise<string> {\n const ctx = getExecutionContext();\n if (!ctx.warehouseId) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"No plugin requires a SQL Warehouse. Add a sql_warehouse resource to your plugin manifest, or set DATABRICKS_WAREHOUSE_ID\",\n );\n }\n return ctx.warehouseId;\n}\n\n/**\n * Get the workspace ID promise.\n */\nexport function getWorkspaceId(): Promise<string> {\n return getExecutionContext().workspaceId;\n}\n\n/**\n * Check if currently running in a user context.\n */\nexport function isInUserContext(): boolean {\n const ctx = executionContextStorage.getStore();\n return ctx !== undefined;\n}\n\n/**\n * Get the user context if one is active, otherwise `undefined`.\n * Unlike `getExecutionContext()`, this does not require `ServiceContext`\n * to be initialized and never throws.\n */\nexport function getUserContext(): UserContext | undefined {\n return executionContextStorage.getStore();\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,SAAgB,iBAAoB,aAA0B,IAAgB;AAC5E,QAAO,wBAAwB,IAAI,aAAa,GAAG;;;;;;;;;;AAWrD,SAAgB,sBAAwC;CACtD,MAAM,cAAc,wBAAwB,UAAU;AACtD,KAAI,YACF,QAAO;AAET,QAAO,eAAe,KAAK;;;;;;;AAQ7B,SAAgB,mBAA2B;CACzC,MAAM,MAAM,qBAAqB;AACjC,KAAI,cAAc,IAAI,CACpB,QAAO,IAAI;AAEb,QAAO,IAAI;;;;;AAMb,SAAgB,qBAAqB;AACnC,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,iBAAkC;CAChD,MAAM,MAAM,qBAAqB;AACjC,KAAI,CAAC,IAAI,YACP,OAAM,mBAAmB,iBACvB,gBACA,2HACD;AAEH,QAAO,IAAI;;;;;AAMb,SAAgB,iBAAkC;AAChD,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,kBAA2B;AAEzC,QADY,wBAAwB,UAAU,KAC/B;;;;;;;AAQjB,SAAgB,iBAA0C;AACxD,QAAO,wBAAwB,UAAU;;;;cAjGI;uBACI;oBAK3B;CAMlB,0BAA0B,IAAI,mBAAgC"}
1
+ {"version":3,"file":"execution-context.js","names":[],"sources":["../../src/context/execution-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { ConfigurationError } from \"../errors\";\nimport { ServiceContext } from \"./service-context\";\nimport {\n type ExecutionContext,\n isUserContext,\n type UserContext,\n} from \"./user-context\";\n\n/**\n * AsyncLocalStorage for execution context.\n * Used to pass user context through the call stack without explicit parameters.\n */\nconst executionContextStorage = new AsyncLocalStorage<UserContext>();\n\n/**\n * Run a function in the context of a user.\n * All calls within the function will have access to the user context.\n *\n * @param userContext - The user context to use\n * @param fn - The function to run\n * @returns The result of the function\n */\nexport function runInUserContext<T>(userContext: UserContext, fn: () => T): T {\n return executionContextStorage.run(userContext, fn);\n}\n\n/**\n * Get the current execution context.\n *\n * - If running inside a user context (via asUser), returns the user context\n * - Otherwise, returns the service context\n *\n * @throws Error if ServiceContext is not initialized\n */\nexport function getExecutionContext(): ExecutionContext {\n const userContext = executionContextStorage.getStore();\n if (userContext) {\n return userContext;\n }\n return ServiceContext.get();\n}\n\n/**\n * Get the current user ID for cache keying and telemetry.\n *\n * Returns the user ID if in user context, otherwise the service user ID.\n */\nexport function getCurrentUserId(): string {\n const ctx = getExecutionContext();\n if (isUserContext(ctx)) {\n return ctx.userId;\n }\n return ctx.serviceUserId;\n}\n\n/**\n * Get the WorkspaceClient for the current execution context.\n */\nexport function getWorkspaceClient() {\n return getExecutionContext().client;\n}\n\n/**\n * Get the warehouse ID promise.\n */\nexport function getWarehouseId(): Promise<string> {\n const ctx = getExecutionContext();\n if (!ctx.warehouseId) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"No plugin requires a SQL Warehouse. Add a sql_warehouse resource to your plugin manifest, or set DATABRICKS_WAREHOUSE_ID\",\n );\n }\n return ctx.warehouseId;\n}\n\n/**\n * Get the workspace ID promise.\n */\nexport function getWorkspaceId(): Promise<string> {\n return getExecutionContext().workspaceId;\n}\n\n/**\n * Check if currently running in a user context.\n */\nexport function isInUserContext(): boolean {\n const ctx = executionContextStorage.getStore();\n return ctx !== undefined;\n}\n\n/**\n * Get the user context if one is active, otherwise `undefined`.\n * Unlike `getExecutionContext()`, this does not require `ServiceContext`\n * to be initialized and never throws.\n */\nexport function getUserContext(): UserContext | undefined {\n return executionContextStorage.getStore();\n}\n"],"mappings":";;;;;;;;;;;AAaA,MAAM,0BAA0B,IAAI,mBAAgC;;;;;;;;;AAUpE,SAAgB,iBAAoB,aAA0B,IAAgB;AAC5E,QAAO,wBAAwB,IAAI,aAAa,GAAG;;;;;;;;;;AAWrD,SAAgB,sBAAwC;CACtD,MAAM,cAAc,wBAAwB,UAAU;AACtD,KAAI,YACF,QAAO;AAET,QAAO,eAAe,KAAK;;;;;;;AAQ7B,SAAgB,mBAA2B;CACzC,MAAM,MAAM,qBAAqB;AACjC,KAAI,cAAc,IAAI,CACpB,QAAO,IAAI;AAEb,QAAO,IAAI;;;;;AAMb,SAAgB,qBAAqB;AACnC,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,iBAAkC;CAChD,MAAM,MAAM,qBAAqB;AACjC,KAAI,CAAC,IAAI,YACP,OAAM,mBAAmB,iBACvB,gBACA,2HACD;AAEH,QAAO,IAAI;;;;;AAMb,SAAgB,iBAAkC;AAChD,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,kBAA2B;AAEzC,QADY,wBAAwB,UAAU,KAC/B;;;;;;;AAQjB,SAAgB,iBAA0C;AACxD,QAAO,wBAAwB,UAAU"}
@@ -1,14 +1,4 @@
1
- import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
- import { ServiceContext, init_service_context } from "./service-context.js";
3
- import { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, runInUserContext } from "./execution-context.js";
1
+ import { ServiceContext } from "./service-context.js";
2
+ import { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, runInUserContext } from "./execution-context.js";
4
3
 
5
- //#region src/context/index.ts
6
- var init_context = __esmMin((() => {
7
- init_execution_context();
8
- init_service_context();
9
- }));
10
-
11
- //#endregion
12
- init_context();
13
- export { getWorkspaceClient, init_context };
14
- //# sourceMappingURL=index.js.map
4
+ export { getWorkspaceClient };
@@ -1,8 +1,7 @@
1
- import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
1
  import { AuthenticationError } from "../errors/authentication.js";
3
2
  import { ConfigurationError } from "../errors/configuration.js";
4
3
  import { InitializationError } from "../errors/initialization.js";
5
- import { init_errors } from "../errors/index.js";
4
+ import "../errors/index.js";
6
5
  import { name, version } from "../appkit/package.js";
7
6
  import { createHash } from "node:crypto";
8
7
  import { ConfigError, WorkspaceClient } from "@databricks/sdk-experimental";
@@ -17,149 +16,151 @@ function getClientOptions() {
17
16
  ...isDev && { userAgentExtra: { mode: "dev" } }
18
17
  };
19
18
  }
20
- var ServiceContext;
21
- var init_service_context = __esmMin((() => {
22
- init_errors();
23
- ServiceContext = class ServiceContext {
24
- static instance = null;
25
- static initPromise = null;
26
- /**
27
- * Initialize the service context. Should be called once at app startup.
28
- * Safe to call multiple times - will return the same instance.
29
- *
30
- * @param options - Which shared resources to resolve (derived from plugin manifests).
31
- * @param client - Optional pre-configured WorkspaceClient to use instead
32
- * of creating one from environment credentials.
33
- */
34
- static async initialize(options, client) {
35
- if (ServiceContext.instance) return ServiceContext.instance;
36
- if (ServiceContext.initPromise) return ServiceContext.initPromise;
37
- ServiceContext.initPromise = ServiceContext.createContext(options, client);
38
- ServiceContext.instance = await ServiceContext.initPromise;
39
- return ServiceContext.instance;
40
- }
41
- /**
42
- * Get the initialized service context.
43
- * @throws Error if not initialized
44
- */
45
- static get() {
46
- if (!ServiceContext.instance) throw InitializationError.notInitialized("ServiceContext", "Call ServiceContext.initialize() first");
47
- return ServiceContext.instance;
48
- }
49
- /**
50
- * Check if the service context has been initialized.
51
- */
52
- static isInitialized() {
53
- return ServiceContext.instance !== null;
54
- }
55
- /**
56
- * Create a user context from request headers.
57
- *
58
- * @param token - The user's access token from x-forwarded-access-token header
59
- * @param userId - The user's ID from x-forwarded-user header
60
- * @param userName - Optional user name
61
- * @throws Error if token is not provided
62
- */
63
- static createUserContext(token, userId, userName, userEmail) {
64
- if (!token) throw AuthenticationError.missingToken("user token");
65
- const host = process.env.DATABRICKS_HOST;
66
- if (!host) throw ConfigurationError.missingEnvVar("DATABRICKS_HOST");
67
- const serviceCtx = ServiceContext.get();
19
+ /**
20
+ * ServiceContext is a singleton that manages the service principal's
21
+ * WorkspaceClient and shared resources like warehouse/workspace IDs.
22
+ *
23
+ * It's initialized once at app startup and provides the foundation
24
+ * for both service principal and user context execution.
25
+ */
26
+ var ServiceContext = class ServiceContext {
27
+ static instance = null;
28
+ static initPromise = null;
29
+ /**
30
+ * Initialize the service context. Should be called once at app startup.
31
+ * Safe to call multiple times - will return the same instance.
32
+ *
33
+ * @param options - Which shared resources to resolve (derived from plugin manifests).
34
+ * @param client - Optional pre-configured WorkspaceClient to use instead
35
+ * of creating one from environment credentials.
36
+ */
37
+ static async initialize(options, client) {
38
+ if (ServiceContext.instance) return ServiceContext.instance;
39
+ if (ServiceContext.initPromise) return ServiceContext.initPromise;
40
+ ServiceContext.initPromise = ServiceContext.createContext(options, client);
41
+ ServiceContext.instance = await ServiceContext.initPromise;
42
+ return ServiceContext.instance;
43
+ }
44
+ /**
45
+ * Get the initialized service context.
46
+ * @throws Error if not initialized
47
+ */
48
+ static get() {
49
+ if (!ServiceContext.instance) throw InitializationError.notInitialized("ServiceContext", "Call ServiceContext.initialize() first");
50
+ return ServiceContext.instance;
51
+ }
52
+ /**
53
+ * Check if the service context has been initialized.
54
+ */
55
+ static isInitialized() {
56
+ return ServiceContext.instance !== null;
57
+ }
58
+ /**
59
+ * Create a user context from request headers.
60
+ *
61
+ * @param token - The user's access token from x-forwarded-access-token header
62
+ * @param userId - The user's ID from x-forwarded-user header
63
+ * @param userName - Optional user name
64
+ * @throws Error if token is not provided
65
+ */
66
+ static createUserContext(token, userId, userName, userEmail) {
67
+ if (!token) throw AuthenticationError.missingToken("user token");
68
+ const host = process.env.DATABRICKS_HOST;
69
+ if (!host) throw ConfigurationError.missingEnvVar("DATABRICKS_HOST");
70
+ const serviceCtx = ServiceContext.get();
71
+ return {
72
+ client: new WorkspaceClient({
73
+ token,
74
+ host,
75
+ authType: "pat"
76
+ }, getClientOptions()),
77
+ userId,
78
+ userName,
79
+ userEmail,
80
+ tokenFingerprint: createHash("sha256").update(token).digest("hex").slice(0, 16),
81
+ warehouseId: serviceCtx.warehouseId,
82
+ workspaceId: serviceCtx.workspaceId,
83
+ isUserContext: true
84
+ };
85
+ }
86
+ /**
87
+ * Get the client options for WorkspaceClient.
88
+ * Exposed for testing purposes.
89
+ */
90
+ static getClientOptions() {
91
+ return getClientOptions();
92
+ }
93
+ static async createContext(options, client) {
94
+ try {
95
+ const wsClient = client ?? new WorkspaceClient({}, getClientOptions());
96
+ const [resolvedWorkspaceId, currentUser, resolvedWarehouseId] = await Promise.all([
97
+ ServiceContext.getWorkspaceId(wsClient),
98
+ wsClient.currentUser.me(),
99
+ options?.warehouseId ? ServiceContext.getWarehouseId(wsClient) : Promise.resolve(void 0)
100
+ ]);
101
+ if (!currentUser.id) throw ConfigurationError.resourceNotFound("Service user ID");
102
+ const warehouseId = options?.warehouseId && resolvedWarehouseId !== void 0 ? Promise.resolve(resolvedWarehouseId) : void 0;
68
103
  return {
69
- client: new WorkspaceClient({
70
- token,
71
- host,
72
- authType: "pat"
73
- }, getClientOptions()),
74
- userId,
75
- userName,
76
- userEmail,
77
- tokenFingerprint: createHash("sha256").update(token).digest("hex").slice(0, 16),
78
- warehouseId: serviceCtx.warehouseId,
79
- workspaceId: serviceCtx.workspaceId,
80
- isUserContext: true
104
+ client: wsClient,
105
+ serviceUserId: currentUser.id,
106
+ warehouseId,
107
+ workspaceId: Promise.resolve(resolvedWorkspaceId)
81
108
  };
109
+ } catch (e) {
110
+ if (e instanceof ConfigError) throw ConfigurationError.databricksAuthenticationSetupFailed(e.baseMessage, { cause: e });
111
+ throw e;
82
112
  }
83
- /**
84
- * Get the client options for WorkspaceClient.
85
- * Exposed for testing purposes.
86
- */
87
- static getClientOptions() {
88
- return getClientOptions();
89
- }
90
- static async createContext(options, client) {
91
- try {
92
- const wsClient = client ?? new WorkspaceClient({}, getClientOptions());
93
- const [resolvedWorkspaceId, currentUser, resolvedWarehouseId] = await Promise.all([
94
- ServiceContext.getWorkspaceId(wsClient),
95
- wsClient.currentUser.me(),
96
- options?.warehouseId ? ServiceContext.getWarehouseId(wsClient) : Promise.resolve(void 0)
97
- ]);
98
- if (!currentUser.id) throw ConfigurationError.resourceNotFound("Service user ID");
99
- const warehouseId = options?.warehouseId && resolvedWarehouseId !== void 0 ? Promise.resolve(resolvedWarehouseId) : void 0;
100
- return {
101
- client: wsClient,
102
- serviceUserId: currentUser.id,
103
- warehouseId,
104
- workspaceId: Promise.resolve(resolvedWorkspaceId)
105
- };
106
- } catch (e) {
107
- if (e instanceof ConfigError) throw ConfigurationError.databricksAuthenticationSetupFailed(e.baseMessage, { cause: e });
108
- throw e;
109
- }
110
- }
111
- static async getWorkspaceId(client) {
112
- if (process.env.DATABRICKS_WORKSPACE_ID) return process.env.DATABRICKS_WORKSPACE_ID;
113
+ }
114
+ static async getWorkspaceId(client) {
115
+ if (process.env.DATABRICKS_WORKSPACE_ID) return process.env.DATABRICKS_WORKSPACE_ID;
116
+ const response = await client.apiClient.request({
117
+ path: "/api/2.0/preview/scim/v2/Me",
118
+ method: "GET",
119
+ headers: new Headers(),
120
+ raw: false,
121
+ query: {},
122
+ responseHeaders: ["x-databricks-org-id"]
123
+ });
124
+ if (!response["x-databricks-org-id"]) throw ConfigurationError.resourceNotFound("Workspace ID");
125
+ return response["x-databricks-org-id"];
126
+ }
127
+ static async getWarehouseId(client) {
128
+ if (process.env.DATABRICKS_WAREHOUSE_ID) return process.env.DATABRICKS_WAREHOUSE_ID;
129
+ if (process.env.NODE_ENV === "development") {
113
130
  const response = await client.apiClient.request({
114
- path: "/api/2.0/preview/scim/v2/Me",
131
+ path: "/api/2.0/sql/warehouses",
115
132
  method: "GET",
116
133
  headers: new Headers(),
117
134
  raw: false,
118
- query: {},
119
- responseHeaders: ["x-databricks-org-id"]
135
+ query: { skip_cannot_use: "true" }
120
136
  });
121
- if (!response["x-databricks-org-id"]) throw ConfigurationError.resourceNotFound("Workspace ID");
122
- return response["x-databricks-org-id"];
123
- }
124
- static async getWarehouseId(client) {
125
- if (process.env.DATABRICKS_WAREHOUSE_ID) return process.env.DATABRICKS_WAREHOUSE_ID;
126
- if (process.env.NODE_ENV === "development") {
127
- const response = await client.apiClient.request({
128
- path: "/api/2.0/sql/warehouses",
129
- method: "GET",
130
- headers: new Headers(),
131
- raw: false,
132
- query: { skip_cannot_use: "true" }
133
- });
134
- const priorities = {
135
- RUNNING: 0,
136
- STOPPED: 1,
137
- STARTING: 2,
138
- STOPPING: 3,
139
- DELETED: 99,
140
- DELETING: 99
141
- };
142
- const warehouses = (response.warehouses || []).sort((a, b) => {
143
- return priorities[a.state] - priorities[b.state];
144
- });
145
- if (response.warehouses.length === 0) throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
146
- const firstWarehouse = warehouses[0];
147
- if (firstWarehouse.state === "DELETED" || firstWarehouse.state === "DELETING" || !firstWarehouse.id) throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
148
- return firstWarehouse.id;
149
- }
150
- throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
151
- }
152
- /**
153
- * Reset the service context. Only for testing purposes.
154
- */
155
- static reset() {
156
- ServiceContext.instance = null;
157
- ServiceContext.initPromise = null;
137
+ const priorities = {
138
+ RUNNING: 0,
139
+ STOPPED: 1,
140
+ STARTING: 2,
141
+ STOPPING: 3,
142
+ DELETED: 99,
143
+ DELETING: 99
144
+ };
145
+ const warehouses = (response.warehouses || []).sort((a, b) => {
146
+ return priorities[a.state] - priorities[b.state];
147
+ });
148
+ if (response.warehouses.length === 0) throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
149
+ const firstWarehouse = warehouses[0];
150
+ if (firstWarehouse.state === "DELETED" || firstWarehouse.state === "DELETING" || !firstWarehouse.id) throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
151
+ return firstWarehouse.id;
158
152
  }
159
- };
160
- }));
153
+ throw ConfigurationError.resourceNotFound("Warehouse ID", "Please configure the DATABRICKS_WAREHOUSE_ID environment variable");
154
+ }
155
+ /**
156
+ * Reset the service context. Only for testing purposes.
157
+ */
158
+ static reset() {
159
+ ServiceContext.instance = null;
160
+ ServiceContext.initPromise = null;
161
+ }
162
+ };
161
163
 
162
164
  //#endregion
163
- init_service_context();
164
- export { ServiceContext, init_service_context };
165
+ export { ServiceContext };
165
166
  //# sourceMappingURL=service-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"service-context.js","names":["productName","productVersion"],"sources":["../../src/context/service-context.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n type ClientOptions,\n ConfigError,\n type sql,\n WorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport { coerce } from \"semver\";\nimport {\n name as productName,\n version as productVersion,\n} from \"../../package.json\";\nimport {\n AuthenticationError,\n ConfigurationError,\n InitializationError,\n} from \"../errors\";\nimport type { UserContext } from \"./user-context\";\n\n/**\n * Service context holds the service principal client and shared resources.\n * This is initialized once at app startup and shared across all requests.\n */\nexport interface ServiceContextState {\n /** WorkspaceClient authenticated as the service principal */\n client: WorkspaceClient;\n /** The service principal's user ID */\n serviceUserId: string;\n /** Promise that resolves to the warehouse ID (only present when a plugin requires `SQL_WAREHOUSE` resource) */\n warehouseId?: Promise<string>;\n /** Promise that resolves to the workspace ID */\n workspaceId: Promise<string>;\n}\n\nfunction getClientOptions(): ClientOptions {\n const isDev = process.env.NODE_ENV === \"development\";\n const semver = coerce(productVersion);\n const normalizedVersion = (semver?.version ??\n productVersion) as ClientOptions[\"productVersion\"];\n\n return {\n product: productName,\n productVersion: normalizedVersion,\n ...(isDev && { userAgentExtra: { mode: \"dev\" } }),\n };\n}\n\n/**\n * ServiceContext is a singleton that manages the service principal's\n * WorkspaceClient and shared resources like warehouse/workspace IDs.\n *\n * It's initialized once at app startup and provides the foundation\n * for both service principal and user context execution.\n */\nexport class ServiceContext {\n private static instance: ServiceContextState | null = null;\n private static initPromise: Promise<ServiceContextState> | null = null;\n\n /**\n * Initialize the service context. Should be called once at app startup.\n * Safe to call multiple times - will return the same instance.\n *\n * @param options - Which shared resources to resolve (derived from plugin manifests).\n * @param client - Optional pre-configured WorkspaceClient to use instead\n * of creating one from environment credentials.\n */\n static async initialize(\n options?: { warehouseId?: boolean },\n client?: WorkspaceClient,\n ): Promise<ServiceContextState> {\n if (ServiceContext.instance) {\n return ServiceContext.instance;\n }\n\n if (ServiceContext.initPromise) {\n return ServiceContext.initPromise;\n }\n\n ServiceContext.initPromise = ServiceContext.createContext(options, client);\n ServiceContext.instance = await ServiceContext.initPromise;\n return ServiceContext.instance;\n }\n\n /**\n * Get the initialized service context.\n * @throws Error if not initialized\n */\n static get(): ServiceContextState {\n if (!ServiceContext.instance) {\n throw InitializationError.notInitialized(\n \"ServiceContext\",\n \"Call ServiceContext.initialize() first\",\n );\n }\n return ServiceContext.instance;\n }\n\n /**\n * Check if the service context has been initialized.\n */\n static isInitialized(): boolean {\n return ServiceContext.instance !== null;\n }\n\n /**\n * Create a user context from request headers.\n *\n * @param token - The user's access token from x-forwarded-access-token header\n * @param userId - The user's ID from x-forwarded-user header\n * @param userName - Optional user name\n * @throws Error if token is not provided\n */\n static createUserContext(\n token: string,\n userId: string,\n userName?: string,\n userEmail?: string,\n ): UserContext {\n if (!token) {\n throw AuthenticationError.missingToken(\"user token\");\n }\n\n const host = process.env.DATABRICKS_HOST;\n if (!host) {\n throw ConfigurationError.missingEnvVar(\"DATABRICKS_HOST\");\n }\n\n const serviceCtx = ServiceContext.get();\n\n // Create user client with the OAuth token from Databricks Apps\n // Note: We use authType: \"pat\" because the token is passed as a Bearer token\n // just like a PAT, even though it's technically an OAuth token\n const userClient = new WorkspaceClient(\n {\n token,\n host,\n authType: \"pat\",\n },\n getClientOptions(),\n );\n\n const tokenFingerprint = createHash(\"sha256\")\n .update(token)\n .digest(\"hex\")\n .slice(0, 16);\n\n return {\n client: userClient,\n userId,\n userName,\n userEmail,\n tokenFingerprint,\n warehouseId: serviceCtx.warehouseId,\n workspaceId: serviceCtx.workspaceId,\n isUserContext: true,\n };\n }\n\n /**\n * Get the client options for WorkspaceClient.\n * Exposed for testing purposes.\n */\n static getClientOptions(): ClientOptions {\n return getClientOptions();\n }\n\n private static async createContext(\n options?: { warehouseId?: boolean },\n client?: WorkspaceClient,\n ): Promise<ServiceContextState> {\n try {\n const wsClient = client ?? new WorkspaceClient({}, getClientOptions());\n\n const [resolvedWorkspaceId, currentUser, resolvedWarehouseId] =\n await Promise.all([\n ServiceContext.getWorkspaceId(wsClient),\n wsClient.currentUser.me(),\n options?.warehouseId\n ? ServiceContext.getWarehouseId(wsClient)\n : Promise.resolve(undefined as string | undefined),\n ]);\n\n if (!currentUser.id) {\n throw ConfigurationError.resourceNotFound(\"Service user ID\");\n }\n\n const warehouseId =\n options?.warehouseId && resolvedWarehouseId !== undefined\n ? Promise.resolve(resolvedWarehouseId)\n : undefined;\n\n return {\n client: wsClient,\n serviceUserId: currentUser.id,\n warehouseId,\n workspaceId: Promise.resolve(resolvedWorkspaceId),\n };\n } catch (e) {\n if (e instanceof ConfigError) {\n throw ConfigurationError.databricksAuthenticationSetupFailed(\n e.baseMessage,\n { cause: e },\n );\n }\n throw e;\n }\n }\n\n private static async getWorkspaceId(\n client: WorkspaceClient,\n ): Promise<string> {\n if (process.env.DATABRICKS_WORKSPACE_ID) {\n return process.env.DATABRICKS_WORKSPACE_ID;\n }\n\n const response = (await client.apiClient.request({\n path: \"/api/2.0/preview/scim/v2/Me\",\n method: \"GET\",\n headers: new Headers(),\n raw: false,\n query: {},\n responseHeaders: [\"x-databricks-org-id\"],\n })) as { \"x-databricks-org-id\": string };\n\n if (!response[\"x-databricks-org-id\"]) {\n throw ConfigurationError.resourceNotFound(\"Workspace ID\");\n }\n\n return response[\"x-databricks-org-id\"];\n }\n\n private static async getWarehouseId(\n client: WorkspaceClient,\n ): Promise<string> {\n if (process.env.DATABRICKS_WAREHOUSE_ID) {\n return process.env.DATABRICKS_WAREHOUSE_ID;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n const response = (await client.apiClient.request({\n path: \"/api/2.0/sql/warehouses\",\n method: \"GET\",\n headers: new Headers(),\n raw: false,\n query: {\n skip_cannot_use: \"true\",\n },\n })) as { warehouses: sql.EndpointInfo[] };\n\n const priorities: Record<sql.State, number> = {\n RUNNING: 0,\n STOPPED: 1,\n STARTING: 2,\n STOPPING: 3,\n DELETED: 99,\n DELETING: 99,\n };\n\n const warehouses = (response.warehouses || []).sort((a, b) => {\n return (\n priorities[a.state as sql.State] - priorities[b.state as sql.State]\n );\n });\n\n if (response.warehouses.length === 0) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n const firstWarehouse = warehouses[0];\n if (\n firstWarehouse.state === \"DELETED\" ||\n firstWarehouse.state === \"DELETING\" ||\n !firstWarehouse.id\n ) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n return firstWarehouse.id;\n }\n\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n /**\n * Reset the service context. Only for testing purposes.\n */\n static reset(): void {\n ServiceContext.instance = null;\n ServiceContext.initPromise = null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service-context.js","names":["productName","productVersion"],"sources":["../../src/context/service-context.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n type ClientOptions,\n ConfigError,\n type sql,\n WorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport { coerce } from \"semver\";\nimport {\n name as productName,\n version as productVersion,\n} from \"../../package.json\";\nimport {\n AuthenticationError,\n ConfigurationError,\n InitializationError,\n} from \"../errors\";\nimport type { UserContext } from \"./user-context\";\n\n/**\n * Service context holds the service principal client and shared resources.\n * This is initialized once at app startup and shared across all requests.\n */\nexport interface ServiceContextState {\n /** WorkspaceClient authenticated as the service principal */\n client: WorkspaceClient;\n /** The service principal's user ID */\n serviceUserId: string;\n /** Promise that resolves to the warehouse ID (only present when a plugin requires `SQL_WAREHOUSE` resource) */\n warehouseId?: Promise<string>;\n /** Promise that resolves to the workspace ID */\n workspaceId: Promise<string>;\n}\n\nfunction getClientOptions(): ClientOptions {\n const isDev = process.env.NODE_ENV === \"development\";\n const semver = coerce(productVersion);\n const normalizedVersion = (semver?.version ??\n productVersion) as ClientOptions[\"productVersion\"];\n\n return {\n product: productName,\n productVersion: normalizedVersion,\n ...(isDev && { userAgentExtra: { mode: \"dev\" } }),\n };\n}\n\n/**\n * ServiceContext is a singleton that manages the service principal's\n * WorkspaceClient and shared resources like warehouse/workspace IDs.\n *\n * It's initialized once at app startup and provides the foundation\n * for both service principal and user context execution.\n */\nexport class ServiceContext {\n private static instance: ServiceContextState | null = null;\n private static initPromise: Promise<ServiceContextState> | null = null;\n\n /**\n * Initialize the service context. Should be called once at app startup.\n * Safe to call multiple times - will return the same instance.\n *\n * @param options - Which shared resources to resolve (derived from plugin manifests).\n * @param client - Optional pre-configured WorkspaceClient to use instead\n * of creating one from environment credentials.\n */\n static async initialize(\n options?: { warehouseId?: boolean },\n client?: WorkspaceClient,\n ): Promise<ServiceContextState> {\n if (ServiceContext.instance) {\n return ServiceContext.instance;\n }\n\n if (ServiceContext.initPromise) {\n return ServiceContext.initPromise;\n }\n\n ServiceContext.initPromise = ServiceContext.createContext(options, client);\n ServiceContext.instance = await ServiceContext.initPromise;\n return ServiceContext.instance;\n }\n\n /**\n * Get the initialized service context.\n * @throws Error if not initialized\n */\n static get(): ServiceContextState {\n if (!ServiceContext.instance) {\n throw InitializationError.notInitialized(\n \"ServiceContext\",\n \"Call ServiceContext.initialize() first\",\n );\n }\n return ServiceContext.instance;\n }\n\n /**\n * Check if the service context has been initialized.\n */\n static isInitialized(): boolean {\n return ServiceContext.instance !== null;\n }\n\n /**\n * Create a user context from request headers.\n *\n * @param token - The user's access token from x-forwarded-access-token header\n * @param userId - The user's ID from x-forwarded-user header\n * @param userName - Optional user name\n * @throws Error if token is not provided\n */\n static createUserContext(\n token: string,\n userId: string,\n userName?: string,\n userEmail?: string,\n ): UserContext {\n if (!token) {\n throw AuthenticationError.missingToken(\"user token\");\n }\n\n const host = process.env.DATABRICKS_HOST;\n if (!host) {\n throw ConfigurationError.missingEnvVar(\"DATABRICKS_HOST\");\n }\n\n const serviceCtx = ServiceContext.get();\n\n // Create user client with the OAuth token from Databricks Apps\n // Note: We use authType: \"pat\" because the token is passed as a Bearer token\n // just like a PAT, even though it's technically an OAuth token\n const userClient = new WorkspaceClient(\n {\n token,\n host,\n authType: \"pat\",\n },\n getClientOptions(),\n );\n\n const tokenFingerprint = createHash(\"sha256\")\n .update(token)\n .digest(\"hex\")\n .slice(0, 16);\n\n return {\n client: userClient,\n userId,\n userName,\n userEmail,\n tokenFingerprint,\n warehouseId: serviceCtx.warehouseId,\n workspaceId: serviceCtx.workspaceId,\n isUserContext: true,\n };\n }\n\n /**\n * Get the client options for WorkspaceClient.\n * Exposed for testing purposes.\n */\n static getClientOptions(): ClientOptions {\n return getClientOptions();\n }\n\n private static async createContext(\n options?: { warehouseId?: boolean },\n client?: WorkspaceClient,\n ): Promise<ServiceContextState> {\n try {\n const wsClient = client ?? new WorkspaceClient({}, getClientOptions());\n\n const [resolvedWorkspaceId, currentUser, resolvedWarehouseId] =\n await Promise.all([\n ServiceContext.getWorkspaceId(wsClient),\n wsClient.currentUser.me(),\n options?.warehouseId\n ? ServiceContext.getWarehouseId(wsClient)\n : Promise.resolve(undefined as string | undefined),\n ]);\n\n if (!currentUser.id) {\n throw ConfigurationError.resourceNotFound(\"Service user ID\");\n }\n\n const warehouseId =\n options?.warehouseId && resolvedWarehouseId !== undefined\n ? Promise.resolve(resolvedWarehouseId)\n : undefined;\n\n return {\n client: wsClient,\n serviceUserId: currentUser.id,\n warehouseId,\n workspaceId: Promise.resolve(resolvedWorkspaceId),\n };\n } catch (e) {\n if (e instanceof ConfigError) {\n throw ConfigurationError.databricksAuthenticationSetupFailed(\n e.baseMessage,\n { cause: e },\n );\n }\n throw e;\n }\n }\n\n private static async getWorkspaceId(\n client: WorkspaceClient,\n ): Promise<string> {\n if (process.env.DATABRICKS_WORKSPACE_ID) {\n return process.env.DATABRICKS_WORKSPACE_ID;\n }\n\n const response = (await client.apiClient.request({\n path: \"/api/2.0/preview/scim/v2/Me\",\n method: \"GET\",\n headers: new Headers(),\n raw: false,\n query: {},\n responseHeaders: [\"x-databricks-org-id\"],\n })) as { \"x-databricks-org-id\": string };\n\n if (!response[\"x-databricks-org-id\"]) {\n throw ConfigurationError.resourceNotFound(\"Workspace ID\");\n }\n\n return response[\"x-databricks-org-id\"];\n }\n\n private static async getWarehouseId(\n client: WorkspaceClient,\n ): Promise<string> {\n if (process.env.DATABRICKS_WAREHOUSE_ID) {\n return process.env.DATABRICKS_WAREHOUSE_ID;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n const response = (await client.apiClient.request({\n path: \"/api/2.0/sql/warehouses\",\n method: \"GET\",\n headers: new Headers(),\n raw: false,\n query: {\n skip_cannot_use: \"true\",\n },\n })) as { warehouses: sql.EndpointInfo[] };\n\n const priorities: Record<sql.State, number> = {\n RUNNING: 0,\n STOPPED: 1,\n STARTING: 2,\n STOPPING: 3,\n DELETED: 99,\n DELETING: 99,\n };\n\n const warehouses = (response.warehouses || []).sort((a, b) => {\n return (\n priorities[a.state as sql.State] - priorities[b.state as sql.State]\n );\n });\n\n if (response.warehouses.length === 0) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n const firstWarehouse = warehouses[0];\n if (\n firstWarehouse.state === \"DELETED\" ||\n firstWarehouse.state === \"DELETING\" ||\n !firstWarehouse.id\n ) {\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n return firstWarehouse.id;\n }\n\n throw ConfigurationError.resourceNotFound(\n \"Warehouse ID\",\n \"Please configure the DATABRICKS_WAREHOUSE_ID environment variable\",\n );\n }\n\n /**\n * Reset the service context. Only for testing purposes.\n */\n static reset(): void {\n ServiceContext.instance = null;\n ServiceContext.initPromise = null;\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,3 @@
1
- import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
-
3
1
  //#region src/context/user-context.ts
4
2
  /**
5
3
  * Check if an execution context is a user context.
@@ -7,9 +5,7 @@ import { __esmMin } from "../_virtual/_rolldown/runtime.js";
7
5
  function isUserContext(ctx) {
8
6
  return "isUserContext" in ctx && ctx.isUserContext === true;
9
7
  }
10
- var init_user_context = __esmMin((() => {}));
11
8
 
12
9
  //#endregion
13
- init_user_context();
14
- export { init_user_context, isUserContext };
10
+ export { isUserContext };
15
11
  //# sourceMappingURL=user-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"user-context.js","names":[],"sources":["../../src/context/user-context.ts"],"sourcesContent":["import type { ServiceContextState } from \"./service-context\";\n\n/**\n * User execution context extends the service context with user-specific data.\n * Created on-demand when asUser(req) is called.\n */\nexport interface UserContext {\n /** WorkspaceClient authenticated as the user */\n client: ServiceContextState[\"client\"];\n /** The user's ID (from request headers) */\n userId: string;\n /** The user's name (from request headers) */\n userName?: string;\n /** The user's email (from `x-forwarded-email` header) */\n userEmail?: string;\n /** Truncated SHA-256 hash of the user's OBO token, used to detect token rotation */\n tokenFingerprint?: string;\n /** Promise that resolves to the warehouse ID (inherited from service context, only present when a plugin requires `SQL_WAREHOUSE` resource) */\n warehouseId?: Promise<string>;\n /** Promise that resolves to the workspace ID (inherited from service context) */\n workspaceId: Promise<string>;\n /** Flag indicating this is a user context */\n isUserContext: true;\n}\n\n/**\n * Execution context can be either service or user context.\n */\nexport type ExecutionContext = ServiceContextState | UserContext;\n\n/**\n * Check if an execution context is a user context.\n */\nexport function isUserContext(ctx: ExecutionContext): ctx is UserContext {\n return \"isUserContext\" in ctx && ctx.isUserContext === true;\n}\n"],"mappings":";;;;;;AAiCA,SAAgB,cAAc,KAA2C;AACvE,QAAO,mBAAmB,OAAO,IAAI,kBAAkB"}
1
+ {"version":3,"file":"user-context.js","names":[],"sources":["../../src/context/user-context.ts"],"sourcesContent":["import type { ServiceContextState } from \"./service-context\";\n\n/**\n * User execution context extends the service context with user-specific data.\n * Created on-demand when asUser(req) is called.\n */\nexport interface UserContext {\n /** WorkspaceClient authenticated as the user */\n client: ServiceContextState[\"client\"];\n /** The user's ID (from request headers) */\n userId: string;\n /** The user's name (from request headers) */\n userName?: string;\n /** The user's email (from `x-forwarded-email` header) */\n userEmail?: string;\n /** Truncated SHA-256 hash of the user's OBO token, used to detect token rotation */\n tokenFingerprint?: string;\n /** Promise that resolves to the warehouse ID (inherited from service context, only present when a plugin requires `SQL_WAREHOUSE` resource) */\n warehouseId?: Promise<string>;\n /** Promise that resolves to the workspace ID (inherited from service context) */\n workspaceId: Promise<string>;\n /** Flag indicating this is a user context */\n isUserContext: true;\n}\n\n/**\n * Execution context can be either service or user context.\n */\nexport type ExecutionContext = ServiceContextState | UserContext;\n\n/**\n * Check if an execution context is a user context.\n */\nexport function isUserContext(ctx: ExecutionContext): ctx is UserContext {\n return \"isUserContext\" in ctx && ctx.isUserContext === true;\n}\n"],"mappings":";;;;AAiCA,SAAgB,cAAc,KAA2C;AACvE,QAAO,mBAAmB,OAAO,IAAI,kBAAkB"}
@@ -1,8 +1,7 @@
1
1
  import { ConfigurationError } from "../../errors/configuration.js";
2
- import { init_errors } from "../../errors/index.js";
2
+ import "../../errors/index.js";
3
3
 
4
4
  //#region src/core/agent/create-agent.ts
5
- init_errors();
6
5
  /**
7
6
  * Pure factory for agent definitions. Returns the passed-in definition after
8
7
  * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape
@@ -1 +1 @@
1
- {"version":3,"file":"create-agent.js","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"sourcesContent":["import { ConfigurationError } from \"../../errors\";\nimport type { AgentDefinition } from \"./types\";\n\n/**\n * Pure factory for agent definitions. Returns the passed-in definition after\n * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape\n * and is safe to call at module top-level.\n *\n * The returned value is a plain `AgentDefinition` — no adapter construction,\n * no side effects. Register it with `agents({ agents: { name: def } })` or run\n * it standalone via `runAgent(def, input)`.\n *\n * @example\n * ```ts\n * const support = createAgent({\n * instructions: \"You help customers.\",\n * model: \"databricks-claude-sonnet-4-5\",\n * tools: {\n * get_weather: tool({ ... }),\n * },\n * });\n * ```\n */\nexport function createAgent(def: AgentDefinition): AgentDefinition {\n detectCycles(def);\n return def;\n}\n\n/**\n * Walks the `agents: { ... }` sub-agent tree via DFS and throws if a cycle is\n * found. Cycles would cause infinite recursion at tool-invocation time.\n */\nfunction detectCycles(def: AgentDefinition): void {\n const visiting = new Set<AgentDefinition>();\n const visited = new Set<AgentDefinition>();\n\n const walk = (current: AgentDefinition, path: string[]): void => {\n if (visited.has(current)) return;\n if (visiting.has(current)) {\n throw new ConfigurationError(\n `Agent sub-agent cycle detected: ${path.join(\" -> \")}`,\n );\n }\n visiting.add(current);\n for (const [childKey, child] of Object.entries(current.agents ?? {})) {\n walk(child, [...path, childKey]);\n }\n visiting.delete(current);\n visited.add(current);\n };\n\n walk(def, [def.name ?? \"(root)\"]);\n}\n"],"mappings":";;;;aAAkD;;;;;;;;;;;;;;;;;;;;;AAuBlD,SAAgB,YAAY,KAAuC;AACjE,cAAa,IAAI;AACjB,QAAO;;;;;;AAOT,SAAS,aAAa,KAA4B;CAChD,MAAM,2BAAW,IAAI,KAAsB;CAC3C,MAAM,0BAAU,IAAI,KAAsB;CAE1C,MAAM,QAAQ,SAA0B,SAAyB;AAC/D,MAAI,QAAQ,IAAI,QAAQ,CAAE;AAC1B,MAAI,SAAS,IAAI,QAAQ,CACvB,OAAM,IAAI,mBACR,mCAAmC,KAAK,KAAK,OAAO,GACrD;AAEH,WAAS,IAAI,QAAQ;AACrB,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,QAAQ,UAAU,EAAE,CAAC,CAClE,MAAK,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC;AAElC,WAAS,OAAO,QAAQ;AACxB,UAAQ,IAAI,QAAQ;;AAGtB,MAAK,KAAK,CAAC,IAAI,QAAQ,SAAS,CAAC"}
1
+ {"version":3,"file":"create-agent.js","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"sourcesContent":["import { ConfigurationError } from \"../../errors\";\nimport type { AgentDefinition } from \"./types\";\n\n/**\n * Pure factory for agent definitions. Returns the passed-in definition after\n * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape\n * and is safe to call at module top-level.\n *\n * The returned value is a plain `AgentDefinition` — no adapter construction,\n * no side effects. Register it with `agents({ agents: { name: def } })` or run\n * it standalone via `runAgent(def, input)`.\n *\n * @example\n * ```ts\n * const support = createAgent({\n * instructions: \"You help customers.\",\n * model: \"databricks-claude-sonnet-4-5\",\n * tools: {\n * get_weather: tool({ ... }),\n * },\n * });\n * ```\n */\nexport function createAgent(def: AgentDefinition): AgentDefinition {\n detectCycles(def);\n return def;\n}\n\n/**\n * Walks the `agents: { ... }` sub-agent tree via DFS and throws if a cycle is\n * found. Cycles would cause infinite recursion at tool-invocation time.\n */\nfunction detectCycles(def: AgentDefinition): void {\n const visiting = new Set<AgentDefinition>();\n const visited = new Set<AgentDefinition>();\n\n const walk = (current: AgentDefinition, path: string[]): void => {\n if (visited.has(current)) return;\n if (visiting.has(current)) {\n throw new ConfigurationError(\n `Agent sub-agent cycle detected: ${path.join(\" -> \")}`,\n );\n }\n visiting.add(current);\n for (const [childKey, child] of Object.entries(current.agents ?? {})) {\n walk(child, [...path, childKey]);\n }\n visiting.delete(current);\n visited.add(current);\n };\n\n walk(def, [def.name ?? \"(root)\"]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,YAAY,KAAuC;AACjE,cAAa,IAAI;AACjB,QAAO;;;;;;AAOT,SAAS,aAAa,KAA4B;CAChD,MAAM,2BAAW,IAAI,KAAsB;CAC3C,MAAM,0BAAU,IAAI,KAAsB;CAE1C,MAAM,QAAQ,SAA0B,SAAyB;AAC/D,MAAI,QAAQ,IAAI,QAAQ,CAAE;AAC1B,MAAI,SAAS,IAAI,QAAQ,CACvB,OAAM,IAAI,mBACR,mCAAmC,KAAK,KAAK,OAAO,GACrD;AAEH,WAAS,IAAI,QAAQ;AACrB,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,QAAQ,UAAU,EAAE,CAAC,CAClE,MAAK,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC;AAElC,WAAS,OAAO,QAAQ;AACxB,UAAQ,IAAI,QAAQ;;AAGtB,MAAK,KAAK,CAAC,IAAI,QAAQ,SAAS,CAAC"}