@databricks/appkit 0.5.3 → 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 +14 -3
- package/NOTICE.md +2 -0
- package/bin/appkit.js +0 -0
- package/dist/appkit/package.js +1 -1
- package/dist/cache/index.js +2 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/storage/persistent.js.map +1 -1
- 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/connectors/index.js +2 -2
- package/dist/connectors/{lakebase → lakebase-v1}/client.js +31 -17
- package/dist/connectors/lakebase-v1/client.js.map +1 -0
- package/dist/connectors/lakebase-v1/defaults.js +18 -0
- package/dist/connectors/lakebase-v1/defaults.js.map +1 -0
- package/dist/connectors/lakebase-v1/index.js +3 -0
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js +5 -1
- package/dist/core/appkit.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/plugin.d.ts +95 -3
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +94 -6
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts +3 -1
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +3 -1
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/analytics/index.js +1 -0
- package/dist/plugins/analytics/manifest.js +21 -0
- package/dist/plugins/analytics/manifest.js.map +1 -0
- package/dist/plugins/analytics/manifest.json +36 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/server/index.d.ts +3 -1
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +3 -1
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/server/manifest.js +21 -0
- package/dist/plugins/server/manifest.js.map +1 -0
- package/dist/plugins/server/manifest.json +36 -0
- package/dist/registry/index.js +5 -0
- package/dist/registry/manifest-loader.d.ts +44 -0
- package/dist/registry/manifest-loader.d.ts.map +1 -0
- package/dist/registry/manifest-loader.js +97 -0
- package/dist/registry/manifest-loader.js.map +1 -0
- package/dist/registry/resource-registry.d.ts +133 -0
- package/dist/registry/resource-registry.d.ts.map +1 -0
- package/dist/registry/resource-registry.js +297 -0
- package/dist/registry/resource-registry.js.map +1 -0
- package/dist/registry/types.d.ts +181 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +89 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/shared/src/plugin.d.ts +66 -1
- package/dist/shared/src/plugin.d.ts.map +1 -1
- package/dist/type-generator/query-registry.js +4 -1
- package/dist/type-generator/query-registry.js.map +1 -1
- package/dist/type-generator/spinner.js +3 -0
- package/dist/type-generator/spinner.js.map +1 -1
- package/dist/type-generator/types.js.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 +4 -4
- package/docs/docs/app-management.md +1 -1
- package/docs/docs/architecture/index.html +4 -4
- package/docs/docs/architecture.md +1 -1
- package/docs/docs/category/development/index.html +4 -4
- package/docs/docs/category/development.md +1 -1
- 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 +7 -35
- package/docs/docs/development/ai-assisted-development.md +3 -52
- package/docs/docs/development/index.html +5 -5
- package/docs/docs/development/llm-guide/index.html +3 -3
- package/docs/docs/development/local-development/index.html +5 -5
- package/docs/docs/development/local-development.md +2 -2
- package/docs/docs/development/project-setup/index.html +3 -3
- package/docs/docs/development/remote-bridge/index.html +4 -4
- package/docs/docs/development/remote-bridge.md +1 -1
- package/docs/docs/development/type-generation/index.html +3 -3
- package/docs/docs/development.md +2 -2
- package/docs/docs/index.html +5 -20
- package/docs/docs/plugins/index.html +18 -8
- package/docs/docs/plugins.md +82 -4
- package/docs/docs.md +1 -32
- package/llms.txt +14 -3
- package/package.json +4 -1
- package/dist/connectors/lakebase/client.js.map +0 -1
- package/dist/connectors/lakebase/defaults.js +0 -13
- package/dist/connectors/lakebase/defaults.js.map +0 -1
- package/dist/connectors/lakebase/index.js +0 -3
- package/dist/utils/env-validator.js +0 -14
- package/dist/utils/env-validator.js.map +0 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { JSONSchema7 } from "json-schema";
|
|
2
|
+
|
|
3
|
+
//#region src/registry/types.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resource Registry Type System
|
|
7
|
+
*
|
|
8
|
+
* This module defines the type system for the AppKit Resource Registry,
|
|
9
|
+
* which enables plugins to declare their Databricks resource requirements
|
|
10
|
+
* in a machine-readable format.
|
|
11
|
+
*
|
|
12
|
+
* Resource types are exposed as first-class citizens with their specific
|
|
13
|
+
* permissions, making it simple for users to declare dependencies.
|
|
14
|
+
* Internal tooling handles conversion to Databricks app.yaml format.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Supported resource types that plugins can depend on.
|
|
18
|
+
* Each type has its own set of valid permissions.
|
|
19
|
+
*/
|
|
20
|
+
declare enum ResourceType {
|
|
21
|
+
/** Secret scope for secure credential storage */
|
|
22
|
+
SECRET = "secret",
|
|
23
|
+
/** Databricks Job for scheduled or triggered workflows */
|
|
24
|
+
JOB = "job",
|
|
25
|
+
/** Databricks SQL Warehouse for query execution */
|
|
26
|
+
SQL_WAREHOUSE = "sql_warehouse",
|
|
27
|
+
/** Model serving endpoint for ML inference */
|
|
28
|
+
SERVING_ENDPOINT = "serving_endpoint",
|
|
29
|
+
/** Unity Catalog Volume for file storage */
|
|
30
|
+
VOLUME = "volume",
|
|
31
|
+
/** Vector Search Index for similarity search */
|
|
32
|
+
VECTOR_SEARCH_INDEX = "vector_search_index",
|
|
33
|
+
/** Unity Catalog Function */
|
|
34
|
+
UC_FUNCTION = "uc_function",
|
|
35
|
+
/** Unity Catalog Connection for external data sources */
|
|
36
|
+
UC_CONNECTION = "uc_connection",
|
|
37
|
+
/** Database (Lakebase) for persistent storage */
|
|
38
|
+
DATABASE = "database",
|
|
39
|
+
/** Genie Space for AI assistant */
|
|
40
|
+
GENIE_SPACE = "genie_space",
|
|
41
|
+
/** MLflow Experiment for ML tracking */
|
|
42
|
+
EXPERIMENT = "experiment",
|
|
43
|
+
/** Databricks App dependency */
|
|
44
|
+
APP = "app",
|
|
45
|
+
}
|
|
46
|
+
/** Permissions for SECRET resources */
|
|
47
|
+
type SecretPermission = "MANAGE" | "READ" | "WRITE";
|
|
48
|
+
/** Permissions for JOB resources */
|
|
49
|
+
type JobPermission = "CAN_MANAGE" | "CAN_MANAGE_RUN" | "CAN_VIEW";
|
|
50
|
+
/** Permissions for SQL_WAREHOUSE resources */
|
|
51
|
+
type SqlWarehousePermission = "CAN_MANAGE" | "CAN_USE";
|
|
52
|
+
/** Permissions for SERVING_ENDPOINT resources */
|
|
53
|
+
type ServingEndpointPermission = "CAN_MANAGE" | "CAN_QUERY" | "CAN_VIEW";
|
|
54
|
+
/** Permissions for VOLUME resources */
|
|
55
|
+
type VolumePermission = "READ_VOLUME" | "WRITE_VOLUME";
|
|
56
|
+
/** Permissions for VECTOR_SEARCH_INDEX resources */
|
|
57
|
+
type VectorSearchIndexPermission = "SELECT";
|
|
58
|
+
/** Permissions for UC_FUNCTION resources */
|
|
59
|
+
type UcFunctionPermission = "EXECUTE";
|
|
60
|
+
/** Permissions for UC_CONNECTION resources */
|
|
61
|
+
type UcConnectionPermission = "USE_CONNECTION";
|
|
62
|
+
/** Permissions for DATABASE resources */
|
|
63
|
+
type DatabasePermission = "CAN_CONNECT_AND_CREATE";
|
|
64
|
+
/** Permissions for GENIE_SPACE resources */
|
|
65
|
+
type GenieSpacePermission = "CAN_EDIT" | "CAN_VIEW" | "CAN_RUN" | "CAN_MANAGE";
|
|
66
|
+
/** Permissions for EXPERIMENT resources */
|
|
67
|
+
type ExperimentPermission = "CAN_READ" | "CAN_EDIT" | "CAN_MANAGE";
|
|
68
|
+
/** Permissions for APP resources */
|
|
69
|
+
type AppPermission = "CAN_USE";
|
|
70
|
+
/**
|
|
71
|
+
* Union of all possible permission levels across all resource types.
|
|
72
|
+
*/
|
|
73
|
+
type ResourcePermission = SecretPermission | JobPermission | SqlWarehousePermission | ServingEndpointPermission | VolumePermission | VectorSearchIndexPermission | UcFunctionPermission | UcConnectionPermission | DatabasePermission | GenieSpacePermission | ExperimentPermission | AppPermission;
|
|
74
|
+
/**
|
|
75
|
+
* Defines a single field for a resource. Each field has its own environment variable and optional description.
|
|
76
|
+
* Single-value types use one key (e.g. id); multi-value types (database, secret) use multiple (e.g. instance_name, database_name or scope, key).
|
|
77
|
+
*/
|
|
78
|
+
interface ResourceFieldEntry {
|
|
79
|
+
/** Environment variable name for this field */
|
|
80
|
+
env: string;
|
|
81
|
+
/** Human-readable description for this field */
|
|
82
|
+
description?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Declares a resource requirement for a plugin.
|
|
86
|
+
* Can be defined statically in a manifest or dynamically via getResourceRequirements().
|
|
87
|
+
*/
|
|
88
|
+
interface ResourceRequirement {
|
|
89
|
+
/** Type of Databricks resource required */
|
|
90
|
+
type: ResourceType;
|
|
91
|
+
/** Unique alias for this resource within the plugin (e.g., 'warehouse', 'secrets'). Used for UI/display. */
|
|
92
|
+
alias: string;
|
|
93
|
+
/** Stable key for machine use (env naming, composite keys, app.yaml). Required. */
|
|
94
|
+
resourceKey: string;
|
|
95
|
+
/** Human-readable description of why this resource is needed */
|
|
96
|
+
description: string;
|
|
97
|
+
/** Required permission level for the resource */
|
|
98
|
+
permission: ResourcePermission;
|
|
99
|
+
/**
|
|
100
|
+
* Map of field name to env and optional description.
|
|
101
|
+
* Single-value types use one key (e.g. id); multi-value (database, secret) use multiple keys.
|
|
102
|
+
*/
|
|
103
|
+
fields: Record<string, ResourceFieldEntry>;
|
|
104
|
+
/** Whether this resource is required (true) or optional (false) */
|
|
105
|
+
required: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Internal representation of a resource in the registry.
|
|
109
|
+
* Extends ResourceRequirement with resolution state and plugin ownership.
|
|
110
|
+
*/
|
|
111
|
+
interface ResourceEntry extends ResourceRequirement {
|
|
112
|
+
/** Plugin(s) that require this resource (comma-separated if multiple) */
|
|
113
|
+
plugin: string;
|
|
114
|
+
/** Whether the resource has been resolved (all field env vars set) */
|
|
115
|
+
resolved: boolean;
|
|
116
|
+
/** Resolved value per field name. Populated by validate() when all field env vars are set. */
|
|
117
|
+
values?: Record<string, string>;
|
|
118
|
+
/**
|
|
119
|
+
* Per-plugin permission tracking.
|
|
120
|
+
* Maps plugin name to the permission it originally requested.
|
|
121
|
+
* Populated when multiple plugins share the same resource.
|
|
122
|
+
*/
|
|
123
|
+
permissionSources?: Record<string, ResourcePermission>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Result of validating all registered resources against the environment.
|
|
127
|
+
*/
|
|
128
|
+
interface ValidationResult {
|
|
129
|
+
/** Whether all required resources are available */
|
|
130
|
+
valid: boolean;
|
|
131
|
+
/** List of missing required resources */
|
|
132
|
+
missing: ResourceEntry[];
|
|
133
|
+
/** Complete list of all registered resources (required and optional) */
|
|
134
|
+
all: ResourceEntry[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Configuration schema definition for plugin config.
|
|
138
|
+
* Re-exported from the standard JSON Schema Draft 7 types.
|
|
139
|
+
*
|
|
140
|
+
* @see {@link https://json-schema.org/draft-07/json-schema-release-notes | JSON Schema Draft 7}
|
|
141
|
+
*/
|
|
142
|
+
type ConfigSchema = JSONSchema7;
|
|
143
|
+
/**
|
|
144
|
+
* Plugin manifest that declares metadata and resource requirements.
|
|
145
|
+
* Attached to plugin classes as a static property.
|
|
146
|
+
*/
|
|
147
|
+
interface PluginManifest {
|
|
148
|
+
/** Plugin identifier (matches plugin.name) */
|
|
149
|
+
name: string;
|
|
150
|
+
/** Human-readable display name for UI/CLI */
|
|
151
|
+
displayName: string;
|
|
152
|
+
/** Brief description of what the plugin does */
|
|
153
|
+
description: string;
|
|
154
|
+
/**
|
|
155
|
+
* Resource requirements declaration
|
|
156
|
+
*/
|
|
157
|
+
resources: {
|
|
158
|
+
/** Resources that must be available for the plugin to function */
|
|
159
|
+
required: Omit<ResourceRequirement, "required">[];
|
|
160
|
+
/** Resources that enhance functionality but are not mandatory */
|
|
161
|
+
optional: Omit<ResourceRequirement, "required">[];
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Configuration schema for the plugin.
|
|
165
|
+
* Defines the shape and validation rules for plugin config.
|
|
166
|
+
*/
|
|
167
|
+
config?: {
|
|
168
|
+
schema: ConfigSchema;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Optional metadata for community plugins
|
|
172
|
+
*/
|
|
173
|
+
author?: string;
|
|
174
|
+
version?: string;
|
|
175
|
+
repository?: string;
|
|
176
|
+
keywords?: string[];
|
|
177
|
+
license?: string;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { ConfigSchema, PluginManifest, ResourceEntry, ResourceFieldEntry, ResourcePermission, ResourceRequirement, ResourceType, ValidationResult };
|
|
181
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/registry/types.ts"],"sourcesContent":[],"mappings":";;;;;;;AAgBA;AA2CA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAOA;AAGY,aAhFA,YAAA;EAqFA;EAAkB,MAAA,GAAA,QAAA;;KAE1B,GAAA,KAAA;;eAEA,GAAA,eAAA;;kBAEA,GAAA,kBAAA;;QAEA,GAAA,QAAA;;qBAEA,GAAA,qBAAA;;aAEA,GAAA,aAAA;EAAa;EAmCA,aAAA,GAAA,eAAkB;EAWlB;EAAmB,QAAA,GAAA,UAAA;;aActB,GAAA,aAAA;;YAMJ,GAAA,YAAA;EAAM;EAUC,GAAA,GAAA,KAAA;;;AAeoB,KAjJzB,gBAAA,GAiJyB,QAAA,GAAA,MAAA,GAAA,OAAA;;AAfE,KA/H3B,aAAA,GA+H2B,YAAA,GAAA,gBAAA,GAAA,UAAA;;AAqBtB,KAjJL,sBAAA,GAiJqB,YAAA,GAAA,SAAA;;AAKtB,KAnJC,yBAAA,GAmJD,YAAA,GAAA,WAAA,GAAA,UAAA;;AAGS,KAnJR,gBAAA,GAmJQ,aAAA,GAAA,cAAA;AAWpB;AAMiB,KAjKL,2BAAA,GAiKmB,QAAA;;AAeZ,KA7KP,oBAAA,GA6KO,SAAA;;AAGA,KA7KP,sBAAA,GA6KO,gBAAA;;AAQP,KAlLA,kBAAA,GAkLA,wBAAA;;KA/KA,oBAAA;;KAOA,oBAAA;;KAGA,aAAA;;;;KAKA,kBAAA,GACR,mBACA,gBACA,yBACA,4BACA,mBACA,8BACA,uBACA,yBACA,qBACA,uBACA,uBACA;;;;;UAmCa,kBAAA;;;;;;;;;;UAWA,mBAAA;;QAET;;;;;;;;cAYM;;;;;UAMJ,eAAe;;;;;;;;UAUR,aAAA,SAAsB;;;;;;WAQ5B;;;;;;sBAOW,eAAe;;;;;UAMpB,gBAAA;;;;WAKN;;OAGJ;;;;;;;;KAWK,YAAA,GAAe;;;;;UAMV,cAAA;;;;;;;;;;;;cAeH,KAAK;;cAGL,KAAK;;;;;;;YAQP"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//#region src/registry/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resource Registry Type System
|
|
4
|
+
*
|
|
5
|
+
* This module defines the type system for the AppKit Resource Registry,
|
|
6
|
+
* which enables plugins to declare their Databricks resource requirements
|
|
7
|
+
* in a machine-readable format.
|
|
8
|
+
*
|
|
9
|
+
* Resource types are exposed as first-class citizens with their specific
|
|
10
|
+
* permissions, making it simple for users to declare dependencies.
|
|
11
|
+
* Internal tooling handles conversion to Databricks app.yaml format.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Supported resource types that plugins can depend on.
|
|
15
|
+
* Each type has its own set of valid permissions.
|
|
16
|
+
*/
|
|
17
|
+
let ResourceType = /* @__PURE__ */ function(ResourceType) {
|
|
18
|
+
/** Secret scope for secure credential storage */
|
|
19
|
+
ResourceType["SECRET"] = "secret";
|
|
20
|
+
/** Databricks Job for scheduled or triggered workflows */
|
|
21
|
+
ResourceType["JOB"] = "job";
|
|
22
|
+
/** Databricks SQL Warehouse for query execution */
|
|
23
|
+
ResourceType["SQL_WAREHOUSE"] = "sql_warehouse";
|
|
24
|
+
/** Model serving endpoint for ML inference */
|
|
25
|
+
ResourceType["SERVING_ENDPOINT"] = "serving_endpoint";
|
|
26
|
+
/** Unity Catalog Volume for file storage */
|
|
27
|
+
ResourceType["VOLUME"] = "volume";
|
|
28
|
+
/** Vector Search Index for similarity search */
|
|
29
|
+
ResourceType["VECTOR_SEARCH_INDEX"] = "vector_search_index";
|
|
30
|
+
/** Unity Catalog Function */
|
|
31
|
+
ResourceType["UC_FUNCTION"] = "uc_function";
|
|
32
|
+
/** Unity Catalog Connection for external data sources */
|
|
33
|
+
ResourceType["UC_CONNECTION"] = "uc_connection";
|
|
34
|
+
/** Database (Lakebase) for persistent storage */
|
|
35
|
+
ResourceType["DATABASE"] = "database";
|
|
36
|
+
/** Genie Space for AI assistant */
|
|
37
|
+
ResourceType["GENIE_SPACE"] = "genie_space";
|
|
38
|
+
/** MLflow Experiment for ML tracking */
|
|
39
|
+
ResourceType["EXPERIMENT"] = "experiment";
|
|
40
|
+
/** Databricks App dependency */
|
|
41
|
+
ResourceType["APP"] = "app";
|
|
42
|
+
return ResourceType;
|
|
43
|
+
}({});
|
|
44
|
+
/**
|
|
45
|
+
* Permission hierarchy per resource type (weakest to strongest).
|
|
46
|
+
* Used to compare permissions when merging; higher index = more permissive.
|
|
47
|
+
* Unknown permissions are treated as less than any known permission.
|
|
48
|
+
*/
|
|
49
|
+
const PERMISSION_HIERARCHY_BY_TYPE = {
|
|
50
|
+
[ResourceType.SECRET]: [
|
|
51
|
+
"READ",
|
|
52
|
+
"WRITE",
|
|
53
|
+
"MANAGE"
|
|
54
|
+
],
|
|
55
|
+
[ResourceType.JOB]: [
|
|
56
|
+
"CAN_VIEW",
|
|
57
|
+
"CAN_MANAGE_RUN",
|
|
58
|
+
"CAN_MANAGE"
|
|
59
|
+
],
|
|
60
|
+
[ResourceType.SQL_WAREHOUSE]: ["CAN_USE", "CAN_MANAGE"],
|
|
61
|
+
[ResourceType.SERVING_ENDPOINT]: [
|
|
62
|
+
"CAN_VIEW",
|
|
63
|
+
"CAN_QUERY",
|
|
64
|
+
"CAN_MANAGE"
|
|
65
|
+
],
|
|
66
|
+
[ResourceType.VOLUME]: ["READ_VOLUME", "WRITE_VOLUME"],
|
|
67
|
+
[ResourceType.VECTOR_SEARCH_INDEX]: ["SELECT"],
|
|
68
|
+
[ResourceType.UC_FUNCTION]: ["EXECUTE"],
|
|
69
|
+
[ResourceType.UC_CONNECTION]: ["USE_CONNECTION"],
|
|
70
|
+
[ResourceType.DATABASE]: ["CAN_CONNECT_AND_CREATE"],
|
|
71
|
+
[ResourceType.GENIE_SPACE]: [
|
|
72
|
+
"CAN_VIEW",
|
|
73
|
+
"CAN_RUN",
|
|
74
|
+
"CAN_EDIT",
|
|
75
|
+
"CAN_MANAGE"
|
|
76
|
+
],
|
|
77
|
+
[ResourceType.EXPERIMENT]: [
|
|
78
|
+
"CAN_READ",
|
|
79
|
+
"CAN_EDIT",
|
|
80
|
+
"CAN_MANAGE"
|
|
81
|
+
],
|
|
82
|
+
[ResourceType.APP]: ["CAN_USE"]
|
|
83
|
+
};
|
|
84
|
+
/** Set of valid permissions per type (for validation). */
|
|
85
|
+
const PERMISSIONS_BY_TYPE = PERMISSION_HIERARCHY_BY_TYPE;
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
export { PERMISSIONS_BY_TYPE, PERMISSION_HIERARCHY_BY_TYPE, ResourceType };
|
|
89
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/registry/types.ts"],"sourcesContent":["/**\n * Resource Registry Type System\n *\n * This module defines the type system for the AppKit Resource Registry,\n * which enables plugins to declare their Databricks resource requirements\n * in a machine-readable format.\n *\n * Resource types are exposed as first-class citizens with their specific\n * permissions, making it simple for users to declare dependencies.\n * Internal tooling handles conversion to Databricks app.yaml format.\n */\n\n/**\n * Supported resource types that plugins can depend on.\n * Each type has its own set of valid permissions.\n */\nexport enum ResourceType {\n /** Secret scope for secure credential storage */\n SECRET = \"secret\",\n\n /** Databricks Job for scheduled or triggered workflows */\n JOB = \"job\",\n\n /** Databricks SQL Warehouse for query execution */\n SQL_WAREHOUSE = \"sql_warehouse\",\n\n /** Model serving endpoint for ML inference */\n SERVING_ENDPOINT = \"serving_endpoint\",\n\n /** Unity Catalog Volume for file storage */\n VOLUME = \"volume\",\n\n /** Vector Search Index for similarity search */\n VECTOR_SEARCH_INDEX = \"vector_search_index\",\n\n /** Unity Catalog Function */\n UC_FUNCTION = \"uc_function\",\n\n /** Unity Catalog Connection for external data sources */\n UC_CONNECTION = \"uc_connection\",\n\n /** Database (Lakebase) for persistent storage */\n DATABASE = \"database\",\n\n /** Genie Space for AI assistant */\n GENIE_SPACE = \"genie_space\",\n\n /** MLflow Experiment for ML tracking */\n EXPERIMENT = \"experiment\",\n\n /** Databricks App dependency */\n APP = \"app\",\n}\n\n// ============================================================================\n// Permissions per resource type\n// ============================================================================\n\n/** Permissions for SECRET resources */\nexport type SecretPermission = \"MANAGE\" | \"READ\" | \"WRITE\";\n\n/** Permissions for JOB resources */\nexport type JobPermission = \"CAN_MANAGE\" | \"CAN_MANAGE_RUN\" | \"CAN_VIEW\";\n\n/** Permissions for SQL_WAREHOUSE resources */\nexport type SqlWarehousePermission = \"CAN_MANAGE\" | \"CAN_USE\";\n\n/** Permissions for SERVING_ENDPOINT resources */\nexport type ServingEndpointPermission = \"CAN_MANAGE\" | \"CAN_QUERY\" | \"CAN_VIEW\";\n\n/** Permissions for VOLUME resources */\nexport type VolumePermission = \"READ_VOLUME\" | \"WRITE_VOLUME\";\n\n/** Permissions for VECTOR_SEARCH_INDEX resources */\nexport type VectorSearchIndexPermission = \"SELECT\";\n\n/** Permissions for UC_FUNCTION resources */\nexport type UcFunctionPermission = \"EXECUTE\";\n\n/** Permissions for UC_CONNECTION resources */\nexport type UcConnectionPermission = \"USE_CONNECTION\";\n\n/** Permissions for DATABASE resources */\nexport type DatabasePermission = \"CAN_CONNECT_AND_CREATE\";\n\n/** Permissions for GENIE_SPACE resources */\nexport type GenieSpacePermission =\n | \"CAN_EDIT\"\n | \"CAN_VIEW\"\n | \"CAN_RUN\"\n | \"CAN_MANAGE\";\n\n/** Permissions for EXPERIMENT resources */\nexport type ExperimentPermission = \"CAN_READ\" | \"CAN_EDIT\" | \"CAN_MANAGE\";\n\n/** Permissions for APP resources */\nexport type AppPermission = \"CAN_USE\";\n\n/**\n * Union of all possible permission levels across all resource types.\n */\nexport type ResourcePermission =\n | SecretPermission\n | JobPermission\n | SqlWarehousePermission\n | ServingEndpointPermission\n | VolumePermission\n | VectorSearchIndexPermission\n | UcFunctionPermission\n | UcConnectionPermission\n | DatabasePermission\n | GenieSpacePermission\n | ExperimentPermission\n | AppPermission;\n\n/**\n * Permission hierarchy per resource type (weakest to strongest).\n * Used to compare permissions when merging; higher index = more permissive.\n * Unknown permissions are treated as less than any known permission.\n */\nexport const PERMISSION_HIERARCHY_BY_TYPE: Record<\n ResourceType,\n readonly ResourcePermission[]\n> = {\n [ResourceType.SECRET]: [\"READ\", \"WRITE\", \"MANAGE\"],\n [ResourceType.JOB]: [\"CAN_VIEW\", \"CAN_MANAGE_RUN\", \"CAN_MANAGE\"],\n [ResourceType.SQL_WAREHOUSE]: [\"CAN_USE\", \"CAN_MANAGE\"],\n [ResourceType.SERVING_ENDPOINT]: [\"CAN_VIEW\", \"CAN_QUERY\", \"CAN_MANAGE\"],\n [ResourceType.VOLUME]: [\"READ_VOLUME\", \"WRITE_VOLUME\"],\n [ResourceType.VECTOR_SEARCH_INDEX]: [\"SELECT\"],\n [ResourceType.UC_FUNCTION]: [\"EXECUTE\"],\n [ResourceType.UC_CONNECTION]: [\"USE_CONNECTION\"],\n [ResourceType.DATABASE]: [\"CAN_CONNECT_AND_CREATE\"],\n [ResourceType.GENIE_SPACE]: [\"CAN_VIEW\", \"CAN_RUN\", \"CAN_EDIT\", \"CAN_MANAGE\"],\n [ResourceType.EXPERIMENT]: [\"CAN_READ\", \"CAN_EDIT\", \"CAN_MANAGE\"],\n [ResourceType.APP]: [\"CAN_USE\"],\n} as const;\n\n/** Set of valid permissions per type (for validation). */\nexport const PERMISSIONS_BY_TYPE: Record<\n ResourceType,\n readonly ResourcePermission[]\n> = PERMISSION_HIERARCHY_BY_TYPE;\n\n/**\n * Defines a single field for a resource. Each field has its own environment variable and optional description.\n * Single-value types use one key (e.g. id); multi-value types (database, secret) use multiple (e.g. instance_name, database_name or scope, key).\n */\nexport interface ResourceFieldEntry {\n /** Environment variable name for this field */\n env: string;\n /** Human-readable description for this field */\n description?: string;\n}\n\n/**\n * Declares a resource requirement for a plugin.\n * Can be defined statically in a manifest or dynamically via getResourceRequirements().\n */\nexport interface ResourceRequirement {\n /** Type of Databricks resource required */\n type: ResourceType;\n\n /** Unique alias for this resource within the plugin (e.g., 'warehouse', 'secrets'). Used for UI/display. */\n alias: string;\n\n /** Stable key for machine use (env naming, composite keys, app.yaml). Required. */\n resourceKey: string;\n\n /** Human-readable description of why this resource is needed */\n description: string;\n\n /** Required permission level for the resource */\n permission: ResourcePermission;\n\n /**\n * Map of field name to env and optional description.\n * Single-value types use one key (e.g. id); multi-value (database, secret) use multiple keys.\n */\n fields: Record<string, ResourceFieldEntry>;\n\n /** Whether this resource is required (true) or optional (false) */\n required: boolean;\n}\n\n/**\n * Internal representation of a resource in the registry.\n * Extends ResourceRequirement with resolution state and plugin ownership.\n */\nexport interface ResourceEntry extends ResourceRequirement {\n /** Plugin(s) that require this resource (comma-separated if multiple) */\n plugin: string;\n\n /** Whether the resource has been resolved (all field env vars set) */\n resolved: boolean;\n\n /** Resolved value per field name. Populated by validate() when all field env vars are set. */\n values?: Record<string, string>;\n\n /**\n * Per-plugin permission tracking.\n * Maps plugin name to the permission it originally requested.\n * Populated when multiple plugins share the same resource.\n */\n permissionSources?: Record<string, ResourcePermission>;\n}\n\n/**\n * Result of validating all registered resources against the environment.\n */\nexport interface ValidationResult {\n /** Whether all required resources are available */\n valid: boolean;\n\n /** List of missing required resources */\n missing: ResourceEntry[];\n\n /** Complete list of all registered resources (required and optional) */\n all: ResourceEntry[];\n}\n\nimport type { JSONSchema7 } from \"json-schema\";\n\n/**\n * Configuration schema definition for plugin config.\n * Re-exported from the standard JSON Schema Draft 7 types.\n *\n * @see {@link https://json-schema.org/draft-07/json-schema-release-notes | JSON Schema Draft 7}\n */\nexport type ConfigSchema = JSONSchema7;\n\n/**\n * Plugin manifest that declares metadata and resource requirements.\n * Attached to plugin classes as a static property.\n */\nexport interface PluginManifest {\n /** Plugin identifier (matches plugin.name) */\n name: string;\n\n /** Human-readable display name for UI/CLI */\n displayName: string;\n\n /** Brief description of what the plugin does */\n description: string;\n\n /**\n * Resource requirements declaration\n */\n resources: {\n /** Resources that must be available for the plugin to function */\n required: Omit<ResourceRequirement, \"required\">[];\n\n /** Resources that enhance functionality but are not mandatory */\n optional: Omit<ResourceRequirement, \"required\">[];\n };\n\n /**\n * Configuration schema for the plugin.\n * Defines the shape and validation rules for plugin config.\n */\n config?: {\n schema: ConfigSchema;\n };\n\n /**\n * Optional metadata for community plugins\n */\n author?: string;\n version?: string;\n repository?: string;\n keywords?: string[];\n license?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAY,sDAAL;;AAEL;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;;;;;;;;AAqEF,MAAa,+BAGT;EACD,aAAa,SAAS;EAAC;EAAQ;EAAS;EAAS;EACjD,aAAa,MAAM;EAAC;EAAY;EAAkB;EAAa;EAC/D,aAAa,gBAAgB,CAAC,WAAW,aAAa;EACtD,aAAa,mBAAmB;EAAC;EAAY;EAAa;EAAa;EACvE,aAAa,SAAS,CAAC,eAAe,eAAe;EACrD,aAAa,sBAAsB,CAAC,SAAS;EAC7C,aAAa,cAAc,CAAC,UAAU;EACtC,aAAa,gBAAgB,CAAC,iBAAiB;EAC/C,aAAa,WAAW,CAAC,yBAAyB;EAClD,aAAa,cAAc;EAAC;EAAY;EAAW;EAAY;EAAa;EAC5E,aAAa,aAAa;EAAC;EAAY;EAAY;EAAa;EAChE,aAAa,MAAM,CAAC,UAAU;CAChC;;AAGD,MAAa,sBAGT"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
+
import { JSONSchema7 } from "json-schema";
|
|
2
3
|
|
|
3
4
|
//#region ../shared/src/plugin.d.ts
|
|
4
5
|
/** Base plugin interface. */
|
|
5
6
|
interface BasePlugin {
|
|
6
7
|
name: string;
|
|
7
8
|
abortActiveOperations?(): void;
|
|
8
|
-
validateEnv(): void;
|
|
9
9
|
setup(): Promise<void>;
|
|
10
10
|
injectRoutes(router: express.Router): void;
|
|
11
11
|
getEndpoints(): PluginEndpointMap;
|
|
@@ -24,10 +24,75 @@ type TelemetryOptions = boolean | {
|
|
|
24
24
|
logs?: boolean;
|
|
25
25
|
};
|
|
26
26
|
type PluginPhase = "core" | "normal" | "deferred";
|
|
27
|
+
/**
|
|
28
|
+
* Plugin constructor with required manifest declaration.
|
|
29
|
+
* All plugins must declare a manifest with their metadata and resource requirements.
|
|
30
|
+
*/
|
|
27
31
|
type PluginConstructor<C = BasePluginConfig, I extends BasePlugin = BasePlugin> = (new (config: C) => I) & {
|
|
28
32
|
DEFAULT_CONFIG?: Record<string, unknown>;
|
|
29
33
|
phase?: PluginPhase;
|
|
34
|
+
/**
|
|
35
|
+
* Static manifest declaring plugin metadata and resource requirements.
|
|
36
|
+
* Required for all plugins.
|
|
37
|
+
*/
|
|
38
|
+
manifest: PluginManifest;
|
|
39
|
+
/**
|
|
40
|
+
* Optional runtime resource requirements based on config.
|
|
41
|
+
* Use this when resource requirements depend on plugin configuration.
|
|
42
|
+
*/
|
|
43
|
+
getResourceRequirements?(config: C): ResourceRequirement[];
|
|
30
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Manifest declaration for plugins (imported from registry types).
|
|
47
|
+
* Re-exported here to avoid circular dependencies.
|
|
48
|
+
*/
|
|
49
|
+
interface PluginManifest {
|
|
50
|
+
name: string;
|
|
51
|
+
displayName: string;
|
|
52
|
+
description: string;
|
|
53
|
+
resources: {
|
|
54
|
+
required: Omit<ResourceRequirement, "required">[];
|
|
55
|
+
optional: Omit<ResourceRequirement, "required">[];
|
|
56
|
+
};
|
|
57
|
+
config?: {
|
|
58
|
+
schema: JSONSchema7;
|
|
59
|
+
};
|
|
60
|
+
author?: string;
|
|
61
|
+
version?: string;
|
|
62
|
+
repository?: string;
|
|
63
|
+
keywords?: string[];
|
|
64
|
+
license?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Defines a single field for a resource.
|
|
68
|
+
* Each field maps to its own environment variable and optional description.
|
|
69
|
+
* Single-value types use one key (e.g. id); multi-value types (database, secret)
|
|
70
|
+
* use multiple (e.g. instance_name, database_name or scope, key).
|
|
71
|
+
*/
|
|
72
|
+
interface ResourceFieldEntry {
|
|
73
|
+
/** Environment variable name for this field */
|
|
74
|
+
env: string;
|
|
75
|
+
/** Human-readable description for this field */
|
|
76
|
+
description?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resource requirement declaration (imported from registry types).
|
|
80
|
+
* Re-exported here to avoid circular dependencies.
|
|
81
|
+
*/
|
|
82
|
+
interface ResourceRequirement {
|
|
83
|
+
type: string;
|
|
84
|
+
alias: string;
|
|
85
|
+
/** Stable key for machine use (env naming, composite keys, app.yaml). */
|
|
86
|
+
resourceKey: string;
|
|
87
|
+
description: string;
|
|
88
|
+
permission: string;
|
|
89
|
+
/**
|
|
90
|
+
* Map of field name to env and optional description.
|
|
91
|
+
* Single-value types use one key (e.g. id); multi-value (database, secret) use multiple keys.
|
|
92
|
+
*/
|
|
93
|
+
fields: Record<string, ResourceFieldEntry>;
|
|
94
|
+
required: boolean;
|
|
95
|
+
}
|
|
31
96
|
/**
|
|
32
97
|
* Extracts the exports type from a plugin.
|
|
33
98
|
* This is the return type of the plugin's exports() method.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../../shared/src/plugin.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../../shared/src/plugin.ts"],"sourcesContent":[],"mappings":";;;;;UAIiB,UAAA;EAAA,IAAA,EAAA,MAAA;EAAU,qBAAA,GAAA,EAAA,IAAA;OAKhB,EAAA,EAAA,OAAA,CAAA,IAAA,CAAA;cAEoB,CAAA,MAAA,EAAR,OAAA,CAAQ,MAAA,CAAA,EAAA,IAAA;cAEb,EAAA,EAAA,iBAAA;EAAiB,OAAA,GAAA,EAAA,OAAA;AAMnC;AAaA;AAaY,UA1BK,gBAAA,CA0BM;EAMX,IAAA,CAAA,EAAA,MAAA;EAAiB,IAAA,CAAA,EAAA,MAAA;MACvB,EAAA,MAAA,CAAA,EAAA,OAAA;WACM,CAAA,EAxBE,gBAwBF;;AAEF,KAvBE,gBAAA,GAuBF,OAAA,GAAA;QACL,CAAA,EAAA,OAAA;SACc,CAAA,EAAA,OAAA;MACT,CAAA,EAAA,OAAA;;AAiBqB,KA9BnB,WAAA,GA8BmB,MAAA,GAAA,QAAA,GAAA,UAAA;;;;;AASnB,KAjCA,iBAiCA,CAAA,IAhCN,gBAgCM,EAAA,UA/BA,UA+BA,GA/Ba,UA+Bb,CAAA,GAAA,CAAA,KAAA,MAAA,EA7BF,CA6BE,EAAA,GA5BP,CA4BO,CAAA,GAAA;EAAW,cAAA,CAAA,EA3BJ,MA2BI,CAAA,MAAA,EAAA,OAAA,CAAA;EAeN,KAAA,CAAA,EAzCP,WAyCO;EAWA;;;;EAWD,QAAA,EA1DJ,cA0DI;EAqCJ;;;;yBAC2B,EAAA,MAAA,EA3FJ,CA2FI,CAAA,EA3FA,mBA2FA,EAAA;;;AAMvC;;;AAMgB,UAhGC,cAAA,CAgGD;MAAgB,EAAA,MAAA;EAAG,WAAA,EAAA,MAAA;EAQvB,WAAA,EAAS,MAAA;EAAA,SAAA,EAAA;IACW,QAAA,EApGlB,IAoGkB,CApGb,mBAoGa,EAAA,UAAA,CAAA,EAAA;IAAX,QAAA,EAnGP,IAmGO,CAnGF,mBAmGE,EAAA,UAAA,CAAA,EAAA;;QAEA,CAAA,EAAA;IACU,MAAA,EAnGnB,WAmGmB;;QAA3B,CAAA,EAAA,MAAA;SAD6B,CAAA,EAAA,MAAA;EAAU,UAAA,CAAA,EAAA,MAAA;EAK/B,QAAA,CAAA,EAAA,MAAU,EAAA;EAAA,OAAA,CAAA,EAAA,MAAA;;;;;AACtB;;;AAEgB,UA3FC,kBAAA,CA2FD;;KAAM,EAAA,MAAA;;EAAP,WAAA,CAAA,EAAA,MAAA;AAGf;AACA;AACA;AAEA;AAEA;AAAuB,UAzFN,mBAAA,CAyFM;MAGb,EAAA,MAAA;OAEO,EAAA,MAAA;;aAAmC,EAAA,MAAA;EAAO,WAAA,EAAA,MAAA;EAI/C,UAAA,EAAA,MAAA;;;;;UAvFF,eAAe;;;;;;;;KAqCb,wBAAwB,cAClC,uCAAqC,IAAI;;;;;KAM/B,kBAAkB;;;;;;gBAMd,gBAAgB;;;;;;;KAQpB,6BACS,WAAW,iDAExB,aAAa,YAAY,WAC7B,cAAc,aAAa;KAInB;UAAgC;UAAW;QAAS;;KACpD,6CACD,MACN,WAAW,GAAG,GAAG;;KAGV,UAAA,GAAa,OAAA,CAAQ;KACrB,YAAA,GAAe,OAAA,CAAQ;KACvB,WAAA,GAAc,OAAA,CAAQ;KAEtB,UAAA;KAEA,WAAA;;;UAGF;;iBAEO,kBAAkB,iBAAiB;;;KAIxC,iBAAA,GAAoB"}
|
|
@@ -98,10 +98,13 @@ async function generateQueriesFromDescribe(queryFolder, warehouseId, options = {
|
|
|
98
98
|
warehouse_id: warehouseId
|
|
99
99
|
});
|
|
100
100
|
if (result.status.state === "FAILED") {
|
|
101
|
+
const sqlError = result.status.error?.message || "Query execution failed";
|
|
101
102
|
spinner.stop(`✗ ${queryName} - failed`);
|
|
103
|
+
spinner.printDetail(`SQL Error: ${sqlError}`);
|
|
104
|
+
spinner.printDetail(`Query: ${cleanedSql.slice(0, 200)}`);
|
|
102
105
|
failedQueries.push({
|
|
103
106
|
name: queryName,
|
|
104
|
-
error:
|
|
107
|
+
error: sqlError
|
|
105
108
|
});
|
|
106
109
|
continue;
|
|
107
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-registry.js","names":[],"sources":["../../src/type-generator/query-registry.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport { createLogger } from \"../logging/logger\";\nimport { CACHE_VERSION, hashSQL, loadCache, saveCache } from \"./cache\";\nimport { Spinner } from \"./spinner\";\nimport {\n type DatabricksStatementExecutionResponse,\n type QuerySchema,\n sqlTypeToHelper,\n sqlTypeToMarker,\n} from \"./types\";\n\nconst logger = createLogger(\"type-generator:query-registry\");\n\n/**\n * Extract parameters from a SQL query\n * @param sql - the SQL query to extract parameters from\n * @returns an array of parameter names\n */\nexport function extractParameters(sql: string): string[] {\n const matches = sql.matchAll(/:([a-zA-Z_]\\w*)/g);\n const params = new Set<string>();\n for (const match of matches) {\n params.add(match[1]);\n }\n return Array.from(params);\n}\n\n// parameters that are injected by the server\nexport const SERVER_INJECTED_PARAMS = [\"workspaceId\"];\n\nexport function convertToQueryType(\n result: DatabricksStatementExecutionResponse,\n sql: string,\n queryName: string,\n): string {\n const dataRows = result.result?.data_array || [];\n const columns = dataRows.map((row) => ({\n name: row[0] || \"\",\n type_name: row[1]?.toUpperCase() || \"STRING\",\n comment: row[2] || undefined,\n }));\n\n const params = extractParameters(sql).filter(\n (p) => !SERVER_INJECTED_PARAMS.includes(p),\n );\n\n const paramTypes = extractParameterTypes(sql);\n\n // generate parameters types with JSDoc hints\n const paramsType =\n params.length > 0\n ? `{\\n ${params\n .map((p) => {\n const sqlType = paramTypes[p];\n // if no type annotation, use SQLTypeMarker (union type)\n const markerType = sqlType\n ? sqlTypeToMarker[sqlType]\n : \"SQLTypeMarker\";\n const helper = sqlType ? sqlTypeToHelper[sqlType] : \"sql.*()\";\n return `/** ${sqlType || \"any\"} - use ${helper} */\\n ${p}: ${markerType}`;\n })\n .join(\";\\n \")};\\n }`\n : \"Record<string, never>\";\n\n // generate result fields with JSDoc\n const resultFields = columns.map((column) => {\n const normalizedType = normalizeTypeName(column.type_name);\n const mappedType = typeMap[normalizedType] || \"unknown\";\n // validate column name is a valid identifier\n const name = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(column.name)\n ? column.name\n : `\"${column.name}\"`;\n\n // generate comment for column\n const comment = column.comment\n ? `/** ${column.comment} */\\n `\n : `/** @sqlType ${column.type_name} */\\n `;\n\n return `${comment}${name}: ${mappedType}`;\n });\n\n return `{\n name: \"${queryName}\";\n parameters: ${paramsType};\n result: Array<{\n ${resultFields.join(\";\\n \")};\n }>;\n }`;\n}\n\nexport function extractParameterTypes(sql: string): Record<string, string> {\n const paramTypes: Record<string, string> = {};\n const regex =\n /--\\s*@param\\s+(\\w+)\\s+(STRING|NUMERIC|BOOLEAN|DATE|TIMESTAMP|BINARY)/gi;\n const matches = sql.matchAll(regex);\n for (const match of matches) {\n const [, paramName, paramType] = match;\n paramTypes[paramName] = paramType.toUpperCase();\n }\n\n return paramTypes;\n}\n\n/**\n * Generate query schemas from a folder of SQL files\n * It uses DESCRIBE QUERY to get the schema without executing the query\n * @param queryFolder - the folder containing the SQL files\n * @param warehouseId - the warehouse id to use for schema analysis\n * @param options - options for the query generation\n * @param options.noCache - if true, skip the cache and regenerate all types\n * @returns an array of query schemas\n */\nexport async function generateQueriesFromDescribe(\n queryFolder: string,\n warehouseId: string,\n options: { noCache?: boolean } = {},\n): Promise<QuerySchema[]> {\n const { noCache = false } = options;\n\n // read all query files in the folder\n const queryFiles = fs\n .readdirSync(queryFolder)\n .filter((file) => file.endsWith(\".sql\"));\n\n logger.debug(\"Found %d SQL queries\", queryFiles.length);\n\n // load cache\n const cache = noCache ? { version: CACHE_VERSION, queries: {} } : loadCache();\n\n const client = new WorkspaceClient({});\n const querySchemas: QuerySchema[] = [];\n const failedQueries: { name: string; error: string }[] = [];\n const spinner = new Spinner();\n\n // process each query file\n for (let i = 0; i < queryFiles.length; i++) {\n const file = queryFiles[i];\n const rawName = path.basename(file, \".sql\");\n const queryName = normalizeQueryName(rawName);\n\n // read query file content\n const sql = fs.readFileSync(path.join(queryFolder, file), \"utf8\");\n const sqlHash = hashSQL(sql);\n\n // check cache\n const cached = cache.queries[queryName];\n if (cached && cached.hash === sqlHash) {\n querySchemas.push({ name: queryName, type: cached.type });\n spinner.start(`Processing ${queryName} (${i + 1}/${queryFiles.length})`);\n spinner.stop(`✓ ${queryName} (cached)`);\n continue;\n }\n\n spinner.start(`Processing ${queryName} (${i + 1}/${queryFiles.length})`);\n\n const sqlWithDefaults = sql.replace(/:([a-zA-Z_]\\w*)/g, \"''\");\n\n // strip trailing semicolon for DESCRIBE QUERY\n const cleanedSql = sqlWithDefaults.trim().replace(/;\\s*$/, \"\");\n\n // execute DESCRIBE QUERY to get schema without running the actual query\n try {\n const result = (await client.statementExecution.executeStatement({\n statement: `DESCRIBE QUERY ${cleanedSql}`,\n warehouse_id: warehouseId,\n })) as DatabricksStatementExecutionResponse;\n\n if (result.status.state === \"FAILED\") {\n spinner.stop(`✗ ${queryName} - failed`);\n failedQueries.push({\n name: queryName,\n error: \"Query execution failed\",\n });\n continue;\n }\n\n // convert result to query schema\n const type = convertToQueryType(result, sql, queryName);\n querySchemas.push({ name: queryName, type });\n\n // update cache\n cache.queries[queryName] = { hash: sqlHash, type };\n\n spinner.stop(`✓ ${queryName}`);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n spinner.stop(`✗ ${queryName} - ${errorMessage}`);\n failedQueries.push({ name: queryName, error: errorMessage });\n }\n }\n\n // save cache\n saveCache(cache);\n\n // log warning if there are failed queries\n if (failedQueries.length > 0) {\n logger.debug(\"Warning: %d queries failed\", failedQueries.length);\n }\n\n return querySchemas;\n}\n\n/**\n * Normalize query name by removing the .obo extension\n * @param queryName - the query name to normalize\n * @returns the normalized query name\n */\nexport function normalizeQueryName(fileName: string): string {\n return fileName.replace(/\\.obo$/, \"\");\n}\n\n/**\n * Normalize SQL type name by removing parameters/generics\n * Examples:\n * DECIMAL(38,6) -> DECIMAL\n * ARRAY<STRING> -> ARRAY\n * MAP<STRING,INT> -> MAP\n * STRUCT<name:STRING> -> STRUCT\n * INTERVAL DAY TO SECOND -> INTERVAL\n * GEOGRAPHY(4326) -> GEOGRAPHY\n */\nexport function normalizeTypeName(typeName: string): string {\n return typeName\n .replace(/\\(.*\\)$/, \"\") // remove (p, s) eg: DECIMAL(38,6) -> DECIMAL\n .replace(/<.*>$/, \"\") // remove <T> eg: ARRAY<STRING> -> ARRAY\n .split(\" \")[0]; // take first word eg: INTERVAL DAY TO SECOND -> INTERVAL\n}\n\n/** Type Map for Databricks data types to JavaScript types */\nconst typeMap: Record<string, string> = {\n // string types\n STRING: \"string\",\n BINARY: \"string\",\n // boolean\n BOOLEAN: \"boolean\",\n // numeric types\n TINYINT: \"number\",\n SMALLINT: \"number\",\n INT: \"number\",\n BIGINT: \"number\",\n FLOAT: \"number\",\n DOUBLE: \"number\",\n DECIMAL: \"number\",\n // date/time types\n DATE: \"string\",\n TIMESTAMP: \"string\",\n TIMESTAMP_NTZ: \"string\",\n INTERVAL: \"string\",\n // complex types\n ARRAY: \"unknown[]\",\n MAP: \"Record<string, unknown>\",\n STRUCT: \"Record<string, unknown>\",\n OBJECT: \"Record<string, unknown>\",\n VARIANT: \"unknown\",\n // spatial types\n GEOGRAPHY: \"unknown\",\n GEOMETRY: \"unknown\",\n // null type\n VOID: \"null\",\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-registry.js","names":[],"sources":["../../src/type-generator/query-registry.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport { createLogger } from \"../logging/logger\";\nimport { CACHE_VERSION, hashSQL, loadCache, saveCache } from \"./cache\";\nimport { Spinner } from \"./spinner\";\nimport {\n type DatabricksStatementExecutionResponse,\n type QuerySchema,\n sqlTypeToHelper,\n sqlTypeToMarker,\n} from \"./types\";\n\nconst logger = createLogger(\"type-generator:query-registry\");\n\n/**\n * Extract parameters from a SQL query\n * @param sql - the SQL query to extract parameters from\n * @returns an array of parameter names\n */\nexport function extractParameters(sql: string): string[] {\n const matches = sql.matchAll(/:([a-zA-Z_]\\w*)/g);\n const params = new Set<string>();\n for (const match of matches) {\n params.add(match[1]);\n }\n return Array.from(params);\n}\n\n// parameters that are injected by the server\nexport const SERVER_INJECTED_PARAMS = [\"workspaceId\"];\n\nexport function convertToQueryType(\n result: DatabricksStatementExecutionResponse,\n sql: string,\n queryName: string,\n): string {\n const dataRows = result.result?.data_array || [];\n const columns = dataRows.map((row) => ({\n name: row[0] || \"\",\n type_name: row[1]?.toUpperCase() || \"STRING\",\n comment: row[2] || undefined,\n }));\n\n const params = extractParameters(sql).filter(\n (p) => !SERVER_INJECTED_PARAMS.includes(p),\n );\n\n const paramTypes = extractParameterTypes(sql);\n\n // generate parameters types with JSDoc hints\n const paramsType =\n params.length > 0\n ? `{\\n ${params\n .map((p) => {\n const sqlType = paramTypes[p];\n // if no type annotation, use SQLTypeMarker (union type)\n const markerType = sqlType\n ? sqlTypeToMarker[sqlType]\n : \"SQLTypeMarker\";\n const helper = sqlType ? sqlTypeToHelper[sqlType] : \"sql.*()\";\n return `/** ${sqlType || \"any\"} - use ${helper} */\\n ${p}: ${markerType}`;\n })\n .join(\";\\n \")};\\n }`\n : \"Record<string, never>\";\n\n // generate result fields with JSDoc\n const resultFields = columns.map((column) => {\n const normalizedType = normalizeTypeName(column.type_name);\n const mappedType = typeMap[normalizedType] || \"unknown\";\n // validate column name is a valid identifier\n const name = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(column.name)\n ? column.name\n : `\"${column.name}\"`;\n\n // generate comment for column\n const comment = column.comment\n ? `/** ${column.comment} */\\n `\n : `/** @sqlType ${column.type_name} */\\n `;\n\n return `${comment}${name}: ${mappedType}`;\n });\n\n return `{\n name: \"${queryName}\";\n parameters: ${paramsType};\n result: Array<{\n ${resultFields.join(\";\\n \")};\n }>;\n }`;\n}\n\nexport function extractParameterTypes(sql: string): Record<string, string> {\n const paramTypes: Record<string, string> = {};\n const regex =\n /--\\s*@param\\s+(\\w+)\\s+(STRING|NUMERIC|BOOLEAN|DATE|TIMESTAMP|BINARY)/gi;\n const matches = sql.matchAll(regex);\n for (const match of matches) {\n const [, paramName, paramType] = match;\n paramTypes[paramName] = paramType.toUpperCase();\n }\n\n return paramTypes;\n}\n\n/**\n * Generate query schemas from a folder of SQL files\n * It uses DESCRIBE QUERY to get the schema without executing the query\n * @param queryFolder - the folder containing the SQL files\n * @param warehouseId - the warehouse id to use for schema analysis\n * @param options - options for the query generation\n * @param options.noCache - if true, skip the cache and regenerate all types\n * @returns an array of query schemas\n */\nexport async function generateQueriesFromDescribe(\n queryFolder: string,\n warehouseId: string,\n options: { noCache?: boolean } = {},\n): Promise<QuerySchema[]> {\n const { noCache = false } = options;\n\n // read all query files in the folder\n const queryFiles = fs\n .readdirSync(queryFolder)\n .filter((file) => file.endsWith(\".sql\"));\n\n logger.debug(\"Found %d SQL queries\", queryFiles.length);\n\n // load cache\n const cache = noCache ? { version: CACHE_VERSION, queries: {} } : loadCache();\n\n const client = new WorkspaceClient({});\n const querySchemas: QuerySchema[] = [];\n const failedQueries: { name: string; error: string }[] = [];\n const spinner = new Spinner();\n\n // process each query file\n for (let i = 0; i < queryFiles.length; i++) {\n const file = queryFiles[i];\n const rawName = path.basename(file, \".sql\");\n const queryName = normalizeQueryName(rawName);\n\n // read query file content\n const sql = fs.readFileSync(path.join(queryFolder, file), \"utf8\");\n const sqlHash = hashSQL(sql);\n\n // check cache\n const cached = cache.queries[queryName];\n if (cached && cached.hash === sqlHash) {\n querySchemas.push({ name: queryName, type: cached.type });\n spinner.start(`Processing ${queryName} (${i + 1}/${queryFiles.length})`);\n spinner.stop(`✓ ${queryName} (cached)`);\n continue;\n }\n\n spinner.start(`Processing ${queryName} (${i + 1}/${queryFiles.length})`);\n\n const sqlWithDefaults = sql.replace(/:([a-zA-Z_]\\w*)/g, \"''\");\n\n // strip trailing semicolon for DESCRIBE QUERY\n const cleanedSql = sqlWithDefaults.trim().replace(/;\\s*$/, \"\");\n\n // execute DESCRIBE QUERY to get schema without running the actual query\n try {\n const result = (await client.statementExecution.executeStatement({\n statement: `DESCRIBE QUERY ${cleanedSql}`,\n warehouse_id: warehouseId,\n })) as DatabricksStatementExecutionResponse;\n\n if (result.status.state === \"FAILED\") {\n const sqlError =\n result.status.error?.message || \"Query execution failed\";\n spinner.stop(`✗ ${queryName} - failed`);\n spinner.printDetail(`SQL Error: ${sqlError}`);\n spinner.printDetail(`Query: ${cleanedSql.slice(0, 200)}`);\n failedQueries.push({\n name: queryName,\n error: sqlError,\n });\n continue;\n }\n\n // convert result to query schema\n const type = convertToQueryType(result, sql, queryName);\n querySchemas.push({ name: queryName, type });\n\n // update cache\n cache.queries[queryName] = { hash: sqlHash, type };\n\n spinner.stop(`✓ ${queryName}`);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n spinner.stop(`✗ ${queryName} - ${errorMessage}`);\n failedQueries.push({ name: queryName, error: errorMessage });\n }\n }\n\n // save cache\n saveCache(cache);\n\n // log warning if there are failed queries\n if (failedQueries.length > 0) {\n logger.debug(\"Warning: %d queries failed\", failedQueries.length);\n }\n\n return querySchemas;\n}\n\n/**\n * Normalize query name by removing the .obo extension\n * @param queryName - the query name to normalize\n * @returns the normalized query name\n */\nexport function normalizeQueryName(fileName: string): string {\n return fileName.replace(/\\.obo$/, \"\");\n}\n\n/**\n * Normalize SQL type name by removing parameters/generics\n * Examples:\n * DECIMAL(38,6) -> DECIMAL\n * ARRAY<STRING> -> ARRAY\n * MAP<STRING,INT> -> MAP\n * STRUCT<name:STRING> -> STRUCT\n * INTERVAL DAY TO SECOND -> INTERVAL\n * GEOGRAPHY(4326) -> GEOGRAPHY\n */\nexport function normalizeTypeName(typeName: string): string {\n return typeName\n .replace(/\\(.*\\)$/, \"\") // remove (p, s) eg: DECIMAL(38,6) -> DECIMAL\n .replace(/<.*>$/, \"\") // remove <T> eg: ARRAY<STRING> -> ARRAY\n .split(\" \")[0]; // take first word eg: INTERVAL DAY TO SECOND -> INTERVAL\n}\n\n/** Type Map for Databricks data types to JavaScript types */\nconst typeMap: Record<string, string> = {\n // string types\n STRING: \"string\",\n BINARY: \"string\",\n // boolean\n BOOLEAN: \"boolean\",\n // numeric types\n TINYINT: \"number\",\n SMALLINT: \"number\",\n INT: \"number\",\n BIGINT: \"number\",\n FLOAT: \"number\",\n DOUBLE: \"number\",\n DECIMAL: \"number\",\n // date/time types\n DATE: \"string\",\n TIMESTAMP: \"string\",\n TIMESTAMP_NTZ: \"string\",\n INTERVAL: \"string\",\n // complex types\n ARRAY: \"unknown[]\",\n MAP: \"Record<string, unknown>\",\n STRUCT: \"Record<string, unknown>\",\n OBJECT: \"Record<string, unknown>\",\n VARIANT: \"unknown\",\n // spatial types\n GEOGRAPHY: \"unknown\",\n GEOMETRY: \"unknown\",\n // null type\n VOID: \"null\",\n};\n"],"mappings":";;;;;;;;;AAaA,MAAM,SAAS,aAAa,gCAAgC;;;;;;AAO5D,SAAgB,kBAAkB,KAAuB;CACvD,MAAM,UAAU,IAAI,SAAS,mBAAmB;CAChD,MAAM,yBAAS,IAAI,KAAa;AAChC,MAAK,MAAM,SAAS,QAClB,QAAO,IAAI,MAAM,GAAG;AAEtB,QAAO,MAAM,KAAK,OAAO;;AAI3B,MAAa,yBAAyB,CAAC,cAAc;AAErD,SAAgB,mBACd,QACA,KACA,WACQ;CAER,MAAM,WADW,OAAO,QAAQ,cAAc,EAAE,EACvB,KAAK,SAAS;EACrC,MAAM,IAAI,MAAM;EAChB,WAAW,IAAI,IAAI,aAAa,IAAI;EACpC,SAAS,IAAI,MAAM;EACpB,EAAE;CAEH,MAAM,SAAS,kBAAkB,IAAI,CAAC,QACnC,MAAM,CAAC,uBAAuB,SAAS,EAAE,CAC3C;CAED,MAAM,aAAa,sBAAsB,IAAI;AAmC7C,QAAO;aACI,UAAU;kBAhCnB,OAAO,SAAS,IACZ,YAAY,OACT,KAAK,MAAM;EACV,MAAM,UAAU,WAAW;EAE3B,MAAM,aAAa,UACf,gBAAgB,WAChB;EACJ,MAAM,SAAS,UAAU,gBAAgB,WAAW;AACpD,SAAO,OAAO,WAAW,MAAM,SAAS,OAAO,aAAa,EAAE,IAAI;GAClE,CACD,KAAK,YAAY,CAAC,YACrB,wBAqBqB;;QAlBN,QAAQ,KAAK,WAAW;EAE3C,MAAM,aAAa,QADI,kBAAkB,OAAO,UAAU,KACZ;EAE9C,MAAM,OAAO,6BAA6B,KAAK,OAAO,KAAK,GACvD,OAAO,OACP,IAAI,OAAO,KAAK;AAOpB,SAAO,GAJS,OAAO,UACnB,OAAO,OAAO,QAAQ,eACtB,gBAAgB,OAAO,UAAU,eAEjB,KAAK,IAAI;GAC7B,CAMiB,KAAK,YAAY,CAAC;;;;AAKvC,SAAgB,sBAAsB,KAAqC;CACzE,MAAM,aAAqC,EAAE;CAG7C,MAAM,UAAU,IAAI,SADlB,yEACiC;AACnC,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,GAAG,WAAW,aAAa;AACjC,aAAW,aAAa,UAAU,aAAa;;AAGjD,QAAO;;;;;;;;;;;AAYT,eAAsB,4BACpB,aACA,aACA,UAAiC,EAAE,EACX;CACxB,MAAM,EAAE,UAAU,UAAU;CAG5B,MAAM,aAAa,GAChB,YAAY,YAAY,CACxB,QAAQ,SAAS,KAAK,SAAS,OAAO,CAAC;AAE1C,QAAO,MAAM,wBAAwB,WAAW,OAAO;CAGvD,MAAM,QAAQ,UAAU;EAAE,SAAS;EAAe,SAAS,EAAE;EAAE,GAAG,WAAW;CAE7E,MAAM,SAAS,IAAI,gBAAgB,EAAE,CAAC;CACtC,MAAM,eAA8B,EAAE;CACtC,MAAM,gBAAmD,EAAE;CAC3D,MAAM,UAAU,IAAI,SAAS;AAG7B,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC1C,MAAM,OAAO,WAAW;EAExB,MAAM,YAAY,mBADF,KAAK,SAAS,MAAM,OAAO,CACE;EAG7C,MAAM,MAAM,GAAG,aAAa,KAAK,KAAK,aAAa,KAAK,EAAE,OAAO;EACjE,MAAM,UAAU,QAAQ,IAAI;EAG5B,MAAM,SAAS,MAAM,QAAQ;AAC7B,MAAI,UAAU,OAAO,SAAS,SAAS;AACrC,gBAAa,KAAK;IAAE,MAAM;IAAW,MAAM,OAAO;IAAM,CAAC;AACzD,WAAQ,MAAM,cAAc,UAAU,IAAI,IAAI,EAAE,GAAG,WAAW,OAAO,GAAG;AACxE,WAAQ,KAAK,KAAK,UAAU,WAAW;AACvC;;AAGF,UAAQ,MAAM,cAAc,UAAU,IAAI,IAAI,EAAE,GAAG,WAAW,OAAO,GAAG;EAKxE,MAAM,aAHkB,IAAI,QAAQ,oBAAoB,KAAK,CAG1B,MAAM,CAAC,QAAQ,SAAS,GAAG;AAG9D,MAAI;GACF,MAAM,SAAU,MAAM,OAAO,mBAAmB,iBAAiB;IAC/D,WAAW,kBAAkB;IAC7B,cAAc;IACf,CAAC;AAEF,OAAI,OAAO,OAAO,UAAU,UAAU;IACpC,MAAM,WACJ,OAAO,OAAO,OAAO,WAAW;AAClC,YAAQ,KAAK,KAAK,UAAU,WAAW;AACvC,YAAQ,YAAY,cAAc,WAAW;AAC7C,YAAQ,YAAY,UAAU,WAAW,MAAM,GAAG,IAAI,GAAG;AACzD,kBAAc,KAAK;KACjB,MAAM;KACN,OAAO;KACR,CAAC;AACF;;GAIF,MAAM,OAAO,mBAAmB,QAAQ,KAAK,UAAU;AACvD,gBAAa,KAAK;IAAE,MAAM;IAAW;IAAM,CAAC;AAG5C,SAAM,QAAQ,aAAa;IAAE,MAAM;IAAS;IAAM;AAElD,WAAQ,KAAK,KAAK,YAAY;WACvB,OAAO;GACd,MAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,WAAQ,KAAK,KAAK,UAAU,KAAK,eAAe;AAChD,iBAAc,KAAK;IAAE,MAAM;IAAW,OAAO;IAAc,CAAC;;;AAKhE,WAAU,MAAM;AAGhB,KAAI,cAAc,SAAS,EACzB,QAAO,MAAM,8BAA8B,cAAc,OAAO;AAGlE,QAAO;;;;;;;AAQT,SAAgB,mBAAmB,UAA0B;AAC3D,QAAO,SAAS,QAAQ,UAAU,GAAG;;;;;;;;;;;;AAavC,SAAgB,kBAAkB,UAA0B;AAC1D,QAAO,SACJ,QAAQ,WAAW,GAAG,CACtB,QAAQ,SAAS,GAAG,CACpB,MAAM,IAAI,CAAC;;;AAIhB,MAAM,UAAkC;CAEtC,QAAQ;CACR,QAAQ;CAER,SAAS;CAET,SAAS;CACT,UAAU;CACV,KAAK;CACL,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,SAAS;CAET,MAAM;CACN,WAAW;CACX,eAAe;CACf,UAAU;CAEV,OAAO;CACP,KAAK;CACL,QAAQ;CACR,QAAQ;CACR,SAAS;CAET,WAAW;CACX,UAAU;CAEV,MAAM;CACP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spinner.js","names":[],"sources":["../../src/type-generator/spinner.ts"],"sourcesContent":["/**\n * Simple loading spinner for CLI\n */\nexport class Spinner {\n private frames = [\" \", \". \", \".. \", \"...\"];\n private current = 0;\n private interval: NodeJS.Timeout | null = null;\n private text = \"\";\n\n start(text: string) {\n this.text = text;\n this.current = 0;\n process.stdout.write(` ${this.text}${this.frames[0]}`);\n this.interval = setInterval(() => {\n this.current = (this.current + 1) % this.frames.length;\n process.stdout.write(`\\r ${this.text}${this.frames[this.current]}`);\n }, 300);\n }\n\n stop(finalText?: string) {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n // clear the line and write the final text\n process.stdout.write(`\\x1b[2K\\r ${finalText || this.text}\\n`);\n }\n}\n"],"mappings":";;;;AAGA,IAAa,UAAb,MAAqB;CACnB,AAAQ,SAAS;EAAC;EAAO;EAAO;EAAO;EAAM;CAC7C,AAAQ,UAAU;CAClB,AAAQ,WAAkC;CAC1C,AAAQ,OAAO;CAEf,MAAM,MAAc;AAClB,OAAK,OAAO;AACZ,OAAK,UAAU;AACf,UAAQ,OAAO,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,KAAK;AACvD,OAAK,WAAW,kBAAkB;AAChC,QAAK,WAAW,KAAK,UAAU,KAAK,KAAK,OAAO;AAChD,WAAQ,OAAO,MAAM,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,WAAW;KACnE,IAAI;;CAGT,KAAK,WAAoB;AACvB,MAAI,KAAK,UAAU;AACjB,iBAAc,KAAK,SAAS;AAC5B,QAAK,WAAW;;AAGlB,UAAQ,OAAO,MAAM,cAAc,aAAa,KAAK,KAAK,IAAI"}
|
|
1
|
+
{"version":3,"file":"spinner.js","names":[],"sources":["../../src/type-generator/spinner.ts"],"sourcesContent":["/**\n * Simple loading spinner for CLI\n */\nexport class Spinner {\n private frames = [\" \", \". \", \".. \", \"...\"];\n private current = 0;\n private interval: NodeJS.Timeout | null = null;\n private text = \"\";\n\n start(text: string) {\n this.text = text;\n this.current = 0;\n process.stdout.write(` ${this.text}${this.frames[0]}`);\n this.interval = setInterval(() => {\n this.current = (this.current + 1) % this.frames.length;\n process.stdout.write(`\\r ${this.text}${this.frames[this.current]}`);\n }, 300);\n }\n\n stop(finalText?: string) {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n // clear the line and write the final text\n process.stdout.write(`\\x1b[2K\\r ${finalText || this.text}\\n`);\n }\n\n printDetail(text: string) {\n process.stdout.write(`\\x1b[2m ${text}\\x1b[0m\\n`);\n }\n}\n"],"mappings":";;;;AAGA,IAAa,UAAb,MAAqB;CACnB,AAAQ,SAAS;EAAC;EAAO;EAAO;EAAO;EAAM;CAC7C,AAAQ,UAAU;CAClB,AAAQ,WAAkC;CAC1C,AAAQ,OAAO;CAEf,MAAM,MAAc;AAClB,OAAK,OAAO;AACZ,OAAK,UAAU;AACf,UAAQ,OAAO,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,KAAK;AACvD,OAAK,WAAW,kBAAkB;AAChC,QAAK,WAAW,KAAK,UAAU,KAAK,KAAK,OAAO;AAChD,WAAQ,OAAO,MAAM,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,WAAW;KACnE,IAAI;;CAGT,KAAK,WAAoB;AACvB,MAAI,KAAK,UAAU;AACjB,iBAAc,KAAK,SAAS;AAC5B,QAAK,WAAW;;AAGlB,UAAQ,OAAO,MAAM,cAAc,aAAa,KAAK,KAAK,IAAI;;CAGhE,YAAY,MAAc;AACxB,UAAQ,OAAO,MAAM,cAAc,KAAK,WAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../src/type-generator/types.ts"],"sourcesContent":["/**\n * Databricks statement execution response interface for DESCRIBE QUERY\n * @property statement_id - the id of the statement\n * @property status - the status of the statement\n * @property result - the result containing column schema as rows [col_name, data_type, comment]\n */\nexport interface DatabricksStatementExecutionResponse {\n statement_id: string;\n status: {
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/type-generator/types.ts"],"sourcesContent":["/**\n * Databricks statement execution response interface for DESCRIBE QUERY\n * @property statement_id - the id of the statement\n * @property status - the status of the statement\n * @property result - the result containing column schema as rows [col_name, data_type, comment]\n */\nexport interface DatabricksStatementExecutionResponse {\n statement_id: string;\n status: {\n state: string;\n error?: { error_code?: string; message?: string };\n };\n result?: {\n data_array?: (string | null)[][];\n };\n}\n\n/**\n * Map of SQL types to their corresponding marker types\n * Used to convert SQL types to their corresponding marker types\n */\nexport const sqlTypeToMarker: Record<string, string> = {\n // string\n STRING: \"SQLStringMarker\",\n BINARY: \"SQLBinaryMarker\",\n // boolean\n BOOLEAN: \"SQLBooleanMarker\",\n // numeric\n NUMERIC: \"SQLNumberMarker\",\n INT: \"SQLNumberMarker\",\n BIGINT: \"SQLNumberMarker\",\n TINYINT: \"SQLNumberMarker\",\n SMALLINT: \"SQLNumberMarker\",\n FLOAT: \"SQLNumberMarker\",\n DOUBLE: \"SQLNumberMarker\",\n DECIMAL: \"SQLNumberMarker\",\n // date/time\n DATE: \"SQLDateMarker\",\n TIMESTAMP: \"SQLTimestampMarker\",\n TIMESTAMP_NTZ: \"SQLTimestampMarker\",\n};\n\n/**\n * Map of SQL types to their corresponding helper function names\n * Used to generate JSDoc hints for parameters\n */\nexport const sqlTypeToHelper: Record<string, string> = {\n // string\n STRING: \"sql.string()\",\n BINARY: \"sql.binary()\",\n // boolean\n BOOLEAN: \"sql.boolean()\",\n // numeric\n NUMERIC: \"sql.number()\",\n INT: \"sql.number()\",\n BIGINT: \"sql.number()\",\n TINYINT: \"sql.number()\",\n SMALLINT: \"sql.number()\",\n FLOAT: \"sql.number()\",\n DOUBLE: \"sql.number()\",\n DECIMAL: \"sql.number()\",\n // date/time\n DATE: \"sql.date()\",\n TIMESTAMP: \"sql.timestamp()\",\n TIMESTAMP_NTZ: \"sql.timestamp()\",\n};\n\n/**\n * Query schema interface\n * @property name - the name of the query\n * @property type - the type of the query (string, number, boolean, object, array, etc.)\n */\nexport interface QuerySchema {\n name: string;\n type: string;\n}\n"],"mappings":";;;;;AAqBA,MAAa,kBAA0C;CAErD,QAAQ;CACR,QAAQ;CAER,SAAS;CAET,SAAS;CACT,KAAK;CACL,QAAQ;CACR,SAAS;CACT,UAAU;CACV,OAAO;CACP,QAAQ;CACR,SAAS;CAET,MAAM;CACN,WAAW;CACX,eAAe;CAChB;;;;;AAMD,MAAa,kBAA0C;CAErD,QAAQ;CACR,QAAQ;CAER,SAAS;CAET,SAAS;CACT,KAAK;CACL,QAAQ;CACR,SAAS;CACT,UAAU;CACV,OAAO;CACP,QAAQ;CACR,SAAS;CAET,MAAM;CACN,WAAW;CACX,eAAe;CAChB"}
|