@databricks/appkit 0.5.4 → 0.7.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 (228) hide show
  1. package/CLAUDE.md +23 -1
  2. package/NOTICE.md +3 -1
  3. package/bin/appkit.js +0 -0
  4. package/dist/_virtual/_rolldown/runtime.js +1 -33
  5. package/dist/appkit/package.js +1 -1
  6. package/dist/cache/index.d.ts.map +1 -1
  7. package/dist/cache/index.js +7 -7
  8. package/dist/cache/index.js.map +1 -1
  9. package/dist/cache/storage/persistent.js +23 -21
  10. package/dist/cache/storage/persistent.js.map +1 -1
  11. package/dist/cli/commands/plugins-sync.js +369 -0
  12. package/dist/cli/commands/plugins-sync.js.map +1 -0
  13. package/dist/cli/commands/plugins.js +19 -0
  14. package/dist/cli/commands/plugins.js.map +1 -0
  15. package/dist/cli/index.js +2 -0
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/connectors/index.js +2 -2
  18. package/dist/connectors/lakebase/index.d.ts +16 -0
  19. package/dist/connectors/lakebase/index.d.ts.map +1 -0
  20. package/dist/connectors/lakebase/index.js +21 -2
  21. package/dist/connectors/lakebase/index.js.map +1 -0
  22. package/dist/connectors/lakebase-v1/client.js +13 -0
  23. package/dist/connectors/lakebase-v1/client.js.map +1 -0
  24. package/dist/connectors/lakebase-v1/index.js +3 -0
  25. package/dist/connectors/sql-warehouse/client.js +2 -2
  26. package/dist/context/execution-context.js +1 -7
  27. package/dist/context/execution-context.js.map +1 -1
  28. package/dist/context/index.js +4 -16
  29. package/dist/context/index.js.map +1 -1
  30. package/dist/core/appkit.d.ts.map +1 -1
  31. package/dist/core/appkit.js +6 -2
  32. package/dist/core/appkit.js.map +1 -1
  33. package/dist/index.d.ts +5 -1
  34. package/dist/index.js +8 -3
  35. package/dist/index.js.map +1 -1
  36. package/dist/logging/logger.js +1 -1
  37. package/dist/plugin/plugin.d.ts +95 -3
  38. package/dist/plugin/plugin.d.ts.map +1 -1
  39. package/dist/plugin/plugin.js +98 -10
  40. package/dist/plugin/plugin.js.map +1 -1
  41. package/dist/plugins/analytics/analytics.d.ts +3 -1
  42. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  43. package/dist/plugins/analytics/analytics.js +5 -3
  44. package/dist/plugins/analytics/analytics.js.map +1 -1
  45. package/dist/plugins/analytics/index.js +1 -0
  46. package/dist/plugins/analytics/manifest.js +21 -0
  47. package/dist/plugins/analytics/manifest.js.map +1 -0
  48. package/dist/plugins/analytics/manifest.json +36 -0
  49. package/dist/plugins/index.js +2 -0
  50. package/dist/plugins/server/index.d.ts +3 -1
  51. package/dist/plugins/server/index.d.ts.map +1 -1
  52. package/dist/plugins/server/index.js +5 -3
  53. package/dist/plugins/server/index.js.map +1 -1
  54. package/dist/plugins/server/manifest.js +21 -0
  55. package/dist/plugins/server/manifest.js.map +1 -0
  56. package/dist/plugins/server/manifest.json +36 -0
  57. package/dist/registry/index.js +5 -0
  58. package/dist/registry/manifest-loader.d.ts +44 -0
  59. package/dist/registry/manifest-loader.d.ts.map +1 -0
  60. package/dist/registry/manifest-loader.js +97 -0
  61. package/dist/registry/manifest-loader.js.map +1 -0
  62. package/dist/registry/resource-registry.d.ts +133 -0
  63. package/dist/registry/resource-registry.d.ts.map +1 -0
  64. package/dist/registry/resource-registry.js +297 -0
  65. package/dist/registry/resource-registry.js.map +1 -0
  66. package/dist/registry/types.d.ts +181 -0
  67. package/dist/registry/types.d.ts.map +1 -0
  68. package/dist/registry/types.js +89 -0
  69. package/dist/registry/types.js.map +1 -0
  70. package/dist/shared/src/plugin.d.ts +66 -1
  71. package/dist/shared/src/plugin.d.ts.map +1 -1
  72. package/docs/docs/api/appkit/Class.AppKitError/index.html +5 -5
  73. package/docs/docs/api/appkit/Class.AuthenticationError/index.html +4 -4
  74. package/docs/docs/api/appkit/Class.ConfigurationError/index.html +4 -4
  75. package/docs/docs/api/appkit/Class.ConnectionError/index.html +4 -4
  76. package/docs/docs/api/appkit/Class.ExecutionError/index.html +4 -4
  77. package/docs/docs/api/appkit/Class.InitializationError/index.html +4 -4
  78. package/docs/docs/api/appkit/Class.Plugin/index.html +28 -16
  79. package/docs/docs/api/appkit/Class.Plugin.md +90 -30
  80. package/docs/docs/api/appkit/Class.ResourceRegistry/index.html +150 -0
  81. package/docs/docs/api/appkit/Class.ResourceRegistry.md +301 -0
  82. package/docs/docs/api/appkit/Class.ServerError/index.html +5 -5
  83. package/docs/docs/api/appkit/Class.TunnelError/index.html +4 -4
  84. package/docs/docs/api/appkit/Class.ValidationError/index.html +4 -4
  85. package/docs/docs/api/appkit/Enumeration.RequestedClaimsPermissionSet/index.html +21 -0
  86. package/docs/docs/api/appkit/Enumeration.RequestedClaimsPermissionSet.md +14 -0
  87. package/docs/docs/api/appkit/Enumeration.ResourceType/index.html +66 -0
  88. package/docs/docs/api/appkit/Enumeration.ResourceType.md +135 -0
  89. package/docs/docs/api/appkit/Function.appKitTypesPlugin/index.html +4 -4
  90. package/docs/docs/api/appkit/Function.createApp/index.html +5 -5
  91. package/docs/docs/api/appkit/Function.createLakebasePool/index.html +24 -0
  92. package/docs/docs/api/appkit/Function.createLakebasePool.md +20 -0
  93. package/docs/docs/api/appkit/Function.generateDatabaseCredential/index.html +30 -0
  94. package/docs/docs/api/appkit/Function.generateDatabaseCredential.md +57 -0
  95. package/docs/docs/api/appkit/Function.getExecutionContext/index.html +5 -5
  96. package/docs/docs/api/appkit/Function.getLakebaseOrmConfig/index.html +39 -0
  97. package/docs/docs/api/appkit/Function.getLakebaseOrmConfig.md +90 -0
  98. package/docs/docs/api/appkit/Function.getLakebasePgConfig/index.html +27 -0
  99. package/docs/docs/api/appkit/Function.getLakebasePgConfig.md +31 -0
  100. package/docs/docs/api/appkit/Function.getPluginManifest/index.html +26 -0
  101. package/docs/docs/api/appkit/Function.getPluginManifest.md +24 -0
  102. package/docs/docs/api/appkit/Function.getResourceRequirements/index.html +28 -0
  103. package/docs/docs/api/appkit/Function.getResourceRequirements.md +42 -0
  104. package/docs/docs/api/appkit/Function.getWorkspaceClient/index.html +22 -0
  105. package/docs/docs/api/appkit/Function.getWorkspaceClient.md +18 -0
  106. package/docs/docs/api/appkit/Function.isSQLTypeMarker/index.html +5 -5
  107. package/docs/docs/api/appkit/Interface.BasePluginConfig/index.html +4 -4
  108. package/docs/docs/api/appkit/Interface.CacheConfig/index.html +5 -5
  109. package/docs/docs/api/appkit/Interface.DatabaseCredential/index.html +28 -0
  110. package/docs/docs/api/appkit/Interface.DatabaseCredential.md +32 -0
  111. package/docs/docs/api/appkit/Interface.GenerateDatabaseCredentialRequest/index.html +38 -0
  112. package/docs/docs/api/appkit/Interface.GenerateDatabaseCredentialRequest.md +54 -0
  113. package/docs/docs/api/appkit/Interface.ITelemetry/index.html +5 -5
  114. package/docs/docs/api/appkit/Interface.LakebasePoolConfig/index.html +79 -0
  115. package/docs/docs/api/appkit/Interface.LakebasePoolConfig.md +126 -0
  116. package/docs/docs/api/appkit/Interface.PluginManifest/index.html +63 -0
  117. package/docs/docs/api/appkit/Interface.PluginManifest.md +135 -0
  118. package/docs/docs/api/appkit/Interface.RequestedClaims/index.html +26 -0
  119. package/docs/docs/api/appkit/Interface.RequestedClaims.md +25 -0
  120. package/docs/docs/api/appkit/Interface.RequestedResource/index.html +27 -0
  121. package/docs/docs/api/appkit/Interface.RequestedResource.md +32 -0
  122. package/docs/docs/api/appkit/Interface.ResourceEntry/index.html +83 -0
  123. package/docs/docs/api/appkit/Interface.ResourceEntry.md +156 -0
  124. package/docs/docs/api/appkit/Interface.ResourceFieldEntry/index.html +26 -0
  125. package/docs/docs/api/appkit/Interface.ResourceFieldEntry.md +25 -0
  126. package/docs/docs/api/appkit/Interface.ResourceRequirement/index.html +51 -0
  127. package/docs/docs/api/appkit/Interface.ResourceRequirement.md +84 -0
  128. package/docs/docs/api/appkit/Interface.StreamExecutionSettings/index.html +5 -5
  129. package/docs/docs/api/appkit/Interface.TelemetryConfig/index.html +5 -5
  130. package/docs/docs/api/appkit/Interface.ValidationResult/index.html +29 -0
  131. package/docs/docs/api/appkit/Interface.ValidationResult.md +36 -0
  132. package/docs/docs/api/appkit/TypeAlias.ConfigSchema/index.html +21 -0
  133. package/docs/docs/api/appkit/TypeAlias.ConfigSchema.md +12 -0
  134. package/docs/docs/api/appkit/TypeAlias.IAppRouter/index.html +5 -5
  135. package/docs/docs/api/appkit/TypeAlias.ResourcePermission/index.html +18 -0
  136. package/docs/docs/api/appkit/TypeAlias.ResourcePermission.md +20 -0
  137. package/docs/docs/api/appkit/Variable.sql/index.html +5 -5
  138. package/docs/docs/api/appkit/index.html +10 -8
  139. package/docs/docs/api/appkit-ui/data/AreaChart/index.html +3 -3
  140. package/docs/docs/api/appkit-ui/data/BarChart/index.html +3 -3
  141. package/docs/docs/api/appkit-ui/data/DataTable/index.html +3 -3
  142. package/docs/docs/api/appkit-ui/data/DonutChart/index.html +3 -3
  143. package/docs/docs/api/appkit-ui/data/HeatmapChart/index.html +3 -3
  144. package/docs/docs/api/appkit-ui/data/LineChart/index.html +3 -3
  145. package/docs/docs/api/appkit-ui/data/PieChart/index.html +3 -3
  146. package/docs/docs/api/appkit-ui/data/RadarChart/index.html +3 -3
  147. package/docs/docs/api/appkit-ui/data/ScatterChart/index.html +3 -3
  148. package/docs/docs/api/appkit-ui/index.html +3 -3
  149. package/docs/docs/api/appkit-ui/styling/index.html +3 -3
  150. package/docs/docs/api/appkit-ui/ui/Accordion/index.html +3 -3
  151. package/docs/docs/api/appkit-ui/ui/Alert/index.html +3 -3
  152. package/docs/docs/api/appkit-ui/ui/AlertDialog/index.html +3 -3
  153. package/docs/docs/api/appkit-ui/ui/AspectRatio/index.html +3 -3
  154. package/docs/docs/api/appkit-ui/ui/Avatar/index.html +3 -3
  155. package/docs/docs/api/appkit-ui/ui/Badge/index.html +3 -3
  156. package/docs/docs/api/appkit-ui/ui/Breadcrumb/index.html +3 -3
  157. package/docs/docs/api/appkit-ui/ui/Button/index.html +3 -3
  158. package/docs/docs/api/appkit-ui/ui/ButtonGroup/index.html +3 -3
  159. package/docs/docs/api/appkit-ui/ui/Calendar/index.html +3 -3
  160. package/docs/docs/api/appkit-ui/ui/Card/index.html +3 -3
  161. package/docs/docs/api/appkit-ui/ui/Carousel/index.html +3 -3
  162. package/docs/docs/api/appkit-ui/ui/ChartContainer/index.html +3 -3
  163. package/docs/docs/api/appkit-ui/ui/Checkbox/index.html +3 -3
  164. package/docs/docs/api/appkit-ui/ui/Collapsible/index.html +3 -3
  165. package/docs/docs/api/appkit-ui/ui/Command/index.html +3 -3
  166. package/docs/docs/api/appkit-ui/ui/ContextMenu/index.html +3 -3
  167. package/docs/docs/api/appkit-ui/ui/Dialog/index.html +3 -3
  168. package/docs/docs/api/appkit-ui/ui/Drawer/index.html +3 -3
  169. package/docs/docs/api/appkit-ui/ui/DropdownMenu/index.html +3 -3
  170. package/docs/docs/api/appkit-ui/ui/Empty/index.html +3 -3
  171. package/docs/docs/api/appkit-ui/ui/Field/index.html +3 -3
  172. package/docs/docs/api/appkit-ui/ui/FormControl/index.html +3 -3
  173. package/docs/docs/api/appkit-ui/ui/HoverCard/index.html +3 -3
  174. package/docs/docs/api/appkit-ui/ui/Input/index.html +3 -3
  175. package/docs/docs/api/appkit-ui/ui/InputGroup/index.html +3 -3
  176. package/docs/docs/api/appkit-ui/ui/InputOTP/index.html +3 -3
  177. package/docs/docs/api/appkit-ui/ui/Item/index.html +3 -3
  178. package/docs/docs/api/appkit-ui/ui/Kbd/index.html +3 -3
  179. package/docs/docs/api/appkit-ui/ui/Label/index.html +3 -3
  180. package/docs/docs/api/appkit-ui/ui/Menubar/index.html +3 -3
  181. package/docs/docs/api/appkit-ui/ui/NavigationMenu/index.html +3 -3
  182. package/docs/docs/api/appkit-ui/ui/Pagination/index.html +3 -3
  183. package/docs/docs/api/appkit-ui/ui/Popover/index.html +3 -3
  184. package/docs/docs/api/appkit-ui/ui/Progress/index.html +3 -3
  185. package/docs/docs/api/appkit-ui/ui/RadioGroup/index.html +3 -3
  186. package/docs/docs/api/appkit-ui/ui/ResizableHandle/index.html +3 -3
  187. package/docs/docs/api/appkit-ui/ui/ScrollArea/index.html +3 -3
  188. package/docs/docs/api/appkit-ui/ui/Select/index.html +3 -3
  189. package/docs/docs/api/appkit-ui/ui/Separator/index.html +3 -3
  190. package/docs/docs/api/appkit-ui/ui/Sheet/index.html +3 -3
  191. package/docs/docs/api/appkit-ui/ui/Sidebar/index.html +3 -3
  192. package/docs/docs/api/appkit-ui/ui/Skeleton/index.html +3 -3
  193. package/docs/docs/api/appkit-ui/ui/Slider/index.html +3 -3
  194. package/docs/docs/api/appkit-ui/ui/Spinner/index.html +3 -3
  195. package/docs/docs/api/appkit-ui/ui/Switch/index.html +3 -3
  196. package/docs/docs/api/appkit-ui/ui/Table/index.html +3 -3
  197. package/docs/docs/api/appkit-ui/ui/Tabs/index.html +3 -3
  198. package/docs/docs/api/appkit-ui/ui/Textarea/index.html +3 -3
  199. package/docs/docs/api/appkit-ui/ui/Toaster/index.html +3 -3
  200. package/docs/docs/api/appkit-ui/ui/Toggle/index.html +3 -3
  201. package/docs/docs/api/appkit-ui/ui/ToggleGroup/index.html +3 -3
  202. package/docs/docs/api/appkit-ui/ui/Tooltip/index.html +3 -3
  203. package/docs/docs/api/appkit.md +55 -28
  204. package/docs/docs/api/index.html +3 -3
  205. package/docs/docs/app-management/index.html +3 -3
  206. package/docs/docs/architecture/index.html +4 -4
  207. package/docs/docs/architecture.md +1 -1
  208. package/docs/docs/category/development/index.html +3 -3
  209. package/docs/docs/configuration/index.html +3 -3
  210. package/docs/docs/core-principles/index.html +3 -3
  211. package/docs/docs/development/ai-assisted-development/index.html +3 -3
  212. package/docs/docs/development/index.html +3 -3
  213. package/docs/docs/development/llm-guide/index.html +3 -3
  214. package/docs/docs/development/local-development/index.html +3 -3
  215. package/docs/docs/development/project-setup/index.html +3 -3
  216. package/docs/docs/development/remote-bridge/index.html +3 -3
  217. package/docs/docs/development/type-generation/index.html +3 -3
  218. package/docs/docs/index.html +3 -3
  219. package/docs/docs/plugins/index.html +18 -8
  220. package/docs/docs/plugins.md +82 -4
  221. package/llms.txt +23 -1
  222. package/package.json +8 -4
  223. package/dist/connectors/lakebase/client.js +0 -362
  224. package/dist/connectors/lakebase/client.js.map +0 -1
  225. package/dist/connectors/lakebase/defaults.js +0 -13
  226. package/dist/connectors/lakebase/defaults.js.map +0 -1
  227. package/dist/utils/env-validator.js +0 -14
  228. package/dist/utils/env-validator.js.map +0 -1
