@databricks/appkit-ui 0.8.0 → 0.10.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 (237) hide show
  1. package/CLAUDE.md +3 -1
  2. package/NOTICE.md +1 -0
  3. package/dist/cli/commands/plugin/add-resource/add-resource.js +61 -0
  4. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -0
  5. package/dist/cli/commands/plugin/create/create.js +162 -0
  6. package/dist/cli/commands/plugin/create/create.js.map +1 -0
  7. package/dist/cli/commands/plugin/create/prompt-resource.js +95 -0
  8. package/dist/cli/commands/plugin/create/prompt-resource.js.map +1 -0
  9. package/dist/cli/commands/plugin/create/resource-defaults.js +105 -0
  10. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -0
  11. package/dist/cli/commands/plugin/create/scaffold.js +195 -0
  12. package/dist/cli/commands/plugin/create/scaffold.js.map +1 -0
  13. package/dist/cli/commands/plugin/index.js +22 -0
  14. package/dist/cli/commands/plugin/index.js.map +1 -0
  15. package/dist/cli/commands/plugin/list/list.js +113 -0
  16. package/dist/cli/commands/plugin/list/list.js.map +1 -0
  17. package/dist/cli/commands/plugin/schema-resources.js +82 -0
  18. package/dist/cli/commands/plugin/schema-resources.js.map +1 -0
  19. package/dist/cli/commands/{plugins-sync.js → plugin/sync/sync.js} +98 -79
  20. package/dist/cli/commands/plugin/sync/sync.js.map +1 -0
  21. package/dist/cli/commands/plugin/validate/validate-manifest.js +216 -0
  22. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -0
  23. package/dist/cli/commands/plugin/validate/validate.js +67 -0
  24. package/dist/cli/commands/plugin/validate/validate.js.map +1 -0
  25. package/dist/cli/index.js +2 -2
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/react/charts/area/index.d.ts +2 -2
  28. package/dist/react/charts/bar/index.d.ts +2 -2
  29. package/dist/react/charts/base.d.ts +2 -2
  30. package/dist/react/charts/base.d.ts.map +1 -1
  31. package/dist/react/charts/create-chart.d.ts +2 -2
  32. package/dist/react/charts/create-chart.d.ts.map +1 -1
  33. package/dist/react/charts/heatmap/index.d.ts +2 -2
  34. package/dist/react/charts/line/index.d.ts +2 -2
  35. package/dist/react/charts/options.d.ts.map +1 -1
  36. package/dist/react/charts/pie/index.d.ts +3 -3
  37. package/dist/react/charts/radar/index.d.ts +2 -2
  38. package/dist/react/charts/scatter/index.d.ts +2 -2
  39. package/dist/react/charts/wrapper.d.ts +2 -2
  40. package/dist/react/charts/wrapper.d.ts.map +1 -1
  41. package/dist/react/table/data-table.d.ts +2 -2
  42. package/dist/react/table/data-table.d.ts.map +1 -1
  43. package/dist/react/ui/accordion.d.ts +5 -5
  44. package/dist/react/ui/accordion.d.ts.map +1 -1
  45. package/dist/react/ui/alert-dialog.d.ts +12 -12
  46. package/dist/react/ui/alert.d.ts +4 -4
  47. package/dist/react/ui/alert.d.ts.map +1 -1
  48. package/dist/react/ui/aspect-ratio.d.ts +2 -2
  49. package/dist/react/ui/avatar.d.ts +4 -4
  50. package/dist/react/ui/badge.d.ts +2 -2
  51. package/dist/react/ui/breadcrumb.d.ts +8 -8
  52. package/dist/react/ui/button-group.d.ts +4 -4
  53. package/dist/react/ui/button.d.ts +2 -2
  54. package/dist/react/ui/calendar.d.ts +3 -3
  55. package/dist/react/ui/card.d.ts +8 -8
  56. package/dist/react/ui/carousel.d.ts +6 -6
  57. package/dist/react/ui/chart.d.ts +5 -5
  58. package/dist/react/ui/checkbox.d.ts +2 -2
  59. package/dist/react/ui/collapsible.d.ts +4 -4
  60. package/dist/react/ui/command.d.ts +10 -10
  61. package/dist/react/ui/context-menu.d.ts +16 -16
  62. package/dist/react/ui/dialog.d.ts +11 -11
  63. package/dist/react/ui/drawer.d.ts +11 -11
  64. package/dist/react/ui/drawer.d.ts.map +1 -1
  65. package/dist/react/ui/dropdown-menu.d.ts +16 -16
  66. package/dist/react/ui/empty.d.ts +7 -7
  67. package/dist/react/ui/field.d.ts +11 -11
  68. package/dist/react/ui/form.d.ts +7 -7
  69. package/dist/react/ui/hover-card.d.ts +4 -4
  70. package/dist/react/ui/input-group.d.ts +7 -7
  71. package/dist/react/ui/input-otp.d.ts +5 -5
  72. package/dist/react/ui/input.d.ts +2 -2
  73. package/dist/react/ui/item.d.ts +11 -11
  74. package/dist/react/ui/kbd.d.ts +3 -3
  75. package/dist/react/ui/label.d.ts +2 -2
  76. package/dist/react/ui/menubar.d.ts +17 -17
  77. package/dist/react/ui/navigation-menu.d.ts +9 -9
  78. package/dist/react/ui/pagination.d.ts +8 -8
  79. package/dist/react/ui/popover.d.ts +5 -5
  80. package/dist/react/ui/progress.d.ts +2 -2
  81. package/dist/react/ui/radio-group.d.ts +3 -3
  82. package/dist/react/ui/resizable.d.ts +4 -4
  83. package/dist/react/ui/scroll-area.d.ts +3 -3
  84. package/dist/react/ui/select.d.ts +11 -11
  85. package/dist/react/ui/separator.d.ts +2 -2
  86. package/dist/react/ui/sheet.d.ts +9 -9
  87. package/dist/react/ui/sidebar.d.ts +24 -24
  88. package/dist/react/ui/skeleton.d.ts +2 -2
  89. package/dist/react/ui/slider.d.ts +2 -2
  90. package/dist/react/ui/sonner.d.ts +2 -2
  91. package/dist/react/ui/spinner.d.ts +2 -2
  92. package/dist/react/ui/switch.d.ts +2 -2
  93. package/dist/react/ui/table.d.ts +9 -9
  94. package/dist/react/ui/tabs.d.ts +5 -5
  95. package/dist/react/ui/textarea.d.ts +2 -2
  96. package/dist/react/ui/toggle-group.d.ts +3 -3
  97. package/dist/react/ui/toggle.d.ts +2 -2
  98. package/dist/react/ui/tooltip.d.ts +5 -5
  99. package/dist/react/ui/tooltip.d.ts.map +1 -1
  100. package/dist/schemas/plugin-manifest.schema.json +439 -0
  101. package/dist/schemas/template-plugins.schema.json +103 -0
  102. package/docs/docs/api/appkit/Class.AppKitError/index.html +3 -3
  103. package/docs/docs/api/appkit/Class.AuthenticationError/index.html +3 -3
  104. package/docs/docs/api/appkit/Class.ConfigurationError/index.html +3 -3
  105. package/docs/docs/api/appkit/Class.ConnectionError/index.html +3 -3
  106. package/docs/docs/api/appkit/Class.ExecutionError/index.html +3 -3
  107. package/docs/docs/api/appkit/Class.InitializationError/index.html +3 -3
  108. package/docs/docs/api/appkit/Class.Plugin/index.html +3 -3
  109. package/docs/docs/api/appkit/Class.ResourceRegistry/index.html +3 -3
  110. package/docs/docs/api/appkit/Class.ServerError/index.html +3 -3
  111. package/docs/docs/api/appkit/Class.TunnelError/index.html +3 -3
  112. package/docs/docs/api/appkit/Class.ValidationError/index.html +3 -3
  113. package/docs/docs/api/appkit/Enumeration.RequestedClaimsPermissionSet/index.html +3 -3
  114. package/docs/docs/api/appkit/Enumeration.ResourceType/index.html +6 -19
  115. package/docs/docs/api/appkit/Enumeration.ResourceType.md +1 -25
  116. package/docs/docs/api/appkit/Function.appKitTypesPlugin/index.html +3 -3
  117. package/docs/docs/api/appkit/Function.createApp/index.html +3 -3
  118. package/docs/docs/api/appkit/Function.createLakebasePool/index.html +3 -3
  119. package/docs/docs/api/appkit/Function.generateDatabaseCredential/index.html +3 -3
  120. package/docs/docs/api/appkit/Function.getExecutionContext/index.html +3 -3
  121. package/docs/docs/api/appkit/Function.getLakebaseOrmConfig/index.html +3 -3
  122. package/docs/docs/api/appkit/Function.getLakebasePgConfig/index.html +3 -3
  123. package/docs/docs/api/appkit/Function.getPluginManifest/index.html +3 -3
  124. package/docs/docs/api/appkit/Function.getResourceRequirements/index.html +4 -4
  125. package/docs/docs/api/appkit/Function.getUsernameWithApiLookup/index.html +35 -0
  126. package/docs/docs/api/appkit/Function.getUsernameWithApiLookup.md +29 -0
  127. package/docs/docs/api/appkit/Function.getWorkspaceClient/index.html +5 -5
  128. package/docs/docs/api/appkit/Function.getWorkspaceClient.md +2 -2
  129. package/docs/docs/api/appkit/Function.isSQLTypeMarker/index.html +3 -3
  130. package/docs/docs/api/appkit/Interface.BasePluginConfig/index.html +3 -3
  131. package/docs/docs/api/appkit/Interface.CacheConfig/index.html +3 -3
  132. package/docs/docs/api/appkit/Interface.DatabaseCredential/index.html +3 -3
  133. package/docs/docs/api/appkit/Interface.GenerateDatabaseCredentialRequest/index.html +3 -3
  134. package/docs/docs/api/appkit/Interface.ITelemetry/index.html +3 -3
  135. package/docs/docs/api/appkit/Interface.LakebasePoolConfig/index.html +3 -3
  136. package/docs/docs/api/appkit/Interface.PluginManifest/index.html +3 -3
  137. package/docs/docs/api/appkit/Interface.RequestedClaims/index.html +3 -3
  138. package/docs/docs/api/appkit/Interface.RequestedResource/index.html +3 -3
  139. package/docs/docs/api/appkit/Interface.ResourceEntry/index.html +3 -3
  140. package/docs/docs/api/appkit/Interface.ResourceFieldEntry/index.html +3 -3
  141. package/docs/docs/api/appkit/Interface.ResourceRequirement/index.html +3 -3
  142. package/docs/docs/api/appkit/Interface.StreamExecutionSettings/index.html +3 -3
  143. package/docs/docs/api/appkit/Interface.TelemetryConfig/index.html +3 -3
  144. package/docs/docs/api/appkit/Interface.ValidationResult/index.html +3 -3
  145. package/docs/docs/api/appkit/TypeAlias.ConfigSchema/index.html +3 -3
  146. package/docs/docs/api/appkit/TypeAlias.IAppRouter/index.html +3 -3
  147. package/docs/docs/api/appkit/TypeAlias.ResourcePermission/index.html +4 -4
  148. package/docs/docs/api/appkit/TypeAlias.ToPlugin/index.html +23 -0
  149. package/docs/docs/api/appkit/TypeAlias.ToPlugin.md +24 -0
  150. package/docs/docs/api/appkit/Variable.sql/index.html +4 -4
  151. package/docs/docs/api/appkit/index.html +6 -6
  152. package/docs/docs/api/appkit-ui/data/AreaChart/index.html +2 -2
  153. package/docs/docs/api/appkit-ui/data/BarChart/index.html +2 -2
  154. package/docs/docs/api/appkit-ui/data/DataTable/index.html +2 -2
  155. package/docs/docs/api/appkit-ui/data/DonutChart/index.html +2 -2
  156. package/docs/docs/api/appkit-ui/data/HeatmapChart/index.html +2 -2
  157. package/docs/docs/api/appkit-ui/data/LineChart/index.html +2 -2
  158. package/docs/docs/api/appkit-ui/data/PieChart/index.html +2 -2
  159. package/docs/docs/api/appkit-ui/data/RadarChart/index.html +2 -2
  160. package/docs/docs/api/appkit-ui/data/ScatterChart/index.html +2 -2
  161. package/docs/docs/api/appkit-ui/index.html +2 -2
  162. package/docs/docs/api/appkit-ui/styling/index.html +2 -2
  163. package/docs/docs/api/appkit-ui/ui/Accordion/index.html +2 -2
  164. package/docs/docs/api/appkit-ui/ui/Alert/index.html +2 -2
  165. package/docs/docs/api/appkit-ui/ui/AlertDialog/index.html +2 -2
  166. package/docs/docs/api/appkit-ui/ui/AspectRatio/index.html +2 -2
  167. package/docs/docs/api/appkit-ui/ui/Avatar/index.html +2 -2
  168. package/docs/docs/api/appkit-ui/ui/Badge/index.html +2 -2
  169. package/docs/docs/api/appkit-ui/ui/Breadcrumb/index.html +2 -2
  170. package/docs/docs/api/appkit-ui/ui/Button/index.html +2 -2
  171. package/docs/docs/api/appkit-ui/ui/ButtonGroup/index.html +2 -2
  172. package/docs/docs/api/appkit-ui/ui/Calendar/index.html +2 -2
  173. package/docs/docs/api/appkit-ui/ui/Card/index.html +2 -2
  174. package/docs/docs/api/appkit-ui/ui/Carousel/index.html +2 -2
  175. package/docs/docs/api/appkit-ui/ui/ChartContainer/index.html +2 -2
  176. package/docs/docs/api/appkit-ui/ui/Checkbox/index.html +2 -2
  177. package/docs/docs/api/appkit-ui/ui/Collapsible/index.html +2 -2
  178. package/docs/docs/api/appkit-ui/ui/Command/index.html +2 -2
  179. package/docs/docs/api/appkit-ui/ui/ContextMenu/index.html +2 -2
  180. package/docs/docs/api/appkit-ui/ui/Dialog/index.html +2 -2
  181. package/docs/docs/api/appkit-ui/ui/Drawer/index.html +2 -2
  182. package/docs/docs/api/appkit-ui/ui/DropdownMenu/index.html +2 -2
  183. package/docs/docs/api/appkit-ui/ui/Empty/index.html +2 -2
  184. package/docs/docs/api/appkit-ui/ui/Field/index.html +2 -2
  185. package/docs/docs/api/appkit-ui/ui/FormControl/index.html +2 -2
  186. package/docs/docs/api/appkit-ui/ui/HoverCard/index.html +2 -2
  187. package/docs/docs/api/appkit-ui/ui/Input/index.html +2 -2
  188. package/docs/docs/api/appkit-ui/ui/InputGroup/index.html +2 -2
  189. package/docs/docs/api/appkit-ui/ui/InputOTP/index.html +2 -2
  190. package/docs/docs/api/appkit-ui/ui/Item/index.html +2 -2
  191. package/docs/docs/api/appkit-ui/ui/Kbd/index.html +2 -2
  192. package/docs/docs/api/appkit-ui/ui/Label/index.html +2 -2
  193. package/docs/docs/api/appkit-ui/ui/Menubar/index.html +2 -2
  194. package/docs/docs/api/appkit-ui/ui/NavigationMenu/index.html +2 -2
  195. package/docs/docs/api/appkit-ui/ui/Pagination/index.html +2 -2
  196. package/docs/docs/api/appkit-ui/ui/Popover/index.html +2 -2
  197. package/docs/docs/api/appkit-ui/ui/Progress/index.html +2 -2
  198. package/docs/docs/api/appkit-ui/ui/RadioGroup/index.html +2 -2
  199. package/docs/docs/api/appkit-ui/ui/ResizableHandle/index.html +2 -2
  200. package/docs/docs/api/appkit-ui/ui/ScrollArea/index.html +2 -2
  201. package/docs/docs/api/appkit-ui/ui/Select/index.html +2 -2
  202. package/docs/docs/api/appkit-ui/ui/Separator/index.html +2 -2
  203. package/docs/docs/api/appkit-ui/ui/Sheet/index.html +2 -2
  204. package/docs/docs/api/appkit-ui/ui/Sidebar/index.html +2 -2
  205. package/docs/docs/api/appkit-ui/ui/Skeleton/index.html +2 -2
  206. package/docs/docs/api/appkit-ui/ui/Slider/index.html +2 -2
  207. package/docs/docs/api/appkit-ui/ui/Spinner/index.html +2 -2
  208. package/docs/docs/api/appkit-ui/ui/Switch/index.html +2 -2
  209. package/docs/docs/api/appkit-ui/ui/Table/index.html +2 -2
  210. package/docs/docs/api/appkit-ui/ui/Tabs/index.html +2 -2
  211. package/docs/docs/api/appkit-ui/ui/Textarea/index.html +2 -2
  212. package/docs/docs/api/appkit-ui/ui/Toaster/index.html +2 -2
  213. package/docs/docs/api/appkit-ui/ui/Toggle/index.html +2 -2
  214. package/docs/docs/api/appkit-ui/ui/ToggleGroup/index.html +2 -2
  215. package/docs/docs/api/appkit-ui/ui/Tooltip/index.html +2 -2
  216. package/docs/docs/api/appkit.md +6 -4
  217. package/docs/docs/api/index.html +2 -2
  218. package/docs/docs/app-management/index.html +2 -2
  219. package/docs/docs/architecture/index.html +2 -2
  220. package/docs/docs/category/development/index.html +2 -2
  221. package/docs/docs/configuration/index.html +2 -2
  222. package/docs/docs/core-principles/index.html +2 -2
  223. package/docs/docs/development/ai-assisted-development/index.html +2 -2
  224. package/docs/docs/development/index.html +2 -2
  225. package/docs/docs/development/llm-guide/index.html +2 -2
  226. package/docs/docs/development/local-development/index.html +2 -2
  227. package/docs/docs/development/project-setup/index.html +2 -2
  228. package/docs/docs/development/remote-bridge/index.html +2 -2
  229. package/docs/docs/development/type-generation/index.html +2 -2
  230. package/docs/docs/index.html +2 -2
  231. package/docs/docs/plugins/index.html +35 -4
  232. package/docs/docs/plugins.md +97 -1
  233. package/llms.txt +3 -1
  234. package/package.json +2 -1
  235. package/dist/cli/commands/plugins-sync.js.map +0 -1
  236. package/dist/cli/commands/plugins.js +0 -19
  237. package/dist/cli/commands/plugins.js.map +0 -1
