@databricks/appkit 0.61.1 → 0.63.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 (194) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/agents/databricks.d.ts.map +1 -1
  3. package/dist/agents/databricks.js.map +1 -1
  4. package/dist/agents/supervisor-api.d.ts.map +1 -1
  5. package/dist/agents/supervisor-api.js.map +1 -1
  6. package/dist/app/index.d.ts.map +1 -1
  7. package/dist/app/index.js.map +1 -1
  8. package/dist/appkit/package.js +1 -1
  9. package/dist/cache/index.d.ts.map +1 -1
  10. package/dist/cache/index.js.map +1 -1
  11. package/dist/cache/storage/memory.js.map +1 -1
  12. package/dist/cache/storage/persistent.js.map +1 -1
  13. package/dist/cli/commands/codemod/index.js.map +1 -1
  14. package/dist/cli/commands/codemod/on-plugins-ready.js.map +1 -1
  15. package/dist/cli/commands/docs.js.map +1 -1
  16. package/dist/cli/commands/doctor/bundle.js.map +1 -1
  17. package/dist/cli/commands/doctor/index.js.map +1 -1
  18. package/dist/cli/commands/doctor/report.js.map +1 -1
  19. package/dist/cli/commands/doctor/resolve-targets.js.map +1 -1
  20. package/dist/cli/commands/doctor/run.js.map +1 -1
  21. package/dist/cli/commands/generate-types.js.map +1 -1
  22. package/dist/cli/commands/lint.js.map +1 -1
  23. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  24. package/dist/cli/commands/plugin/create/create.js.map +1 -1
  25. package/dist/cli/commands/plugin/create/prompt-resource.js.map +1 -1
  26. package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
  27. package/dist/cli/commands/plugin/index.js.map +1 -1
  28. package/dist/cli/commands/plugin/list/list.js.map +1 -1
  29. package/dist/cli/commands/plugin/promote/promote.js.map +1 -1
  30. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  31. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  32. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  33. package/dist/cli/commands/registry/add.js.map +1 -1
  34. package/dist/cli/commands/registry/client.js.map +1 -1
  35. package/dist/cli/commands/registry/config-writer.js.map +1 -1
  36. package/dist/cli/commands/registry/env-reconcile.js.map +1 -1
  37. package/dist/cli/commands/registry/env-writer.js.map +1 -1
  38. package/dist/cli/commands/registry/index.js.map +1 -1
  39. package/dist/cli/commands/registry/info.js.map +1 -1
  40. package/dist/cli/commands/registry/list.js.map +1 -1
  41. package/dist/cli/commands/registry/requirements.js.map +1 -1
  42. package/dist/cli/commands/registry/server-register.js.map +1 -1
  43. package/dist/cli/commands/registry/workspace-picker.js.map +1 -1
  44. package/dist/cli/commands/setup.js.map +1 -1
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/connectors/files/client.js.map +1 -1
  47. package/dist/connectors/lakebase/index.d.ts.map +1 -1
  48. package/dist/connectors/lakebase/index.js.map +1 -1
  49. package/dist/connectors/lakebase/pool-manager.d.ts.map +1 -1
  50. package/dist/connectors/lakebase/pool-manager.js.map +1 -1
  51. package/dist/connectors/lakebase/routing-pool.d.ts.map +1 -1
  52. package/dist/connectors/lakebase/routing-pool.js.map +1 -1
  53. package/dist/connectors/mcp/client.d.ts.map +1 -1
  54. package/dist/connectors/mcp/client.js.map +1 -1
  55. package/dist/connectors/sql-warehouse/client.js.map +1 -1
  56. package/dist/context/client-options.js.map +1 -1
  57. package/dist/context/execution-context.d.ts.map +1 -1
  58. package/dist/context/execution-context.js.map +1 -1
  59. package/dist/context/index.d.ts +1 -1
  60. package/dist/context/service-context.d.ts +55 -3
  61. package/dist/context/service-context.d.ts.map +1 -1
  62. package/dist/context/service-context.js.map +1 -1
  63. package/dist/core/agent/build-toolkit.js.map +1 -1
  64. package/dist/core/agent/load-agents.d.ts.map +1 -1
  65. package/dist/core/agent/load-agents.js.map +1 -1
  66. package/dist/core/agent/run-agent.d.ts.map +1 -1
  67. package/dist/core/agent/run-agent.js.map +1 -1
  68. package/dist/core/agent/toolkit-resolver.js.map +1 -1
  69. package/dist/core/agent/tools/define-tool.d.ts.map +1 -1
  70. package/dist/core/agent/tools/define-tool.js.map +1 -1
  71. package/dist/core/agent/tools/tool.d.ts.map +1 -1
  72. package/dist/core/agent/tools/tool.js.map +1 -1
  73. package/dist/core/agent/types.d.ts.map +1 -1
  74. package/dist/core/agent/types.js.map +1 -1
  75. package/dist/core/appkit.d.ts.map +1 -1
  76. package/dist/core/appkit.js.map +1 -1
  77. package/dist/core/lifecycle-manager.js.map +1 -1
  78. package/dist/core/plugin-context.d.ts +13 -0
  79. package/dist/core/plugin-context.d.ts.map +1 -1
  80. package/dist/core/plugin-context.js +12 -1
  81. package/dist/core/plugin-context.js.map +1 -1
  82. package/dist/errors/configuration.d.ts.map +1 -1
  83. package/dist/errors/configuration.js.map +1 -1
  84. package/dist/logging/logger.js.map +1 -1
  85. package/dist/logging/wide-event-emitter.js.map +1 -1
  86. package/dist/plugin/dev-reader.d.ts.map +1 -1
  87. package/dist/plugin/dev-reader.js.map +1 -1
  88. package/dist/plugin/interceptors/cache.js.map +1 -1
  89. package/dist/plugin/interceptors/retry.js.map +1 -1
  90. package/dist/plugin/interceptors/telemetry.js.map +1 -1
  91. package/dist/plugin/plugin.d.ts +2 -2
  92. package/dist/plugin/plugin.d.ts.map +1 -1
  93. package/dist/plugin/plugin.js.map +1 -1
  94. package/dist/plugins/agents/agents.d.ts.map +1 -1
  95. package/dist/plugins/agents/agents.js.map +1 -1
  96. package/dist/plugins/agents/event-translator.js.map +1 -1
  97. package/dist/plugins/agents/thread-store.js.map +1 -1
  98. package/dist/plugins/ai-search/ai-search.d.ts.map +1 -1
  99. package/dist/plugins/ai-search/ai-search.js.map +1 -1
  100. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  101. package/dist/plugins/analytics/analytics.js.map +1 -1
  102. package/dist/plugins/analytics/mv/cache.js.map +1 -1
  103. package/dist/plugins/analytics/mv/constants.js.map +1 -1
  104. package/dist/plugins/analytics/mv/formatters.js.map +1 -1
  105. package/dist/plugins/analytics/mv/metadata.js.map +1 -1
  106. package/dist/plugins/analytics/mv/registry.js.map +1 -1
  107. package/dist/plugins/analytics/mv/schemas.js.map +1 -1
  108. package/dist/plugins/analytics/query.js.map +1 -1
  109. package/dist/plugins/analytics/result-delivery.js.map +1 -1
  110. package/dist/plugins/files/helpers.js.map +1 -1
  111. package/dist/plugins/files/plugin.d.ts.map +1 -1
  112. package/dist/plugins/files/plugin.js.map +1 -1
  113. package/dist/plugins/files/types.d.ts.map +1 -1
  114. package/dist/plugins/genie/genie.d.ts.map +1 -1
  115. package/dist/plugins/genie/genie.js.map +1 -1
  116. package/dist/plugins/jobs/plugin.d.ts.map +1 -1
  117. package/dist/plugins/jobs/plugin.js.map +1 -1
  118. package/dist/plugins/jobs/types.d.ts.map +1 -1
  119. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  120. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  121. package/dist/plugins/lakebase/types.d.ts.map +1 -1
  122. package/dist/plugins/server/base-server.js.map +1 -1
  123. package/dist/plugins/server/client-config-sanitizer.js.map +1 -1
  124. package/dist/plugins/server/index.d.ts.map +1 -1
  125. package/dist/plugins/server/index.js.map +1 -1
  126. package/dist/plugins/server/react-source-loc-vite-plugin.js.map +1 -1
  127. package/dist/plugins/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
  128. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  129. package/dist/plugins/server/static-server.js.map +1 -1
  130. package/dist/plugins/server/utils.js.map +1 -1
  131. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  132. package/dist/plugins/serving/schema-filter.js.map +1 -1
  133. package/dist/plugins/serving/serving.d.ts.map +1 -1
  134. package/dist/plugins/serving/serving.js.map +1 -1
  135. package/dist/plugins/ui-variants/index.js.map +1 -1
  136. package/dist/registry/manifest-loader.d.ts.map +1 -1
  137. package/dist/registry/manifest-loader.js.map +1 -1
  138. package/dist/registry/resource-registry.d.ts.map +1 -1
  139. package/dist/registry/resource-registry.js.map +1 -1
  140. package/dist/registry/types.d.ts.map +1 -1
  141. package/dist/registry/types.js.map +1 -1
  142. package/dist/schemas/manifest.d.ts.map +1 -1
  143. package/dist/schemas/manifest.js.map +1 -1
  144. package/dist/schemas/metric-fqn.js.map +1 -1
  145. package/dist/shared/src/plugin.d.ts.map +1 -1
  146. package/dist/shared/src/schemas/manifest.d.ts.map +1 -1
  147. package/dist/shared/src/schemas/metric-fqn.js.map +1 -1
  148. package/dist/shared/src/schemas/metric-metadata-bundle.js.map +1 -1
  149. package/dist/shared/src/schemas/metric-source.js.map +1 -1
  150. package/dist/shared/src/sse/analytics.js.map +1 -1
  151. package/dist/shared/src/workspace-client/legacy.js.map +1 -1
  152. package/dist/shared/src/workspace-client/types.d.ts.map +1 -1
  153. package/dist/stream/sse-writer.js.map +1 -1
  154. package/dist/stream/stream-manager.d.ts.map +1 -1
  155. package/dist/stream/stream-manager.js.map +1 -1
  156. package/dist/stream/types.js.map +1 -1
  157. package/dist/telemetry/instrumentations.js.map +1 -1
  158. package/dist/telemetry/telemetry-manager.js.map +1 -1
  159. package/dist/telemetry/telemetry-provider.js.map +1 -1
  160. package/dist/telemetry/trace-sampler.js.map +1 -1
  161. package/dist/testing/expect-stream.d.ts +111 -0
  162. package/dist/testing/expect-stream.d.ts.map +1 -0
  163. package/dist/testing/expect-stream.js +154 -0
  164. package/dist/testing/expect-stream.js.map +1 -0
  165. package/dist/testing/fixtures.d.ts +257 -0
  166. package/dist/testing/fixtures.d.ts.map +1 -0
  167. package/dist/testing/fixtures.js +385 -0
  168. package/dist/testing/fixtures.js.map +1 -0
  169. package/dist/testing/index.d.ts +5 -0
  170. package/dist/testing/index.js +5 -0
  171. package/dist/testing/test-plugin-context.d.ts +158 -0
  172. package/dist/testing/test-plugin-context.d.ts.map +1 -0
  173. package/dist/testing/test-plugin-context.js +139 -0
  174. package/dist/testing/test-plugin-context.js.map +1 -0
  175. package/dist/type-generator/cache.js.map +1 -1
  176. package/dist/type-generator/index.js.map +1 -1
  177. package/dist/type-generator/migration.js.map +1 -1
  178. package/dist/type-generator/mv-registry/config.js.map +1 -1
  179. package/dist/type-generator/query-registry.js.map +1 -1
  180. package/dist/type-generator/serving/cache.js.map +1 -1
  181. package/dist/type-generator/serving/generator.js.map +1 -1
  182. package/dist/type-generator/serving/server-file-extractor.d.ts.map +1 -1
  183. package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
  184. package/dist/type-generator/serving/vite-plugin.d.ts.map +1 -1
  185. package/dist/type-generator/serving/vite-plugin.js.map +1 -1
  186. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  187. package/dist/type-generator/vite-plugin.js.map +1 -1
  188. package/dist/workspace-client/legacy.js.map +1 -1
  189. package/dist/workspace-client/types.d.ts.map +1 -1
  190. package/docs/plugins/testing.md +245 -0
  191. package/llms.txt +1 -0
  192. package/package.json +15 -2
  193. package/sbom.cdx.json +1 -1
  194. package/skills/appkit-ui-variants/SKILL.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"legacy.js","names":[],"sources":["../../src/workspace-client/legacy.ts"],"sourcesContent":["/**\n * The single module in AppKit allowed to import `@databricks/sdk-experimental`\n * directly (enforced by the Biome `noRestrictedImports` boundary rule). Every\n * other AppKit module reaches the SDK through the wrapper's re-exports and the\n * {@link WorkspaceClient} facade.\n *\n * Isolating the SDK here is what makes the incremental migration to the modular\n * Databricks SDK a localized change: to migrate a service, swap its getter in\n * `client.ts` from the legacy delegate to the modular client — nothing else in\n * the codebase imports the SDK, so the blast radius is one connector + its test.\n */\n\nimport type {\n ClientOptions,\n WorkspaceClient as SdkWorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport * as SDK from \"@databricks/sdk-experimental\";\n\nconst { WorkspaceClient: SdkWorkspaceClientCtor } = SDK;\n\n/** The concrete legacy SDK client type. */\nexport type LegacyWorkspaceClient = SdkWorkspaceClient;\n\n/**\n * Options used to construct the wrapper. Mirrors the subset of the old SDK's\n * `Config` + `ClientOptions` that AppKit relies on today; we deliberately do\n * NOT re-expose every old-SDK config knob.\n */\nexport interface WorkspaceClientOptions {\n /** Databricks host, e.g. https://my-workspace.cloud.databricks.com. Defaults to DATABRICKS_HOST / profile resolution. */\n host?: string;\n /** `~/.databrickscfg` profile name. Used when no host/token is provided. */\n profile?: string;\n /** Bearer token. When set, `authType` defaults to \"pat\". */\n token?: string;\n /** Authentication strategy passed to the legacy client. */\n authType?: \"pat\";\n /**\n * SDK client options (product / productVersion / userAgentExtra) used to\n * stamp the outbound User-Agent. Produced by `getClientOptions()`; omitted\n * by build-time callers that don't stamp a User-Agent.\n */\n clientOptions?: ClientOptions;\n}\n\n/**\n * Construct a legacy `WorkspaceClient` from wrapper options.\n *\n * Centralised so the wrapper facade, the `.toLegacyWorkspaceClient()` escape\n * hatch, and any per-request OBO client all build it the same way.\n */\nexport function buildLegacyWorkspaceClient(\n opts: WorkspaceClientOptions,\n): LegacyWorkspaceClient {\n // Check `token !== undefined`, NOT truthiness: an explicitly-passed token\n // must stick to the PAT path even when it's an empty string. Falling through\n // to the default-auth branch on an empty OBO token would silently\n // authenticate as the service principal instead of failing — a privilege\n // escalation in the OBO path. An invalid/empty token should blow up loudly.\n const cfg =\n opts.token !== undefined\n ? { host: opts.host, token: opts.token, authType: opts.authType ?? \"pat\" }\n : opts.host\n ? { host: opts.host }\n : opts.profile\n ? { profile: opts.profile }\n : {};\n return new SdkWorkspaceClientCtor(cfg, opts.clientOptions);\n}\n\n// ── SDK type re-exports ──────────────────────────────────────────────\nexport type {\n CancellationToken,\n ClientOptions,\n} from \"@databricks/sdk-experimental\";\n// ── SDK value re-exports ─────────────────────────────────────────────\n//\n// AppKit modules import these from the wrapper instead of the SDK so the\n// boundary rule holds. `Context` bridges AbortSignal → CancellationToken\n// (serving/jobs/sql-warehouse); `Time`/`TimeUnits` drive genie polling;\n// `ConfigError` is matched in service-context's auth-failure handling.\n//\n// These are sourced off the namespace import rather than `export { ... } from`\n// because the SDK is CommonJS: its `Time` export is emitted as a getter that\n// calls `__importDefault(...)`, which defeats Node's static named-export\n// detection — a direct `export { Time }` throws \"does not provide an export\n// named 'Time'\" at ESM link time. `Time` is only reachable via the module\n// object, so we fall back to `SDK.default.Time` (matching the original genie\n// connector's `SDK.Time ?? SDK.default.Time` guard).\nexport const { ConfigError, Context, TimeUnits, loadConfigFile } = SDK;\nexport const Time =\n SDK.Time ?? (SDK as unknown as { default: typeof SDK }).default.Time;\n\n// Deep-import types used by the genie connector's waiter idiom. Not exposed\n// on the SDK's top-level index, so re-exported here to keep the genie\n// connector off a direct `@databricks/sdk-experimental/dist/**` import.\nexport type { GenieMessage } from \"@databricks/sdk-experimental/dist/apis/dashboards\";\nexport type { Waiter } from \"@databricks/sdk-experimental/dist/wait\";\n"],"mappings":";;;AAkBA,MAAM,EAAE,iBAAiB,2BAA2B;;;;;;;AAiCpD,SAAgB,2BACd,MACuB;AAcvB,QAAO,IAAI,uBAPT,KAAK,UAAU,SACX;EAAE,MAAM,KAAK;EAAM,OAAO,KAAK;EAAO,UAAU,KAAK,YAAY;EAAO,GACxE,KAAK,OACH,EAAE,MAAM,KAAK,MAAM,GACnB,KAAK,UACH,EAAE,SAAS,KAAK,SAAS,GACzB,EAAE,EAC2B,KAAK,cAAc;;AAsB5D,MAAa,EAAE,aAAa,SAAS,WAAW,mBAAmB;AACnE,MAAa,OACX,IAAI,QAAS,IAA2C,QAAQ"}