@@ -56,12 +56,6 @@ function getWarehouseId() {
56
56
  function getWorkspaceId() {
57
57
  return getExecutionContext().workspaceId;
58
58
  }
59
- /**
60
- * Check if currently running in a user context.
61
- */
62
- function isInUserContext() {
63
- return executionContextStorage.getStore() !== void 0;
64
- }
65
59
  var executionContextStorage;
66
60
  var init_execution_context = __esmMin((() => {
67
61
  init_service_context();
@@ -71,5 +65,5 @@ var init_execution_context = __esmMin((() => {
71
65
 
72
66
  //#endregion
73
67
  init_execution_context();
74
- export { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, isInUserContext, runInUserContext };
68
+ export { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, runInUserContext };
75
69
  //# 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 { 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 return getExecutionContext().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"],"mappings":";;;;;;;;;;;;;;AAsBA,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;AAChD,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,iBAAkC;AAChD,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,kBAA2B;AAEzC,QADY,wBAAwB,UAAU,KAC/B;;;;uBAhFkC;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 { 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 return getExecutionContext().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"],"mappings":";;;;;;;;;;;;;;AAsBA,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;AAChD,QAAO,qBAAqB,CAAC;;;;;AAM/B,SAAgB,iBAAkC;AAChD,QAAO,qBAAqB,CAAC;;;;uBAxEoB;oBAK3B;CAMlB,0BAA0B,IAAI,mBAAgC"}
@@ -1,27 +1,15 @@
1
- import { __esmMin, __exportAll } from "../_virtual/_rolldown/runtime.js";
1
+ import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
2
  import { ServiceContext, init_service_context } from "./service-context.js";
3
- import { init_user_context, isUserContext } from "./user-context.js";
4
- import { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, isInUserContext, runInUserContext } from "./execution-context.js";
3
+ import { isUserContext } from "./user-context.js";
4
+ import { getCurrentUserId, getExecutionContext, getWarehouseId, getWorkspaceClient, getWorkspaceId, init_execution_context, runInUserContext } from "./execution-context.js";
5
5
 
6
6
  //#region src/context/index.ts
7
- var context_exports = /* @__PURE__ */ __exportAll({
8
- ServiceContext: () => ServiceContext,
9
- getCurrentUserId: () => getCurrentUserId,
10
- getExecutionContext: () => getExecutionContext,
11
- getWarehouseId: () => getWarehouseId,
12
- getWorkspaceClient: () => getWorkspaceClient,
13
- getWorkspaceId: () => getWorkspaceId,
14
- isInUserContext: () => isInUserContext,
15
- isUserContext: () => isUserContext,
16
- runInUserContext: () => runInUserContext
17
- });
18
7
  var init_context = __esmMin((() => {
19
8
  init_execution_context();
20
9
  init_service_context();
21
- init_user_context();
22
10
  }));
23
11
 
24
12
  //#endregion
25
13
  init_context();
26
- export { context_exports, init_context };
14
+ export { init_context };
27
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/context/index.ts"],"sourcesContent":["export {\n getCurrentUserId,\n getExecutionContext,\n getWarehouseId,\n getWorkspaceClient,\n getWorkspaceId,\n isInUserContext,\n runInUserContext,\n} from \"./execution-context\";\nexport { ServiceContext, type ServiceContextState } from \"./service-context\";\nexport {\n type ExecutionContext,\n isUserContext,\n type UserContext,\n} from \"./user-context\";\n"],"mappings":";;;;;;;;;;;;;;;;;;yBAQ6B;uBACgD;oBAKrD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/context/index.ts"],"sourcesContent":["export {\n getCurrentUserId,\n getExecutionContext,\n getWarehouseId,\n getWorkspaceClient,\n getWorkspaceId,\n isInUserContext,\n runInUserContext,\n} from \"./execution-context\";\nexport { ServiceContext, type ServiceContextState } from \"./service-context\";\nexport {\n type ExecutionContext,\n isUserContext,\n type UserContext,\n} from \"./user-context\";\n"],"mappings":";;;;;;;yBAQ6B;uBACgD"}
@@ -1 +1 @@
1
- {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;iBAyLsB,oBACV,WAAW;YAGT;cACE;UACJ;WACC;IAEV,QAAQ,UAAU"}
1
+ {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;iBA6LsB,oBACV,WAAW;YAGT;cACE;UACJ;WACC;IAEV,QAAQ,UAAU"}
@@ -1,8 +1,10 @@
1
1
  import { TelemetryManager } from "../telemetry/telemetry-manager.js";
2
2
  import "../telemetry/index.js";
3
+ import { CacheManager } from "../cache/index.js";
3
4
  import { ServiceContext } from "../context/service-context.js";
4
5
  import { init_context } from "../context/index.js";
5
- import { CacheManager } from "../cache/index.js";
6
+ import { ResourceRegistry } from "../registry/resource-registry.js";
7
+ import "../registry/index.js";
6
8
 
7
9
  //#region src/core/appkit.ts
8
10
  init_context();
@@ -31,7 +33,6 @@ var AppKit = class AppKit {
31
33
  ...extraData
32
34
  });
33
35
  this.#pluginInstances[name] = pluginInstance;
34
- pluginInstance.validateEnv();
35
36
  this.#setupPromises.push(pluginInstance.setup());
36
37
  const self = this;
37
38
  Object.defineProperty(this, name, {
@@ -71,6 +72,9 @@ var AppKit = class AppKit {
71
72
  await CacheManager.getInstance(config?.cache);
72
73
  await ServiceContext.initialize(config?.client);
73
74
  const rawPlugins = config.plugins;
75
+ const registry = new ResourceRegistry();
76
+ registry.collectResources(rawPlugins);
77
+ registry.enforceValidation();
74
78
  const instance = new AppKit({ plugins: AppKit.preparePlugins(rawPlugins) });
75
79
  await Promise.all(instance.#setupPromises);
76
80
  return instance;
@@ -1 +1 @@
1
- {"version":3,"file":"appkit.js","names":["#pluginInstances","#setupPromises"],"sources":["../../src/core/appkit.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type {\n BasePlugin,\n CacheConfig,\n InputPluginMap,\n OptionalConfigPluginDef,\n PluginConstructor,\n PluginData,\n PluginMap,\n} from \"shared\";\nimport { CacheManager } from \"../cache\";\nimport { ServiceContext } from \"../context\";\nimport type { TelemetryConfig } from \"../telemetry\";\nimport { TelemetryManager } from \"../telemetry\";\n\nexport class AppKit<TPlugins extends InputPluginMap> {\n #pluginInstances: Record<string, BasePlugin> = {};\n #setupPromises: Promise<void>[] = [];\n\n private constructor(config: { plugins: TPlugins }) {\n const { plugins, ...globalConfig } = config;\n\n const pluginEntries = Object.entries(plugins);\n\n const corePlugins = pluginEntries.filter(([_, p]) => {\n return (p?.plugin?.phase ?? \"normal\") === \"core\";\n });\n const normalPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"normal\",\n );\n const deferredPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"deferred\",\n );\n\n for (const [name, pluginData] of corePlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData);\n }\n }\n\n for (const [name, pluginData] of normalPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData);\n }\n }\n\n for (const [name, pluginData] of deferredPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n plugins: this.#pluginInstances,\n });\n }\n }\n }\n\n private createAndRegisterPlugin<T extends PluginConstructor>(\n config: Omit<{ plugins: TPlugins }, \"plugins\">,\n name: string,\n pluginData: OptionalConfigPluginDef<T>,\n extraData?: Record<string, unknown>,\n ) {\n const { plugin: Plugin, config: pluginConfig } = pluginData;\n const baseConfig = {\n ...config,\n ...Plugin.DEFAULT_CONFIG,\n ...pluginConfig,\n name,\n ...extraData,\n };\n const pluginInstance = new Plugin(baseConfig);\n\n this.#pluginInstances[name] = pluginInstance;\n\n pluginInstance.validateEnv();\n\n this.#setupPromises.push(pluginInstance.setup());\n\n const self = this;\n\n Object.defineProperty(this, name, {\n get() {\n const plugin = self.#pluginInstances[name];\n return self.wrapWithAsUser(plugin);\n },\n enumerable: true,\n });\n }\n\n /**\n * Binds all function properties in an exports object to the given context.\n */\n private bindExportMethods(\n exports: Record<string, unknown>,\n context: BasePlugin,\n ) {\n for (const key in exports) {\n if (Object.hasOwn(exports, key) && typeof exports[key] === \"function\") {\n exports[key] = (exports[key] as (...args: unknown[]) => unknown).bind(\n context,\n );\n }\n }\n }\n\n /**\n * Wraps a plugin's exports with an `asUser` method that returns\n * a user-scoped version of the exports.\n */\n private wrapWithAsUser<T extends BasePlugin>(plugin: T) {\n // If plugin doesn't implement exports(), return empty object\n const pluginExports = (plugin.exports?.() ?? {}) as Record<string, unknown>;\n this.bindExportMethods(pluginExports, plugin);\n\n // If plugin doesn't support asUser (no asUser method), return exports as-is\n if (typeof (plugin as any).asUser !== \"function\") {\n return pluginExports;\n }\n\n return {\n ...pluginExports,\n /**\n * Execute operations using the user's identity from the request.\n * Returns user-scoped exports where all methods execute with the\n * user's Databricks credentials instead of the service principal.\n */\n asUser: (req: import(\"express\").Request) => {\n const userPlugin = (plugin as any).asUser(req);\n const userExports = (userPlugin.exports?.() ?? {}) as Record<\n string,\n unknown\n >;\n this.bindExportMethods(userExports, userPlugin);\n return userExports;\n },\n };\n }\n\n static async _createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n } = {},\n ): Promise<PluginMap<T>> {\n // Initialize core services\n TelemetryManager.initialize(config?.telemetry);\n await CacheManager.getInstance(config?.cache);\n\n // Initialize ServiceContext for Databricks client management\n // This provides the service principal client and shared resources\n await ServiceContext.initialize(config?.client);\n\n const rawPlugins = config.plugins as T;\n const preparedPlugins = AppKit.preparePlugins(rawPlugins);\n const mergedConfig = {\n plugins: preparedPlugins,\n };\n\n const instance = new AppKit(mergedConfig);\n\n await Promise.all(instance.#setupPromises);\n\n return instance as unknown as PluginMap<T>;\n }\n\n private static preparePlugins(\n plugins: PluginData<PluginConstructor, unknown, string>[],\n ) {\n const result: InputPluginMap = {};\n for (const currentPlugin of plugins) {\n result[currentPlugin.name] = {\n plugin: currentPlugin.plugin,\n config: currentPlugin.config as Record<string, unknown>,\n };\n }\n return result;\n }\n}\n\n/**\n * Bootstraps AppKit with the provided configuration.\n */\nexport async function createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n>(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n } = {},\n): Promise<PluginMap<T>> {\n return AppKit._createApp(config);\n}\n"],"mappings":";;;;;;;cAW4C;AAI5C,IAAa,SAAb,MAAa,OAAwC;CACnD,mBAA+C,EAAE;CACjD,iBAAkC,EAAE;CAEpC,AAAQ,YAAY,QAA+B;EACjD,MAAM,EAAE,SAAS,GAAG,iBAAiB;EAErC,MAAM,gBAAgB,OAAO,QAAQ,QAAQ;EAE7C,MAAM,cAAc,cAAc,QAAQ,CAAC,GAAG,OAAO;AACnD,WAAQ,GAAG,QAAQ,SAAS,cAAc;IAC1C;EACF,MAAM,gBAAgB,cAAc,QACjC,CAAC,GAAG,QAAQ,GAAG,QAAQ,SAAS,cAAc,SAChD;EACD,MAAM,kBAAkB,cAAc,QACnC,CAAC,GAAG,QAAQ,GAAG,QAAQ,SAAS,cAAc,WAChD;AAED,OAAK,MAAM,CAAC,MAAM,eAAe,YAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,WAAW;AAIhE,OAAK,MAAM,CAAC,MAAM,eAAe,cAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,WAAW;AAIhE,OAAK,MAAM,CAAC,MAAM,eAAe,gBAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,YAAY,EAC3D,SAAS,MAAKA,iBACf,CAAC;;CAKR,AAAQ,wBACN,QACA,MACA,YACA,WACA;EACA,MAAM,EAAE,QAAQ,QAAQ,QAAQ,iBAAiB;EAQjD,MAAM,iBAAiB,IAAI,OAPR;GACjB,GAAG;GACH,GAAG,OAAO;GACV,GAAG;GACH;GACA,GAAG;GACJ,CAC4C;AAE7C,QAAKA,gBAAiB,QAAQ;AAE9B,iBAAe,aAAa;AAE5B,QAAKC,cAAe,KAAK,eAAe,OAAO,CAAC;EAEhD,MAAM,OAAO;AAEb,SAAO,eAAe,MAAM,MAAM;GAChC,MAAM;IACJ,MAAM,SAAS,MAAKD,gBAAiB;AACrC,WAAO,KAAK,eAAe,OAAO;;GAEpC,YAAY;GACb,CAAC;;;;;CAMJ,AAAQ,kBACN,SACA,SACA;AACA,OAAK,MAAM,OAAO,QAChB,KAAI,OAAO,OAAO,SAAS,IAAI,IAAI,OAAO,QAAQ,SAAS,WACzD,SAAQ,OAAQ,QAAQ,KAAyC,KAC/D,QACD;;;;;;CASP,AAAQ,eAAqC,QAAW;EAEtD,MAAM,gBAAiB,OAAO,WAAW,IAAI,EAAE;AAC/C,OAAK,kBAAkB,eAAe,OAAO;AAG7C,MAAI,OAAQ,OAAe,WAAW,WACpC,QAAO;AAGT,SAAO;GACL,GAAG;GAMH,SAAS,QAAmC;IAC1C,MAAM,aAAc,OAAe,OAAO,IAAI;IAC9C,MAAM,cAAe,WAAW,WAAW,IAAI,EAAE;AAIjD,SAAK,kBAAkB,aAAa,WAAW;AAC/C,WAAO;;GAEV;;CAGH,aAAa,WAGX,SAKI,EAAE,EACiB;AAEvB,mBAAiB,WAAW,QAAQ,UAAU;AAC9C,QAAM,aAAa,YAAY,QAAQ,MAAM;AAI7C,QAAM,eAAe,WAAW,QAAQ,OAAO;EAE/C,MAAM,aAAa,OAAO;EAM1B,MAAM,WAAW,IAAI,OAJA,EACnB,SAFsB,OAAO,eAAe,WAAW,EAGxD,CAEwC;AAEzC,QAAM,QAAQ,IAAI,UAASC,cAAe;AAE1C,SAAO;;CAGT,OAAe,eACb,SACA;EACA,MAAM,SAAyB,EAAE;AACjC,OAAK,MAAM,iBAAiB,QAC1B,QAAO,cAAc,QAAQ;GAC3B,QAAQ,cAAc;GACtB,QAAQ,cAAc;GACvB;AAEH,SAAO;;;;;;AAOX,eAAsB,UAGpB,SAKI,EAAE,EACiB;AACvB,QAAO,OAAO,WAAW,OAAO"}
1
+ {"version":3,"file":"appkit.js","names":["#pluginInstances","#setupPromises"],"sources":["../../src/core/appkit.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type {\n BasePlugin,\n CacheConfig,\n InputPluginMap,\n OptionalConfigPluginDef,\n PluginConstructor,\n PluginData,\n PluginMap,\n} from \"shared\";\nimport { CacheManager } from \"../cache\";\nimport { ServiceContext } from \"../context\";\nimport { ResourceRegistry } from \"../registry\";\nimport type { TelemetryConfig } from \"../telemetry\";\nimport { TelemetryManager } from \"../telemetry\";\n\nexport class AppKit<TPlugins extends InputPluginMap> {\n #pluginInstances: Record<string, BasePlugin> = {};\n #setupPromises: Promise<void>[] = [];\n\n private constructor(config: { plugins: TPlugins }) {\n const { plugins, ...globalConfig } = config;\n\n const pluginEntries = Object.entries(plugins);\n\n const corePlugins = pluginEntries.filter(([_, p]) => {\n return (p?.plugin?.phase ?? \"normal\") === \"core\";\n });\n const normalPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"normal\",\n );\n const deferredPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"deferred\",\n );\n\n for (const [name, pluginData] of corePlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData);\n }\n }\n\n for (const [name, pluginData] of normalPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData);\n }\n }\n\n for (const [name, pluginData] of deferredPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n plugins: this.#pluginInstances,\n });\n }\n }\n }\n\n private createAndRegisterPlugin<T extends PluginConstructor>(\n config: Omit<{ plugins: TPlugins }, \"plugins\">,\n name: string,\n pluginData: OptionalConfigPluginDef<T>,\n extraData?: Record<string, unknown>,\n ) {\n const { plugin: Plugin, config: pluginConfig } = pluginData;\n const baseConfig = {\n ...config,\n ...Plugin.DEFAULT_CONFIG,\n ...pluginConfig,\n name,\n ...extraData,\n };\n const pluginInstance = new Plugin(baseConfig);\n\n this.#pluginInstances[name] = pluginInstance;\n\n this.#setupPromises.push(pluginInstance.setup());\n\n const self = this;\n\n Object.defineProperty(this, name, {\n get() {\n const plugin = self.#pluginInstances[name];\n return self.wrapWithAsUser(plugin);\n },\n enumerable: true,\n });\n }\n\n /**\n * Binds all function properties in an exports object to the given context.\n */\n private bindExportMethods(\n exports: Record<string, unknown>,\n context: BasePlugin,\n ) {\n for (const key in exports) {\n if (Object.hasOwn(exports, key) && typeof exports[key] === \"function\") {\n exports[key] = (exports[key] as (...args: unknown[]) => unknown).bind(\n context,\n );\n }\n }\n }\n\n /**\n * Wraps a plugin's exports with an `asUser` method that returns\n * a user-scoped version of the exports.\n */\n private wrapWithAsUser<T extends BasePlugin>(plugin: T) {\n // If plugin doesn't implement exports(), return empty object\n const pluginExports = (plugin.exports?.() ?? {}) as Record<string, unknown>;\n this.bindExportMethods(pluginExports, plugin);\n\n // If plugin doesn't support asUser (no asUser method), return exports as-is\n if (typeof (plugin as any).asUser !== \"function\") {\n return pluginExports;\n }\n\n return {\n ...pluginExports,\n /**\n * Execute operations using the user's identity from the request.\n * Returns user-scoped exports where all methods execute with the\n * user's Databricks credentials instead of the service principal.\n */\n asUser: (req: import(\"express\").Request) => {\n const userPlugin = (plugin as any).asUser(req);\n const userExports = (userPlugin.exports?.() ?? {}) as Record<\n string,\n unknown\n >;\n this.bindExportMethods(userExports, userPlugin);\n return userExports;\n },\n };\n }\n\n static async _createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n } = {},\n ): Promise<PluginMap<T>> {\n // Initialize core services\n TelemetryManager.initialize(config?.telemetry);\n await CacheManager.getInstance(config?.cache);\n\n // Initialize ServiceContext for Databricks client management\n // This provides the service principal client and shared resources\n await ServiceContext.initialize(config?.client);\n\n const rawPlugins = config.plugins as T;\n\n const registry = new ResourceRegistry();\n registry.collectResources(rawPlugins);\n registry.enforceValidation();\n\n const preparedPlugins = AppKit.preparePlugins(rawPlugins);\n const mergedConfig = {\n plugins: preparedPlugins,\n };\n\n const instance = new AppKit(mergedConfig);\n\n await Promise.all(instance.#setupPromises);\n\n return instance as unknown as PluginMap<T>;\n }\n\n private static preparePlugins(\n plugins: PluginData<PluginConstructor, unknown, string>[],\n ) {\n const result: InputPluginMap = {};\n for (const currentPlugin of plugins) {\n result[currentPlugin.name] = {\n plugin: currentPlugin.plugin,\n config: currentPlugin.config as Record<string, unknown>,\n };\n }\n return result;\n }\n}\n\n/**\n * Bootstraps AppKit with the provided configuration.\n */\nexport async function createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n>(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n } = {},\n): Promise<PluginMap<T>> {\n return AppKit._createApp(config);\n}\n"],"mappings":";;;;;;;;;cAW4C;AAK5C,IAAa,SAAb,MAAa,OAAwC;CACnD,mBAA+C,EAAE;CACjD,iBAAkC,EAAE;CAEpC,AAAQ,YAAY,QAA+B;EACjD,MAAM,EAAE,SAAS,GAAG,iBAAiB;EAErC,MAAM,gBAAgB,OAAO,QAAQ,QAAQ;EAE7C,MAAM,cAAc,cAAc,QAAQ,CAAC,GAAG,OAAO;AACnD,WAAQ,GAAG,QAAQ,SAAS,cAAc;IAC1C;EACF,MAAM,gBAAgB,cAAc,QACjC,CAAC,GAAG,QAAQ,GAAG,QAAQ,SAAS,cAAc,SAChD;EACD,MAAM,kBAAkB,cAAc,QACnC,CAAC,GAAG,QAAQ,GAAG,QAAQ,SAAS,cAAc,WAChD;AAED,OAAK,MAAM,CAAC,MAAM,eAAe,YAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,WAAW;AAIhE,OAAK,MAAM,CAAC,MAAM,eAAe,cAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,WAAW;AAIhE,OAAK,MAAM,CAAC,MAAM,eAAe,gBAC/B,KAAI,WACF,MAAK,wBAAwB,cAAc,MAAM,YAAY,EAC3D,SAAS,MAAKA,iBACf,CAAC;;CAKR,AAAQ,wBACN,QACA,MACA,YACA,WACA;EACA,MAAM,EAAE,QAAQ,QAAQ,QAAQ,iBAAiB;EAQjD,MAAM,iBAAiB,IAAI,OAPR;GACjB,GAAG;GACH,GAAG,OAAO;GACV,GAAG;GACH;GACA,GAAG;GACJ,CAC4C;AAE7C,QAAKA,gBAAiB,QAAQ;AAE9B,QAAKC,cAAe,KAAK,eAAe,OAAO,CAAC;EAEhD,MAAM,OAAO;AAEb,SAAO,eAAe,MAAM,MAAM;GAChC,MAAM;IACJ,MAAM,SAAS,MAAKD,gBAAiB;AACrC,WAAO,KAAK,eAAe,OAAO;;GAEpC,YAAY;GACb,CAAC;;;;;CAMJ,AAAQ,kBACN,SACA,SACA;AACA,OAAK,MAAM,OAAO,QAChB,KAAI,OAAO,OAAO,SAAS,IAAI,IAAI,OAAO,QAAQ,SAAS,WACzD,SAAQ,OAAQ,QAAQ,KAAyC,KAC/D,QACD;;;;;;CASP,AAAQ,eAAqC,QAAW;EAEtD,MAAM,gBAAiB,OAAO,WAAW,IAAI,EAAE;AAC/C,OAAK,kBAAkB,eAAe,OAAO;AAG7C,MAAI,OAAQ,OAAe,WAAW,WACpC,QAAO;AAGT,SAAO;GACL,GAAG;GAMH,SAAS,QAAmC;IAC1C,MAAM,aAAc,OAAe,OAAO,IAAI;IAC9C,MAAM,cAAe,WAAW,WAAW,IAAI,EAAE;AAIjD,SAAK,kBAAkB,aAAa,WAAW;AAC/C,WAAO;;GAEV;;CAGH,aAAa,WAGX,SAKI,EAAE,EACiB;AAEvB,mBAAiB,WAAW,QAAQ,UAAU;AAC9C,QAAM,aAAa,YAAY,QAAQ,MAAM;AAI7C,QAAM,eAAe,WAAW,QAAQ,OAAO;EAE/C,MAAM,aAAa,OAAO;EAE1B,MAAM,WAAW,IAAI,kBAAkB;AACvC,WAAS,iBAAiB,WAAW;AACrC,WAAS,mBAAmB;EAO5B,MAAM,WAAW,IAAI,OAJA,EACnB,SAFsB,OAAO,eAAe,WAAW,EAGxD,CAEwC;AAEzC,QAAM,QAAQ,IAAI,UAASC,cAAe;AAE1C,SAAO;;CAGT,OAAe,eACb,SACA;EACA,MAAM,SAAyB,EAAE;AACjC,OAAK,MAAM,iBAAiB,QAC1B,QAAO,cAAc,QAAQ;GAC3B,QAAQ,cAAc;GACtB,QAAQ,cAAc;GACvB;AAEH,SAAO;;;;;;AAOX,eAAsB,UAGpB,SAKI,EAAE,EACiB;AACvB,QAAO,OAAO,WAAW,OAAO"}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { CacheConfig } from "./shared/src/cache.js";
3
3
  import { StreamExecutionSettings } from "./shared/src/execute.js";
4
4
  import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
5
5
  import { CacheManager } from "./cache/index.js";
6
+ import { DatabaseCredential, GenerateDatabaseCredentialRequest, LakebasePoolConfig, RequestedClaims, RequestedClaimsPermissionSet, RequestedResource, createLakebasePool, generateDatabaseCredential, getLakebaseOrmConfig, getLakebasePgConfig, getWorkspaceClient } from "./connectors/lakebase/index.js";
6
7
  import { getExecutionContext } from "./context/execution-context.js";
7
8
  import { ITelemetry, TelemetryConfig } from "./telemetry/types.js";
8
9
  import { Counter, Histogram, SeverityNumber, Span, SpanStatusCode } from "./telemetry/index.js";
@@ -19,6 +20,9 @@ import { ValidationError } from "./errors/validation.js";
19
20
  import { Plugin } from "./plugin/plugin.js";
20
21
  import { toPlugin } from "./plugin/to-plugin.js";
21
22
  import { analytics } from "./plugins/analytics/analytics.js";
23
+ import { ConfigSchema, PluginManifest, ResourceEntry, ResourceFieldEntry, ResourcePermission, ResourceRequirement, ResourceType, ValidationResult } from "./registry/types.js";
24
+ import { getPluginManifest, getResourceRequirements } from "./registry/manifest-loader.js";
25
+ import { ResourceRegistry } from "./registry/resource-registry.js";
22
26
  import { server } from "./plugins/server/index.js";
23
27
  import { appKitTypesPlugin } from "./type-generator/vite-plugin.js";
24
- export { AppKitError, AuthenticationError, type BasePluginConfig, type CacheConfig, CacheManager, ConfigurationError, ConnectionError, type Counter, ExecutionError, type Histogram, type IAppRouter, type ITelemetry, InitializationError, Plugin, ServerError, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, getExecutionContext, isSQLTypeMarker, server, sql, toPlugin };
28
+ export { AppKitError, AuthenticationError, type BasePluginConfig, type CacheConfig, CacheManager, type ConfigSchema, ConfigurationError, ConnectionError, type Counter, type DatabaseCredential, ExecutionError, type GenerateDatabaseCredentialRequest, type Histogram, type IAppRouter, type ITelemetry, InitializationError, type LakebasePoolConfig, Plugin, type PluginManifest, type RequestedClaims, RequestedClaimsPermissionSet, type RequestedResource, type ResourceEntry, type ResourceFieldEntry, type ResourcePermission, ResourceRegistry, type ResourceRequirement, ResourceType, ServerError, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, TunnelError, ValidationError, type ValidationResult, analytics, appKitTypesPlugin, createApp, createLakebasePool, generateDatabaseCredential, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getWorkspaceClient, isSQLTypeMarker, server, sql, toPlugin };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
2
- import { SeverityNumber, SpanStatusCode } from "./telemetry/index.js";
2
+ import { RequestedClaimsPermissionSet, createLakebasePool, generateDatabaseCredential, getLakebaseOrmConfig, getLakebasePgConfig, getWorkspaceClient } from "./connectors/lakebase/index.js";
3
3
  import { AppKitError } from "./errors/base.js";
4
4
  import { AuthenticationError } from "./errors/authentication.js";
5
5
  import { ConfigurationError } from "./errors/configuration.js";
@@ -10,9 +10,14 @@ import { ServerError } from "./errors/server.js";
10
10
  import { TunnelError } from "./errors/tunnel.js";
11
11
  import { ValidationError } from "./errors/validation.js";
12
12
  import { init_errors } from "./errors/index.js";
13
+ import { SeverityNumber, SpanStatusCode } from "./telemetry/index.js";
14
+ import { CacheManager } from "./cache/index.js";
13
15
  import { getExecutionContext } from "./context/execution-context.js";
14
16
  import { init_context } from "./context/index.js";
15
- import { CacheManager } from "./cache/index.js";
17
+ import { ResourceType } from "./registry/types.js";
18
+ import { getPluginManifest, getResourceRequirements } from "./registry/manifest-loader.js";
19
+ import { ResourceRegistry } from "./registry/resource-registry.js";
20
+ import "./registry/index.js";
16
21
  import { createApp } from "./core/appkit.js";
17
22
  import "./core/index.js";
18
23
  import { Plugin } from "./plugin/plugin.js";
@@ -28,5 +33,5 @@ init_context();
28
33
  init_errors();
29
34
 
30
35
  //#endregion
31
- export { AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, ExecutionError, InitializationError, Plugin, ServerError, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, getExecutionContext, isSQLTypeMarker, server, sql, toPlugin };
36
+ export { AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, ExecutionError, InitializationError, Plugin, RequestedClaimsPermissionSet, ResourceRegistry, ResourceType, ServerError, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, createLakebasePool, generateDatabaseCredential, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getWorkspaceClient, isSQLTypeMarker, server, sql, toPlugin };
32
37
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @packageDocumentation\n *\n * Core library for building Databricks applications with type-safe SQL queries,\n * plugin architecture, and React integration.\n */\n\n// Types from shared\nexport type {\n BasePluginConfig,\n CacheConfig,\n IAppRouter,\n StreamExecutionSettings,\n} from \"shared\";\nexport { isSQLTypeMarker, sql } from \"shared\";\nexport { CacheManager } from \"./cache\";\nexport { getExecutionContext } from \"./context\";\nexport { createApp } from \"./core\";\n// Errors\nexport {\n AppKitError,\n AuthenticationError,\n ConfigurationError,\n ConnectionError,\n ExecutionError,\n InitializationError,\n ServerError,\n TunnelError,\n ValidationError,\n} from \"./errors\";\n// Plugin authoring\nexport { Plugin, toPlugin } from \"./plugin\";\nexport { analytics, server } from \"./plugins\";\n// Telemetry (for advanced custom telemetry)\nexport {\n type Counter,\n type Histogram,\n type ITelemetry,\n SeverityNumber,\n type Span,\n SpanStatusCode,\n type TelemetryConfig,\n} from \"./telemetry\";\n// Vite plugin and type generation\nexport { appKitTypesPlugin } from \"./type-generator/vite-plugin\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;cAgBgD;aAa9B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @packageDocumentation\n *\n * Core library for building Databricks applications with type-safe SQL queries,\n * plugin architecture, and React integration.\n */\n\n// Types from shared\nexport type {\n BasePluginConfig,\n CacheConfig,\n IAppRouter,\n StreamExecutionSettings,\n} from \"shared\";\nexport { isSQLTypeMarker, sql } from \"shared\";\nexport { CacheManager } from \"./cache\";\nexport type {\n DatabaseCredential,\n GenerateDatabaseCredentialRequest,\n LakebasePoolConfig,\n RequestedClaims,\n RequestedResource,\n} from \"./connectors/lakebase\";\n// Lakebase Autoscaling connector\nexport {\n createLakebasePool,\n generateDatabaseCredential,\n getLakebaseOrmConfig,\n getLakebasePgConfig,\n getWorkspaceClient,\n RequestedClaimsPermissionSet,\n} from \"./connectors/lakebase\";\nexport { getExecutionContext } from \"./context\";\nexport { createApp } from \"./core\";\n// Errors\nexport {\n AppKitError,\n AuthenticationError,\n ConfigurationError,\n ConnectionError,\n ExecutionError,\n InitializationError,\n ServerError,\n TunnelError,\n ValidationError,\n} from \"./errors\";\n// Plugin authoring\nexport { Plugin, toPlugin } from \"./plugin\";\nexport { analytics, server } from \"./plugins\";\n// Registry types and utilities for plugin manifests\nexport type {\n ConfigSchema,\n PluginManifest,\n ResourceEntry,\n ResourceFieldEntry,\n ResourcePermission,\n ResourceRequirement,\n ValidationResult,\n} from \"./registry\";\nexport {\n getPluginManifest,\n getResourceRequirements,\n ResourceRegistry,\n ResourceType,\n} from \"./registry\";\n// Telemetry (for advanced custom telemetry)\nexport {\n type Counter,\n type Histogram,\n type ITelemetry,\n SeverityNumber,\n type Span,\n SpanStatusCode,\n type TelemetryConfig,\n} from \"./telemetry\";\n// Vite plugin and type generation\nexport { appKitTypesPlugin } from \"./type-generator/vite-plugin\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgCgD;aAa9B"}
@@ -1,9 +1,9 @@
1
1
  import { DEFAULT_SAMPLING_CONFIG, shouldSample } from "./sampling.js";
2
2
  import { WideEvent } from "./wide-event.js";
3
3
  import { WideEventEmitter } from "./wide-event-emitter.js";
4
- import { trace } from "@opentelemetry/api";
5
4
  import { AsyncLocalStorage } from "node:async_hooks";
6
5
  import { format } from "node:util";
6
+ import { trace } from "@opentelemetry/api";
7
7
  import { createDebug } from "obug";
8
8
 
9
9
  //#region src/logging/logger.ts
@@ -8,7 +8,92 @@ import { DevFileReader } from "./dev-reader.js";
8
8
  import express from "express";
9
9
 
10
10
  //#region src/plugin/plugin.d.ts
11
- /** Base abstract class for creating AppKit plugins */
11
+
12
+ /**
13
+ * Base abstract class for creating AppKit plugins.
14
+ *
15
+ * All plugins must declare a static `manifest` property with their metadata
16
+ * and resource requirements. The manifest defines:
17
+ * - `required` resources: Always needed for the plugin to function
18
+ * - `optional` resources: May be needed depending on plugin configuration
19
+ *
20
+ * ## Static vs Runtime Resource Requirements
21
+ *
22
+ * The manifest is static and doesn't know the plugin's runtime configuration.
23
+ * For resources that become required based on config options, plugins can
24
+ * implement a static `getResourceRequirements(config)` method.
25
+ *
26
+ * At runtime, this method is called with the actual config to determine
27
+ * which "optional" resources should be treated as "required".
28
+ *
29
+ * @example Basic plugin with static requirements
30
+ * ```typescript
31
+ * import { Plugin, toPlugin, PluginManifest, ResourceType } from '@databricks/appkit';
32
+ *
33
+ * const myManifest: PluginManifest = {
34
+ * name: 'myPlugin',
35
+ * displayName: 'My Plugin',
36
+ * description: 'Does something awesome',
37
+ * resources: {
38
+ * required: [
39
+ * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }
40
+ * ],
41
+ * optional: []
42
+ * }
43
+ * };
44
+ *
45
+ * class MyPlugin extends Plugin<MyConfig> {
46
+ * static manifest = myManifest;
47
+ * name = 'myPlugin';
48
+ * }
49
+ * ```
50
+ *
51
+ * @example Plugin with config-dependent resources
52
+ * ```typescript
53
+ * interface MyConfig extends BasePluginConfig {
54
+ * enableCaching?: boolean;
55
+ * }
56
+ *
57
+ * const myManifest: PluginManifest = {
58
+ * name: 'myPlugin',
59
+ * resources: {
60
+ * required: [
61
+ * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }
62
+ * ],
63
+ * optional: [
64
+ * // Database is optional in the static manifest
65
+ * { type: ResourceType.DATABASE, alias: 'cache', description: 'Required if caching enabled', ... }
66
+ * ]
67
+ * }
68
+ * };
69
+ *
70
+ * class MyPlugin extends Plugin<MyConfig> {
71
+ * static manifest = myManifest;
72
+ * name = 'myPlugin';
73
+ *
74
+ * // Runtime method: converts optional resources to required based on config
75
+ * static getResourceRequirements(config: MyConfig) {
76
+ * const resources = [];
77
+ * if (config.enableCaching) {
78
+ * // When caching is enabled, Database becomes required
79
+ * resources.push({
80
+ * type: ResourceType.DATABASE,
81
+ * alias: 'cache',
82
+ * resourceKey: 'database',
83
+ * description: 'Cache storage for query results',
84
+ * permission: 'CAN_CONNECT_AND_CREATE',
85
+ * fields: {
86
+ * instance_name: { env: 'DATABRICKS_CACHE_INSTANCE' },
87
+ * database_name: { env: 'DATABRICKS_CACHE_DB' },
88
+ * },
89
+ * required: true // Mark as required at runtime
90
+ * });
91
+ * }
92
+ * return resources;
93
+ * }
94
+ * }
95
+ * ```
96
+ */
12
97
  declare abstract class Plugin<TConfig extends BasePluginConfig = BasePluginConfig> implements BasePlugin {
13
98
  protected config: TConfig;
14
99
  protected isReady: boolean;
@@ -17,13 +102,20 @@ declare abstract class Plugin<TConfig extends BasePluginConfig = BasePluginConfi
17
102
  protected devFileReader: DevFileReader;
18
103
  protected streamManager: StreamManager;
19
104
  protected telemetry: ITelemetry;
20
- protected abstract envVars: string[];
21
105
  /** Registered endpoints for this plugin */
22
106
  private registeredEndpoints;
107
+ /**
108
+ * Plugin initialization phase.
109
+ * - 'core': Initialized first (e.g., config plugins)
110
+ * - 'normal': Initialized second (most plugins)
111
+ * - 'deferred': Initialized last (e.g., server plugin)
112
+ */
23
113
  static phase: PluginPhase;
114
+ /**
115
+ * Plugin name identifier.
116
+ */
24
117
  name: string;
25
118
  constructor(config: TConfig);
26
- validateEnv(): void;
27
119
  injectRoutes(_: express.Router): void;
28
120
  setup(): Promise<void>;
29
121
  getEndpoints(): PluginEndpointMap;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/plugin/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;uBA8DsB,uBACJ,mBAAmB,6BACxB;oBAgBmB;EAlBV,UAAM,OAAA,EAAA,OAAA;EAAA,UAAA,KAAA,EAKT,YALS;YACV,GAAA,EAKD,UALC;YAAmB,aAAA,EAMV,aANU;YAiBL,aAAA,EAVL,aAUK;YAbb,SAAA,EAII,UAJJ;qBACF,OAAA,EAAA,MAAA,EAAA;;UAEU,mBAAA;SACJ,KAAA,EAMP,WANO;MAMP,EAAA,MAAA;aAGgB,CAAA,MAAA,EAAA,OAAA;aAeN,CAAA,CAAA,EAAA,IAAA;cAIb,CAAA,CAAA,EAJK,OAAA,CAAQ,MAIb,CAAA,EAAA,IAAA;OAEK,CAAA,CAAA,EAFL,OAEK,CAAA,IAAA,CAAA;cA0BI,CAAA,CAAA,EA1BJ,iBA0BI;uBA6Db,CAAA,CAAA,EAAA,IAAA;;;;;;SA8DiC,CAAA,CAAA,EAAA,OAAA;;;;;;;;;;cA3H5B,OAAA,CAAQ;;;;;;;kCA6Db,kBACD,qBAAqB,aAChB,4CACO;qCA2DF,gBAAgB,QAAQ,aAC7B,4CAER,QAAQ;;sCA0BD,OAAA,CAAQ,gBACR"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/plugin/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAiJA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAAsB,uBACJ,mBAAmB,6BACxB;oBAyBmB;;mBAtBb;iBACF;2BACU;2BACA;uBACJ;;;;;;;;;gBAWP;;;;;sBAOgB;kBAWd,OAAA,CAAQ;WAIb;kBAEK;;;;;;;;;;;;;;;;;cA0BJ,OAAA,CAAQ;;;;;;;kCA6Db,kBACD,qBAAqB,aAChB,4CACO;qCA2DF,gBAAgB,QAAQ,aAC7B,4CAER,QAAQ;;sCA0BD,OAAA,CAAQ,gBACR"}
@@ -1,15 +1,14 @@
1
- import { normalizeTelemetryOptions } from "../telemetry/config.js";
2
1
  import { createLogger } from "../logging/logger.js";
3
- import { TelemetryManager } from "../telemetry/telemetry-manager.js";
4
- import "../telemetry/index.js";
5
2
  import { AuthenticationError } from "../errors/authentication.js";
6
3
  import { init_errors } from "../errors/index.js";
7
- import { validateEnv } from "../utils/env-validator.js";
4
+ import { normalizeTelemetryOptions } from "../telemetry/config.js";
5
+ import { TelemetryManager } from "../telemetry/telemetry-manager.js";
6
+ import "../telemetry/index.js";
8
7
  import { deepMerge } from "../utils/merge.js";
8
+ import { CacheManager } from "../cache/index.js";
9
9
  import { ServiceContext } from "../context/service-context.js";
10
10
  import { getCurrentUserId, runInUserContext } from "../context/execution-context.js";
11
11
  import { init_context } from "../context/index.js";
12
- import { CacheManager } from "../cache/index.js";
13
12
  import { AppManager } from "../app/index.js";
14
13
  import { StreamManager } from "../stream/stream-manager.js";
15
14
  import "../stream/index.js";
@@ -31,14 +30,97 @@ const logger = createLogger("plugin");
31
30
  const EXCLUDED_FROM_PROXY = new Set([
32
31
  "setup",
33
32
  "shutdown",
34
- "validateEnv",
35
33
  "injectRoutes",
36
34
  "getEndpoints",
37
35
  "abortActiveOperations",
38
36
  "asUser",
39
37
  "constructor"
40
38
  ]);
41
- /** Base abstract class for creating AppKit plugins */
39
+ /**
40
+ * Base abstract class for creating AppKit plugins.
41
+ *
42
+ * All plugins must declare a static `manifest` property with their metadata
43
+ * and resource requirements. The manifest defines:
44
+ * - `required` resources: Always needed for the plugin to function
45
+ * - `optional` resources: May be needed depending on plugin configuration
46
+ *
47
+ * ## Static vs Runtime Resource Requirements
48
+ *
49
+ * The manifest is static and doesn't know the plugin's runtime configuration.
50
+ * For resources that become required based on config options, plugins can
51
+ * implement a static `getResourceRequirements(config)` method.
52
+ *
53
+ * At runtime, this method is called with the actual config to determine
54
+ * which "optional" resources should be treated as "required".
55
+ *
56
+ * @example Basic plugin with static requirements
57
+ * ```typescript
58
+ * import { Plugin, toPlugin, PluginManifest, ResourceType } from '@databricks/appkit';
59
+ *
60
+ * const myManifest: PluginManifest = {
61
+ * name: 'myPlugin',
62
+ * displayName: 'My Plugin',
63
+ * description: 'Does something awesome',
64
+ * resources: {
65
+ * required: [
66
+ * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }
67
+ * ],
68
+ * optional: []
69
+ * }
70
+ * };
71
+ *
72
+ * class MyPlugin extends Plugin<MyConfig> {
73
+ * static manifest = myManifest;
74
+ * name = 'myPlugin';
75
+ * }
76
+ * ```
77
+ *
78
+ * @example Plugin with config-dependent resources
79
+ * ```typescript
80
+ * interface MyConfig extends BasePluginConfig {
81
+ * enableCaching?: boolean;
82
+ * }
83
+ *
84
+ * const myManifest: PluginManifest = {
85
+ * name: 'myPlugin',
86
+ * resources: {
87
+ * required: [
88
+ * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }
89
+ * ],
90
+ * optional: [
91
+ * // Database is optional in the static manifest
92
+ * { type: ResourceType.DATABASE, alias: 'cache', description: 'Required if caching enabled', ... }
93
+ * ]
94
+ * }
95
+ * };
96
+ *
97
+ * class MyPlugin extends Plugin<MyConfig> {
98
+ * static manifest = myManifest;
99
+ * name = 'myPlugin';
100
+ *
101
+ * // Runtime method: converts optional resources to required based on config
102
+ * static getResourceRequirements(config: MyConfig) {
103
+ * const resources = [];
104
+ * if (config.enableCaching) {
105
+ * // When caching is enabled, Database becomes required
106
+ * resources.push({
107
+ * type: ResourceType.DATABASE,
108
+ * alias: 'cache',
109
+ * resourceKey: 'database',
110
+ * description: 'Cache storage for query results',
111
+ * permission: 'CAN_CONNECT_AND_CREATE',
112
+ * fields: {
113
+ * instance_name: { env: 'DATABRICKS_CACHE_INSTANCE' },
114
+ * database_name: { env: 'DATABRICKS_CACHE_DB' },
115
+ * },
116
+ * required: true // Mark as required at runtime
117
+ * });
118
+ * }
119
+ * return resources;
120
+ * }
121
+ * }
122
+ * ```
123
+ */
42
124
  var Plugin = class {
43
125
  isReady = false;
44
126
  cache;
@@ -48,7 +130,16 @@ var Plugin = class {
48
130
  telemetry;
49
131
  /** Registered endpoints for this plugin */
50
132
  registeredEndpoints = {};
133
+ /**
134
+ * Plugin initialization phase.
135
+ * - 'core': Initialized first (e.g., config plugins)
136
+ * - 'normal': Initialized second (most plugins)
137
+ * - 'deferred': Initialized last (e.g., server plugin)
138
+ */
51
139
  static phase = "normal";
140
+ /**
141
+ * Plugin name identifier.
142
+ */
52
143
  name;
53
144
  constructor(config) {
54
145
  this.config = config;
@@ -60,9 +151,6 @@ var Plugin = class {
60
151
  this.devFileReader = DevFileReader.getInstance();
61
152
  this.isReady = true;
62
153
  }
63
- validateEnv() {
64
- validateEnv(this.envVars);
65
- }
66
154
  injectRoutes(_) {}
67
155
  async setup() {}
68
156
  getEndpoints() {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../src/plugin/plugin.ts"],"sourcesContent":["import type express from \"express\";\nimport type {\n BasePlugin,\n BasePluginConfig,\n IAppResponse,\n PluginEndpointMap,\n PluginExecuteConfig,\n PluginExecutionSettings,\n PluginPhase,\n RouteConfig,\n StreamExecuteHandler,\n StreamExecutionSettings,\n} from \"shared\";\nimport { AppManager } from \"../app\";\nimport { CacheManager } from \"../cache\";\nimport {\n getCurrentUserId,\n runInUserContext,\n ServiceContext,\n type UserContext,\n} from \"../context\";\nimport { AuthenticationError } from \"../errors\";\nimport { createLogger } from \"../logging/logger\";\nimport { StreamManager } from \"../stream\";\nimport {\n type ITelemetry,\n normalizeTelemetryOptions,\n TelemetryManager,\n} from \"../telemetry\";\nimport { deepMerge, validateEnv } from \"../utils\";\nimport { DevFileReader } from \"./dev-reader\";\nimport { CacheInterceptor } from \"./interceptors/cache\";\nimport { RetryInterceptor } from \"./interceptors/retry\";\nimport { TelemetryInterceptor } from \"./interceptors/telemetry\";\nimport { TimeoutInterceptor } from \"./interceptors/timeout\";\nimport type {\n ExecutionInterceptor,\n InterceptorContext,\n} from \"./interceptors/types\";\n\nconst logger = createLogger(\"plugin\");\n\n/**\n * Methods that should not be proxied by asUser().\n * These are lifecycle/internal methods that don't make sense\n * to execute in a user context.\n */\nconst EXCLUDED_FROM_PROXY = new Set([\n // Lifecycle methods\n \"setup\",\n \"shutdown\",\n \"validateEnv\",\n \"injectRoutes\",\n \"getEndpoints\",\n \"abortActiveOperations\",\n // asUser itself - prevent chaining like .asUser().asUser()\n \"asUser\",\n // Internal methods\n \"constructor\",\n]);\n\n/** Base abstract class for creating AppKit plugins */\nexport abstract class Plugin<\n TConfig extends BasePluginConfig = BasePluginConfig,\n> implements BasePlugin\n{\n protected isReady = false;\n protected cache: CacheManager;\n protected app: AppManager;\n protected devFileReader: DevFileReader;\n protected streamManager: StreamManager;\n protected telemetry: ITelemetry;\n protected abstract envVars: string[];\n\n /** Registered endpoints for this plugin */\n private registeredEndpoints: PluginEndpointMap = {};\n\n static phase: PluginPhase = \"normal\";\n name: string;\n\n constructor(protected config: TConfig) {\n this.name = config.name ?? \"plugin\";\n this.telemetry = TelemetryManager.getProvider(this.name, config.telemetry);\n this.streamManager = new StreamManager();\n this.cache = CacheManager.getInstanceSync();\n this.app = new AppManager();\n this.devFileReader = DevFileReader.getInstance();\n\n this.isReady = true;\n }\n\n validateEnv() {\n validateEnv(this.envVars);\n }\n\n injectRoutes(_: express.Router) {\n return;\n }\n\n async setup() {}\n\n getEndpoints(): PluginEndpointMap {\n return this.registeredEndpoints;\n }\n\n abortActiveOperations(): void {\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the public exports for this plugin.\n * Override this to define a custom public API.\n * By default, returns an empty object.\n */\n exports(): unknown {\n return {};\n }\n\n /**\n * Execute operations using the user's identity from the request.\n * Returns a proxy of this plugin where all method calls execute\n * with the user's Databricks credentials instead of the service principal.\n *\n * @param req - The Express request containing the user token in headers\n * @returns A proxied plugin instance that executes as the user\n * @throws Error if user token is not available in request headers\n */\n asUser(req: express.Request): this {\n const token = req.headers[\"x-forwarded-access-token\"] as string;\n const userId = req.headers[\"x-forwarded-user\"] as string;\n const isDev = process.env.NODE_ENV === \"development\";\n\n // In local development, fall back to service principal\n // since there's no user token available\n if (!token && isDev) {\n logger.warn(\n \"asUser() called without user token in development mode. Using service principal.\",\n );\n\n return this;\n }\n\n if (!token) {\n throw AuthenticationError.missingToken(\"user token\");\n }\n\n if (!userId && !isDev) {\n throw AuthenticationError.missingUserId();\n }\n\n const effectiveUserId = userId || \"dev-user\";\n\n const userContext = ServiceContext.createUserContext(\n token,\n effectiveUserId,\n );\n\n // Return a proxy that wraps method calls in user context\n return this._createUserContextProxy(userContext);\n }\n\n /**\n * Creates a proxy that wraps method calls in a user context.\n * This allows all plugin methods to automatically use the user's\n * Databricks credentials.\n */\n private _createUserContextProxy(userContext: UserContext): this {\n return new Proxy(this, {\n get: (target, prop, receiver) => {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof value !== \"function\") {\n return value;\n }\n\n if (typeof prop === \"string\" && EXCLUDED_FROM_PROXY.has(prop)) {\n return value;\n }\n\n return (...args: unknown[]) => {\n return runInUserContext(userContext, () => value.apply(target, args));\n };\n },\n }) as this;\n }\n\n // streaming execution with interceptors\n protected async executeStream<T>(\n res: IAppResponse,\n fn: StreamExecuteHandler<T>,\n options: StreamExecutionSettings,\n userKey?: string,\n ) {\n // destructure options\n const {\n stream: streamConfig,\n default: defaultConfig,\n user: userConfig,\n } = options;\n\n // build execution options\n const executeConfig = this._buildExecutionConfig({\n default: defaultConfig,\n user: userConfig,\n });\n\n // get user key from context if not provided\n const effectiveUserKey = userKey ?? getCurrentUserId();\n\n const self = this;\n\n // wrapper function to ensure it returns a generator\n const asyncWrapperFn = async function* (streamSignal?: AbortSignal) {\n // build execution context\n const context: InterceptorContext = {\n signal: streamSignal,\n metadata: new Map(),\n userKey: effectiveUserKey,\n };\n\n // build interceptors\n const interceptors = self._buildInterceptors(executeConfig);\n\n // wrap the function to ensure it returns a promise\n const wrappedFn = async () => {\n const result = await fn(context.signal);\n return result;\n };\n\n // execute the function with interceptors\n const result = await self._executeWithInterceptors(\n wrappedFn as (signal?: AbortSignal) => Promise<T>,\n interceptors,\n context,\n );\n\n // check if result is a generator\n if (self._checkIfGenerator(result)) {\n yield* result;\n } else {\n yield result;\n }\n };\n\n // stream the result to the client\n await this.streamManager.stream(res, asyncWrapperFn, streamConfig);\n }\n\n // single sync execution with interceptors\n protected async execute<T>(\n fn: (signal?: AbortSignal) => Promise<T>,\n options: PluginExecutionSettings,\n userKey?: string,\n ): Promise<T | undefined> {\n const executeConfig = this._buildExecutionConfig(options);\n\n const interceptors = this._buildInterceptors(executeConfig);\n\n // get user key from context if not provided\n const effectiveUserKey = userKey ?? getCurrentUserId();\n\n const context: InterceptorContext = {\n metadata: new Map(),\n userKey: effectiveUserKey,\n };\n\n try {\n return await this._executeWithInterceptors(fn, interceptors, context);\n } catch (_error) {\n // production-safe, don't crash sdk\n return undefined;\n }\n }\n\n protected registerEndpoint(name: string, path: string): void {\n this.registeredEndpoints[name] = path;\n }\n\n protected route<_TResponse>(\n router: express.Router,\n config: RouteConfig,\n ): void {\n const { name, method, path, handler } = config;\n\n router[method](path, handler);\n\n this.registerEndpoint(name, `/api/${this.name}${path}`);\n }\n\n // build execution options by merging defaults, plugin config, and user overrides\n private _buildExecutionConfig(\n options: PluginExecutionSettings,\n ): PluginExecuteConfig {\n const { default: methodDefaults, user: userOverride } = options;\n\n // Merge: method defaults <- plugin config <- user override (highest priority)\n return deepMerge(\n deepMerge(methodDefaults, this.config),\n userOverride ?? {},\n ) as PluginExecuteConfig;\n }\n\n // build interceptors based on execute options\n private _buildInterceptors(\n options: PluginExecuteConfig,\n ): ExecutionInterceptor[] {\n const interceptors: ExecutionInterceptor[] = [];\n\n // order matters: telemetry → timeout → retry → cache (innermost to outermost)\n\n const telemetryConfig = normalizeTelemetryOptions(this.config.telemetry);\n if (\n telemetryConfig.traces &&\n (options.telemetryInterceptor?.enabled ?? true)\n ) {\n interceptors.push(\n new TelemetryInterceptor(this.telemetry, options.telemetryInterceptor),\n );\n }\n\n if (options.timeout && options.timeout > 0) {\n interceptors.push(new TimeoutInterceptor(options.timeout));\n }\n\n if (\n options.retry?.enabled &&\n options.retry.attempts &&\n options.retry.attempts > 1\n ) {\n interceptors.push(new RetryInterceptor(options.retry));\n }\n\n if (options.cache?.enabled && options.cache.cacheKey?.length) {\n interceptors.push(new CacheInterceptor(this.cache, options.cache));\n }\n\n return interceptors;\n }\n\n // execute method wrapped with interceptors\n private async _executeWithInterceptors<T>(\n fn: (signal?: AbortSignal) => Promise<T>,\n interceptors: ExecutionInterceptor[],\n context: InterceptorContext,\n ): Promise<T> {\n // no interceptors, execute directly\n if (interceptors.length === 0) {\n return fn(context.signal);\n }\n // build nested execution chain from interceptors\n let wrappedFn = () => fn(context.signal);\n\n // wrap each interceptor around the previous function\n for (const interceptor of interceptors) {\n const previousFn = wrappedFn;\n wrappedFn = () => interceptor.intercept(previousFn, context);\n }\n\n return wrappedFn();\n }\n\n private _checkIfGenerator(\n result: any,\n ): result is AsyncGenerator<any, void, unknown> {\n return (\n result && typeof result === \"object\" && Symbol.asyncIterator in result\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../src/plugin/plugin.ts"],"sourcesContent":["import type express from \"express\";\nimport type {\n BasePlugin,\n BasePluginConfig,\n IAppResponse,\n PluginEndpointMap,\n PluginExecuteConfig,\n PluginExecutionSettings,\n PluginPhase,\n RouteConfig,\n StreamExecuteHandler,\n StreamExecutionSettings,\n} from \"shared\";\nimport { AppManager } from \"../app\";\nimport { CacheManager } from \"../cache\";\nimport {\n getCurrentUserId,\n runInUserContext,\n ServiceContext,\n type UserContext,\n} from \"../context\";\nimport { AuthenticationError } from \"../errors\";\nimport { createLogger } from \"../logging/logger\";\nimport { StreamManager } from \"../stream\";\nimport {\n type ITelemetry,\n normalizeTelemetryOptions,\n TelemetryManager,\n} from \"../telemetry\";\nimport { deepMerge } from \"../utils\";\nimport { DevFileReader } from \"./dev-reader\";\nimport { CacheInterceptor } from \"./interceptors/cache\";\nimport { RetryInterceptor } from \"./interceptors/retry\";\nimport { TelemetryInterceptor } from \"./interceptors/telemetry\";\nimport { TimeoutInterceptor } from \"./interceptors/timeout\";\nimport type {\n ExecutionInterceptor,\n InterceptorContext,\n} from \"./interceptors/types\";\n\nconst logger = createLogger(\"plugin\");\n\n/**\n * Methods that should not be proxied by asUser().\n * These are lifecycle/internal methods that don't make sense\n * to execute in a user context.\n */\nconst EXCLUDED_FROM_PROXY = new Set([\n // Lifecycle methods\n \"setup\",\n \"shutdown\",\n \"injectRoutes\",\n \"getEndpoints\",\n \"abortActiveOperations\",\n // asUser itself - prevent chaining like .asUser().asUser()\n \"asUser\",\n // Internal methods\n \"constructor\",\n]);\n\n/**\n * Base abstract class for creating AppKit plugins.\n *\n * All plugins must declare a static `manifest` property with their metadata\n * and resource requirements. The manifest defines:\n * - `required` resources: Always needed for the plugin to function\n * - `optional` resources: May be needed depending on plugin configuration\n *\n * ## Static vs Runtime Resource Requirements\n *\n * The manifest is static and doesn't know the plugin's runtime configuration.\n * For resources that become required based on config options, plugins can\n * implement a static `getResourceRequirements(config)` method.\n *\n * At runtime, this method is called with the actual config to determine\n * which \"optional\" resources should be treated as \"required\".\n *\n * @example Basic plugin with static requirements\n * ```typescript\n * import { Plugin, toPlugin, PluginManifest, ResourceType } from '@databricks/appkit';\n *\n * const myManifest: PluginManifest = {\n * name: 'myPlugin',\n * displayName: 'My Plugin',\n * description: 'Does something awesome',\n * resources: {\n * required: [\n * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }\n * ],\n * optional: []\n * }\n * };\n *\n * class MyPlugin extends Plugin<MyConfig> {\n * static manifest = myManifest;\n * name = 'myPlugin';\n * }\n * ```\n *\n * @example Plugin with config-dependent resources\n * ```typescript\n * interface MyConfig extends BasePluginConfig {\n * enableCaching?: boolean;\n * }\n *\n * const myManifest: PluginManifest = {\n * name: 'myPlugin',\n * resources: {\n * required: [\n * { type: ResourceType.SQL_WAREHOUSE, alias: 'warehouse', ... }\n * ],\n * optional: [\n * // Database is optional in the static manifest\n * { type: ResourceType.DATABASE, alias: 'cache', description: 'Required if caching enabled', ... }\n * ]\n * }\n * };\n *\n * class MyPlugin extends Plugin<MyConfig> {\n * static manifest = myManifest;\n * name = 'myPlugin';\n *\n * // Runtime method: converts optional resources to required based on config\n * static getResourceRequirements(config: MyConfig) {\n * const resources = [];\n * if (config.enableCaching) {\n * // When caching is enabled, Database becomes required\n * resources.push({\n * type: ResourceType.DATABASE,\n * alias: 'cache',\n * resourceKey: 'database',\n * description: 'Cache storage for query results',\n * permission: 'CAN_CONNECT_AND_CREATE',\n * fields: {\n * instance_name: { env: 'DATABRICKS_CACHE_INSTANCE' },\n * database_name: { env: 'DATABRICKS_CACHE_DB' },\n * },\n * required: true // Mark as required at runtime\n * });\n * }\n * return resources;\n * }\n * }\n * ```\n */\nexport abstract class Plugin<\n TConfig extends BasePluginConfig = BasePluginConfig,\n> implements BasePlugin\n{\n protected isReady = false;\n protected cache: CacheManager;\n protected app: AppManager;\n protected devFileReader: DevFileReader;\n protected streamManager: StreamManager;\n protected telemetry: ITelemetry;\n\n /** Registered endpoints for this plugin */\n private registeredEndpoints: PluginEndpointMap = {};\n\n /**\n * Plugin initialization phase.\n * - 'core': Initialized first (e.g., config plugins)\n * - 'normal': Initialized second (most plugins)\n * - 'deferred': Initialized last (e.g., server plugin)\n */\n static phase: PluginPhase = \"normal\";\n\n /**\n * Plugin name identifier.\n */\n name: string;\n\n constructor(protected config: TConfig) {\n this.name = config.name ?? \"plugin\";\n this.telemetry = TelemetryManager.getProvider(this.name, config.telemetry);\n this.streamManager = new StreamManager();\n this.cache = CacheManager.getInstanceSync();\n this.app = new AppManager();\n this.devFileReader = DevFileReader.getInstance();\n\n this.isReady = true;\n }\n\n injectRoutes(_: express.Router) {\n return;\n }\n\n async setup() {}\n\n getEndpoints(): PluginEndpointMap {\n return this.registeredEndpoints;\n }\n\n abortActiveOperations(): void {\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the public exports for this plugin.\n * Override this to define a custom public API.\n * By default, returns an empty object.\n */\n exports(): unknown {\n return {};\n }\n\n /**\n * Execute operations using the user's identity from the request.\n * Returns a proxy of this plugin where all method calls execute\n * with the user's Databricks credentials instead of the service principal.\n *\n * @param req - The Express request containing the user token in headers\n * @returns A proxied plugin instance that executes as the user\n * @throws Error if user token is not available in request headers\n */\n asUser(req: express.Request): this {\n const token = req.headers[\"x-forwarded-access-token\"] as string;\n const userId = req.headers[\"x-forwarded-user\"] as string;\n const isDev = process.env.NODE_ENV === \"development\";\n\n // In local development, fall back to service principal\n // since there's no user token available\n if (!token && isDev) {\n logger.warn(\n \"asUser() called without user token in development mode. Using service principal.\",\n );\n\n return this;\n }\n\n if (!token) {\n throw AuthenticationError.missingToken(\"user token\");\n }\n\n if (!userId && !isDev) {\n throw AuthenticationError.missingUserId();\n }\n\n const effectiveUserId = userId || \"dev-user\";\n\n const userContext = ServiceContext.createUserContext(\n token,\n effectiveUserId,\n );\n\n // Return a proxy that wraps method calls in user context\n return this._createUserContextProxy(userContext);\n }\n\n /**\n * Creates a proxy that wraps method calls in a user context.\n * This allows all plugin methods to automatically use the user's\n * Databricks credentials.\n */\n private _createUserContextProxy(userContext: UserContext): this {\n return new Proxy(this, {\n get: (target, prop, receiver) => {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof value !== \"function\") {\n return value;\n }\n\n if (typeof prop === \"string\" && EXCLUDED_FROM_PROXY.has(prop)) {\n return value;\n }\n\n return (...args: unknown[]) => {\n return runInUserContext(userContext, () => value.apply(target, args));\n };\n },\n }) as this;\n }\n\n // streaming execution with interceptors\n protected async executeStream<T>(\n res: IAppResponse,\n fn: StreamExecuteHandler<T>,\n options: StreamExecutionSettings,\n userKey?: string,\n ) {\n // destructure options\n const {\n stream: streamConfig,\n default: defaultConfig,\n user: userConfig,\n } = options;\n\n // build execution options\n const executeConfig = this._buildExecutionConfig({\n default: defaultConfig,\n user: userConfig,\n });\n\n // get user key from context if not provided\n const effectiveUserKey = userKey ?? getCurrentUserId();\n\n const self = this;\n\n // wrapper function to ensure it returns a generator\n const asyncWrapperFn = async function* (streamSignal?: AbortSignal) {\n // build execution context\n const context: InterceptorContext = {\n signal: streamSignal,\n metadata: new Map(),\n userKey: effectiveUserKey,\n };\n\n // build interceptors\n const interceptors = self._buildInterceptors(executeConfig);\n\n // wrap the function to ensure it returns a promise\n const wrappedFn = async () => {\n const result = await fn(context.signal);\n return result;\n };\n\n // execute the function with interceptors\n const result = await self._executeWithInterceptors(\n wrappedFn as (signal?: AbortSignal) => Promise<T>,\n interceptors,\n context,\n );\n\n // check if result is a generator\n if (self._checkIfGenerator(result)) {\n yield* result;\n } else {\n yield result;\n }\n };\n\n // stream the result to the client\n await this.streamManager.stream(res, asyncWrapperFn, streamConfig);\n }\n\n // single sync execution with interceptors\n protected async execute<T>(\n fn: (signal?: AbortSignal) => Promise<T>,\n options: PluginExecutionSettings,\n userKey?: string,\n ): Promise<T | undefined> {\n const executeConfig = this._buildExecutionConfig(options);\n\n const interceptors = this._buildInterceptors(executeConfig);\n\n // get user key from context if not provided\n const effectiveUserKey = userKey ?? getCurrentUserId();\n\n const context: InterceptorContext = {\n metadata: new Map(),\n userKey: effectiveUserKey,\n };\n\n try {\n return await this._executeWithInterceptors(fn, interceptors, context);\n } catch (_error) {\n // production-safe, don't crash sdk\n return undefined;\n }\n }\n\n protected registerEndpoint(name: string, path: string): void {\n this.registeredEndpoints[name] = path;\n }\n\n protected route<_TResponse>(\n router: express.Router,\n config: RouteConfig,\n ): void {\n const { name, method, path, handler } = config;\n\n router[method](path, handler);\n\n this.registerEndpoint(name, `/api/${this.name}${path}`);\n }\n\n // build execution options by merging defaults, plugin config, and user overrides\n private _buildExecutionConfig(\n options: PluginExecutionSettings,\n ): PluginExecuteConfig {\n const { default: methodDefaults, user: userOverride } = options;\n\n // Merge: method defaults <- plugin config <- user override (highest priority)\n return deepMerge(\n deepMerge(methodDefaults, this.config),\n userOverride ?? {},\n ) as PluginExecuteConfig;\n }\n\n // build interceptors based on execute options\n private _buildInterceptors(\n options: PluginExecuteConfig,\n ): ExecutionInterceptor[] {\n const interceptors: ExecutionInterceptor[] = [];\n\n // order matters: telemetry → timeout → retry → cache (innermost to outermost)\n\n const telemetryConfig = normalizeTelemetryOptions(this.config.telemetry);\n if (\n telemetryConfig.traces &&\n (options.telemetryInterceptor?.enabled ?? true)\n ) {\n interceptors.push(\n new TelemetryInterceptor(this.telemetry, options.telemetryInterceptor),\n );\n }\n\n if (options.timeout && options.timeout > 0) {\n interceptors.push(new TimeoutInterceptor(options.timeout));\n }\n\n if (\n options.retry?.enabled &&\n options.retry.attempts &&\n options.retry.attempts > 1\n ) {\n interceptors.push(new RetryInterceptor(options.retry));\n }\n\n if (options.cache?.enabled && options.cache.cacheKey?.length) {\n interceptors.push(new CacheInterceptor(this.cache, options.cache));\n }\n\n return interceptors;\n }\n\n // execute method wrapped with interceptors\n private async _executeWithInterceptors<T>(\n fn: (signal?: AbortSignal) => Promise<T>,\n interceptors: ExecutionInterceptor[],\n context: InterceptorContext,\n ): Promise<T> {\n // no interceptors, execute directly\n if (interceptors.length === 0) {\n return fn(context.signal);\n }\n // build nested execution chain from interceptors\n let wrappedFn = () => fn(context.signal);\n\n // wrap each interceptor around the previous function\n for (const interceptor of interceptors) {\n const previousFn = wrappedFn;\n wrappedFn = () => interceptor.intercept(previousFn, context);\n }\n\n return wrappedFn();\n }\n\n private _checkIfGenerator(\n result: any,\n ): result is AsyncGenerator<any, void, unknown> {\n return (\n result && typeof result === \"object\" && Symbol.asyncIterator in result\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAoBoB;aAC4B;AAmBhD,MAAM,SAAS,aAAa,SAAS;;;;;;AAOrC,MAAM,sBAAsB,IAAI,IAAI;CAElC;CACA;CACA;CACA;CACA;CAEA;CAEA;CACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuFF,IAAsB,SAAtB,MAGA;CACE,AAAU,UAAU;CACpB,AAAU;CACV,AAAU;CACV,AAAU;CACV,AAAU;CACV,AAAU;;CAGV,AAAQ,sBAAyC,EAAE;;;;;;;CAQnD,OAAO,QAAqB;;;;CAK5B;CAEA,YAAY,AAAU,QAAiB;EAAjB;AACpB,OAAK,OAAO,OAAO,QAAQ;AAC3B,OAAK,YAAY,iBAAiB,YAAY,KAAK,MAAM,OAAO,UAAU;AAC1E,OAAK,gBAAgB,IAAI,eAAe;AACxC,OAAK,QAAQ,aAAa,iBAAiB;AAC3C,OAAK,MAAM,IAAI,YAAY;AAC3B,OAAK,gBAAgB,cAAc,aAAa;AAEhD,OAAK,UAAU;;CAGjB,aAAa,GAAmB;CAIhC,MAAM,QAAQ;CAEd,eAAkC;AAChC,SAAO,KAAK;;CAGd,wBAA8B;AAC5B,OAAK,cAAc,UAAU;;;;;;;CAQ/B,UAAmB;AACjB,SAAO,EAAE;;;;;;;;;;;CAYX,OAAO,KAA4B;EACjC,MAAM,QAAQ,IAAI,QAAQ;EAC1B,MAAM,SAAS,IAAI,QAAQ;EAC3B,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAIvC,MAAI,CAAC,SAAS,OAAO;AACnB,UAAO,KACL,mFACD;AAED,UAAO;;AAGT,MAAI,CAAC,MACH,OAAM,oBAAoB,aAAa,aAAa;AAGtD,MAAI,CAAC,UAAU,CAAC,MACd,OAAM,oBAAoB,eAAe;EAG3C,MAAM,kBAAkB,UAAU;EAElC,MAAM,cAAc,eAAe,kBACjC,OACA,gBACD;AAGD,SAAO,KAAK,wBAAwB,YAAY;;;;;;;CAQlD,AAAQ,wBAAwB,aAAgC;AAC9D,SAAO,IAAI,MAAM,MAAM,EACrB,MAAM,QAAQ,MAAM,aAAa;GAC/B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS;AAEjD,OAAI,OAAO,UAAU,WACnB,QAAO;AAGT,OAAI,OAAO,SAAS,YAAY,oBAAoB,IAAI,KAAK,CAC3D,QAAO;AAGT,WAAQ,GAAG,SAAoB;AAC7B,WAAO,iBAAiB,mBAAmB,MAAM,MAAM,QAAQ,KAAK,CAAC;;KAG1E,CAAC;;CAIJ,MAAgB,cACd,KACA,IACA,SACA,SACA;EAEA,MAAM,EACJ,QAAQ,cACR,SAAS,eACT,MAAM,eACJ;EAGJ,MAAM,gBAAgB,KAAK,sBAAsB;GAC/C,SAAS;GACT,MAAM;GACP,CAAC;EAGF,MAAM,mBAAmB,WAAW,kBAAkB;EAEtD,MAAM,OAAO;EAGb,MAAM,iBAAiB,iBAAiB,cAA4B;GAElE,MAAM,UAA8B;IAClC,QAAQ;IACR,0BAAU,IAAI,KAAK;IACnB,SAAS;IACV;GAGD,MAAM,eAAe,KAAK,mBAAmB,cAAc;GAG3D,MAAM,YAAY,YAAY;AAE5B,WADe,MAAM,GAAG,QAAQ,OAAO;;GAKzC,MAAM,SAAS,MAAM,KAAK,yBACxB,WACA,cACA,QACD;AAGD,OAAI,KAAK,kBAAkB,OAAO,CAChC,QAAO;OAEP,OAAM;;AAKV,QAAM,KAAK,cAAc,OAAO,KAAK,gBAAgB,aAAa;;CAIpE,MAAgB,QACd,IACA,SACA,SACwB;EACxB,MAAM,gBAAgB,KAAK,sBAAsB,QAAQ;EAEzD,MAAM,eAAe,KAAK,mBAAmB,cAAc;EAG3D,MAAM,mBAAmB,WAAW,kBAAkB;EAEtD,MAAM,UAA8B;GAClC,0BAAU,IAAI,KAAK;GACnB,SAAS;GACV;AAED,MAAI;AACF,UAAO,MAAM,KAAK,yBAAyB,IAAI,cAAc,QAAQ;WAC9D,QAAQ;AAEf;;;CAIJ,AAAU,iBAAiB,MAAc,MAAoB;AAC3D,OAAK,oBAAoB,QAAQ;;CAGnC,AAAU,MACR,QACA,QACM;EACN,MAAM,EAAE,MAAM,QAAQ,MAAM,YAAY;AAExC,SAAO,QAAQ,MAAM,QAAQ;AAE7B,OAAK,iBAAiB,MAAM,QAAQ,KAAK,OAAO,OAAO;;CAIzD,AAAQ,sBACN,SACqB;EACrB,MAAM,EAAE,SAAS,gBAAgB,MAAM,iBAAiB;AAGxD,SAAO,UACL,UAAU,gBAAgB,KAAK,OAAO,EACtC,gBAAgB,EAAE,CACnB;;CAIH,AAAQ,mBACN,SACwB;EACxB,MAAM,eAAuC,EAAE;AAK/C,MADwB,0BAA0B,KAAK,OAAO,UAAU,CAEtD,WACf,QAAQ,sBAAsB,WAAW,MAE1C,cAAa,KACX,IAAI,qBAAqB,KAAK,WAAW,QAAQ,qBAAqB,CACvE;AAGH,MAAI,QAAQ,WAAW,QAAQ,UAAU,EACvC,cAAa,KAAK,IAAI,mBAAmB,QAAQ,QAAQ,CAAC;AAG5D,MACE,QAAQ,OAAO,WACf,QAAQ,MAAM,YACd,QAAQ,MAAM,WAAW,EAEzB,cAAa,KAAK,IAAI,iBAAiB,QAAQ,MAAM,CAAC;AAGxD,MAAI,QAAQ,OAAO,WAAW,QAAQ,MAAM,UAAU,OACpD,cAAa,KAAK,IAAI,iBAAiB,KAAK,OAAO,QAAQ,MAAM,CAAC;AAGpE,SAAO;;CAIT,MAAc,yBACZ,IACA,cACA,SACY;AAEZ,MAAI,aAAa,WAAW,EAC1B,QAAO,GAAG,QAAQ,OAAO;EAG3B,IAAI,kBAAkB,GAAG,QAAQ,OAAO;AAGxC,OAAK,MAAM,eAAe,cAAc;GACtC,MAAM,aAAa;AACnB,qBAAkB,YAAY,UAAU,YAAY,QAAQ;;AAG9D,SAAO,WAAW;;CAGpB,AAAQ,kBACN,QAC8C;AAC9C,SACE,UAAU,OAAO,WAAW,YAAY,OAAO,iBAAiB"}
@@ -2,13 +2,15 @@ import { IAppRouter, ToPlugin } from "../../shared/src/plugin.js";
2
2
  import { SQLTypeMarker } from "../../shared/src/sql/types.js";
3
3
  import { Plugin } from "../../plugin/plugin.js";
4
4
  import { IAnalyticsConfig } from "./types.js";
5
+ import { PluginManifest } from "../../registry/types.js";
5
6
  import { WorkspaceClient } from "@databricks/sdk-experimental";
6
7
  import express from "express";
7
8
 
8
9
  //#region src/plugins/analytics/analytics.d.ts
9
10
  declare class AnalyticsPlugin extends Plugin {
10
11
  name: string;
11
- protected envVars: string[];
12
+ /** Plugin manifest declaring metadata and resource requirements */
13
+ static manifest: PluginManifest;
12
14
  protected static description: string;
13
15
  protected config: IAnalyticsConfig;
14
16
  private SQLClient;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0Ba,eAAA,SAAwB,MAAA;;;EAAxB,iBAAA,WAAgB,EAAA,MAAA;EAAA,UAAA,MAAA,EAKD,gBALC;UAKD,SAAA;UAMN,cAAA;aAWC,CAAA,MAAA,EAXD,gBAWC;cA0BN,CAAA,MAAA,EA1BM,UA0BN,CAAA,EAAA,IAAA;;;;;mBA2CZ,CAAA,GAAA,EA3CI,OAAA,CAAQ,OA2CZ,EAAA,GAAA,EA1CI,OAAA,CAAQ,QA0CZ,CAAA,EAzCA,OAyCA,CAAA,IAAA,CAAA;;;;;mBAkHA,CAAA,GAAA,EApHI,OAAA,CAAQ,OAoHZ,EAAA,GAAA,EAnHI,OAAA,CAAQ,QAmHZ,CAAA,EAlHA,OAkHA,CAAA,IAAA,CAAA;;;;;;;;;;;;;AAqDL;;;OAAsB,CAAA,KAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAxDL,MAwDK,CAAA,MAAA,EAxDU,aAwDV,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA,gBAAA,CAAA,EAvDC,MAuDD,CAAA,MAAA,EAAA,GAAA,CAAA,EAAA,MAAA,CAAA,EAtDT,WAsDS,CAAA,EArDjB,OAqDiB,CAAA,GAAA,CAAA;;;;0CA5BD,yCAER,cACR,QAAQ;cAIO;;;;;;;;;wCAnCH,eAAe,sDACT,8BACV,gBACR;;;;;;cAqDQ,WAAS,gBAAA,iBAAA"}
1
+ {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cA2Ba,eAAA,SAAwB,MAAA;;;mBAAR;EAAhB,iBAAA,WAAgB,EAAA,MAAA;EAAA,UAAA,MAAA,EAOD,gBAPC;UAAA,SAAA;UAOD,cAAA;aAMN,CAAA,MAAA,EAAA,gBAAA;cAWC,CAAA,MAAA,EAAA,UAAA,CAAA,EAAA,IAAA;;;;;mBAoEN,CAAA,GAAA,EA1CR,OAAA,CAAQ,OA0CA,EAAA,GAAA,EAzCR,OAAA,CAAQ,QAyCA,CAAA,EAxCZ,OAwCY,CAAA,IAAA,CAAA;;;;;mBAkHJ,CAAA,GAAA,EAnHJ,OAAA,CAAQ,OAmHJ,EAAA,GAAA,EAlHJ,OAAA,CAAQ,QAkHJ,CAAA,EAjHR,OAiHQ,CAAA,IAAA,CAAA;;;;;;;;;;;;;;AAsDb;;OAAsB,CAAA,KAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAxDL,MAwDK,CAAA,MAAA,EAxDU,aAwDV,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA,gBAAA,CAAA,EAvDC,MAuDD,CAAA,MAAA,EAAA,GAAA,CAAA,EAAA,MAAA,CAAA,EAtDT,WAsDS,CAAA,EArDjB,OAqDiB,CAAA,GAAA,CAAA;;;;0CA5BD,yCAER,cACR,QAAQ;cAIO;;;;;;;;;wCAnCH,eAAe,sDACT,8BACV,gBACR;;;;;;cAqDQ,WAAS,gBAAA,iBAAA"}
@@ -1,12 +1,13 @@
1
1
  import { createLogger } from "../../logging/logger.js";
2
2
  import { getCurrentUserId, getWarehouseId, getWorkspaceClient } from "../../context/execution-context.js";
3
3
  import { init_context } from "../../context/index.js";
4
- import { SQLWarehouseConnector } from "../../connectors/sql-warehouse/client.js";
5
- import "../../connectors/index.js";
6
4
  import { Plugin } from "../../plugin/plugin.js";
7
5
  import { toPlugin } from "../../plugin/to-plugin.js";
8
6
  import "../../plugin/index.js";
7
+ import { SQLWarehouseConnector } from "../../connectors/sql-warehouse/client.js";
8
+ import "../../connectors/index.js";
9
9
  import { queryDefaults } from "./defaults.js";
10
+ import { analyticsManifest } from "./manifest.js";
10
11
  import { QueryProcessor } from "./query.js";
11
12
 
12
13
  //#region src/plugins/analytics/analytics.ts
@@ -14,7 +15,8 @@ init_context();
14
15
  const logger = createLogger("analytics");
15
16
  var AnalyticsPlugin = class extends Plugin {
16
17
  name = "analytics";
17
- envVars = [];
18
+ /** Plugin manifest declaring metadata and resource requirements */
19
+ static manifest = analyticsManifest;
18
20
  static description = "Analytics plugin for data analysis";
19
21
  SQLClient;
20
22
  queryProcessor;