package/CLAUDE.md CHANGED
@@ -38,7 +38,7 @@ The CLI will display the documentation content directly in the terminal.
38
38
  - [Class: TunnelError](./docs/docs/api./docs/Class.TunnelError.md): Error thrown when remote tunnel operations fail.
39
39
  - [Class: ValidationError](./docs/docs/api./docs/Class.ValidationError.md): Error thrown when input validation fails.
40
40
  - [Enumeration: RequestedClaimsPermissionSet](./docs/docs/api./docs/Enumeration.RequestedClaimsPermissionSet.md): Permission set for Unity Catalog table access
41
- - [Enumeration: ResourceType](./docs/docs/api./docs/Enumeration.ResourceType.md): Supported resource types that plugins can depend on.
41
+ - [Enumeration: ResourceType](./docs/docs/api./docs/Enumeration.ResourceType.md): Resource types from schema $defs.resourceType.enum
42
42
  - [Function: appKitTypesPlugin()](./docs/docs/api./docs/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
43
43
  - [Function: createApp()](./docs/docs/api./docs/Function.createApp.md): Bootstraps AppKit with the provided configuration.
44
44
  - [Function: createLakebasePool()](./docs/docs/api./docs/Function.createLakebasePool.md): Create a Lakebase pool with appkit's logger integration.
@@ -48,6 +48,7 @@ The CLI will display the documentation content directly in the terminal.
48
48
  - [Function: getLakebasePgConfig()](./docs/docs/api./docs/Function.getLakebasePgConfig.md): Get Lakebase connection configuration for PostgreSQL clients.
49
49
  - [Function: getPluginManifest()](./docs/docs/api./docs/Function.getPluginManifest.md): Loads and validates the manifest from a plugin constructor.
50
50
  - [Function: getResourceRequirements()](./docs/docs/api./docs/Function.getResourceRequirements.md): Gets the resource requirements from a plugin's manifest.
51
+ - [Function: getUsernameWithApiLookup()](./docs/docs/api./docs/Function.getUsernameWithApiLookup.md): Resolves the PostgreSQL username for a Lakebase connection.
51
52
  - [Function: getWorkspaceClient()](./docs/docs/api./docs/Function.getWorkspaceClient.md): Get workspace client from config or SDK default auth chain
52
53
  - [Function: isSQLTypeMarker()](./docs/docs/api./docs/Function.isSQLTypeMarker.md): Type guard to check if a value is a SQL type marker
53
54
  - [Interface: BasePluginConfig](./docs/docs/api./docs/Interface.BasePluginConfig.md): Base configuration interface for AppKit plugins
@@ -68,6 +69,7 @@ The CLI will display the documentation content directly in the terminal.
68
69
  - [Type Alias: ConfigSchema](./docs/docs/api./docs/TypeAlias.ConfigSchema.md): Configuration schema definition for plugin config.
69
70
  - [Type Alias: IAppRouter](./docs/docs/api./docs/TypeAlias.IAppRouter.md): Express router type for plugin route registration
70
71
  - [Type Alias: ResourcePermission](./docs/docs/api./docs/TypeAlias.ResourcePermission.md): Union of all possible permission levels across all resource types.
72
+ - [Type Alias: ToPlugin()<T, U, N>](./docs/docs/api./docs/TypeAlias.ToPlugin.md): Type Parameters
71
73
  - [Variable: sql](./docs/docs/api./docs/Variable.sql.md): SQL helper namespace
72
74
  - [@databricks/appkit-ui](./docs/docs/api/appkit-ui.md): The library provides a set of UI primitives for building Databricks apps in React.
73
75
  - [AreaChart](./docs/docs/api/appkit-ui/data/AreaChart.md): Area Chart component for trend visualization with filled areas.
package/NOTICE.md CHANGED
@@ -7,6 +7,7 @@ This Software contains code from the following open source projects:
7
7
  | Name | Installed version | License | Code |
8
8
  | :--------------- | :---------------- | :----------- | :--------------------------------------------------- |
9
9
  | [@ast-grep/napi](https://www.npmjs.com/package/@ast-grep/napi) | 0.37.0 | MIT | https://ast-grep.github.io |
10
+ | [@clack/prompts](https://www.npmjs.com/package/@clack/prompts) | 1.0.1 | MIT | https://github.com/bombshell-dev/clack/tree/main/packages/prompts#readme |
10
11
  | [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers) | 5.2.2 | MIT | https://react-hook-form.com |
11
12
  | [@opentelemetry/api](https://www.npmjs.com/package/@opentelemetry/api) | 1.9.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/api |
12
13
  | [@opentelemetry/api-logs](https://www.npmjs.com/package/@opentelemetry/api-logs) | 0.208.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/api-logs |
@@ -0,0 +1,61 @@
1
+ import { humanizeResourceType } from "../create/resource-defaults.js";
2
+ import { promptOneResource } from "../create/prompt-resource.js";
3
+ import { validateManifest } from "../validate/validate-manifest.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { Command } from "commander";
7
+ import process from "node:process";
8
+ import { cancel, intro, outro } from "@clack/prompts";
9
+
10
+ //#region src/cli/commands/plugin/add-resource/add-resource.ts
11
+ async function runPluginAddResource(options) {
12
+ intro("Add resource to plugin manifest");
13
+ const cwd = process.cwd();
14
+ const pluginDir = path.resolve(cwd, options.path ?? ".");
15
+ const manifestPath = path.join(pluginDir, "manifest.json");
16
+ if (!fs.existsSync(manifestPath)) {
17
+ console.error(`manifest.json not found at ${manifestPath}`);
18
+ process.exit(1);
19
+ }
20
+ let manifest;
21
+ try {
22
+ const raw = fs.readFileSync(manifestPath, "utf-8");
23
+ const parsed = JSON.parse(raw);
24
+ const result = validateManifest(parsed);
25
+ if (!result.valid || !result.manifest) {
26
+ console.error("Invalid manifest. Run `appkit plugin validate` for details.");
27
+ process.exit(1);
28
+ }
29
+ manifest = parsed;
30
+ } catch (err) {
31
+ console.error("Failed to read or parse manifest.json:", err instanceof Error ? err.message : err);
32
+ process.exit(1);
33
+ }
34
+ const spec = await promptOneResource();
35
+ if (!spec) {
36
+ cancel("Cancelled.");
37
+ process.exit(0);
38
+ }
39
+ const alias = humanizeResourceType(spec.type);
40
+ const entry = {
41
+ type: spec.type,
42
+ alias,
43
+ resourceKey: spec.resourceKey,
44
+ description: spec.description || `Required for ${alias} functionality.`,
45
+ permission: spec.permission,
46
+ fields: spec.fields
47
+ };
48
+ if (spec.required) manifest.resources.required.push(entry);
49
+ else manifest.resources.optional.push(entry);
50
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
51
+ outro("Resource added.");
52
+ console.log(`\nAdded ${alias} as ${spec.required ? "required" : "optional"} to ${path.relative(cwd, manifestPath)}`);
53
+ }
54
+ const pluginAddResourceCommand = new Command("add-resource").description("Add a resource requirement to an existing plugin manifest (interactive). Overwrites manifest.json in place.").option("-p, --path <dir>", "Plugin directory containing manifest.json (default: .)").action((opts) => runPluginAddResource(opts).catch((err) => {
55
+ console.error(err);
56
+ process.exit(1);
57
+ }));
58
+
59
+ //#endregion
60
+ export { pluginAddResourceCommand };
61
+ //# sourceMappingURL=add-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-resource.js","names":[],"sources":["../../../../../src/cli/commands/plugin/add-resource/add-resource.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { cancel, intro, outro } from \"@clack/prompts\";\nimport { Command } from \"commander\";\nimport { promptOneResource } from \"../create/prompt-resource\";\nimport { humanizeResourceType } from \"../create/resource-defaults\";\nimport type { PluginManifest } from \"../manifest-types\";\nimport { validateManifest } from \"../validate/validate-manifest\";\n\n/** Extended manifest type that preserves extra JSON fields (e.g. $schema, author, version) for round-trip writes. */\ninterface ManifestWithExtras extends PluginManifest {\n [key: string]: unknown;\n}\n\nasync function runPluginAddResource(options: { path?: string }): Promise<void> {\n intro(\"Add resource to plugin manifest\");\n\n const cwd = process.cwd();\n const pluginDir = path.resolve(cwd, options.path ?? \".\");\n const manifestPath = path.join(pluginDir, \"manifest.json\");\n\n if (!fs.existsSync(manifestPath)) {\n console.error(`manifest.json not found at ${manifestPath}`);\n process.exit(1);\n }\n\n let manifest: ManifestWithExtras;\n try {\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const parsed = JSON.parse(raw) as unknown;\n const result = validateManifest(parsed);\n if (!result.valid || !result.manifest) {\n console.error(\n \"Invalid manifest. Run `appkit plugin validate` for details.\",\n );\n process.exit(1);\n }\n manifest = parsed as ManifestWithExtras;\n } catch (err) {\n console.error(\n \"Failed to read or parse manifest.json:\",\n err instanceof Error ? err.message : err,\n );\n process.exit(1);\n }\n\n const spec = await promptOneResource();\n if (!spec) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const alias = humanizeResourceType(spec.type);\n const entry = {\n type: spec.type,\n alias,\n resourceKey: spec.resourceKey,\n description: spec.description || `Required for ${alias} functionality.`,\n permission: spec.permission,\n fields: spec.fields,\n };\n\n if (spec.required) {\n manifest.resources.required.push(entry);\n } else {\n manifest.resources.optional.push(entry);\n }\n\n fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`);\n\n outro(\"Resource added.\");\n console.log(\n `\\nAdded ${alias} as ${spec.required ? \"required\" : \"optional\"} to ${path.relative(cwd, manifestPath)}`,\n );\n}\n\nexport const pluginAddResourceCommand = new Command(\"add-resource\")\n .description(\n \"Add a resource requirement to an existing plugin manifest (interactive). Overwrites manifest.json in place.\",\n )\n .option(\n \"-p, --path <dir>\",\n \"Plugin directory containing manifest.json (default: .)\",\n )\n .action((opts) =>\n runPluginAddResource(opts).catch((err) => {\n console.error(err);\n process.exit(1);\n }),\n );\n"],"mappings":";;;;;;;;;;AAeA,eAAe,qBAAqB,SAA2C;AAC7E,OAAM,kCAAkC;CAExC,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,YAAY,KAAK,QAAQ,KAAK,QAAQ,QAAQ,IAAI;CACxD,MAAM,eAAe,KAAK,KAAK,WAAW,gBAAgB;AAE1D,KAAI,CAAC,GAAG,WAAW,aAAa,EAAE;AAChC,UAAQ,MAAM,8BAA8B,eAAe;AAC3D,UAAQ,KAAK,EAAE;;CAGjB,IAAI;AACJ,KAAI;EACF,MAAM,MAAM,GAAG,aAAa,cAAc,QAAQ;EAClD,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,MAAM,SAAS,iBAAiB,OAAO;AACvC,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,UAAU;AACrC,WAAQ,MACN,8DACD;AACD,WAAQ,KAAK,EAAE;;AAEjB,aAAW;UACJ,KAAK;AACZ,UAAQ,MACN,0CACA,eAAe,QAAQ,IAAI,UAAU,IACtC;AACD,UAAQ,KAAK,EAAE;;CAGjB,MAAM,OAAO,MAAM,mBAAmB;AACtC,KAAI,CAAC,MAAM;AACT,SAAO,aAAa;AACpB,UAAQ,KAAK,EAAE;;CAGjB,MAAM,QAAQ,qBAAqB,KAAK,KAAK;CAC7C,MAAM,QAAQ;EACZ,MAAM,KAAK;EACX;EACA,aAAa,KAAK;EAClB,aAAa,KAAK,eAAe,gBAAgB,MAAM;EACvD,YAAY,KAAK;EACjB,QAAQ,KAAK;EACd;AAED,KAAI,KAAK,SACP,UAAS,UAAU,SAAS,KAAK,MAAM;KAEvC,UAAS,UAAU,SAAS,KAAK,MAAM;AAGzC,IAAG,cAAc,cAAc,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,IAAI;AAExE,OAAM,kBAAkB;AACxB,SAAQ,IACN,WAAW,MAAM,MAAM,KAAK,WAAW,aAAa,WAAW,MAAM,KAAK,SAAS,KAAK,aAAa,GACtG;;AAGH,MAAa,2BAA2B,IAAI,QAAQ,eAAe,CAChE,YACC,8GACD,CACA,OACC,oBACA,yDACD,CACA,QAAQ,SACP,qBAAqB,KAAK,CAAC,OAAO,QAAQ;AACxC,SAAQ,MAAM,IAAI;AAClB,SAAQ,KAAK,EAAE;EACf,CACH"}
@@ -0,0 +1,162 @@
1
+ import { RESOURCE_TYPE_OPTIONS } from "./resource-defaults.js";
2
+ import { promptOneResource } from "./prompt-resource.js";
3
+ import { resolveTargetDir, scaffoldPlugin } from "./scaffold.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { Command } from "commander";
7
+ import process from "node:process";
8
+ import { cancel, confirm, intro, isCancel, multiselect, outro, select, spinner, text } from "@clack/prompts";
9
+
10
+ //#region src/cli/commands/plugin/create/create.ts
11
+ const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
12
+ const DEFAULT_VERSION = "0.1.0";
13
+ async function runPluginCreate() {
14
+ intro("Create a new AppKit plugin");
15
+ try {
16
+ const placement = await select({
17
+ message: "Where should the plugin live?",
18
+ options: [{
19
+ value: "in-repo",
20
+ label: "In this repository (e.g. plugins/my-plugin)",
21
+ hint: "folder path"
22
+ }, {
23
+ value: "isolated",
24
+ label: "New isolated package",
25
+ hint: "full package with package.json"
26
+ }]
27
+ });
28
+ if (isCancel(placement)) {
29
+ cancel("Cancelled.");
30
+ process.exit(0);
31
+ }
32
+ const targetPath = await text({
33
+ message: placement === "in-repo" ? "Folder path for the plugin (e.g. plugins/my-feature)" : "Directory name for the new package (e.g. appkit-plugin-my-feature)",
34
+ placeholder: placement === "in-repo" ? "plugins/my-plugin" : "appkit-plugin-my-feature",
35
+ validate(value) {
36
+ if (!value?.trim()) return "Path is required.";
37
+ if (placement === "in-repo" && (path.isAbsolute(value) || value.trim().startsWith(".."))) return "Use a relative path under the current directory (e.g. plugins/my-plugin).";
38
+ }
39
+ });
40
+ if (isCancel(targetPath)) {
41
+ cancel("Cancelled.");
42
+ process.exit(0);
43
+ }
44
+ const name = await text({
45
+ message: "Plugin name (id)",
46
+ placeholder: "my-plugin",
47
+ validate(value) {
48
+ if (!value?.trim()) return "Name is required.";
49
+ if (!NAME_PATTERN.test(value)) return "Must be lowercase, start with a letter, and use only letters, numbers, and hyphens.";
50
+ }
51
+ });
52
+ if (isCancel(name)) {
53
+ cancel("Cancelled.");
54
+ process.exit(0);
55
+ }
56
+ const displayName = await text({
57
+ message: "Display name",
58
+ placeholder: "My Plugin",
59
+ initialValue: name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" "),
60
+ validate(value) {
61
+ if (!value?.trim()) return "Display name is required.";
62
+ }
63
+ });
64
+ if (isCancel(displayName)) {
65
+ cancel("Cancelled.");
66
+ process.exit(0);
67
+ }
68
+ const description = await text({
69
+ message: "Short description",
70
+ placeholder: "What does this plugin do?",
71
+ validate(value) {
72
+ if (!value?.trim()) return "Description is required.";
73
+ }
74
+ });
75
+ if (isCancel(description)) {
76
+ cancel("Cancelled.");
77
+ process.exit(0);
78
+ }
79
+ const resourceTypes = await multiselect({
80
+ message: "Which Databricks resources does this plugin need?",
81
+ options: RESOURCE_TYPE_OPTIONS.map((o) => ({
82
+ value: o.value,
83
+ label: o.label
84
+ })),
85
+ required: false
86
+ });
87
+ if (isCancel(resourceTypes)) {
88
+ cancel("Cancelled.");
89
+ process.exit(0);
90
+ }
91
+ const resources = [];
92
+ for (const type of resourceTypes) {
93
+ const spec = await promptOneResource({ type });
94
+ if (!spec) {
95
+ cancel("Cancelled.");
96
+ process.exit(0);
97
+ }
98
+ resources.push({
99
+ type: spec.type,
100
+ required: spec.required,
101
+ description: spec.description,
102
+ resourceKey: spec.resourceKey,
103
+ permission: spec.permission,
104
+ fields: spec.fields
105
+ });
106
+ }
107
+ const answers = {
108
+ placement,
109
+ targetPath: targetPath.trim(),
110
+ name: name.trim(),
111
+ displayName: displayName.trim(),
112
+ description: description.trim(),
113
+ resources,
114
+ version: DEFAULT_VERSION
115
+ };
116
+ const targetDir = resolveTargetDir(process.cwd(), answers);
117
+ if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
118
+ const overwrite = await confirm({
119
+ message: `Directory ${answers.targetPath} already exists and is not empty. Overwrite?`,
120
+ initialValue: false
121
+ });
122
+ if (isCancel(overwrite) || !overwrite) {
123
+ cancel("Cancelled.");
124
+ process.exit(0);
125
+ }
126
+ }
127
+ const s = spinner();
128
+ s.start("Writing files…");
129
+ try {
130
+ scaffoldPlugin(targetDir, answers, { isolated: placement === "isolated" });
131
+ s.stop("Files written.");
132
+ } catch (err) {
133
+ s.stop("Failed.");
134
+ throw err;
135
+ }
136
+ const relativePath = path.relative(process.cwd(), targetDir);
137
+ const importPath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
138
+ const exportName = answers.name.split("-").map((s, i) => i === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1)).join("");
139
+ outro("Plugin created successfully.");
140
+ console.log("\nNext steps:\n");
141
+ if (placement === "in-repo") {
142
+ console.log(` 1. Import and register in your server:`);
143
+ console.log(` import { ${exportName} } from "${importPath}";`);
144
+ console.log(` createApp({ plugins: [ ..., ${exportName}() ] });`);
145
+ console.log(` 2. Run \`npx appkit plugin sync --write\` to update appkit.plugins.json.\n`);
146
+ } else {
147
+ console.log(` 1. cd into the new package and install dependencies:`);
148
+ console.log(` cd ${answers.targetPath} && pnpm install`);
149
+ console.log(` 2. Build: pnpm build`);
150
+ console.log(` 3. In your app: pnpm add ./${answers.targetPath} @databricks/appkit`);
151
+ console.log(` 4. Import and register: import { ${exportName} } from "<package-name>";\n`);
152
+ }
153
+ } catch (err) {
154
+ console.error(err);
155
+ process.exit(1);
156
+ }
157
+ }
158
+ const pluginCreateCommand = new Command("create").description("Scaffold a new AppKit plugin (interactive)").action(runPluginCreate);
159
+
160
+ //#endregion
161
+ export { pluginCreateCommand };
162
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","names":[],"sources":["../../../../../src/cli/commands/plugin/create/create.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport {\n cancel,\n confirm,\n intro,\n isCancel,\n multiselect,\n outro,\n select,\n spinner,\n text,\n} from \"@clack/prompts\";\nimport { Command } from \"commander\";\nimport { promptOneResource } from \"./prompt-resource\";\nimport { RESOURCE_TYPE_OPTIONS } from \"./resource-defaults\";\nimport { resolveTargetDir, scaffoldPlugin } from \"./scaffold\";\nimport type { CreateAnswers, Placement } from \"./types\";\n\nconst NAME_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst DEFAULT_VERSION = \"0.1.0\";\n\nasync function runPluginCreate(): Promise<void> {\n intro(\"Create a new AppKit plugin\");\n\n try {\n const placement = await select<Placement>({\n message: \"Where should the plugin live?\",\n options: [\n {\n value: \"in-repo\",\n label: \"In this repository (e.g. plugins/my-plugin)\",\n hint: \"folder path\",\n },\n {\n value: \"isolated\",\n label: \"New isolated package\",\n hint: \"full package with package.json\",\n },\n ],\n });\n if (isCancel(placement)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const placementPrompt =\n placement === \"in-repo\"\n ? \"Folder path for the plugin (e.g. plugins/my-feature)\"\n : \"Directory name for the new package (e.g. appkit-plugin-my-feature)\";\n const targetPath = await text({\n message: placementPrompt,\n placeholder:\n placement === \"in-repo\"\n ? \"plugins/my-plugin\"\n : \"appkit-plugin-my-feature\",\n validate(value) {\n if (!value?.trim()) return \"Path is required.\";\n if (\n placement === \"in-repo\" &&\n (path.isAbsolute(value) || value.trim().startsWith(\"..\"))\n ) {\n return \"Use a relative path under the current directory (e.g. plugins/my-plugin).\";\n }\n return undefined;\n },\n });\n if (isCancel(targetPath)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const name = await text({\n message: \"Plugin name (id)\",\n placeholder: \"my-plugin\",\n validate(value) {\n if (!value?.trim()) return \"Name is required.\";\n if (!NAME_PATTERN.test(value as string)) {\n return \"Must be lowercase, start with a letter, and use only letters, numbers, and hyphens.\";\n }\n return undefined;\n },\n });\n if (isCancel(name)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const displayName = await text({\n message: \"Display name\",\n placeholder: \"My Plugin\",\n initialValue: name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\" \"),\n validate(value) {\n if (!value?.trim()) return \"Display name is required.\";\n return undefined;\n },\n });\n if (isCancel(displayName)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const description = await text({\n message: \"Short description\",\n placeholder: \"What does this plugin do?\",\n validate(value) {\n if (!value?.trim()) return \"Description is required.\";\n return undefined;\n },\n });\n if (isCancel(description)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const resourceTypes = await multiselect({\n message: \"Which Databricks resources does this plugin need?\",\n options: RESOURCE_TYPE_OPTIONS.map((o) => ({\n value: o.value,\n label: o.label,\n })),\n required: false,\n });\n if (isCancel(resourceTypes)) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n const resources: CreateAnswers[\"resources\"] = [];\n for (const type of resourceTypes as string[]) {\n const spec = await promptOneResource({ type });\n if (!spec) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n resources.push({\n type: spec.type,\n required: spec.required,\n description: spec.description,\n resourceKey: spec.resourceKey,\n permission: spec.permission,\n fields: spec.fields,\n });\n }\n\n const answers: CreateAnswers = {\n placement,\n targetPath: (targetPath as string).trim(),\n name: (name as string).trim(),\n displayName: (displayName as string).trim(),\n description: (description as string).trim(),\n resources,\n version: DEFAULT_VERSION,\n };\n\n const targetDir = resolveTargetDir(process.cwd(), answers);\n const dirExists = fs.existsSync(targetDir);\n const hasContent = dirExists && fs.readdirSync(targetDir).length > 0;\n if (hasContent) {\n const overwrite = await confirm({\n message: `Directory ${answers.targetPath} already exists and is not empty. Overwrite?`,\n initialValue: false,\n });\n if (isCancel(overwrite) || !overwrite) {\n cancel(\"Cancelled.\");\n process.exit(0);\n }\n }\n\n const s = spinner();\n s.start(\"Writing files…\");\n try {\n scaffoldPlugin(targetDir, answers, {\n isolated: placement === \"isolated\",\n });\n s.stop(\"Files written.\");\n } catch (err) {\n s.stop(\"Failed.\");\n throw err;\n }\n\n const relativePath = path.relative(process.cwd(), targetDir);\n const importPath = relativePath.startsWith(\".\")\n ? relativePath\n : `./${relativePath}`;\n const exportName = answers.name\n .split(\"-\")\n .map((s, i) => (i === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1)))\n .join(\"\");\n\n outro(\"Plugin created successfully.\");\n\n console.log(\"\\nNext steps:\\n\");\n if (placement === \"in-repo\") {\n console.log(` 1. Import and register in your server:`);\n console.log(` import { ${exportName} } from \"${importPath}\";`);\n console.log(` createApp({ plugins: [ ..., ${exportName}() ] });`);\n console.log(\n ` 2. Run \\`npx appkit plugin sync --write\\` to update appkit.plugins.json.\\n`,\n );\n } else {\n console.log(` 1. cd into the new package and install dependencies:`);\n console.log(` cd ${answers.targetPath} && pnpm install`);\n console.log(` 2. Build: pnpm build`);\n console.log(\n ` 3. In your app: pnpm add ./${answers.targetPath} @databricks/appkit`,\n );\n console.log(\n ` 4. Import and register: import { ${exportName} } from \"<package-name>\";\\n`,\n );\n }\n } catch (err) {\n console.error(err);\n process.exit(1);\n }\n}\n\nexport const pluginCreateCommand = new Command(\"create\")\n .description(\"Scaffold a new AppKit plugin (interactive)\")\n .action(runPluginCreate);\n"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { PERMISSIONS_BY_TYPE, RESOURCE_TYPE_OPTIONS, getDefaultFieldsForType, humanizeResourceType, resourceKeyFromType } from "./resource-defaults.js";
2
+ import { isCancel, select, text } from "@clack/prompts";
3
+
4
+ //#region src/cli/commands/plugin/create/prompt-resource.ts
5
+ /**
6
+ * Prompt for a single resource: type (optional), required, description, resourceKey, permission, and field env vars.
7
+ * When type is provided (e.g. from create's multiselect), type prompt is skipped.
8
+ */
9
+ async function promptOneResource(opts) {
10
+ let type = opts?.type;
11
+ if (!type) {
12
+ const resourceType = await select({
13
+ message: "Resource type",
14
+ options: RESOURCE_TYPE_OPTIONS.map((o) => ({
15
+ value: o.value,
16
+ label: o.label
17
+ }))
18
+ });
19
+ if (isCancel(resourceType)) return null;
20
+ type = resourceType;
21
+ }
22
+ const required = await select({
23
+ message: `${humanizeResourceType(type)} – required or optional?`,
24
+ options: [{
25
+ value: true,
26
+ label: "Required",
27
+ hint: "plugin needs it to function"
28
+ }, {
29
+ value: false,
30
+ label: "Optional",
31
+ hint: "enhances functionality"
32
+ }]
33
+ });
34
+ if (isCancel(required)) return null;
35
+ const description = await text({
36
+ message: `Short description for ${humanizeResourceType(type)}`,
37
+ placeholder: required ? "Required for …" : "Optional for …"
38
+ });
39
+ if (isCancel(description)) return null;
40
+ const defaultKey = resourceKeyFromType(type);
41
+ const resourceKey = await text({
42
+ message: "Resource key (unique identifier within the manifest)",
43
+ initialValue: defaultKey,
44
+ placeholder: defaultKey,
45
+ validate: (val = "") => {
46
+ if (!val.trim()) return "Resource key is required";
47
+ if (!/^[a-z][a-z0-9-]*$/.test(val)) return "Must be lowercase, start with a letter, and contain only letters, numbers, and hyphens";
48
+ }
49
+ });
50
+ if (isCancel(resourceKey)) return null;
51
+ const typePermissions = PERMISSIONS_BY_TYPE[type] ?? ["CAN_VIEW"];
52
+ let permission;
53
+ if (typePermissions.length === 1) permission = typePermissions[0];
54
+ else {
55
+ const selected = await select({
56
+ message: "Permission level",
57
+ options: typePermissions.map((p) => ({
58
+ value: p,
59
+ label: p
60
+ }))
61
+ });
62
+ if (isCancel(selected)) return null;
63
+ permission = selected;
64
+ }
65
+ const defaultFields = getDefaultFieldsForType(type);
66
+ const fields = {};
67
+ for (const [fieldKey, defaults] of Object.entries(defaultFields)) {
68
+ const envName = await text({
69
+ message: `Env var for "${fieldKey}"${defaults.description ? ` (${defaults.description})` : ""}`,
70
+ initialValue: defaults.env,
71
+ placeholder: defaults.env,
72
+ validate: (val = "") => {
73
+ if (!val.trim()) return "Env var name is required";
74
+ if (!/^[A-Z][A-Z0-9_]*$/.test(val)) return "Must be uppercase, start with a letter (e.g. DATABRICKS_WAREHOUSE_ID)";
75
+ }
76
+ });
77
+ if (isCancel(envName)) return null;
78
+ fields[fieldKey] = {
79
+ env: envName.trim(),
80
+ ...defaults.description ? { description: defaults.description } : {}
81
+ };
82
+ }
83
+ return {
84
+ type,
85
+ required,
86
+ description: description?.trim() || "",
87
+ resourceKey: resourceKey.trim(),
88
+ permission,
89
+ fields
90
+ };
91
+ }
92
+
93
+ //#endregion
94
+ export { promptOneResource };
95
+ //# sourceMappingURL=prompt-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-resource.js","names":[],"sources":["../../../../../src/cli/commands/plugin/create/prompt-resource.ts"],"sourcesContent":["import { isCancel, select, text } from \"@clack/prompts\";\nimport {\n getDefaultFieldsForType,\n humanizeResourceType,\n PERMISSIONS_BY_TYPE,\n RESOURCE_TYPE_OPTIONS,\n resourceKeyFromType,\n} from \"./resource-defaults\";\n\n/** Full resource spec collected from prompts (shared by create and add-resource). */\nexport interface ResourceSpec {\n type: string;\n required: boolean;\n description: string;\n resourceKey: string;\n permission: string;\n fields: Record<string, { env: string; description?: string }>;\n}\n\n/**\n * Prompt for a single resource: type (optional), required, description, resourceKey, permission, and field env vars.\n * When type is provided (e.g. from create's multiselect), type prompt is skipped.\n */\nexport async function promptOneResource(opts?: {\n type?: string;\n}): Promise<ResourceSpec | null> {\n let type = opts?.type;\n\n if (!type) {\n const resourceType = await select({\n message: \"Resource type\",\n options: RESOURCE_TYPE_OPTIONS.map((o) => ({\n value: o.value,\n label: o.label,\n })),\n });\n if (isCancel(resourceType)) return null;\n type = resourceType as string;\n }\n\n const required = await select<boolean>({\n message: `${humanizeResourceType(type)} – required or optional?`,\n options: [\n { value: true, label: \"Required\", hint: \"plugin needs it to function\" },\n { value: false, label: \"Optional\", hint: \"enhances functionality\" },\n ],\n });\n if (isCancel(required)) return null;\n\n const description = await text({\n message: `Short description for ${humanizeResourceType(type)}`,\n placeholder: required ? \"Required for …\" : \"Optional for …\",\n });\n if (isCancel(description)) return null;\n\n const defaultKey = resourceKeyFromType(type);\n const resourceKey = await text({\n message: \"Resource key (unique identifier within the manifest)\",\n initialValue: defaultKey,\n placeholder: defaultKey,\n validate: (val = \"\") => {\n if (!val.trim()) return \"Resource key is required\";\n if (!/^[a-z][a-z0-9-]*$/.test(val))\n return \"Must be lowercase, start with a letter, and contain only letters, numbers, and hyphens\";\n },\n });\n if (isCancel(resourceKey)) return null;\n\n const typePermissions = PERMISSIONS_BY_TYPE[type] ?? [\"CAN_VIEW\"];\n let permission: string;\n if (typePermissions.length === 1) {\n permission = typePermissions[0];\n } else {\n const selected = await select({\n message: \"Permission level\",\n options: typePermissions.map((p) => ({ value: p, label: p })),\n });\n if (isCancel(selected)) return null;\n permission = selected as string;\n }\n\n const defaultFields = getDefaultFieldsForType(type);\n const fields: Record<string, { env: string; description?: string }> = {};\n\n for (const [fieldKey, defaults] of Object.entries(defaultFields)) {\n const envName = await text({\n message: `Env var for \"${fieldKey}\"${defaults.description ? ` (${defaults.description})` : \"\"}`,\n initialValue: defaults.env,\n placeholder: defaults.env,\n validate: (val = \"\") => {\n if (!val.trim()) return \"Env var name is required\";\n if (!/^[A-Z][A-Z0-9_]*$/.test(val))\n return \"Must be uppercase, start with a letter (e.g. DATABRICKS_WAREHOUSE_ID)\";\n },\n });\n if (isCancel(envName)) return null;\n fields[fieldKey] = {\n env: (envName as string).trim(),\n ...(defaults.description ? { description: defaults.description } : {}),\n };\n }\n\n return {\n type,\n required: required as boolean,\n description: (description as string)?.trim() || \"\",\n resourceKey: (resourceKey as string).trim(),\n permission,\n fields,\n };\n}\n"],"mappings":";;;;;;;;AAuBA,eAAsB,kBAAkB,MAEP;CAC/B,IAAI,OAAO,MAAM;AAEjB,KAAI,CAAC,MAAM;EACT,MAAM,eAAe,MAAM,OAAO;GAChC,SAAS;GACT,SAAS,sBAAsB,KAAK,OAAO;IACzC,OAAO,EAAE;IACT,OAAO,EAAE;IACV,EAAE;GACJ,CAAC;AACF,MAAI,SAAS,aAAa,CAAE,QAAO;AACnC,SAAO;;CAGT,MAAM,WAAW,MAAM,OAAgB;EACrC,SAAS,GAAG,qBAAqB,KAAK,CAAC;EACvC,SAAS,CACP;GAAE,OAAO;GAAM,OAAO;GAAY,MAAM;GAA+B,EACvE;GAAE,OAAO;GAAO,OAAO;GAAY,MAAM;GAA0B,CACpE;EACF,CAAC;AACF,KAAI,SAAS,SAAS,CAAE,QAAO;CAE/B,MAAM,cAAc,MAAM,KAAK;EAC7B,SAAS,yBAAyB,qBAAqB,KAAK;EAC5D,aAAa,WAAW,mBAAmB;EAC5C,CAAC;AACF,KAAI,SAAS,YAAY,CAAE,QAAO;CAElC,MAAM,aAAa,oBAAoB,KAAK;CAC5C,MAAM,cAAc,MAAM,KAAK;EAC7B,SAAS;EACT,cAAc;EACd,aAAa;EACb,WAAW,MAAM,OAAO;AACtB,OAAI,CAAC,IAAI,MAAM,CAAE,QAAO;AACxB,OAAI,CAAC,oBAAoB,KAAK,IAAI,CAChC,QAAO;;EAEZ,CAAC;AACF,KAAI,SAAS,YAAY,CAAE,QAAO;CAElC,MAAM,kBAAkB,oBAAoB,SAAS,CAAC,WAAW;CACjE,IAAI;AACJ,KAAI,gBAAgB,WAAW,EAC7B,cAAa,gBAAgB;MACxB;EACL,MAAM,WAAW,MAAM,OAAO;GAC5B,SAAS;GACT,SAAS,gBAAgB,KAAK,OAAO;IAAE,OAAO;IAAG,OAAO;IAAG,EAAE;GAC9D,CAAC;AACF,MAAI,SAAS,SAAS,CAAE,QAAO;AAC/B,eAAa;;CAGf,MAAM,gBAAgB,wBAAwB,KAAK;CACnD,MAAM,SAAgE,EAAE;AAExE,MAAK,MAAM,CAAC,UAAU,aAAa,OAAO,QAAQ,cAAc,EAAE;EAChE,MAAM,UAAU,MAAM,KAAK;GACzB,SAAS,gBAAgB,SAAS,GAAG,SAAS,cAAc,KAAK,SAAS,YAAY,KAAK;GAC3F,cAAc,SAAS;GACvB,aAAa,SAAS;GACtB,WAAW,MAAM,OAAO;AACtB,QAAI,CAAC,IAAI,MAAM,CAAE,QAAO;AACxB,QAAI,CAAC,oBAAoB,KAAK,IAAI,CAChC,QAAO;;GAEZ,CAAC;AACF,MAAI,SAAS,QAAQ,CAAE,QAAO;AAC9B,SAAO,YAAY;GACjB,KAAM,QAAmB,MAAM;GAC/B,GAAI,SAAS,cAAc,EAAE,aAAa,SAAS,aAAa,GAAG,EAAE;GACtE;;AAGH,QAAO;EACL;EACU;EACV,aAAc,aAAwB,MAAM,IAAI;EAChD,aAAc,YAAuB,MAAM;EAC3C;EACA;EACD"}
@@ -0,0 +1,105 @@
1
+ import { getResourceTypeOptions, getResourceTypePermissions } from "../schema-resources.js";
2
+
3
+ //#region src/cli/commands/plugin/create/resource-defaults.ts
4
+ /**
5
+ * Resource type and permission defaults for plugin scaffolding.
6
+ * Values are derived from plugin-manifest.schema.json via schema-resources.
7
+ */
8
+ const MANIFEST_SCHEMA_ID = "https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json";
9
+ /** Resource types from schema resourceType enum (value, human label). */
10
+ const RESOURCE_TYPE_OPTIONS = getResourceTypeOptions();
11
+ /** All valid permissions per resource type, from schema allOf/if-then rules. */
12
+ const PERMISSIONS_BY_TYPE = getResourceTypePermissions();
13
+ /** Default (first) permission per resource type for scaffolding. */
14
+ const DEFAULT_PERMISSION_BY_TYPE = Object.fromEntries(Object.entries(PERMISSIONS_BY_TYPE).map(([type, perms]) => [type, perms[0]]));
15
+ /** Default fields per resource type: field key -> { env, description }. */
16
+ const DEFAULT_FIELDS_BY_TYPE = {
17
+ sql_warehouse: { id: {
18
+ env: "DATABRICKS_WAREHOUSE_ID",
19
+ description: "SQL Warehouse ID"
20
+ } },
21
+ secret: {
22
+ scope: {
23
+ env: "SECRET_SCOPE",
24
+ description: "Secret scope name"
25
+ },
26
+ key: {
27
+ env: "SECRET_KEY",
28
+ description: "Secret key"
29
+ }
30
+ },
31
+ job: { id: {
32
+ env: "DATABRICKS_JOB_ID",
33
+ description: "Job ID"
34
+ } },
35
+ serving_endpoint: { id: {
36
+ env: "DATABRICKS_SERVING_ENDPOINT_ID",
37
+ description: "Serving endpoint ID"
38
+ } },
39
+ volume: { name: {
40
+ env: "VOLUME_NAME",
41
+ description: "Volume name"
42
+ } },
43
+ vector_search_index: {
44
+ endpoint_name: {
45
+ env: "VECTOR_SEARCH_ENDPOINT_NAME",
46
+ description: "Vector search endpoint name"
47
+ },
48
+ index_name: {
49
+ env: "VECTOR_SEARCH_INDEX_NAME",
50
+ description: "Vector search index name"
51
+ }
52
+ },
53
+ uc_function: { name: {
54
+ env: "UC_FUNCTION_NAME",
55
+ description: "Unity Catalog function name"
56
+ } },
57
+ uc_connection: { name: {
58
+ env: "UC_CONNECTION_NAME",
59
+ description: "Unity Catalog connection name"
60
+ } },
61
+ database: {
62
+ instance_name: {
63
+ env: "DATABRICKS_INSTANCE_NAME",
64
+ description: "Databricks instance name"
65
+ },
66
+ database_name: {
67
+ env: "DATABASE_NAME",
68
+ description: "Database name"
69
+ }
70
+ },
71
+ genie_space: { id: {
72
+ env: "GENIE_SPACE_ID",
73
+ description: "Genie Space ID"
74
+ } },
75
+ experiment: { id: {
76
+ env: "MLFLOW_EXPERIMENT_ID",
77
+ description: "MLflow experiment ID"
78
+ } },
79
+ app: { id: {
80
+ env: "DATABRICKS_APP_ID",
81
+ description: "Databricks App ID"
82
+ } }
83
+ };
84
+ /** Humanized alias from resource type (e.g. sql_warehouse -> "SQL Warehouse"). */
85
+ function humanizeResourceType(type) {
86
+ const option = RESOURCE_TYPE_OPTIONS.find((o) => o.value === type);
87
+ return option ? option.label : type.replace(/_/g, " ");
88
+ }
89
+ /** Kebab-case resource key from type (e.g. sql_warehouse -> "sql-warehouse"). */
90
+ function resourceKeyFromType(type) {
91
+ return type.replace(/_/g, "-");
92
+ }
93
+ /** Get default fields for a resource type; fallback to single id field. */
94
+ function getDefaultFieldsForType(type) {
95
+ const known = DEFAULT_FIELDS_BY_TYPE[type];
96
+ if (known) return known;
97
+ return { id: {
98
+ env: `DATABRICKS_${resourceKeyFromType(type).toUpperCase().replace(/-/g, "_")}_ID`,
99
+ description: `${humanizeResourceType(type)} ID`
100
+ } };
101
+ }
102
+
103
+ //#endregion
104
+ export { MANIFEST_SCHEMA_ID, PERMISSIONS_BY_TYPE, RESOURCE_TYPE_OPTIONS, getDefaultFieldsForType, humanizeResourceType, resourceKeyFromType };
105
+ //# sourceMappingURL=resource-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-defaults.js","names":[],"sources":["../../../../../src/cli/commands/plugin/create/resource-defaults.ts"],"sourcesContent":["/**\n * Resource type and permission defaults for plugin scaffolding.\n * Values are derived from plugin-manifest.schema.json via schema-resources.\n */\n\nimport {\n getResourceTypeOptions,\n getResourceTypePermissions,\n type ResourceTypeOption,\n} from \"../schema-resources\";\n\nexport const MANIFEST_SCHEMA_ID =\n \"https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json\";\n\nexport type { ResourceTypeOption };\n\n/** Resource types from schema resourceType enum (value, human label). */\nexport const RESOURCE_TYPE_OPTIONS: ResourceTypeOption[] =\n getResourceTypeOptions();\n\n/** All valid permissions per resource type, from schema allOf/if-then rules. */\nexport const PERMISSIONS_BY_TYPE: Record<string, string[]> =\n getResourceTypePermissions();\n\n/** Default (first) permission per resource type for scaffolding. */\nexport const DEFAULT_PERMISSION_BY_TYPE: Record<string, string> =\n Object.fromEntries(\n Object.entries(PERMISSIONS_BY_TYPE).map(([type, perms]) => [\n type,\n perms[0],\n ]),\n );\n\n/** Default fields per resource type: field key -> { env, description }. */\nexport const DEFAULT_FIELDS_BY_TYPE: Record<\n string,\n Record<string, { env: string; description?: string }>\n> = {\n sql_warehouse: {\n id: { env: \"DATABRICKS_WAREHOUSE_ID\", description: \"SQL Warehouse ID\" },\n },\n secret: {\n scope: { env: \"SECRET_SCOPE\", description: \"Secret scope name\" },\n key: { env: \"SECRET_KEY\", description: \"Secret key\" },\n },\n job: {\n id: { env: \"DATABRICKS_JOB_ID\", description: \"Job ID\" },\n },\n serving_endpoint: {\n id: {\n env: \"DATABRICKS_SERVING_ENDPOINT_ID\",\n description: \"Serving endpoint ID\",\n },\n },\n volume: {\n name: { env: \"VOLUME_NAME\", description: \"Volume name\" },\n },\n vector_search_index: {\n endpoint_name: {\n env: \"VECTOR_SEARCH_ENDPOINT_NAME\",\n description: \"Vector search endpoint name\",\n },\n index_name: {\n env: \"VECTOR_SEARCH_INDEX_NAME\",\n description: \"Vector search index name\",\n },\n },\n uc_function: {\n name: {\n env: \"UC_FUNCTION_NAME\",\n description: \"Unity Catalog function name\",\n },\n },\n uc_connection: {\n name: {\n env: \"UC_CONNECTION_NAME\",\n description: \"Unity Catalog connection name\",\n },\n },\n database: {\n instance_name: {\n env: \"DATABRICKS_INSTANCE_NAME\",\n description: \"Databricks instance name\",\n },\n database_name: {\n env: \"DATABASE_NAME\",\n description: \"Database name\",\n },\n },\n genie_space: {\n id: { env: \"GENIE_SPACE_ID\", description: \"Genie Space ID\" },\n },\n experiment: {\n id: { env: \"MLFLOW_EXPERIMENT_ID\", description: \"MLflow experiment ID\" },\n },\n app: {\n id: { env: \"DATABRICKS_APP_ID\", description: \"Databricks App ID\" },\n },\n};\n\n/** Humanized alias from resource type (e.g. sql_warehouse -> \"SQL Warehouse\"). */\nexport function humanizeResourceType(type: string): string {\n const option = RESOURCE_TYPE_OPTIONS.find((o) => o.value === type);\n return option ? option.label : type.replace(/_/g, \" \");\n}\n\n/** Kebab-case resource key from type (e.g. sql_warehouse -> \"sql-warehouse\"). */\nexport function resourceKeyFromType(type: string): string {\n return type.replace(/_/g, \"-\");\n}\n\n/** Get default fields for a resource type; fallback to single id field. */\nexport function getDefaultFieldsForType(\n type: string,\n): Record<string, { env: string; description?: string }> {\n const known = DEFAULT_FIELDS_BY_TYPE[type];\n if (known) return known;\n const key = resourceKeyFromType(type);\n const envName = `DATABRICKS_${key.toUpperCase().replace(/-/g, \"_\")}_ID`;\n return {\n id: { env: envName, description: `${humanizeResourceType(type)} ID` },\n };\n}\n"],"mappings":";;;;;;;AAWA,MAAa,qBACX;;AAKF,MAAa,wBACX,wBAAwB;;AAG1B,MAAa,sBACX,4BAA4B;;AAG9B,MAAa,6BACX,OAAO,YACL,OAAO,QAAQ,oBAAoB,CAAC,KAAK,CAAC,MAAM,WAAW,CACzD,MACA,MAAM,GACP,CAAC,CACH;;AAGH,MAAa,yBAGT;CACF,eAAe,EACb,IAAI;EAAE,KAAK;EAA2B,aAAa;EAAoB,EACxE;CACD,QAAQ;EACN,OAAO;GAAE,KAAK;GAAgB,aAAa;GAAqB;EAChE,KAAK;GAAE,KAAK;GAAc,aAAa;GAAc;EACtD;CACD,KAAK,EACH,IAAI;EAAE,KAAK;EAAqB,aAAa;EAAU,EACxD;CACD,kBAAkB,EAChB,IAAI;EACF,KAAK;EACL,aAAa;EACd,EACF;CACD,QAAQ,EACN,MAAM;EAAE,KAAK;EAAe,aAAa;EAAe,EACzD;CACD,qBAAqB;EACnB,eAAe;GACb,KAAK;GACL,aAAa;GACd;EACD,YAAY;GACV,KAAK;GACL,aAAa;GACd;EACF;CACD,aAAa,EACX,MAAM;EACJ,KAAK;EACL,aAAa;EACd,EACF;CACD,eAAe,EACb,MAAM;EACJ,KAAK;EACL,aAAa;EACd,EACF;CACD,UAAU;EACR,eAAe;GACb,KAAK;GACL,aAAa;GACd;EACD,eAAe;GACb,KAAK;GACL,aAAa;GACd;EACF;CACD,aAAa,EACX,IAAI;EAAE,KAAK;EAAkB,aAAa;EAAkB,EAC7D;CACD,YAAY,EACV,IAAI;EAAE,KAAK;EAAwB,aAAa;EAAwB,EACzE;CACD,KAAK,EACH,IAAI;EAAE,KAAK;EAAqB,aAAa;EAAqB,EACnE;CACF;;AAGD,SAAgB,qBAAqB,MAAsB;CACzD,MAAM,SAAS,sBAAsB,MAAM,MAAM,EAAE,UAAU,KAAK;AAClE,QAAO,SAAS,OAAO,QAAQ,KAAK,QAAQ,MAAM,IAAI;;;AAIxD,SAAgB,oBAAoB,MAAsB;AACxD,QAAO,KAAK,QAAQ,MAAM,IAAI;;;AAIhC,SAAgB,wBACd,MACuD;CACvD,MAAM,QAAQ,uBAAuB;AACrC,KAAI,MAAO,QAAO;AAGlB,QAAO,EACL,IAAI;EAAE,KAFQ,cADJ,oBAAoB,KAAK,CACH,aAAa,CAAC,QAAQ,MAAM,IAAI,CAAC;EAE7C,aAAa,GAAG,qBAAqB,KAAK,CAAC;EAAM,EACtE"}