@databricks/appkit-ui 0.5.4 → 0.6.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.
- package/CLAUDE.md +12 -1
- package/NOTICE.md +2 -0
- package/bin/appkit.js +0 -0
- package/dist/cli/commands/plugins-sync.js +369 -0
- package/dist/cli/commands/plugins-sync.js.map +1 -0
- package/dist/cli/commands/plugins.js +19 -0
- package/dist/cli/commands/plugins.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/react/charts/area/index.d.ts +2 -2
- package/dist/react/charts/bar/index.d.ts +2 -2
- package/dist/react/charts/base.d.ts +2 -2
- package/dist/react/charts/base.d.ts.map +1 -1
- package/dist/react/charts/create-chart.d.ts +2 -2
- package/dist/react/charts/heatmap/index.d.ts +2 -2
- package/dist/react/charts/line/index.d.ts +2 -2
- package/dist/react/charts/options.d.ts.map +1 -1
- package/dist/react/charts/pie/index.d.ts +3 -3
- package/dist/react/charts/radar/index.d.ts +2 -2
- package/dist/react/charts/scatter/index.d.ts +2 -2
- package/dist/react/charts/wrapper.d.ts +2 -2
- package/dist/react/table/data-table.d.ts +2 -2
- package/dist/react/ui/accordion.d.ts +5 -5
- package/dist/react/ui/alert-dialog.d.ts +12 -12
- package/dist/react/ui/alert.d.ts +6 -6
- package/dist/react/ui/aspect-ratio.d.ts +2 -2
- package/dist/react/ui/avatar.d.ts +4 -4
- package/dist/react/ui/badge.d.ts +4 -4
- package/dist/react/ui/breadcrumb.d.ts +8 -8
- package/dist/react/ui/button-group.d.ts +6 -6
- package/dist/react/ui/button.d.ts +5 -5
- package/dist/react/ui/calendar.d.ts +3 -3
- package/dist/react/ui/card.d.ts +8 -8
- package/dist/react/ui/carousel.d.ts +6 -6
- package/dist/react/ui/chart.d.ts +5 -5
- package/dist/react/ui/checkbox.d.ts +2 -2
- package/dist/react/ui/collapsible.d.ts +4 -4
- package/dist/react/ui/command.d.ts +10 -10
- package/dist/react/ui/context-menu.d.ts +16 -16
- package/dist/react/ui/dialog.d.ts +11 -11
- package/dist/react/ui/dialog.d.ts.map +1 -1
- package/dist/react/ui/drawer.d.ts +11 -11
- package/dist/react/ui/dropdown-menu.d.ts +16 -16
- package/dist/react/ui/empty.d.ts +9 -9
- package/dist/react/ui/field.d.ts +13 -13
- package/dist/react/ui/form.d.ts +7 -7
- package/dist/react/ui/hover-card.d.ts +4 -4
- package/dist/react/ui/input-group.d.ts +11 -11
- package/dist/react/ui/input-group.d.ts.map +1 -1
- package/dist/react/ui/input-otp.d.ts +5 -5
- package/dist/react/ui/input.d.ts +2 -2
- package/dist/react/ui/item.d.ts +11 -11
- package/dist/react/ui/kbd.d.ts +3 -3
- package/dist/react/ui/label.d.ts +2 -2
- package/dist/react/ui/menubar.d.ts +17 -17
- package/dist/react/ui/navigation-menu.d.ts +9 -9
- package/dist/react/ui/pagination.d.ts +8 -8
- package/dist/react/ui/popover.d.ts +5 -5
- package/dist/react/ui/progress.d.ts +2 -2
- package/dist/react/ui/radio-group.d.ts +3 -3
- package/dist/react/ui/resizable.d.ts +4 -4
- package/dist/react/ui/scroll-area.d.ts +3 -3
- package/dist/react/ui/select.d.ts +11 -11
- package/dist/react/ui/separator.d.ts +2 -2
- package/dist/react/ui/sheet.d.ts +9 -9
- package/dist/react/ui/sidebar.d.ts +24 -24
- package/dist/react/ui/skeleton.d.ts +2 -2
- package/dist/react/ui/slider.d.ts +2 -2
- package/dist/react/ui/sonner.d.ts +2 -2
- package/dist/react/ui/spinner.d.ts +2 -2
- package/dist/react/ui/switch.d.ts +2 -2
- package/dist/react/ui/table.d.ts +9 -9
- package/dist/react/ui/tabs.d.ts +5 -5
- package/dist/react/ui/textarea.d.ts +2 -2
- package/dist/react/ui/toggle.d.ts +2 -2
- package/dist/react/ui/tooltip.d.ts +5 -5
- package/dist/react/ui/tooltip.d.ts.map +1 -1
- package/docs/docs/api/appkit/Class.AppKitError/index.html +5 -5
- package/docs/docs/api/appkit/Class.AuthenticationError/index.html +4 -4
- package/docs/docs/api/appkit/Class.ConfigurationError/index.html +4 -4
- package/docs/docs/api/appkit/Class.ConnectionError/index.html +4 -4
- package/docs/docs/api/appkit/Class.ExecutionError/index.html +4 -4
- package/docs/docs/api/appkit/Class.InitializationError/index.html +4 -4
- package/docs/docs/api/appkit/Class.Plugin/index.html +28 -16
- package/docs/docs/api/appkit/Class.Plugin.md +90 -30
- package/docs/docs/api/appkit/Class.ResourceRegistry/index.html +150 -0
- package/docs/docs/api/appkit/Class.ResourceRegistry.md +301 -0
- package/docs/docs/api/appkit/Class.ServerError/index.html +5 -5
- package/docs/docs/api/appkit/Class.TunnelError/index.html +4 -4
- package/docs/docs/api/appkit/Class.ValidationError/index.html +4 -4
- package/docs/docs/api/appkit/Enumeration.ResourceType/index.html +66 -0
- package/docs/docs/api/appkit/Enumeration.ResourceType.md +135 -0
- package/docs/docs/api/appkit/Function.appKitTypesPlugin/index.html +4 -4
- package/docs/docs/api/appkit/Function.createApp/index.html +4 -4
- package/docs/docs/api/appkit/Function.getExecutionContext/index.html +5 -5
- package/docs/docs/api/appkit/Function.getPluginManifest/index.html +26 -0
- package/docs/docs/api/appkit/Function.getPluginManifest.md +24 -0
- package/docs/docs/api/appkit/Function.getResourceRequirements/index.html +28 -0
- package/docs/docs/api/appkit/Function.getResourceRequirements.md +42 -0
- package/docs/docs/api/appkit/Function.isSQLTypeMarker/index.html +5 -5
- package/docs/docs/api/appkit/Interface.BasePluginConfig/index.html +4 -4
- package/docs/docs/api/appkit/Interface.CacheConfig/index.html +4 -4
- package/docs/docs/api/appkit/Interface.ITelemetry/index.html +5 -5
- package/docs/docs/api/appkit/Interface.PluginManifest/index.html +63 -0
- package/docs/docs/api/appkit/Interface.PluginManifest.md +135 -0
- package/docs/docs/api/appkit/Interface.ResourceEntry/index.html +83 -0
- package/docs/docs/api/appkit/Interface.ResourceEntry.md +156 -0
- package/docs/docs/api/appkit/Interface.ResourceFieldEntry/index.html +26 -0
- package/docs/docs/api/appkit/Interface.ResourceFieldEntry.md +25 -0
- package/docs/docs/api/appkit/Interface.ResourceRequirement/index.html +51 -0
- package/docs/docs/api/appkit/Interface.ResourceRequirement.md +84 -0
- package/docs/docs/api/appkit/Interface.StreamExecutionSettings/index.html +5 -5
- package/docs/docs/api/appkit/Interface.TelemetryConfig/index.html +5 -5
- package/docs/docs/api/appkit/Interface.ValidationResult/index.html +29 -0
- package/docs/docs/api/appkit/Interface.ValidationResult.md +36 -0
- package/docs/docs/api/appkit/TypeAlias.ConfigSchema/index.html +21 -0
- package/docs/docs/api/appkit/TypeAlias.ConfigSchema.md +12 -0
- package/docs/docs/api/appkit/TypeAlias.IAppRouter/index.html +5 -5
- package/docs/docs/api/appkit/TypeAlias.ResourcePermission/index.html +18 -0
- package/docs/docs/api/appkit/TypeAlias.ResourcePermission.md +20 -0
- package/docs/docs/api/appkit/Variable.sql/index.html +5 -5
- package/docs/docs/api/appkit/index.html +10 -8
- package/docs/docs/api/appkit-ui/data/AreaChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/BarChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/DataTable/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/DonutChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/HeatmapChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/LineChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/PieChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/RadarChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/data/ScatterChart/index.html +3 -3
- package/docs/docs/api/appkit-ui/index.html +3 -3
- package/docs/docs/api/appkit-ui/styling/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Accordion/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Alert/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/AlertDialog/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/AspectRatio/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Avatar/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Badge/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Breadcrumb/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Button/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ButtonGroup/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Calendar/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Card/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Carousel/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ChartContainer/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Checkbox/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Collapsible/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Command/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ContextMenu/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Dialog/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Drawer/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/DropdownMenu/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Empty/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Field/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/FormControl/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/HoverCard/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Input/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/InputGroup/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/InputOTP/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Item/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Kbd/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Label/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Menubar/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/NavigationMenu/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Pagination/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Popover/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Progress/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/RadioGroup/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ResizableHandle/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ScrollArea/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Select/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Separator/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Sheet/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Sidebar/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Skeleton/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Slider/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Spinner/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Switch/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Table/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Tabs/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Textarea/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Toaster/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Toggle/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/ToggleGroup/index.html +3 -3
- package/docs/docs/api/appkit-ui/ui/Tooltip/index.html +3 -3
- package/docs/docs/api/appkit.md +44 -28
- package/docs/docs/api/index.html +3 -3
- package/docs/docs/app-management/index.html +3 -3
- package/docs/docs/architecture/index.html +4 -4
- package/docs/docs/architecture.md +1 -1
- package/docs/docs/category/development/index.html +3 -3
- package/docs/docs/configuration/index.html +3 -3
- package/docs/docs/core-principles/index.html +3 -3
- package/docs/docs/development/ai-assisted-development/index.html +3 -3
- package/docs/docs/development/index.html +3 -3
- package/docs/docs/development/llm-guide/index.html +3 -3
- package/docs/docs/development/local-development/index.html +3 -3
- package/docs/docs/development/project-setup/index.html +3 -3
- package/docs/docs/development/remote-bridge/index.html +3 -3
- package/docs/docs/development/type-generation/index.html +3 -3
- package/docs/docs/index.html +3 -3
- package/docs/docs/plugins/index.html +18 -8
- package/docs/docs/plugins.md +82 -4
- package/llms.txt +12 -1
- package/package.json +3 -1
package/CLAUDE.md
CHANGED
|
@@ -32,20 +32,31 @@ The CLI will display the documentation content directly in the terminal.
|
|
|
32
32
|
- [Class: ConnectionError](./docs/docs/api./docs/Class.ConnectionError.md): Error thrown when a connection or network operation fails.
|
|
33
33
|
- [Class: ExecutionError](./docs/docs/api./docs/Class.ExecutionError.md): Error thrown when an operation execution fails.
|
|
34
34
|
- [Class: InitializationError](./docs/docs/api./docs/Class.InitializationError.md): Error thrown when a service or component is not properly initialized.
|
|
35
|
-
- [Abstract Class: Plugin<TConfig>](./docs/docs/api./docs/Class.Plugin.md): Base abstract class for creating AppKit plugins
|
|
35
|
+
- [Abstract Class: Plugin<TConfig>](./docs/docs/api./docs/Class.Plugin.md): Base abstract class for creating AppKit plugins.
|
|
36
|
+
- [Class: ResourceRegistry](./docs/docs/api./docs/Class.ResourceRegistry.md): Central registry for tracking plugin resource requirements.
|
|
36
37
|
- [Class: ServerError](./docs/docs/api./docs/Class.ServerError.md): Error thrown when server lifecycle operations fail.
|
|
37
38
|
- [Class: TunnelError](./docs/docs/api./docs/Class.TunnelError.md): Error thrown when remote tunnel operations fail.
|
|
38
39
|
- [Class: ValidationError](./docs/docs/api./docs/Class.ValidationError.md): Error thrown when input validation fails.
|
|
40
|
+
- [Enumeration: ResourceType](./docs/docs/api./docs/Enumeration.ResourceType.md): Supported resource types that plugins can depend on.
|
|
39
41
|
- [Function: appKitTypesPlugin()](./docs/docs/api./docs/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
|
|
40
42
|
- [Function: createApp()](./docs/docs/api./docs/Function.createApp.md): Bootstraps AppKit with the provided configuration.
|
|
41
43
|
- [Function: getExecutionContext()](./docs/docs/api./docs/Function.getExecutionContext.md): Get the current execution context.
|
|
44
|
+
- [Function: getPluginManifest()](./docs/docs/api./docs/Function.getPluginManifest.md): Loads and validates the manifest from a plugin constructor.
|
|
45
|
+
- [Function: getResourceRequirements()](./docs/docs/api./docs/Function.getResourceRequirements.md): Gets the resource requirements from a plugin's manifest.
|
|
42
46
|
- [Function: isSQLTypeMarker()](./docs/docs/api./docs/Function.isSQLTypeMarker.md): Type guard to check if a value is a SQL type marker
|
|
43
47
|
- [Interface: BasePluginConfig](./docs/docs/api./docs/Interface.BasePluginConfig.md): Base configuration interface for AppKit plugins
|
|
44
48
|
- [Interface: CacheConfig](./docs/docs/api./docs/Interface.CacheConfig.md): Configuration for caching
|
|
45
49
|
- [Interface: ITelemetry](./docs/docs/api./docs/Interface.ITelemetry.md): Plugin-facing interface for OpenTelemetry instrumentation.
|
|
50
|
+
- [Interface: PluginManifest](./docs/docs/api./docs/Interface.PluginManifest.md): Plugin manifest that declares metadata and resource requirements.
|
|
51
|
+
- [Interface: ResourceEntry](./docs/docs/api./docs/Interface.ResourceEntry.md): Internal representation of a resource in the registry.
|
|
52
|
+
- [Interface: ResourceFieldEntry](./docs/docs/api./docs/Interface.ResourceFieldEntry.md): Defines a single field for a resource. Each field has its own environment variable and optional description.
|
|
53
|
+
- [Interface: ResourceRequirement](./docs/docs/api./docs/Interface.ResourceRequirement.md): Declares a resource requirement for a plugin.
|
|
46
54
|
- [Interface: StreamExecutionSettings](./docs/docs/api./docs/Interface.StreamExecutionSettings.md): Configuration for streaming execution with default and user-scoped settings
|
|
47
55
|
- [Interface: TelemetryConfig](./docs/docs/api./docs/Interface.TelemetryConfig.md): OpenTelemetry configuration for AppKit applications
|
|
56
|
+
- [Interface: ValidationResult](./docs/docs/api./docs/Interface.ValidationResult.md): Result of validating all registered resources against the environment.
|
|
57
|
+
- [Type Alias: ConfigSchema](./docs/docs/api./docs/TypeAlias.ConfigSchema.md): Configuration schema definition for plugin config.
|
|
48
58
|
- [Type Alias: IAppRouter](./docs/docs/api./docs/TypeAlias.IAppRouter.md): Express router type for plugin route registration
|
|
59
|
+
- [Type Alias: ResourcePermission](./docs/docs/api./docs/TypeAlias.ResourcePermission.md): Union of all possible permission levels across all resource types.
|
|
49
60
|
- [Variable: sql](./docs/docs/api./docs/Variable.sql.md): SQL helper namespace
|
|
50
61
|
- [@databricks/appkit-ui](./docs/docs/api/appkit-ui.md): The library provides a set of UI primitives for building Databricks apps in React.
|
|
51
62
|
- [AreaChart](./docs/docs/api/appkit-ui/data/AreaChart.md): Area Chart component for trend visualization with filled areas.
|
package/NOTICE.md
CHANGED
|
@@ -51,6 +51,8 @@ This Software contains code from the following open source projects:
|
|
|
51
51
|
| [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) | 8.21.3 | MIT | https://tanstack.com/table |
|
|
52
52
|
| [@tanstack/react-virtual](https://www.npmjs.com/package/@tanstack/react-virtual) | 3.13.12 | MIT | https://tanstack.com/virtual |
|
|
53
53
|
| [@types/semver](https://www.npmjs.com/package/@types/semver) | 7.7.1 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/semver |
|
|
54
|
+
| [ajv](https://www.npmjs.com/package/ajv) | 6.12.6, 8.17.1 | MIT | https://ajv.js.org |
|
|
55
|
+
| [ajv-formats](https://www.npmjs.com/package/ajv-formats) | 2.1.1, 3.0.1 | MIT | https://github.com/ajv-validator/ajv-formats#readme |
|
|
54
56
|
| [apache-arrow](https://www.npmjs.com/package/apache-arrow) | 21.1.0 | Apache-2.0 | https://arrow.apache.org/js/ |
|
|
55
57
|
| [class-variance-authority](https://www.npmjs.com/package/class-variance-authority) | 0.7.1 | Apache-2.0 | https://github.com/joe-bell/cva#readme |
|
|
56
58
|
| [clsx](https://www.npmjs.com/package/clsx) | 2.1.1 | MIT | https://github.com/lukeed/clsx#readme |
|
package/bin/appkit.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
6
|
+
import Ajv from "ajv";
|
|
7
|
+
import addFormats from "ajv-formats";
|
|
8
|
+
|
|
9
|
+
//#region src/cli/commands/plugins-sync.ts
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const PLUGIN_MANIFEST_SCHEMA_PATH = path.join(__dirname, "..", "..", "..", "schemas", "plugin-manifest.schema.json");
|
|
12
|
+
/**
|
|
13
|
+
* Checks whether a resolved file path is within a given directory boundary.
|
|
14
|
+
* Uses path.resolve + startsWith to prevent directory traversal.
|
|
15
|
+
*
|
|
16
|
+
* @param filePath - The path to check (will be resolved to absolute)
|
|
17
|
+
* @param boundary - The directory that must contain filePath
|
|
18
|
+
* @returns true if filePath is inside boundary (or equal to it)
|
|
19
|
+
*/
|
|
20
|
+
function isWithinDirectory(filePath, boundary) {
|
|
21
|
+
const resolvedPath = path.resolve(filePath);
|
|
22
|
+
const resolvedBoundary = path.resolve(boundary);
|
|
23
|
+
return resolvedPath === resolvedBoundary || resolvedPath.startsWith(`${resolvedBoundary}${path.sep}`);
|
|
24
|
+
}
|
|
25
|
+
let pluginManifestValidator = null;
|
|
26
|
+
/**
|
|
27
|
+
* Loads and compiles the plugin-manifest JSON schema (cached).
|
|
28
|
+
* Returns the compiled validate function or null if the schema cannot be loaded.
|
|
29
|
+
*/
|
|
30
|
+
function getPluginManifestValidator() {
|
|
31
|
+
if (pluginManifestValidator) return pluginManifestValidator;
|
|
32
|
+
try {
|
|
33
|
+
const schemaRaw = fs.readFileSync(PLUGIN_MANIFEST_SCHEMA_PATH, "utf-8");
|
|
34
|
+
const schema = JSON.parse(schemaRaw);
|
|
35
|
+
const ajv = new Ajv({
|
|
36
|
+
allErrors: true,
|
|
37
|
+
strict: false
|
|
38
|
+
});
|
|
39
|
+
addFormats(ajv);
|
|
40
|
+
pluginManifestValidator = ajv.compile(schema);
|
|
41
|
+
return pluginManifestValidator;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.warn("Warning: Could not load plugin-manifest schema for validation:", err instanceof Error ? err.message : err);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validates a parsed JSON object against the plugin-manifest JSON schema.
|
|
49
|
+
* Returns the manifest if valid, or null and logs schema errors.
|
|
50
|
+
*
|
|
51
|
+
* @param obj - The parsed JSON object to validate
|
|
52
|
+
* @param sourcePath - Path to the manifest file (for warning messages)
|
|
53
|
+
* @returns A valid PluginManifest or null
|
|
54
|
+
*/
|
|
55
|
+
function validateManifestWithSchema(obj, sourcePath) {
|
|
56
|
+
if (!obj || typeof obj !== "object") {
|
|
57
|
+
console.warn(`Warning: Manifest at ${sourcePath} is not a valid object`);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const validate = getPluginManifestValidator();
|
|
61
|
+
if (!validate) {
|
|
62
|
+
const m = obj;
|
|
63
|
+
if (typeof m.name === "string" && m.name.length > 0 && typeof m.displayName === "string" && m.displayName.length > 0 && typeof m.description === "string" && m.description.length > 0 && m.resources && typeof m.resources === "object" && Array.isArray(m.resources.required)) return obj;
|
|
64
|
+
console.warn(`Warning: Manifest at ${sourcePath} has invalid structure`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (validate(obj)) return obj;
|
|
68
|
+
const message = (validate.errors ?? []).map((e) => ` ${e.instancePath || "/"} ${e.message}${e.params ? ` (${JSON.stringify(e.params)})` : ""}`).join("\n");
|
|
69
|
+
console.warn(`Warning: Manifest at ${sourcePath} failed schema validation:\n${message}`);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Known packages that may contain AppKit plugins.
|
|
74
|
+
* Always scanned for manifests, even if not imported in the server file.
|
|
75
|
+
*/
|
|
76
|
+
const KNOWN_PLUGIN_PACKAGES = ["@databricks/appkit"];
|
|
77
|
+
/**
|
|
78
|
+
* Candidate paths for the server entry file, relative to cwd.
|
|
79
|
+
* Checked in order; the first that exists is used.
|
|
80
|
+
*/
|
|
81
|
+
const SERVER_FILE_CANDIDATES = ["server/server.ts"];
|
|
82
|
+
/**
|
|
83
|
+
* Find the server entry file by checking candidate paths in order.
|
|
84
|
+
*
|
|
85
|
+
* @param cwd - Current working directory
|
|
86
|
+
* @returns Absolute path to the server file, or null if none found
|
|
87
|
+
*/
|
|
88
|
+
function findServerFile(cwd) {
|
|
89
|
+
for (const candidate of SERVER_FILE_CANDIDATES) {
|
|
90
|
+
const fullPath = path.join(cwd, candidate);
|
|
91
|
+
if (fs.existsSync(fullPath)) return fullPath;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract all named imports from the AST root using structural node traversal.
|
|
97
|
+
* Handles single/double quotes, multiline imports, and aliased imports.
|
|
98
|
+
*
|
|
99
|
+
* @param root - AST root node
|
|
100
|
+
* @returns Array of parsed imports with name, original name, and source
|
|
101
|
+
*/
|
|
102
|
+
function parseImports(root) {
|
|
103
|
+
const imports = [];
|
|
104
|
+
const importStatements = root.findAll({ rule: { kind: "import_statement" } });
|
|
105
|
+
for (const stmt of importStatements) {
|
|
106
|
+
const sourceNode = stmt.find({ rule: { kind: "string" } });
|
|
107
|
+
if (!sourceNode) continue;
|
|
108
|
+
const source = sourceNode.text().replace(/^['"]|['"]$/g, "");
|
|
109
|
+
const namedImports = stmt.find({ rule: { kind: "named_imports" } });
|
|
110
|
+
if (!namedImports) continue;
|
|
111
|
+
const specifiers = namedImports.findAll({ rule: { kind: "import_specifier" } });
|
|
112
|
+
for (const specifier of specifiers) {
|
|
113
|
+
const children = specifier.children();
|
|
114
|
+
if (children.length >= 3) {
|
|
115
|
+
const originalName = children[0].text();
|
|
116
|
+
const localName = children[children.length - 1].text();
|
|
117
|
+
imports.push({
|
|
118
|
+
name: localName,
|
|
119
|
+
originalName,
|
|
120
|
+
source
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
const name = specifier.text();
|
|
124
|
+
imports.push({
|
|
125
|
+
name,
|
|
126
|
+
originalName: name,
|
|
127
|
+
source
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return imports;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Extract local names of plugins actually used in the `plugins: [...]` array
|
|
136
|
+
* passed to `createApp()`. Uses structural AST traversal to find `pair` nodes
|
|
137
|
+
* with key "plugins" and array values containing call expressions.
|
|
138
|
+
*
|
|
139
|
+
* @param root - AST root node
|
|
140
|
+
* @returns Set of local variable names used as plugin calls in the plugins array
|
|
141
|
+
*/
|
|
142
|
+
function parsePluginUsages(root) {
|
|
143
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
144
|
+
const pairs = root.findAll({ rule: { kind: "pair" } });
|
|
145
|
+
for (const pair of pairs) {
|
|
146
|
+
const key = pair.find({ rule: { kind: "property_identifier" } });
|
|
147
|
+
if (!key || key.text() !== "plugins") continue;
|
|
148
|
+
const arrayNode = pair.find({ rule: { kind: "array" } });
|
|
149
|
+
if (!arrayNode) continue;
|
|
150
|
+
for (const child of arrayNode.children()) if (child.kind() === "call_expression") {
|
|
151
|
+
const callee = child.children()[0];
|
|
152
|
+
if (callee?.kind() === "identifier") usedNames.add(callee.text());
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return usedNames;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* File extensions to try when resolving a relative import to a file path.
|
|
159
|
+
*/
|
|
160
|
+
const RESOLVE_EXTENSIONS = [
|
|
161
|
+
".ts",
|
|
162
|
+
".tsx",
|
|
163
|
+
".js",
|
|
164
|
+
".jsx"
|
|
165
|
+
];
|
|
166
|
+
/**
|
|
167
|
+
* Resolve a relative import source to the plugin directory containing a manifest.json.
|
|
168
|
+
* Follows the convention that plugins live in their own directory with a manifest.json.
|
|
169
|
+
*
|
|
170
|
+
* Resolution strategy:
|
|
171
|
+
* 1. If the import path is a directory, look for manifest.json directly in it
|
|
172
|
+
* 2. If the import path + extension is a file, look for manifest.json in its parent directory
|
|
173
|
+
* 3. If the import path is a directory with an index file, look for manifest.json in that directory
|
|
174
|
+
*
|
|
175
|
+
* @param importSource - The relative import specifier (e.g. "./plugins/my-plugin")
|
|
176
|
+
* @param serverFileDir - Absolute path to the directory containing the server file
|
|
177
|
+
* @returns Absolute path to manifest.json, or null if not found
|
|
178
|
+
*/
|
|
179
|
+
function resolveLocalManifest(importSource, serverFileDir, projectRoot) {
|
|
180
|
+
const resolved = path.resolve(serverFileDir, importSource);
|
|
181
|
+
if (!isWithinDirectory(resolved, projectRoot || serverFileDir)) {
|
|
182
|
+
console.warn(`Warning: Skipping import "${importSource}" — resolves outside the project directory`);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
186
|
+
const manifestPath = path.join(resolved, "manifest.json");
|
|
187
|
+
if (fs.existsSync(manifestPath)) return manifestPath;
|
|
188
|
+
}
|
|
189
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
190
|
+
const filePath = `${resolved}${ext}`;
|
|
191
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
192
|
+
const dir = path.dirname(filePath);
|
|
193
|
+
const manifestPath = path.join(dir, "manifest.json");
|
|
194
|
+
if (fs.existsSync(manifestPath)) return manifestPath;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
199
|
+
const indexPath = path.join(resolved, `index${ext}`);
|
|
200
|
+
if (fs.existsSync(indexPath)) {
|
|
201
|
+
const manifestPath = path.join(resolved, "manifest.json");
|
|
202
|
+
if (fs.existsSync(manifestPath)) return manifestPath;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Discover plugin manifests from local (relative) imports in the server file.
|
|
210
|
+
* Resolves each relative import to a directory and looks for manifest.json.
|
|
211
|
+
*
|
|
212
|
+
* @param relativeImports - Parsed imports with relative sources (starting with . or /)
|
|
213
|
+
* @param serverFileDir - Absolute path to the directory containing the server file
|
|
214
|
+
* @param cwd - Current working directory (for computing relative paths in output)
|
|
215
|
+
* @returns Map of plugin name to template plugin entry for local plugins
|
|
216
|
+
*/
|
|
217
|
+
function discoverLocalPlugins(relativeImports, serverFileDir, cwd) {
|
|
218
|
+
const plugins = {};
|
|
219
|
+
for (const imp of relativeImports) {
|
|
220
|
+
const manifestPath = resolveLocalManifest(imp.source, serverFileDir, cwd);
|
|
221
|
+
if (!manifestPath) continue;
|
|
222
|
+
try {
|
|
223
|
+
const content = fs.readFileSync(manifestPath, "utf-8");
|
|
224
|
+
const manifest = validateManifestWithSchema(JSON.parse(content), manifestPath);
|
|
225
|
+
if (!manifest) continue;
|
|
226
|
+
const relativePath = path.relative(cwd, path.dirname(manifestPath));
|
|
227
|
+
plugins[manifest.name] = {
|
|
228
|
+
name: manifest.name,
|
|
229
|
+
displayName: manifest.displayName,
|
|
230
|
+
description: manifest.description,
|
|
231
|
+
package: `./${relativePath}`,
|
|
232
|
+
resources: manifest.resources
|
|
233
|
+
};
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.warn(`Warning: Failed to parse manifest at ${manifestPath}:`, error instanceof Error ? error.message : error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return plugins;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Discover plugin manifests from a package's dist folder.
|
|
242
|
+
* Looks for manifest.json files in dist/plugins/{plugin-name}/ directories.
|
|
243
|
+
*
|
|
244
|
+
* @param packagePath - Path to the package in node_modules
|
|
245
|
+
* @returns Array of plugin manifests found in the package
|
|
246
|
+
*/
|
|
247
|
+
function discoverPluginManifests(packagePath) {
|
|
248
|
+
const pluginsDir = path.join(packagePath, "dist", "plugins");
|
|
249
|
+
const manifests = [];
|
|
250
|
+
if (!fs.existsSync(pluginsDir)) return manifests;
|
|
251
|
+
const entries = fs.readdirSync(pluginsDir, { withFileTypes: true });
|
|
252
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
253
|
+
const manifestPath = path.join(pluginsDir, entry.name, "manifest.json");
|
|
254
|
+
if (fs.existsSync(manifestPath)) try {
|
|
255
|
+
const content = fs.readFileSync(manifestPath, "utf-8");
|
|
256
|
+
const manifest = validateManifestWithSchema(JSON.parse(content), manifestPath);
|
|
257
|
+
if (manifest) manifests.push(manifest);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.warn(`Warning: Failed to parse manifest at ${manifestPath}:`, error instanceof Error ? error.message : error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return manifests;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Scan node_modules for packages with plugin manifests.
|
|
266
|
+
*
|
|
267
|
+
* @param cwd - Current working directory to search from
|
|
268
|
+
* @param packages - Set of npm package names to scan for plugin manifests
|
|
269
|
+
* @returns Map of plugin name to template plugin entry
|
|
270
|
+
*/
|
|
271
|
+
function scanForPlugins(cwd, packages) {
|
|
272
|
+
const plugins = {};
|
|
273
|
+
for (const packageName of packages) {
|
|
274
|
+
const packagePath = path.join(cwd, "node_modules", packageName);
|
|
275
|
+
if (!fs.existsSync(packagePath)) continue;
|
|
276
|
+
const manifests = discoverPluginManifests(packagePath);
|
|
277
|
+
for (const manifest of manifests) plugins[manifest.name] = {
|
|
278
|
+
name: manifest.name,
|
|
279
|
+
displayName: manifest.displayName,
|
|
280
|
+
description: manifest.description,
|
|
281
|
+
package: packageName,
|
|
282
|
+
resources: manifest.resources
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return plugins;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Run the plugins sync command.
|
|
289
|
+
* Parses the server entry file to discover which packages to scan for plugin
|
|
290
|
+
* manifests, then marks plugins that are actually used in the `plugins: [...]`
|
|
291
|
+
* array as requiredByTemplate.
|
|
292
|
+
*/
|
|
293
|
+
function runPluginsSync(options) {
|
|
294
|
+
const cwd = process.cwd();
|
|
295
|
+
const outputPath = path.resolve(cwd, options.output || "appkit.plugins.json");
|
|
296
|
+
if (!isWithinDirectory(outputPath, cwd)) {
|
|
297
|
+
console.error(`Error: Output path "${options.output}" resolves outside the project directory.`);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
console.log("Scanning for AppKit plugins...\n");
|
|
301
|
+
const serverFile = findServerFile(cwd);
|
|
302
|
+
let serverImports = [];
|
|
303
|
+
let pluginUsages = /* @__PURE__ */ new Set();
|
|
304
|
+
if (serverFile) {
|
|
305
|
+
const relativePath = path.relative(cwd, serverFile);
|
|
306
|
+
console.log(`Server entry file: ${relativePath}`);
|
|
307
|
+
const content = fs.readFileSync(serverFile, "utf-8");
|
|
308
|
+
const root = parse(serverFile.endsWith(".tsx") ? Lang.Tsx : Lang.TypeScript, content).root();
|
|
309
|
+
serverImports = parseImports(root);
|
|
310
|
+
pluginUsages = parsePluginUsages(root);
|
|
311
|
+
} else console.log("No server entry file found. Checked:", SERVER_FILE_CANDIDATES.join(", "));
|
|
312
|
+
const npmImports = serverImports.filter((i) => !i.source.startsWith(".") && !i.source.startsWith("/"));
|
|
313
|
+
const localImports = serverImports.filter((i) => i.source.startsWith(".") || i.source.startsWith("/"));
|
|
314
|
+
const npmPackages = new Set([...KNOWN_PLUGIN_PACKAGES, ...npmImports.map((i) => i.source)]);
|
|
315
|
+
const plugins = scanForPlugins(cwd, npmPackages);
|
|
316
|
+
if (serverFile && localImports.length > 0) {
|
|
317
|
+
const localPlugins = discoverLocalPlugins(localImports, path.dirname(serverFile), cwd);
|
|
318
|
+
Object.assign(plugins, localPlugins);
|
|
319
|
+
}
|
|
320
|
+
const pluginCount = Object.keys(plugins).length;
|
|
321
|
+
if (pluginCount === 0) {
|
|
322
|
+
console.log("No plugins found.");
|
|
323
|
+
console.log("\nMake sure you have plugin packages installed:");
|
|
324
|
+
for (const pkg of npmPackages) console.log(` - ${pkg}`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
const serverFileDir = serverFile ? path.dirname(serverFile) : cwd;
|
|
328
|
+
for (const imp of serverImports) {
|
|
329
|
+
if (!pluginUsages.has(imp.name)) continue;
|
|
330
|
+
const isLocal = imp.source.startsWith(".") || imp.source.startsWith("/");
|
|
331
|
+
let plugin;
|
|
332
|
+
if (isLocal) {
|
|
333
|
+
const resolvedImportDir = path.resolve(serverFileDir, imp.source);
|
|
334
|
+
plugin = Object.values(plugins).find((p) => {
|
|
335
|
+
if (!p.package.startsWith(".")) return false;
|
|
336
|
+
return path.resolve(cwd, p.package) === resolvedImportDir && p.name === imp.originalName;
|
|
337
|
+
});
|
|
338
|
+
} else plugin = Object.values(plugins).find((p) => p.package === imp.source && p.name === imp.originalName);
|
|
339
|
+
if (plugin) plugin.requiredByTemplate = true;
|
|
340
|
+
}
|
|
341
|
+
console.log(`\nFound ${pluginCount} plugin(s):`);
|
|
342
|
+
for (const [name, manifest] of Object.entries(plugins)) {
|
|
343
|
+
const resourceCount = manifest.resources.required.length + manifest.resources.optional.length;
|
|
344
|
+
const resourceInfo = resourceCount > 0 ? ` [${resourceCount} resource(s)]` : "";
|
|
345
|
+
const mandatoryTag = manifest.requiredByTemplate ? " (mandatory)" : "";
|
|
346
|
+
console.log(` ${manifest.requiredByTemplate ? "●" : "○"} ${manifest.displayName} (${name}) from ${manifest.package}${resourceInfo}${mandatoryTag}`);
|
|
347
|
+
}
|
|
348
|
+
const templateManifest = {
|
|
349
|
+
$schema: "https://databricks.github.io/appkit/schemas/template-plugins.schema.json",
|
|
350
|
+
version: "1.0",
|
|
351
|
+
plugins
|
|
352
|
+
};
|
|
353
|
+
if (options.write) {
|
|
354
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(templateManifest, null, 2)}\n`);
|
|
355
|
+
console.log(`\n✓ Wrote ${outputPath}`);
|
|
356
|
+
} else {
|
|
357
|
+
console.log("\nTo write the manifest, run:");
|
|
358
|
+
console.log(" npx appkit plugins sync --write\n");
|
|
359
|
+
console.log("Preview:");
|
|
360
|
+
console.log("─".repeat(60));
|
|
361
|
+
console.log(JSON.stringify(templateManifest, null, 2));
|
|
362
|
+
console.log("─".repeat(60));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const pluginsSyncCommand = new Command("sync").description("Sync plugin manifests from installed packages into appkit.plugins.json").option("-w, --write", "Write the manifest file").option("-o, --output <path>", "Output file path (default: ./appkit.plugins.json)").action(runPluginsSync);
|
|
366
|
+
|
|
367
|
+
//#endregion
|
|
368
|
+
export { pluginsSyncCommand };
|
|
369
|
+
//# sourceMappingURL=plugins-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins-sync.js","names":[],"sources":["../../../src/cli/commands/plugins-sync.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Lang, parse, type SgNode } from \"@ast-grep/napi\";\nimport Ajv, { type ErrorObject } from \"ajv\";\nimport addFormats from \"ajv-formats\";\nimport { Command } from \"commander\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n// Resolve to package schemas: from dist/cli/commands -> dist/schemas, from src/cli/commands -> shared/schemas\nconst PLUGIN_MANIFEST_SCHEMA_PATH = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"schemas\",\n \"plugin-manifest.schema.json\",\n);\n\n/**\n * Field entry in a resource requirement (env var + optional description)\n */\ninterface ResourceFieldEntry {\n env: string;\n description?: string;\n}\n\n/**\n * Resource requirement as defined in plugin manifests.\n * Uses fields (single key e.g. id, or multiple e.g. instance_name/database_name, scope/key).\n */\ninterface ResourceRequirement {\n type: string;\n alias: string;\n resourceKey: string;\n description: string;\n permission: string;\n fields: Record<string, ResourceFieldEntry>;\n}\n\n/**\n * Plugin manifest structure (from SDK plugin manifest.json files)\n */\ninterface PluginManifest {\n name: string;\n displayName: string;\n description: string;\n resources: {\n required: ResourceRequirement[];\n optional: ResourceRequirement[];\n };\n config?: { schema: unknown };\n}\n\n/**\n * Plugin entry in the template manifest (includes package source)\n */\ninterface TemplatePlugin extends Omit<PluginManifest, \"config\"> {\n package: string;\n /** When true, this plugin is required by the template and cannot be deselected during CLI init. */\n requiredByTemplate?: boolean;\n}\n\n/**\n * Template plugins manifest structure\n */\ninterface TemplatePluginsManifest {\n $schema: string;\n version: string;\n plugins: Record<string, TemplatePlugin>;\n}\n\n/**\n * Checks whether a resolved file path is within a given directory boundary.\n * Uses path.resolve + startsWith to prevent directory traversal.\n *\n * @param filePath - The path to check (will be resolved to absolute)\n * @param boundary - The directory that must contain filePath\n * @returns true if filePath is inside boundary (or equal to it)\n */\nfunction isWithinDirectory(filePath: string, boundary: string): boolean {\n const resolvedPath = path.resolve(filePath);\n const resolvedBoundary = path.resolve(boundary);\n // Append separator to avoid prefix false-positives (e.g. /foo-bar matching /foo)\n return (\n resolvedPath === resolvedBoundary ||\n resolvedPath.startsWith(`${resolvedBoundary}${path.sep}`)\n );\n}\n\nlet pluginManifestValidator: ReturnType<Ajv[\"compile\"]> | null = null;\n\n/**\n * Loads and compiles the plugin-manifest JSON schema (cached).\n * Returns the compiled validate function or null if the schema cannot be loaded.\n */\nfunction getPluginManifestValidator(): ReturnType<Ajv[\"compile\"]> | null {\n if (pluginManifestValidator) return pluginManifestValidator;\n try {\n const schemaRaw = fs.readFileSync(PLUGIN_MANIFEST_SCHEMA_PATH, \"utf-8\");\n const schema = JSON.parse(schemaRaw) as object;\n const ajv = new Ajv({ allErrors: true, strict: false });\n addFormats(ajv);\n pluginManifestValidator = ajv.compile(schema);\n return pluginManifestValidator;\n } catch (err) {\n console.warn(\n \"Warning: Could not load plugin-manifest schema for validation:\",\n err instanceof Error ? err.message : err,\n );\n return null;\n }\n}\n\n/**\n * Validates a parsed JSON object against the plugin-manifest JSON schema.\n * Returns the manifest if valid, or null and logs schema errors.\n *\n * @param obj - The parsed JSON object to validate\n * @param sourcePath - Path to the manifest file (for warning messages)\n * @returns A valid PluginManifest or null\n */\nfunction validateManifestWithSchema(\n obj: unknown,\n sourcePath: string,\n): PluginManifest | null {\n if (!obj || typeof obj !== \"object\") {\n console.warn(`Warning: Manifest at ${sourcePath} is not a valid object`);\n return null;\n }\n\n const validate = getPluginManifestValidator();\n if (!validate) {\n // Schema not available (e.g. dev without build); fall back to basic shape check\n const m = obj as Record<string, unknown>;\n if (\n typeof m.name === \"string\" &&\n m.name.length > 0 &&\n typeof m.displayName === \"string\" &&\n m.displayName.length > 0 &&\n typeof m.description === \"string\" &&\n m.description.length > 0 &&\n m.resources &&\n typeof m.resources === \"object\" &&\n Array.isArray((m.resources as { required?: unknown }).required)\n ) {\n return obj as PluginManifest;\n }\n console.warn(`Warning: Manifest at ${sourcePath} has invalid structure`);\n return null;\n }\n\n const valid = validate(obj);\n if (valid) return obj as PluginManifest;\n\n const errors: ErrorObject[] = validate.errors ?? [];\n const message = errors\n .map(\n (e: ErrorObject) =>\n ` ${e.instancePath || \"/\"} ${e.message}${e.params ? ` (${JSON.stringify(e.params)})` : \"\"}`,\n )\n .join(\"\\n\");\n console.warn(\n `Warning: Manifest at ${sourcePath} failed schema validation:\\n${message}`,\n );\n return null;\n}\n\n/**\n * Known packages that may contain AppKit plugins.\n * Always scanned for manifests, even if not imported in the server file.\n */\nconst KNOWN_PLUGIN_PACKAGES = [\"@databricks/appkit\"];\n\n/**\n * Candidate paths for the server entry file, relative to cwd.\n * Checked in order; the first that exists is used.\n */\nconst SERVER_FILE_CANDIDATES = [\"server/server.ts\"];\n\n/**\n * Find the server entry file by checking candidate paths in order.\n *\n * @param cwd - Current working directory\n * @returns Absolute path to the server file, or null if none found\n */\nfunction findServerFile(cwd: string): string | null {\n for (const candidate of SERVER_FILE_CANDIDATES) {\n const fullPath = path.join(cwd, candidate);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * Represents a single named import extracted from the server file.\n */\ninterface ParsedImport {\n /** The imported name (or local alias if renamed) */\n name: string;\n /** The original exported name (differs from name when using `import { foo as bar }`) */\n originalName: string;\n /** The module specifier (package name or relative path) */\n source: string;\n}\n\n/**\n * Extract all named imports from the AST root using structural node traversal.\n * Handles single/double quotes, multiline imports, and aliased imports.\n *\n * @param root - AST root node\n * @returns Array of parsed imports with name, original name, and source\n */\nfunction parseImports(root: SgNode): ParsedImport[] {\n const imports: ParsedImport[] = [];\n\n // Find all import_statement nodes in the AST\n const importStatements = root.findAll({\n rule: { kind: \"import_statement\" },\n });\n\n for (const stmt of importStatements) {\n // Extract the module specifier (the string node, e.g. '@databricks/appkit')\n const sourceNode = stmt.find({ rule: { kind: \"string\" } });\n if (!sourceNode) continue;\n\n // Strip surrounding quotes from the string node text\n const source = sourceNode.text().replace(/^['\"]|['\"]$/g, \"\");\n\n // Find named_imports block: { createApp, analytics, server }\n const namedImports = stmt.find({ rule: { kind: \"named_imports\" } });\n if (!namedImports) continue;\n\n // Extract each import_specifier\n const specifiers = namedImports.findAll({\n rule: { kind: \"import_specifier\" },\n });\n\n for (const specifier of specifiers) {\n const children = specifier.children();\n if (children.length >= 3) {\n // Aliased import: `foo as bar` — children are [name, \"as\", alias]\n const originalName = children[0].text();\n const localName = children[children.length - 1].text();\n imports.push({ name: localName, originalName, source });\n } else {\n // Simple import: `foo`\n const name = specifier.text();\n imports.push({ name, originalName: name, source });\n }\n }\n }\n\n return imports;\n}\n\n/**\n * Extract local names of plugins actually used in the `plugins: [...]` array\n * passed to `createApp()`. Uses structural AST traversal to find `pair` nodes\n * with key \"plugins\" and array values containing call expressions.\n *\n * @param root - AST root node\n * @returns Set of local variable names used as plugin calls in the plugins array\n */\nfunction parsePluginUsages(root: SgNode): Set<string> {\n const usedNames = new Set<string>();\n\n // Find all property pairs in the AST\n const pairs = root.findAll({ rule: { kind: \"pair\" } });\n\n for (const pair of pairs) {\n // Check if the property key is \"plugins\"\n const key = pair.find({ rule: { kind: \"property_identifier\" } });\n if (!key || key.text() !== \"plugins\") continue;\n\n // Find the array value\n const arrayNode = pair.find({ rule: { kind: \"array\" } });\n if (!arrayNode) continue;\n\n // Iterate direct children of the array to find call expressions\n for (const child of arrayNode.children()) {\n if (child.kind() === \"call_expression\") {\n // The callee is the first child (the identifier being called)\n const callee = child.children()[0];\n if (callee?.kind() === \"identifier\") {\n usedNames.add(callee.text());\n }\n }\n }\n }\n\n return usedNames;\n}\n\n/**\n * File extensions to try when resolving a relative import to a file path.\n */\nconst RESOLVE_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"];\n\n/**\n * Resolve a relative import source to the plugin directory containing a manifest.json.\n * Follows the convention that plugins live in their own directory with a manifest.json.\n *\n * Resolution strategy:\n * 1. If the import path is a directory, look for manifest.json directly in it\n * 2. If the import path + extension is a file, look for manifest.json in its parent directory\n * 3. If the import path is a directory with an index file, look for manifest.json in that directory\n *\n * @param importSource - The relative import specifier (e.g. \"./plugins/my-plugin\")\n * @param serverFileDir - Absolute path to the directory containing the server file\n * @returns Absolute path to manifest.json, or null if not found\n */\nfunction resolveLocalManifest(\n importSource: string,\n serverFileDir: string,\n projectRoot?: string,\n): string | null {\n const resolved = path.resolve(serverFileDir, importSource);\n\n // Security: Reject paths that escape the project root\n const boundary = projectRoot || serverFileDir;\n if (!isWithinDirectory(resolved, boundary)) {\n console.warn(\n `Warning: Skipping import \"${importSource}\" — resolves outside the project directory`,\n );\n return null;\n }\n\n // Case 1: Import path is a directory with manifest.json\n // e.g. ./plugins/my-plugin → ./plugins/my-plugin/manifest.json\n if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {\n const manifestPath = path.join(resolved, \"manifest.json\");\n if (fs.existsSync(manifestPath)) return manifestPath;\n }\n\n // Case 2: Import path + extension resolves to a file\n // e.g. ./plugins/my-plugin → ./plugins/my-plugin.ts\n // Look for manifest.json in the same directory\n for (const ext of RESOLVE_EXTENSIONS) {\n const filePath = `${resolved}${ext}`;\n if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {\n const dir = path.dirname(filePath);\n const manifestPath = path.join(dir, \"manifest.json\");\n if (fs.existsSync(manifestPath)) return manifestPath;\n break;\n }\n }\n\n // Case 3: Import path is a directory with an index file\n // e.g. ./plugins/my-plugin → ./plugins/my-plugin/index.ts\n for (const ext of RESOLVE_EXTENSIONS) {\n const indexPath = path.join(resolved, `index${ext}`);\n if (fs.existsSync(indexPath)) {\n const manifestPath = path.join(resolved, \"manifest.json\");\n if (fs.existsSync(manifestPath)) return manifestPath;\n break;\n }\n }\n\n return null;\n}\n\n/**\n * Discover plugin manifests from local (relative) imports in the server file.\n * Resolves each relative import to a directory and looks for manifest.json.\n *\n * @param relativeImports - Parsed imports with relative sources (starting with . or /)\n * @param serverFileDir - Absolute path to the directory containing the server file\n * @param cwd - Current working directory (for computing relative paths in output)\n * @returns Map of plugin name to template plugin entry for local plugins\n */\nfunction discoverLocalPlugins(\n relativeImports: ParsedImport[],\n serverFileDir: string,\n cwd: string,\n): TemplatePluginsManifest[\"plugins\"] {\n const plugins: TemplatePluginsManifest[\"plugins\"] = {};\n\n for (const imp of relativeImports) {\n const manifestPath = resolveLocalManifest(imp.source, serverFileDir, cwd);\n if (!manifestPath) continue;\n\n try {\n const content = fs.readFileSync(manifestPath, \"utf-8\");\n const parsed = JSON.parse(content);\n const manifest = validateManifestWithSchema(parsed, manifestPath);\n if (!manifest) continue;\n\n const relativePath = path.relative(cwd, path.dirname(manifestPath));\n\n plugins[manifest.name] = {\n name: manifest.name,\n displayName: manifest.displayName,\n description: manifest.description,\n package: `./${relativePath}`,\n resources: manifest.resources,\n };\n } catch (error) {\n console.warn(\n `Warning: Failed to parse manifest at ${manifestPath}:`,\n error instanceof Error ? error.message : error,\n );\n }\n }\n\n return plugins;\n}\n\n/**\n * Discover plugin manifests from a package's dist folder.\n * Looks for manifest.json files in dist/plugins/{plugin-name}/ directories.\n *\n * @param packagePath - Path to the package in node_modules\n * @returns Array of plugin manifests found in the package\n */\nfunction discoverPluginManifests(packagePath: string): PluginManifest[] {\n const pluginsDir = path.join(packagePath, \"dist\", \"plugins\");\n const manifests: PluginManifest[] = [];\n\n if (!fs.existsSync(pluginsDir)) {\n return manifests;\n }\n\n const entries = fs.readdirSync(pluginsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const manifestPath = path.join(pluginsDir, entry.name, \"manifest.json\");\n if (fs.existsSync(manifestPath)) {\n try {\n const content = fs.readFileSync(manifestPath, \"utf-8\");\n const parsed = JSON.parse(content);\n const manifest = validateManifestWithSchema(parsed, manifestPath);\n if (manifest) {\n manifests.push(manifest);\n }\n } catch (error) {\n console.warn(\n `Warning: Failed to parse manifest at ${manifestPath}:`,\n error instanceof Error ? error.message : error,\n );\n }\n }\n }\n }\n\n return manifests;\n}\n\n/**\n * Scan node_modules for packages with plugin manifests.\n *\n * @param cwd - Current working directory to search from\n * @param packages - Set of npm package names to scan for plugin manifests\n * @returns Map of plugin name to template plugin entry\n */\nfunction scanForPlugins(\n cwd: string,\n packages: Iterable<string>,\n): TemplatePluginsManifest[\"plugins\"] {\n const plugins: TemplatePluginsManifest[\"plugins\"] = {};\n\n for (const packageName of packages) {\n const packagePath = path.join(cwd, \"node_modules\", packageName);\n if (!fs.existsSync(packagePath)) {\n continue;\n }\n\n const manifests = discoverPluginManifests(packagePath);\n for (const manifest of manifests) {\n // Convert to template plugin format (exclude config schema)\n plugins[manifest.name] = {\n name: manifest.name,\n displayName: manifest.displayName,\n description: manifest.description,\n package: packageName,\n resources: manifest.resources,\n };\n }\n }\n\n return plugins;\n}\n\n/**\n * Run the plugins sync command.\n * Parses the server entry file to discover which packages to scan for plugin\n * manifests, then marks plugins that are actually used in the `plugins: [...]`\n * array as requiredByTemplate.\n */\nfunction runPluginsSync(options: { write?: boolean; output?: string }) {\n const cwd = process.cwd();\n const outputPath = path.resolve(cwd, options.output || \"appkit.plugins.json\");\n\n // Security: Reject output paths that escape the project root\n if (!isWithinDirectory(outputPath, cwd)) {\n console.error(\n `Error: Output path \"${options.output}\" resolves outside the project directory.`,\n );\n process.exit(1);\n }\n\n console.log(\"Scanning for AppKit plugins...\\n\");\n\n // Step 1: Parse server file to discover imports and plugin usages\n const serverFile = findServerFile(cwd);\n let serverImports: ParsedImport[] = [];\n let pluginUsages = new Set<string>();\n\n if (serverFile) {\n const relativePath = path.relative(cwd, serverFile);\n console.log(`Server entry file: ${relativePath}`);\n\n const content = fs.readFileSync(serverFile, \"utf-8\");\n const lang = serverFile.endsWith(\".tsx\") ? Lang.Tsx : Lang.TypeScript;\n const ast = parse(lang, content);\n const root = ast.root();\n\n serverImports = parseImports(root);\n pluginUsages = parsePluginUsages(root);\n } else {\n console.log(\n \"No server entry file found. Checked:\",\n SERVER_FILE_CANDIDATES.join(\", \"),\n );\n }\n\n // Step 2: Split imports into npm packages and local (relative) imports\n const npmImports = serverImports.filter(\n (i) => !i.source.startsWith(\".\") && !i.source.startsWith(\"/\"),\n );\n const localImports = serverImports.filter(\n (i) => i.source.startsWith(\".\") || i.source.startsWith(\"/\"),\n );\n\n // Step 3: Scan npm packages for plugin manifests\n const npmPackages = new Set([\n ...KNOWN_PLUGIN_PACKAGES,\n ...npmImports.map((i) => i.source),\n ]);\n const plugins = scanForPlugins(cwd, npmPackages);\n\n // Step 4: Discover local plugin manifests from relative imports\n if (serverFile && localImports.length > 0) {\n const serverFileDir = path.dirname(serverFile);\n const localPlugins = discoverLocalPlugins(localImports, serverFileDir, cwd);\n Object.assign(plugins, localPlugins);\n }\n\n const pluginCount = Object.keys(plugins).length;\n\n if (pluginCount === 0) {\n console.log(\"No plugins found.\");\n console.log(\"\\nMake sure you have plugin packages installed:\");\n for (const pkg of npmPackages) {\n console.log(` - ${pkg}`);\n }\n process.exit(1);\n }\n\n // Step 5: Mark plugins that are imported AND used in the plugins array as mandatory.\n // For npm imports, match by package name + plugin name.\n // For local imports, resolve both paths to absolute and compare.\n const serverFileDir = serverFile ? path.dirname(serverFile) : cwd;\n\n for (const imp of serverImports) {\n if (!pluginUsages.has(imp.name)) continue;\n\n const isLocal = imp.source.startsWith(\".\") || imp.source.startsWith(\"/\");\n let plugin: TemplatePlugin | undefined;\n\n if (isLocal) {\n // Resolve the import source to an absolute path from the server file directory\n const resolvedImportDir = path.resolve(serverFileDir, imp.source);\n plugin = Object.values(plugins).find((p) => {\n if (!p.package.startsWith(\".\")) return false;\n const resolvedPluginDir = path.resolve(cwd, p.package);\n return (\n resolvedPluginDir === resolvedImportDir && p.name === imp.originalName\n );\n });\n } else {\n // npm import: direct string comparison\n plugin = Object.values(plugins).find(\n (p) => p.package === imp.source && p.name === imp.originalName,\n );\n }\n\n if (plugin) {\n plugin.requiredByTemplate = true;\n }\n }\n\n console.log(`\\nFound ${pluginCount} plugin(s):`);\n for (const [name, manifest] of Object.entries(plugins)) {\n const resourceCount =\n manifest.resources.required.length + manifest.resources.optional.length;\n const resourceInfo =\n resourceCount > 0 ? ` [${resourceCount} resource(s)]` : \"\";\n const mandatoryTag = manifest.requiredByTemplate ? \" (mandatory)\" : \"\";\n console.log(\n ` ${manifest.requiredByTemplate ? \"●\" : \"○\"} ${manifest.displayName} (${name}) from ${manifest.package}${resourceInfo}${mandatoryTag}`,\n );\n }\n\n const templateManifest: TemplatePluginsManifest = {\n $schema:\n \"https://databricks.github.io/appkit/schemas/template-plugins.schema.json\",\n version: \"1.0\",\n plugins,\n };\n\n if (options.write) {\n fs.writeFileSync(\n outputPath,\n `${JSON.stringify(templateManifest, null, 2)}\\n`,\n );\n console.log(`\\n✓ Wrote ${outputPath}`);\n } else {\n console.log(\"\\nTo write the manifest, run:\");\n console.log(\" npx appkit plugins sync --write\\n\");\n console.log(\"Preview:\");\n console.log(\"─\".repeat(60));\n console.log(JSON.stringify(templateManifest, null, 2));\n console.log(\"─\".repeat(60));\n }\n}\n\n/** Exported for testing: path boundary check, AST parsing. */\nexport { isWithinDirectory, parseImports, parsePluginUsages };\n\nexport const pluginsSyncCommand = new Command(\"sync\")\n .description(\n \"Sync plugin manifests from installed packages into appkit.plugins.json\",\n )\n .option(\"-w, --write\", \"Write the manifest file\")\n .option(\n \"-o, --output <path>\",\n \"Output file path (default: ./appkit.plugins.json)\",\n )\n .action(runPluginsSync);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { pluginsSyncCommand } from "./plugins-sync.js";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/commands/plugins.ts
|
|
5
|
+
/**
|
|
6
|
+
* Parent command for plugin management operations.
|
|
7
|
+
* Subcommands:
|
|
8
|
+
* - sync: Aggregate plugin manifests into appkit.plugins.json
|
|
9
|
+
*
|
|
10
|
+
* Future subcommands may include:
|
|
11
|
+
* - add: Add a plugin to an existing project
|
|
12
|
+
* - remove: Remove a plugin from a project
|
|
13
|
+
* - list: List available plugins
|
|
14
|
+
*/
|
|
15
|
+
const pluginsCommand = new Command("plugins").description("Plugin management commands").addCommand(pluginsSyncCommand);
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { pluginsCommand };
|
|
19
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","names":[],"sources":["../../../src/cli/commands/plugins.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { pluginsSyncCommand } from \"./plugins-sync.js\";\n\n/**\n * Parent command for plugin management operations.\n * Subcommands:\n * - sync: Aggregate plugin manifests into appkit.plugins.json\n *\n * Future subcommands may include:\n * - add: Add a plugin to an existing project\n * - remove: Remove a plugin from a project\n * - list: List available plugins\n */\nexport const pluginsCommand = new Command(\"plugins\")\n .description(\"Plugin management commands\")\n .addCommand(pluginsSyncCommand);\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,iBAAiB,IAAI,QAAQ,UAAU,CACjD,YAAY,6BAA6B,CACzC,WAAW,mBAAmB"}
|
package/dist/cli/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { docsCommand } from "./commands/docs.js";
|
|
3
3
|
import { generateTypesCommand } from "./commands/generate-types.js";
|
|
4
4
|
import { lintCommand } from "./commands/lint.js";
|
|
5
|
+
import { pluginsCommand } from "./commands/plugins.js";
|
|
5
6
|
import { setupCommand } from "./commands/setup.js";
|
|
6
7
|
import { readFileSync } from "node:fs";
|
|
7
8
|
import { dirname, join } from "node:path";
|
|
@@ -17,6 +18,7 @@ cmd.addCommand(setupCommand);
|
|
|
17
18
|
cmd.addCommand(generateTypesCommand);
|
|
18
19
|
cmd.addCommand(lintCommand);
|
|
19
20
|
cmd.addCommand(docsCommand);
|
|
21
|
+
cmd.addCommand(pluginsCommand);
|
|
20
22
|
cmd.parse();
|
|
21
23
|
|
|
22
24
|
//#endregion
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { docsCommand } from \"./commands/docs.js\";\nimport { generateTypesCommand } from \"./commands/generate-types.js\";\nimport { lintCommand } from \"./commands/lint.js\";\nimport { setupCommand } from \"./commands/setup.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgPath = join(__dirname, \"../../package.json\");\nconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\nconst cmd = new Command();\n\ncmd\n .name(\"appkit\")\n .description(\"CLI tools for Databricks AppKit\")\n .version(pkg.version);\n\ncmd.addCommand(setupCommand);\ncmd.addCommand(generateTypesCommand);\ncmd.addCommand(lintCommand);\ncmd.addCommand(docsCommand);\n\ncmd.parse();\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { docsCommand } from \"./commands/docs.js\";\nimport { generateTypesCommand } from \"./commands/generate-types.js\";\nimport { lintCommand } from \"./commands/lint.js\";\nimport { pluginsCommand } from \"./commands/plugins.js\";\nimport { setupCommand } from \"./commands/setup.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgPath = join(__dirname, \"../../package.json\");\nconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\nconst cmd = new Command();\n\ncmd\n .name(\"appkit\")\n .description(\"CLI tools for Databricks AppKit\")\n .version(pkg.version);\n\ncmd.addCommand(setupCommand);\ncmd.addCommand(generateTypesCommand);\ncmd.addCommand(lintCommand);\ncmd.addCommand(docsCommand);\ncmd.addCommand(pluginsCommand);\n\ncmd.parse();\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,UAAU,KADE,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EACzB,qBAAqB;AACrD,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AAEtD,MAAM,MAAM,IAAI,SAAS;AAEzB,IACG,KAAK,SAAS,CACd,YAAY,kCAAkC,CAC9C,QAAQ,IAAI,QAAQ;AAEvB,IAAI,WAAW,aAAa;AAC5B,IAAI,WAAW,qBAAqB;AACpC,IAAI,WAAW,YAAY;AAC3B,IAAI,WAAW,YAAY;AAC3B,IAAI,WAAW,eAAe;AAE9B,IAAI,OAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AreaChartProps } from "../types.js";
|
|
2
2
|
import "react";
|
|
3
|
-
import * as
|
|
3
|
+
import * as react_jsx_runtime5 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/charts/area/index.d.ts
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
|
14
14
|
* Supports both query mode (queryKey + parameters) and data mode (static data).
|
|
15
15
|
*/
|
|
16
16
|
declare const AreaChart: {
|
|
17
|
-
(props: AreaChartProps):
|
|
17
|
+
(props: AreaChartProps): react_jsx_runtime5.JSX.Element;
|
|
18
18
|
displayName: string;
|
|
19
19
|
};
|
|
20
20
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BarChartProps } from "../types.js";
|
|
2
2
|
import "react";
|
|
3
|
-
import * as
|
|
3
|
+
import * as react_jsx_runtime6 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/charts/bar/index.d.ts
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ import * as react_jsx_runtime1 from "react/jsx-runtime";
|
|
|
14
14
|
* Supports both query mode (queryKey + parameters) and data mode (static data).
|
|
15
15
|
*/
|
|
16
16
|
declare const BarChart: {
|
|
17
|
-
(props: BarChartProps):
|
|
17
|
+
(props: BarChartProps): react_jsx_runtime6.JSX.Element;
|
|
18
18
|
displayName: string;
|
|
19
19
|
};
|
|
20
20
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChartColorPalette, ChartData, ChartType, Orientation } from "./types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime13 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/react/charts/base.d.ts
|
|
5
5
|
interface BaseChartProps {
|
|
@@ -83,7 +83,7 @@ declare function BaseChart({
|
|
|
83
83
|
max,
|
|
84
84
|
options: customOptions,
|
|
85
85
|
className
|
|
86
|
-
}: BaseChartProps):
|
|
86
|
+
}: BaseChartProps): react_jsx_runtime13.JSX.Element;
|
|
87
87
|
//#endregion
|
|
88
88
|
export { BaseChart, BaseChartProps };
|
|
89
89
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","names":[],"sources":["../../../src/react/charts/base.tsx"],"sourcesContent":[],"mappings":";;;;UA0CiB,cAAA;;QAET;EAFS;EAAc,SAAA,EAIlB,SAJkB;;MAIlB,CAAA,EAAA,MAAA;;MAmBI,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;;EA0BC,WAAA,CAAA,EAvCF,WAuCE;EAaF;EAAS,MAAA,CAAA,EAAA,MAAA;;OAEvB,CAAA,EAAA,MAAA;;YAEA,CAAA,EAAA,OAAA;;;;;;;cAOA,CAAA,EAlDe,iBAkDf;;QAEA,CAAA,EAAA,MAAA,EAAA;;YAEA,CAAA,EAAA,OAAA;;QAEA,CAAA,EAAA,OAAA;;SAEA,CAAA,EAAA,OAAA;;YAEA,CAAA,EAAA,MAAA;;UAEA,CAAA,EAAA,OAAA;;aACe,CAAA,EAAA,MAAA;EAAA;;;;;;;;;;;YArCL;;;;;;;;iBAaI,SAAA;;;;;;;;;;UAUN;;;;;;;;;;;;WAYC;;GAER,iBAAc,
|
|
1
|
+
{"version":3,"file":"base.d.ts","names":[],"sources":["../../../src/react/charts/base.tsx"],"sourcesContent":[],"mappings":";;;;UA0CiB,cAAA;;QAET;EAFS;EAAc,SAAA,EAIlB,SAJkB;;MAIlB,CAAA,EAAA,MAAA;;MAmBI,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;;EA0BC,WAAA,CAAA,EAvCF,WAuCE;EAaF;EAAS,MAAA,CAAA,EAAA,MAAA;;OAEvB,CAAA,EAAA,MAAA;;YAEA,CAAA,EAAA,OAAA;;;;;;;cAOA,CAAA,EAlDe,iBAkDf;;QAEA,CAAA,EAAA,MAAA,EAAA;;YAEA,CAAA,EAAA,OAAA;;QAEA,CAAA,EAAA,OAAA;;SAEA,CAAA,EAAA,OAAA;;YAEA,CAAA,EAAA,MAAA;;UAEA,CAAA,EAAA,OAAA;;aACe,CAAA,EAAA,MAAA;EAAA;;;;;;;;;;;YArCL;;;;;;;;iBAaI,SAAA;;;;;;;;;;UAUN;;;;;;;;;;;;WAYC;;GAER,iBAAc,mBAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChartType, UnifiedChartProps } from "./types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime14 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/react/charts/create-chart.d.ts
|
|
5
5
|
|
|
@@ -18,7 +18,7 @@ import * as react_jsx_runtime10 from "react/jsx-runtime";
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
declare function createChart<TProps extends UnifiedChartProps>(chartType: ChartType, displayName: string): {
|
|
21
|
-
(props: TProps):
|
|
21
|
+
(props: TProps): react_jsx_runtime14.JSX.Element;
|
|
22
22
|
displayName: string;
|
|
23
23
|
};
|
|
24
24
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HeatmapChartProps } from "../types.js";
|
|
2
2
|
import "react";
|
|
3
|
-
import * as
|
|
3
|
+
import * as react_jsx_runtime7 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/charts/heatmap/index.d.ts
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ import * as react_jsx_runtime2 from "react/jsx-runtime";
|
|
|
19
19
|
* Supports both query mode (queryKey + parameters) and data mode (static data).
|
|
20
20
|
*/
|
|
21
21
|
declare const HeatmapChart: {
|
|
22
|
-
(props: HeatmapChartProps):
|
|
22
|
+
(props: HeatmapChartProps): react_jsx_runtime7.JSX.Element;
|
|
23
23
|
displayName: string;
|
|
24
24
|
};
|
|
25
25
|
//#endregion
|