1
+ {"version":3,"file":"legacy.js","names":[],"sources":["../../src/workspace-client/legacy.ts"],"sourcesContent":["/**\n * The single module in AppKit allowed to import `@databricks/sdk-experimental`\n * directly (enforced by the oxlint `no-restricted-imports` boundary rule). Every\n * other AppKit module reaches the SDK through the wrapper's re-exports and the\n * {@link WorkspaceClient} facade.\n *\n * Isolating the SDK here is what makes the incremental migration to the modular\n * Databricks SDK a localized change: to migrate a service, swap its getter in\n * `client.ts` from the legacy delegate to the modular client — nothing else in\n * the codebase imports the SDK, so the blast radius is one connector + its test.\n */\n\nimport type {\n ClientOptions,\n WorkspaceClient as SdkWorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport * as SDK from \"@databricks/sdk-experimental\";\n\nconst { WorkspaceClient: SdkWorkspaceClientCtor } = SDK;\n\n/** The concrete legacy SDK client type. */\nexport type LegacyWorkspaceClient = SdkWorkspaceClient;\n\n/**\n * Options used to construct the wrapper. Mirrors the subset of the old SDK's\n * `Config` + `ClientOptions` that AppKit relies on today; we deliberately do\n * NOT re-expose every old-SDK config knob.\n */\nexport interface WorkspaceClientOptions {\n /** Databricks host, e.g. https://my-workspace.cloud.databricks.com. Defaults to DATABRICKS_HOST / profile resolution. */\n host?: string;\n /** `~/.databrickscfg` profile name. Used when no host/token is provided. */\n profile?: string;\n /** Bearer token. When set, `authType` defaults to \"pat\". */\n token?: string;\n /** Authentication strategy passed to the legacy client. */\n authType?: \"pat\";\n /**\n * SDK client options (product / productVersion / userAgentExtra) used to\n * stamp the outbound User-Agent. Produced by `getClientOptions()`; omitted\n * by build-time callers that don't stamp a User-Agent.\n */\n clientOptions?: ClientOptions;\n}\n\n/**\n * Construct a legacy `WorkspaceClient` from wrapper options.\n *\n * Centralised so the wrapper facade, the `.toLegacyWorkspaceClient()` escape\n * hatch, and any per-request OBO client all build it the same way.\n */\nexport function buildLegacyWorkspaceClient(\n opts: WorkspaceClientOptions,\n): LegacyWorkspaceClient {\n // Check `token !== undefined`, NOT truthiness: an explicitly-passed token\n // must stick to the PAT path even when it's an empty string. Falling through\n // to the default-auth branch on an empty OBO token would silently\n // authenticate as the service principal instead of failing — a privilege\n // escalation in the OBO path. An invalid/empty token should blow up loudly.\n const cfg =\n opts.token !== undefined\n ? { host: opts.host, token: opts.token, authType: opts.authType ?? \"pat\" }\n : opts.host\n ? { host: opts.host }\n : opts.profile\n ? { profile: opts.profile }\n : {};\n return new SdkWorkspaceClientCtor(cfg, opts.clientOptions);\n}\n\n// ── SDK type re-exports ──────────────────────────────────────────────\nexport type {\n CancellationToken,\n ClientOptions,\n} from \"@databricks/sdk-experimental\";\n// ── SDK value re-exports ─────────────────────────────────────────────\n//\n// AppKit modules import these from the wrapper instead of the SDK so the\n// boundary rule holds. `Context` bridges AbortSignal → CancellationToken\n// (serving/jobs/sql-warehouse); `Time`/`TimeUnits` drive genie polling;\n// `ConfigError` is matched in service-context's auth-failure handling.\n//\n// These are sourced off the namespace import rather than `export { ... } from`\n// because the SDK is CommonJS: its `Time` export is emitted as a getter that\n// calls `__importDefault(...)`, which defeats Node's static named-export\n// detection — a direct `export { Time }` throws \"does not provide an export\n// named 'Time'\" at ESM link time. `Time` is only reachable via the module\n// object, so we fall back to `SDK.default.Time` (matching the original genie\n// connector's `SDK.Time ?? SDK.default.Time` guard).\nexport const { ConfigError, Context, TimeUnits, loadConfigFile } = SDK;\nexport const Time =\n SDK.Time ?? (SDK as unknown as { default: typeof SDK }).default.Time;\n\n// Deep-import types used by the genie connector's waiter idiom. Not exposed\n// on the SDK's top-level index, so re-exported here to keep the genie\n// connector off a direct `@databricks/sdk-experimental/dist/**` import.\nexport type { GenieMessage } from \"@databricks/sdk-experimental/dist/apis/dashboards\";\nexport type { Waiter } from \"@databricks/sdk-experimental/dist/wait\";\n"],"mappings":";;;AAkBA,MAAM,EAAE,iBAAiB,2BAA2B;;;;;;;AAiCpD,SAAgB,2BACd,MACuB;AAcvB,QAAO,IAAI,uBAPT,KAAK,UAAU,SACX;EAAE,MAAM,KAAK;EAAM,OAAO,KAAK;EAAO,UAAU,KAAK,YAAY;EAAO,GACxE,KAAK,OACH,EAAE,MAAM,KAAK,MAAM,GACnB,KAAK,UACH,EAAE,SAAS,KAAK,SAAS,GACzB,EAAE,EAC2B,KAAK,cAAc;;AAsB5D,MAAa,EAAE,aAAa,SAAS,WAAW,mBAAmB;AACnE,MAAa,OACX,IAAI,QAAS,IAA2C,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/workspace-client/types.ts"],"mappings":";;;;;;;;;;;;UAkCiB,iBAAA;EAWA;EAAA,SATN,KAAA,EAAO,qBAAA;EAYa;EAAA,SATpB,UAAA,EAAY,qBAAA;EAYM;EAAA,SATlB,KAAA,EAAO,qBAAA;EAYM;EAAA,SATb,IAAA,EAAM,qBAAA;EAgBE;EAAA,SAbR,kBAAA,EAAoB,qBAAA;EAoBT;EAAA,SAjBX,gBAAA,EAAkB,qBAAA;EAwBA;EAAA,SArBlB,WAAA,EAAa,qBAAA;EAqB0B;;;;;EAAA,SAdvC,MAAA,EAAQ,qBAAA;;;;;;WAOR,SAAA,EAAW,qBAAA;;;;;;EAOpB,uBAAA,IAA2B,qBAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/workspace-client/types.ts"],"mappings":";;;;;;;;;;;;UA6BiB,iBAAA;EAWA;EAAA,SATN,KAAA,EAAO,qBAAA;EAYa;EAAA,SATpB,UAAA,EAAY,qBAAA;EAYM;EAAA,SATlB,KAAA,EAAO,qBAAA;EAYM;EAAA,SATb,IAAA,EAAM,qBAAA;EAgBE;EAAA,SAbR,kBAAA,EAAoB,qBAAA;EAoBT;EAAA,SAjBX,gBAAA,EAAkB,qBAAA;EAwBA;EAAA,SArBlB,WAAA,EAAa,qBAAA;EAqB0B;;;;;EAAA,SAdvC,MAAA,EAAQ,qBAAA;;;;;;WAOR,SAAA,EAAW,qBAAA;;;;;;EAOpB,uBAAA,IAA2B,qBAAA;AAAA"}
@@ -0,0 +1,245 @@
1
+ # Testing
2
+
3
+ AppKit ships a testing kit at `@databricks/appkit/testing` so you can test a plugin — including its cross-plugin tool calls and streaming responses — without a live Databricks workspace, credentials, or network access. That makes plugin tests fast and lets them run in CI, where no workspace is available.
4
+
5
+ ## Goal[​](#goal "Direct link to Goal")
6
+
7
+ Exercise a plugin's real code paths — route registration, cross-plugin tool dispatch, user-scoped (on-behalf-of) execution, and per-call timeouts — against a real `PluginContext` with only its outer edges faked. Nothing about the context is reimplemented, so a test can't drift from production behavior.
8
+
9
+ The kit has two entry points plus a set of fixture helpers:
10
+
11
+ * **`createTestPluginContext()`** — build a real `PluginContext` with faked edges and attach it to a plugin.
12
+ * **`expectStream(...).toEmit(...)`** — assert the ordered event types a stream emits.
13
+ * **Fixtures** — `createMockRequest`, `createMockResponse`, `mockServiceContext`, and SQL response builders.
14
+
15
+ The kit uses [Vitest](https://vitest.dev)'s `vi` for its mocks, so `vitest` is an **optional peer dependency**: you already have it (you're writing Vitest tests), and the kit resolves to your copy rather than bundling a second one. Because it's optional, it is not installed into apps that never import `@databricks/appkit/testing` — production installs stay free of the test framework. Any Vitest v3 or v4 works.
16
+
17
+ ## `createTestPluginContext()`[​](#createtestplugincontext "Direct link to createtestplugincontext")
18
+
19
+ `PluginContext` is the mediator AppKit passes to every plugin — it buffers routes, tracks tool providers, and runs cross-plugin tool calls with user scoping and a timeout. `createTestPluginContext()` returns the **real** context with three edges faked:
20
+
21
+ | Edge | How it's faked |
22
+ | -------------- | ----------------------------------------------------------------------------------------- |
23
+ | Telemetry | A no-op mock provider — no OpenTelemetry pipeline needed. |
24
+ | Tool providers | Fakes registered through the real `registerToolProvider`, keyed by plugin then tool name. |
25
+ | Routes | The real `addRoute`/`addMiddleware` are wrapped to record what a plugin registers. |
26
+
27
+ Because the context is real, `executeTool` still resolves the user scope via `asUser(req)` and still composes the abort signal from your timeout — so those paths are genuinely under test.
28
+
29
+ ### Registering fake tool responses[​](#registering-fake-tool-responses "Direct link to Registering fake tool responses")
30
+
31
+ Pass canned responses keyed by plugin name, then tool name. A response is either a static value or a function of the call arguments and the composed abort signal:
32
+
33
+ ```ts
34
+ import { createTestPluginContext } from "@databricks/appkit/testing";
35
+
36
+ const mock = createTestPluginContext({
37
+ analytics: {
38
+ // static response
39
+ top_users: [{ user: "alice", events: 42 }],
40
+ // function response — assert on args, or simulate slow/aborting work
41
+ query: (args, signal) => runFakeQuery(args, signal),
42
+ },
43
+ });
44
+
45
+ ```
46
+
47
+ ### Attaching to a plugin[​](#attaching-to-a-plugin "Direct link to Attaching to a plugin")
48
+
49
+ `attach()` wires the context to a plugin the production way: it seeds an in-memory cache (if AppKit hasn't already initialized one), then calls the plugin's `attachContext`, which rebuilds telemetry and flips `isReady` to `true`. Await it before exercising any handler that reads `this.context`, `this.cache`, or gates on `isReady`:
50
+
51
+ ```ts
52
+ const plugin = new MyAgentPlugin({ dir: false });
53
+ await mock.attach(plugin);
54
+
55
+ ```
56
+
57
+ Instantiate the plugin **class** directly (`new MyAgentPlugin(...)`). The `analytics()` / `agents()` factories you pass to `createApp` return a descriptor for the app to construct — for a unit test you want the instance.
58
+
59
+ The cache `attach()` seeds is a process-wide singleton: `CacheManager` is initialized once per test process and reused. Vitest isolates test *files* in separate workers, so caches never leak across files, but tests **within one file** share it. If a test populates the cache and a later test in the same file must not see it, clear it between tests with `resetTestCache()`:
60
+
61
+ ```ts
62
+ import { resetTestCache } from "@databricks/appkit/testing";
63
+
64
+ beforeEach(async () => {
65
+ await resetTestCache(); // no-op if the cache isn't initialized yet
66
+ });
67
+
68
+ ```
69
+
70
+ It also helps *within* a single test — clear the cache to force a miss, then assert the following call is a hit.
71
+
72
+ ### Inspecting what happened[​](#inspecting-what-happened "Direct link to Inspecting what happened")
73
+
74
+ The returned object exposes live views you read after the action under test runs:
75
+
76
+ ```ts
77
+ await someHandler(req, res);
78
+
79
+ // Every cross-plugin tool dispatch, in order.
80
+ expect(mock.toolCalls[0]).toMatchObject({
81
+ plugin: "analytics",
82
+ tool: "query",
83
+ asUser: true, // proves the on-behalf-of path ran
84
+ });
85
+
86
+ // Every route the plugin registered (raw handlers, before wrapping).
87
+ expect(mock.routes).toContainEqual(
88
+ expect.objectContaining({ method: "post", path: "/invocations" }),
89
+ );
90
+
91
+ // The injected telemetry provider records the context's own spans — i.e. the
92
+ // span PluginContext.executeTool opens around each cross-plugin tool call.
93
+ expect(mock.telemetry.getTracer().startActiveSpan).toHaveBeenCalled();
94
+
95
+ ```
96
+
97
+ `mock.telemetry` is injected into the `PluginContext`, so it captures the spans the *context* opens (notably `executeTool`). It is **not** the plugin's own telemetry: `attachContext` rebuilds `this.telemetry` from the real `TelemetryManager`, so spans a plugin opens internally do not land on `mock.telemetry`.
98
+
99
+ `RecordedToolCall.asUser` is the high-value signal for cross-plugin calls: because the fake `asUser` enforces the same token precondition as the real `Plugin.asUser`, a dispatch that records `asUser: true` (with `userId` set) genuinely resolved the caller's user scope, and a request missing `x-forwarded-access-token` **rejects** instead — the OBO distinction that silent `{ executeTool }` stubs cannot verify. Assert both directions: a well-formed request records the expected `userId`, and a token-less one throws.
100
+
101
+ The fake replicates `asUser`'s **token precondition**, not its internal dev-mode telemetry marker: in `NODE_ENV=development` the real `Plugin.asUser` skips impersonation and sets an OTel `isDevOboFallback()` flag, which the fake does not reproduce. Assert OBO through the recorded `asUser`/`userId` fields rather than `isDevOboFallback()`.
102
+
103
+ ## `expectStream(...)`[​](#expectstream "Direct link to expectstream")
104
+
105
+ AppKit plugins stream Server-Sent Events. `expectStream` consumes a stream and asserts the ordered event types it emits. It accepts an async iterable (an agent adapter's `run()`), a plain array of events, an SSE `Response` (or a promise of one) whose body it parses, or a `createMockResponse()` whose captured writes it replays.
106
+
107
+ ```ts
108
+ import { expectStream } from "@databricks/appkit/testing";
109
+
110
+ // In-order subsequence match — interleaved events (heartbeats, deltas) are ignored.
111
+ await expectStream(agent.adapter.run(input)).toEmit("tool_call", "message_delta");
112
+
113
+ // Exact match — the stream's full shape, in order, with nothing else.
114
+ await expectStream(events).toEmitExactly("warehouse_status", "result");
115
+
116
+ // Or collect without asserting.
117
+ const types = await expectStream(res).collectTypes();
118
+
119
+ ```
120
+
121
+ ### Asserting a plugin's streaming route[​](#asserting-a-plugins-streaming-route "Direct link to Asserting a plugin's streaming route")
122
+
123
+ Most plugins stream SSE from a **route handler** (`res.write(...)`), not a bare generator. `createMockResponse()` captures those writes, and `expectStream` reads them straight back — drive the real handler, then assert:
124
+
125
+ ```ts
126
+ import { createMockRequest, createMockResponse, expectStream } from "@databricks/appkit/testing";
127
+
128
+ const res = createMockResponse();
129
+ await plugin._handleStream(createMockRequest({ obo: true }), res);
130
+
131
+ // The mock captured the SSE the handler wrote; expectStream parses it.
132
+ await expectStream(res).toEmit("status", "result");
133
+
134
+ ```
135
+
136
+ `expectStream(res)` and `expectStream(res.sseResponse())` are equivalent — the latter hands you the raw `Response` if you want it. Do **not** pass the SSE body as a string: a string is an iterable of characters, so `expectStream` rejects it with a pointer to `sseResponse()` rather than emitting one "event" per character.
137
+
138
+ `toEmit` checks that the expected types appear **in order** but tolerates other events before, between, or after them — which is what you want for streams that interleave bookkeeping events like heartbeats or metadata. Use `toEmitExactly` when the stream's shape is fully determined.
139
+
140
+ `expectStream` buffers the whole source before asserting, so a stream that never terminates would otherwise hang until the test runner's own timeout. Pass `{ timeout }` to fail fast with a clear error instead:
141
+
142
+ ```ts
143
+ await expectStream(handler.stream(req), { timeout: 1000 }).toEmit("result");
144
+
145
+ ```
146
+
147
+ ## Fixtures[​](#fixtures "Direct link to Fixtures")
148
+
149
+ The kit re-exports the request/response/context fixtures AppKit uses internally:
150
+
151
+ * `createMockRequest(overrides?)` / `createMockResponse()` — Express request/response doubles, including the streaming flags (`headersSent`, `writableEnded`). Pass `obo: true` (or `obo: { userId, token, email }`) to set the forwarded identity headers `asUser` requires, instead of hand-adding them. `createMockResponse()` also captures everything a handler writes; pass it to `expectStream` (or call `sseResponse()`) to assert a streaming route's SSE. (Plugins resolve the workspace client through `getWorkspaceClient()`, not the request — use `mockServiceContext` to control it.)
152
+ * `mockServiceContext(options?)` — spy the `ServiceContext` singleton so code that resolves the service principal or a user context gets test doubles. Call in `beforeEach`, and call the returned `restore()` in `afterEach`.
153
+ * `useServiceContextMock(options?)` — the same, in one line: it registers the `beforeEach` install and `afterEach` restore for you. Call it at the top of a `describe` block (not inside a test), and read the live `.current` handle from within a test:
154
+ <!-- -->
155
+ ```ts
156
+ describe("my plugin", () => {
157
+ const ctx = useServiceContextMock();
158
+ test("...", async () => {
159
+ await handler(createMockRequest({ obo: true }), res);
160
+ expect(ctx.current.createUserContextSpy).toHaveBeenCalled();
161
+ });
162
+ });
163
+
164
+ ```
165
+ * `createSuccessfulSQLResponse(rows, columns)` / `createFailedSQLResponse(message)` — build SQL Warehouse statement responses.
166
+ * `setupDatabricksEnv(overrides?)` — set `DATABRICKS_HOST` / `DATABRICKS_WAREHOUSE_ID` to test values.
167
+ * `resetTestCache()` — clear the shared cache singleton between (or within) tests; no-ops if the cache isn't initialized yet.
168
+
169
+ ## Full example[​](#full-example "Direct link to Full example")
170
+
171
+ Instantiate the plugin **class** directly with `new`. The `analytics()` / `agents()` factory functions you pass to `createApp` return a descriptor for the app to construct — for a unit test you want the instance itself.
172
+
173
+ ```ts
174
+ import { Plugin, type PluginManifest } from "@databricks/appkit";
175
+ import { expectStream, createMockRequest, createTestPluginContext } from "@databricks/appkit/testing";
176
+ import { describe, expect, test } from "vitest";
177
+
178
+ // A small plugin that registers a route and streams two events.
179
+ class GreeterPlugin extends Plugin {
180
+ static manifest = {
181
+ name: "greeter",
182
+ displayName: "Greeter",
183
+ description: "Example plugin",
184
+ resources: { required: [], optional: [] },
185
+ } as PluginManifest<"greeter">;
186
+
187
+ async setup() {
188
+ this.context?.addRoute("get", "/hello", (_req, res) => res.end());
189
+ }
190
+
191
+ async *greet(name: string) {
192
+ yield { type: "greeting_start", name };
193
+ yield { type: "greeting_end", message: `Hello, ${name}!` };
194
+ }
195
+ }
196
+
197
+ describe("greeter plugin", () => {
198
+ test("registers its route through the context", async () => {
199
+ const mock = createTestPluginContext();
200
+ const plugin = new GreeterPlugin({});
201
+
202
+ await mock.attach(plugin);
203
+ await plugin.setup();
204
+
205
+ expect(mock.routes).toContainEqual(
206
+ expect.objectContaining({ method: "get", path: "/hello" }),
207
+ );
208
+ });
209
+
210
+ test("streams events in order", async () => {
211
+ const plugin = new GreeterPlugin({});
212
+ await expectStream(plugin.greet("world")).toEmit(
213
+ "greeting_start",
214
+ "greeting_end",
215
+ );
216
+ });
217
+ });
218
+
219
+ ```
220
+
221
+ To test a plugin that dispatches cross-plugin tool calls, register fake providers and assert on `mock.toolCalls` — including `asUser`, which confirms the on-behalf-of path ran:
222
+
223
+ ```ts
224
+ const mock = createTestPluginContext({ analytics: { query: [{ n: 1 }] } });
225
+ const plugin = new MyAgentPlugin({ dir: false });
226
+ await mock.attach(plugin);
227
+
228
+ // `obo` sets the forwarded identity headers `asUser` needs — without them the
229
+ // dispatch would (correctly) reject with "Missing user token".
230
+ const req = createMockRequest({ obo: true });
231
+ await plugin.runSomethingThatCallsAnalytics(req);
232
+
233
+ expect(mock.toolCalls[0]).toMatchObject({
234
+ plugin: "analytics",
235
+ tool: "query",
236
+ asUser: true,
237
+ });
238
+
239
+ ```
240
+
241
+ ## See also[​](#see-also "Direct link to See also")
242
+
243
+ * [Custom plugins](./docs/plugins/custom-plugins.md) — build the plugins you test with this kit.
244
+ * [Execution context](./docs/plugins/execution-context.md) — how `asUser` and the service principal differ at runtime.
245
+ * [Local development](./docs/development/local-development.md) — run your app with hot reload while iterating.
package/llms.txt CHANGED
@@ -58,6 +58,7 @@ npx @databricks/appkit docs <query>
58
58
  - [Plugin management](./docs/plugins/plugin-management.md): AppKit includes a CLI for managing plugins. All commands are available under npx @databricks/appkit plugin.
59
59
  - [Server plugin](./docs/plugins/server.md): Provides HTTP server capabilities with development and production modes.
60
60
  - [Plugin Stability Tiers](./docs/plugins/stability.md): AppKit plugins have a two-tier stability system that communicates API maturity and breaking-change expectations.
61
+ - [Testing](./docs/plugins/testing.md): AppKit ships a testing kit at @databricks/appkit/testing so you can test a plugin — including its cross-plugin tool calls and streaming responses — without a live Databricks workspace, credentials, or network access. That makes plugin tests fast and lets them run in CI, where no workspace is available.
61
62
 
62
63
  ## appkit API reference [collapsed]
63
64
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@databricks/appkit",
3
3
  "type": "module",
4
- "version": "0.61.1",
4
+ "version": "0.63.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "bin": {
@@ -33,6 +33,10 @@
33
33
  "./beta": "./dist/beta.js",
34
34
  "./dist/shared/src/plugin": "./dist/shared/src/plugin.d.ts",
35
35
  "./type-generator": "./dist/type-generator/index.js",
36
+ "./testing": {
37
+ "types": "./dist/testing/index.d.ts",
38
+ "default": "./dist/testing/index.js"
39
+ },
36
40
  "./package.json": "./package.json"
37
41
  },
38
42
  "scripts": {
@@ -86,6 +90,14 @@
86
90
  "commander": "12.1.0",
87
91
  "yaml": "2.8.2"
88
92
  },
93
+ "peerDependencies": {
94
+ "vitest": ">=3"
95
+ },
96
+ "peerDependenciesMeta": {
97
+ "vitest": {
98
+ "optional": true
99
+ }
100
+ },
89
101
  "devDependencies": {
90
102
  "@opentelemetry/context-async-hooks": "2.8.0",
91
103
  "@types/express": "4.17.25",
@@ -93,7 +105,8 @@
93
105
  "@types/json-schema": "7.0.15",
94
106
  "@types/pg": "8.16.0",
95
107
  "@types/ws": "8.18.1",
96
- "@vitejs/plugin-react": "5.1.1"
108
+ "@vitejs/plugin-react": "5.1.1",
109
+ "vitest": "3.2.4"
97
110
  },
98
111
  "overrides": {
99
112
  "vite": "npm:rolldown-vite@7.1.14"