From da1934b4d1c2a49d61de20dc984a0236beb67824 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 24 Feb 2026 17:56:02 +0100 Subject: [PATCH 1/5] refactor: toPlugin api chore: fixup --- .../server/lakebase-examples-plugin.ts | 7 ++----- apps/dev-playground/server/reconnect-plugin.ts | 7 ++----- .../server/telemetry-example-plugin.ts | 7 ++----- docs/docs/plugins/custom-plugins.md | 9 +++------ packages/appkit/src/plugin/to-plugin.ts | 18 +++++++++++------- .../appkit/src/plugins/analytics/analytics.ts | 7 ++----- packages/appkit/src/plugins/genie/genie.ts | 6 ++---- .../appkit/src/plugins/lakebase/lakebase.ts | 7 ++----- packages/appkit/src/plugins/server/index.ts | 6 ++---- .../server/tests/server.integration.test.ts | 6 ++---- .../commands/plugin/create/scaffold.test.ts | 4 +++- .../src/cli/commands/plugin/create/scaffold.ts | 7 ++----- 12 files changed, 35 insertions(+), 56 deletions(-) diff --git a/apps/dev-playground/server/lakebase-examples-plugin.ts b/apps/dev-playground/server/lakebase-examples-plugin.ts index 930653f8..8f2cf4b5 100644 --- a/apps/dev-playground/server/lakebase-examples-plugin.ts +++ b/apps/dev-playground/server/lakebase-examples-plugin.ts @@ -19,6 +19,7 @@ import * as typeormExample from "./lakebase-examples/typeorm-example"; */ export class LakebaseExamplesPlugin extends Plugin { + static override readonly name = "lakebase-examples"; public name = "lakebase-examples"; protected envVars: string[] = []; @@ -80,8 +81,4 @@ export class LakebaseExamplesPlugin extends Plugin { } } -export const lakebaseExamples = toPlugin< - typeof LakebaseExamplesPlugin, - Record, - "lakebase-examples" ->(LakebaseExamplesPlugin, "lakebase-examples"); +export const lakebaseExamples = toPlugin(LakebaseExamplesPlugin); diff --git a/apps/dev-playground/server/reconnect-plugin.ts b/apps/dev-playground/server/reconnect-plugin.ts index 908b0e1a..8c4b9b9a 100644 --- a/apps/dev-playground/server/reconnect-plugin.ts +++ b/apps/dev-playground/server/reconnect-plugin.ts @@ -14,6 +14,7 @@ interface ReconnectStreamResponse { } export class ReconnectPlugin extends Plugin { + static override readonly name = "reconnect"; public name = "reconnect"; static manifest = { @@ -84,8 +85,4 @@ export class ReconnectPlugin extends Plugin { } } -export const reconnect = toPlugin< - typeof ReconnectPlugin, - Record, - "reconnect" ->(ReconnectPlugin, "reconnect"); +export const reconnect = toPlugin(ReconnectPlugin); diff --git a/apps/dev-playground/server/telemetry-example-plugin.ts b/apps/dev-playground/server/telemetry-example-plugin.ts index 8f879687..1a5a45c9 100644 --- a/apps/dev-playground/server/telemetry-example-plugin.ts +++ b/apps/dev-playground/server/telemetry-example-plugin.ts @@ -16,6 +16,7 @@ import { import type { Request, Response, Router } from "express"; class TelemetryExamples extends Plugin { + static override readonly name = "telemetry-examples"; public name = "telemetry-examples" as const; static manifest = { @@ -522,8 +523,4 @@ class TelemetryExamples extends Plugin { } } -export const telemetryExamples = toPlugin< - typeof TelemetryExamples, - BasePluginConfig, - "telemetryExamples" ->(TelemetryExamples, "telemetryExamples"); +export const telemetryExamples = toPlugin(TelemetryExamples); diff --git a/docs/docs/plugins/custom-plugins.md b/docs/docs/plugins/custom-plugins.md index 59a1ce51..3ed8fef6 100644 --- a/docs/docs/plugins/custom-plugins.md +++ b/docs/docs/plugins/custom-plugins.md @@ -21,9 +21,9 @@ import { Plugin, toPlugin } from "@databricks/appkit"; import type express from "express"; class MyPlugin extends Plugin { + static override readonly name = "myPlugin"; name = "myPlugin"; - // Define resource requirements in the static manifest static manifest = { name: "myPlugin", displayName: "My Plugin", @@ -59,17 +59,13 @@ class MyPlugin extends Plugin { } exports() { - // an object with the methods from this plugin to expose return { myCustomMethod: this.myCustomMethod } } } -export const myPlugin = toPlugin, "myPlugin">( - MyPlugin, - "myPlugin", -); +export const myPlugin = toPlugin(MyPlugin); ``` ## Config-dependent resources @@ -84,6 +80,7 @@ interface MyPluginConfig extends BasePluginConfig { } class MyPlugin extends Plugin { + static override readonly name = "myPlugin"; name = "myPlugin"; static manifest = { diff --git a/packages/appkit/src/plugin/to-plugin.ts b/packages/appkit/src/plugin/to-plugin.ts index 110664d6..1e61513a 100644 --- a/packages/appkit/src/plugin/to-plugin.ts +++ b/packages/appkit/src/plugin/to-plugin.ts @@ -1,15 +1,19 @@ -import type { PluginData, ToPlugin } from "shared"; +import type { PluginConstructor, PluginData, ToPlugin } from "shared"; /** + * Wraps a plugin class so it can be passed to createApp with optional config. + * Infers config type from the constructor and plugin name from the static `name` property. + * * @internal */ -export function toPlugin( +export function toPlugin( plugin: T, - name: N, -): ToPlugin { - return (config: U = {} as U): PluginData => ({ +): ToPlugin[0], T["name"]> { + type Config = ConstructorParameters[0]; + type Name = T["name"]; + return (config: Config = {} as Config): PluginData => ({ plugin: plugin as T, - config: config as U, - name, + config: config as Config, + name: plugin.name as Name, }); } diff --git a/packages/appkit/src/plugins/analytics/analytics.ts b/packages/appkit/src/plugins/analytics/analytics.ts index 288526fc..76e378f8 100644 --- a/packages/appkit/src/plugins/analytics/analytics.ts +++ b/packages/appkit/src/plugins/analytics/analytics.ts @@ -27,6 +27,7 @@ import type { const logger = createLogger("analytics"); export class AnalyticsPlugin extends Plugin { + static override readonly name = "analytics"; name = "analytics"; /** Plugin manifest declaring metadata and resource requirements */ @@ -286,8 +287,4 @@ export class AnalyticsPlugin extends Plugin { /** * @internal */ -export const analytics = toPlugin< - typeof AnalyticsPlugin, - IAnalyticsConfig, - "analytics" ->(AnalyticsPlugin, "analytics"); +export const analytics = toPlugin(AnalyticsPlugin); diff --git a/packages/appkit/src/plugins/genie/genie.ts b/packages/appkit/src/plugins/genie/genie.ts index e78cdaa4..68745475 100644 --- a/packages/appkit/src/plugins/genie/genie.ts +++ b/packages/appkit/src/plugins/genie/genie.ts @@ -18,6 +18,7 @@ import type { const logger = createLogger("genie"); export class GeniePlugin extends Plugin { + static override readonly name = "genie"; name = "genie"; static manifest = manifest as PluginManifest; @@ -231,7 +232,4 @@ export class GeniePlugin extends Plugin { /** * @internal */ -export const genie = toPlugin( - GeniePlugin, - "genie", -); +export const genie = toPlugin(GeniePlugin); diff --git a/packages/appkit/src/plugins/lakebase/lakebase.ts b/packages/appkit/src/plugins/lakebase/lakebase.ts index b6e0c961..252ea49f 100644 --- a/packages/appkit/src/plugins/lakebase/lakebase.ts +++ b/packages/appkit/src/plugins/lakebase/lakebase.ts @@ -31,6 +31,7 @@ const logger = createLogger("lakebase"); * ``` */ export class LakebasePlugin extends Plugin { + static override readonly name = "lakebase"; name = "lakebase"; /** Plugin manifest declaring metadata and resource requirements */ @@ -118,8 +119,4 @@ export class LakebasePlugin extends Plugin { /** * @internal */ -export const lakebase = toPlugin< - typeof LakebasePlugin, - ILakebaseConfig, - "lakebase" ->(LakebasePlugin, "lakebase"); +export const lakebase = toPlugin(LakebasePlugin); diff --git a/packages/appkit/src/plugins/server/index.ts b/packages/appkit/src/plugins/server/index.ts index 9398dc00..4bd8ce1a 100644 --- a/packages/appkit/src/plugins/server/index.ts +++ b/packages/appkit/src/plugins/server/index.ts @@ -44,6 +44,7 @@ export class ServerPlugin extends Plugin { /** Plugin manifest declaring metadata and resource requirements */ static manifest = manifest as PluginManifest; + static override readonly name = "server"; public name = "server" as const; private serverApplication: express.Application; private server: HTTPServer | null; @@ -355,10 +356,7 @@ const EXCLUDED_PLUGINS = [ServerPlugin.name]; /** * @internal */ -export const server = toPlugin( - ServerPlugin, - "server", -); +export const server = toPlugin(ServerPlugin); // Export manifest and types export type { ServerConfig } from "./types"; diff --git a/packages/appkit/src/plugins/server/tests/server.integration.test.ts b/packages/appkit/src/plugins/server/tests/server.integration.test.ts index 84496348..70675540 100644 --- a/packages/appkit/src/plugins/server/tests/server.integration.test.ts +++ b/packages/appkit/src/plugins/server/tests/server.integration.test.ts @@ -98,6 +98,7 @@ describe("ServerPlugin with custom plugin", () => { // Create a simple test plugin class TestPlugin extends Plugin { + static override readonly name = "test-plugin"; static manifest = { name: "test-plugin", displayName: "Test Plugin", @@ -117,10 +118,7 @@ describe("ServerPlugin with custom plugin", () => { } } - const testPlugin = toPlugin( - TestPlugin, - "test-plugin", - ); + const testPlugin = toPlugin(TestPlugin); const app = await createApp({ plugins: [ diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts index 66cd1e25..f99e4e97 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts @@ -100,7 +100,8 @@ describe("scaffold", () => { "utf-8", ); expect(pluginTs).toContain("class MyPlugin"); - expect(pluginTs).toContain("export const myPlugin = toPlugin"); + expect(pluginTs).toContain('static override readonly name = "my-plugin"'); + expect(pluginTs).toContain("export const myPlugin = toPlugin(MyPlugin)"); expect(pluginTs).toContain('import manifest from "./manifest.json"'); expect(pluginTs).toContain("manifest as PluginManifest"); }); @@ -118,6 +119,7 @@ describe("scaffold", () => { ); expect(indexTs).toContain("MyPlugin"); expect(indexTs).toContain("myPlugin"); + expect(indexTs).toContain("manifest"); }); it("includes resources in manifest when provided", () => { diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.ts index 0149826b..f44353c8 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.ts @@ -124,6 +124,7 @@ export function scaffoldPlugin( import manifest from "./manifest.json"; export class ${className} extends Plugin { + static override readonly name = "${answers.name}"; name = "${answers.name}"; static manifest = manifest as PluginManifest; @@ -141,11 +142,7 @@ export class ${className} extends Plugin { } } -export const ${exportName} = toPlugin< - typeof ${className}, - Record, - "${answers.name}" ->(${className}, "${answers.name}"); +export const ${exportName} = toPlugin(${className}); `; writeTracked(path.join(targetDir, `${answers.name}.ts`), pluginTs, written); From 64ae4aca296f6775c466746166954c4eaa24743a Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 10 Mar 2026 10:43:10 +0100 Subject: [PATCH 2/5] chore: fixup --- .../server/lakebase-examples-plugin.ts | 11 +++-- .../dev-playground/server/reconnect-plugin.ts | 7 +--- .../server/telemetry-example-plugin.ts | 6 +-- docs/docs/api/appkit/Class.Plugin.md | 5 +-- .../docs/api/appkit/Class.ResourceRegistry.md | 2 +- docs/docs/api/appkit/Function.createApp.md | 2 +- .../api/appkit/Interface.PluginManifest.md | 12 ++++-- docs/docs/api/appkit/TypeAlias.PluginData.md | 41 +++++++++++++++++++ docs/docs/api/appkit/TypeAlias.ToPlugin.md | 2 +- docs/docs/api/appkit/index.md | 1 + docs/docs/api/appkit/typedoc-sidebar.ts | 5 +++ packages/appkit/src/index.ts | 1 + packages/appkit/src/plugin/plugin.ts | 10 ++--- packages/appkit/src/plugin/to-plugin.ts | 8 ++-- .../appkit/src/plugins/analytics/analytics.ts | 5 +-- packages/appkit/src/plugins/genie/genie.ts | 5 +-- .../appkit/src/plugins/lakebase/lakebase.ts | 5 +-- packages/appkit/src/plugins/server/index.ts | 8 +--- .../server/tests/server.integration.test.ts | 5 +-- .../appkit/src/registry/manifest-loader.ts | 2 +- packages/appkit/src/registry/types.ts | 6 +-- .../commands/plugin/create/scaffold.test.ts | 2 - .../cli/commands/plugin/create/scaffold.ts | 5 +-- packages/shared/src/plugin.ts | 4 +- 24 files changed, 95 insertions(+), 65 deletions(-) create mode 100644 docs/docs/api/appkit/TypeAlias.PluginData.md diff --git a/apps/dev-playground/server/lakebase-examples-plugin.ts b/apps/dev-playground/server/lakebase-examples-plugin.ts index 8f2cf4b5..d8200210 100644 --- a/apps/dev-playground/server/lakebase-examples-plugin.ts +++ b/apps/dev-playground/server/lakebase-examples-plugin.ts @@ -1,4 +1,9 @@ -import { getUsernameWithApiLookup, Plugin, toPlugin } from "@databricks/appkit"; +import { + getUsernameWithApiLookup, + Plugin, + type PluginManifest, + toPlugin, +} from "@databricks/appkit"; import type { IAppRouter } from "shared"; import * as drizzleExample from "./lakebase-examples/drizzle-example"; import * as rawExample from "./lakebase-examples/raw-driver-example"; @@ -19,8 +24,6 @@ import * as typeormExample from "./lakebase-examples/typeorm-example"; */ export class LakebaseExamplesPlugin extends Plugin { - static override readonly name = "lakebase-examples"; - public name = "lakebase-examples"; protected envVars: string[] = []; static manifest = { @@ -31,7 +34,7 @@ export class LakebaseExamplesPlugin extends Plugin { required: [], optional: [], }, - }; + } satisfies PluginManifest; async setup() { // Check if Lakebase is configured diff --git a/apps/dev-playground/server/reconnect-plugin.ts b/apps/dev-playground/server/reconnect-plugin.ts index 8c4b9b9a..609b8e0e 100644 --- a/apps/dev-playground/server/reconnect-plugin.ts +++ b/apps/dev-playground/server/reconnect-plugin.ts @@ -1,4 +1,4 @@ -import { Plugin, toPlugin } from "@databricks/appkit"; +import { Plugin, type PluginManifest, toPlugin } from "@databricks/appkit"; import type { IAppRouter, StreamExecutionSettings } from "shared"; interface ReconnectResponse { @@ -14,9 +14,6 @@ interface ReconnectStreamResponse { } export class ReconnectPlugin extends Plugin { - static override readonly name = "reconnect"; - public name = "reconnect"; - static manifest = { name: "reconnect", displayName: "Reconnect Plugin", @@ -25,7 +22,7 @@ export class ReconnectPlugin extends Plugin { required: [], optional: [], }, - }; + } satisfies PluginManifest<"reconnect">; injectRoutes(router: IAppRouter): void { this.route(router, { diff --git a/apps/dev-playground/server/telemetry-example-plugin.ts b/apps/dev-playground/server/telemetry-example-plugin.ts index 1a5a45c9..576e56ac 100644 --- a/apps/dev-playground/server/telemetry-example-plugin.ts +++ b/apps/dev-playground/server/telemetry-example-plugin.ts @@ -8,6 +8,7 @@ import { type Counter, type Histogram, Plugin, + type PluginManifest, SeverityNumber, type Span, SpanStatusCode, @@ -16,9 +17,6 @@ import { import type { Request, Response, Router } from "express"; class TelemetryExamples extends Plugin { - static override readonly name = "telemetry-examples"; - public name = "telemetry-examples" as const; - static manifest = { name: "telemetry-examples", displayName: "Telemetry Examples Plugin", @@ -27,7 +25,7 @@ class TelemetryExamples extends Plugin { required: [], optional: [], }, - }; + } as PluginManifest<"telemetry-examples">; private requestCounter: Counter; private durationHistogram: Histogram; diff --git a/docs/docs/api/appkit/Class.Plugin.md b/docs/docs/api/appkit/Class.Plugin.md index 18d49a1e..005b8d35 100644 --- a/docs/docs/api/appkit/Class.Plugin.md +++ b/docs/docs/api/appkit/Class.Plugin.md @@ -35,7 +35,6 @@ const myManifest: PluginManifest = { class MyPlugin extends Plugin { static manifest = myManifest; - name = 'myPlugin'; } ``` @@ -58,8 +57,7 @@ const myManifest: PluginManifest = { }; class MyPlugin extends Plugin { - static manifest = myManifest; - name = 'myPlugin'; + static manifest = myManifest<"myPlugin">; // Runtime method: converts optional resources to required based on config static getResourceRequirements(config: MyConfig) { @@ -327,7 +325,6 @@ and adds `asUser(req)` for user-scoped execution. ```ts class MyPlugin extends Plugin { - name = "myPlugin"; private getData() { return []; } exports() { diff --git a/docs/docs/api/appkit/Class.ResourceRegistry.md b/docs/docs/api/appkit/Class.ResourceRegistry.md index e8c4d841..e0291fb6 100644 --- a/docs/docs/api/appkit/Class.ResourceRegistry.md +++ b/docs/docs/api/appkit/Class.ResourceRegistry.md @@ -45,7 +45,7 @@ For each plugin, loads its manifest (required) and runtime resource requirements | Parameter | Type | Description | | ------ | ------ | ------ | -| `rawPlugins` | `PluginData`\<`PluginConstructor`, `unknown`, `string`\>[] | Array of plugin data entries from createApp configuration | +| `rawPlugins` | [`PluginData`](TypeAlias.PluginData.md)\<`PluginConstructor`, `unknown`, `string`\>[] | Array of plugin data entries from createApp configuration | #### Returns diff --git a/docs/docs/api/appkit/Function.createApp.md b/docs/docs/api/appkit/Function.createApp.md index 6bcdf998..cb703386 100644 --- a/docs/docs/api/appkit/Function.createApp.md +++ b/docs/docs/api/appkit/Function.createApp.md @@ -20,7 +20,7 @@ with an `asUser(req)` method for user-scoped execution. | Type Parameter | | ------ | -| `T` *extends* `PluginData`\<`PluginConstructor`, `unknown`, `string`\>[] | +| `T` *extends* [`PluginData`](TypeAlias.PluginData.md)\<`PluginConstructor`, `unknown`, `string`\>[] | ## Parameters diff --git a/docs/docs/api/appkit/Interface.PluginManifest.md b/docs/docs/api/appkit/Interface.PluginManifest.md index 405bf1b7..e4b45a8b 100644 --- a/docs/docs/api/appkit/Interface.PluginManifest.md +++ b/docs/docs/api/appkit/Interface.PluginManifest.md @@ -1,8 +1,14 @@ -# Interface: PluginManifest +# Interface: PluginManifest\ Plugin manifest that declares metadata and resource requirements. Attached to plugin classes as a static property. +## Type Parameters + +| Type Parameter | Default type | +| ------ | ------ | +| `TName` *extends* `string` | `string` | + ## Properties ### author? @@ -83,10 +89,10 @@ optional license: string; ### name ```ts -name: string; +name: TName; ``` -Plugin identifier (matches plugin.name) +Plugin identifier — the single source of truth for the plugin's name *** diff --git a/docs/docs/api/appkit/TypeAlias.PluginData.md b/docs/docs/api/appkit/TypeAlias.PluginData.md new file mode 100644 index 00000000..9dadfd09 --- /dev/null +++ b/docs/docs/api/appkit/TypeAlias.PluginData.md @@ -0,0 +1,41 @@ +# Type Alias: PluginData\ + +```ts +type PluginData = { + config: U; + name: N; + plugin: T; +}; +``` + +## Type Parameters + +| Type Parameter | +| ------ | +| `T` | +| `U` | +| `N` | + +## Properties + +### config + +```ts +config: U; +``` + +*** + +### name + +```ts +name: N; +``` + +*** + +### plugin + +```ts +plugin: T; +``` diff --git a/docs/docs/api/appkit/TypeAlias.ToPlugin.md b/docs/docs/api/appkit/TypeAlias.ToPlugin.md index 337928b5..4217499d 100644 --- a/docs/docs/api/appkit/TypeAlias.ToPlugin.md +++ b/docs/docs/api/appkit/TypeAlias.ToPlugin.md @@ -20,4 +20,4 @@ type ToPlugin = (config?: U) => PluginData; ## Returns -`PluginData`\<`T`, `U`, `N`\> +[`PluginData`](TypeAlias.PluginData.md)\<`T`, `U`, `N`\> diff --git a/docs/docs/api/appkit/index.md b/docs/docs/api/appkit/index.md index 2bd9ad71..4ad80820 100644 --- a/docs/docs/api/appkit/index.md +++ b/docs/docs/api/appkit/index.md @@ -52,6 +52,7 @@ plugin architecture, and React integration. | ------ | ------ | | [ConfigSchema](TypeAlias.ConfigSchema.md) | Configuration schema definition for plugin config. Re-exported from the standard JSON Schema Draft 7 types. | | [IAppRouter](TypeAlias.IAppRouter.md) | Express router type for plugin route registration | +| [PluginData](TypeAlias.PluginData.md) | - | | [ResourcePermission](TypeAlias.ResourcePermission.md) | Union of all possible permission levels across all resource types. | | [ToPlugin](TypeAlias.ToPlugin.md) | - | diff --git a/docs/docs/api/appkit/typedoc-sidebar.ts b/docs/docs/api/appkit/typedoc-sidebar.ts index 21df87aa..2f17b1d2 100644 --- a/docs/docs/api/appkit/typedoc-sidebar.ts +++ b/docs/docs/api/appkit/typedoc-sidebar.ts @@ -173,6 +173,11 @@ const typedocSidebar: SidebarsConfig = { id: "api/appkit/TypeAlias.IAppRouter", label: "IAppRouter" }, + { + type: "doc", + id: "api/appkit/TypeAlias.PluginData", + label: "PluginData" + }, { type: "doc", id: "api/appkit/TypeAlias.ResourcePermission", diff --git a/packages/appkit/src/index.ts b/packages/appkit/src/index.ts index 3e603e47..0d762fde 100644 --- a/packages/appkit/src/index.ts +++ b/packages/appkit/src/index.ts @@ -10,6 +10,7 @@ export type { BasePluginConfig, CacheConfig, IAppRouter, + PluginData, StreamExecutionSettings, } from "shared"; export { isSQLTypeMarker, sql } from "shared"; diff --git a/packages/appkit/src/plugin/plugin.ts b/packages/appkit/src/plugin/plugin.ts index ff39388d..4d96575c 100644 --- a/packages/appkit/src/plugin/plugin.ts +++ b/packages/appkit/src/plugin/plugin.ts @@ -93,7 +93,6 @@ const EXCLUDED_FROM_PROXY = new Set([ * * class MyPlugin extends Plugin { * static manifest = myManifest; - * name = 'myPlugin'; * } * ``` * @@ -117,8 +116,7 @@ const EXCLUDED_FROM_PROXY = new Set([ * }; * * class MyPlugin extends Plugin { - * static manifest = myManifest; - * name = 'myPlugin'; + * static manifest = myManifest<"myPlugin">; * * // Runtime method: converts optional resources to required based on config * static getResourceRequirements(config: MyConfig) { @@ -171,7 +169,10 @@ export abstract class Plugin< name: string; constructor(protected config: TConfig) { - this.name = config.name ?? "plugin"; + this.name = + config.name ?? + (this.constructor as { manifest?: { name: string } }).manifest?.name ?? + "plugin"; this.telemetry = TelemetryManager.getProvider(this.name, config.telemetry); this.streamManager = new StreamManager(); this.cache = CacheManager.getInstanceSync(); @@ -207,7 +208,6 @@ export abstract class Plugin< * @example * ```ts * class MyPlugin extends Plugin { - * name = "myPlugin"; * private getData() { return []; } * * exports() { diff --git a/packages/appkit/src/plugin/to-plugin.ts b/packages/appkit/src/plugin/to-plugin.ts index 1e61513a..77725027 100644 --- a/packages/appkit/src/plugin/to-plugin.ts +++ b/packages/appkit/src/plugin/to-plugin.ts @@ -6,14 +6,14 @@ import type { PluginConstructor, PluginData, ToPlugin } from "shared"; * * @internal */ -export function toPlugin( +export function toPlugin( plugin: T, -): ToPlugin[0], T["name"]> { +): ToPlugin[0], T["manifest"]["name"]> { type Config = ConstructorParameters[0]; - type Name = T["name"]; + type Name = T["manifest"]["name"]; return (config: Config = {} as Config): PluginData => ({ plugin: plugin as T, config: config as Config, - name: plugin.name as Name, + name: plugin.manifest.name as Name, }); } diff --git a/packages/appkit/src/plugins/analytics/analytics.ts b/packages/appkit/src/plugins/analytics/analytics.ts index 76e378f8..86b60986 100644 --- a/packages/appkit/src/plugins/analytics/analytics.ts +++ b/packages/appkit/src/plugins/analytics/analytics.ts @@ -27,11 +27,8 @@ import type { const logger = createLogger("analytics"); export class AnalyticsPlugin extends Plugin { - static override readonly name = "analytics"; - name = "analytics"; - /** Plugin manifest declaring metadata and resource requirements */ - static manifest = manifest as PluginManifest; + static manifest = manifest as PluginManifest<"analytics">; protected static description = "Analytics plugin for data analysis"; protected declare config: IAnalyticsConfig; diff --git a/packages/appkit/src/plugins/genie/genie.ts b/packages/appkit/src/plugins/genie/genie.ts index 68745475..860e0679 100644 --- a/packages/appkit/src/plugins/genie/genie.ts +++ b/packages/appkit/src/plugins/genie/genie.ts @@ -18,10 +18,7 @@ import type { const logger = createLogger("genie"); export class GeniePlugin extends Plugin { - static override readonly name = "genie"; - name = "genie"; - - static manifest = manifest as PluginManifest; + static manifest = manifest as PluginManifest<"genie">; protected static description = "AI/BI Genie space integration for natural language data queries"; diff --git a/packages/appkit/src/plugins/lakebase/lakebase.ts b/packages/appkit/src/plugins/lakebase/lakebase.ts index 252ea49f..a3c69f3a 100644 --- a/packages/appkit/src/plugins/lakebase/lakebase.ts +++ b/packages/appkit/src/plugins/lakebase/lakebase.ts @@ -31,11 +31,8 @@ const logger = createLogger("lakebase"); * ``` */ export class LakebasePlugin extends Plugin { - static override readonly name = "lakebase"; - name = "lakebase"; - /** Plugin manifest declaring metadata and resource requirements */ - static manifest = manifest as PluginManifest; + static manifest = manifest as PluginManifest<"lakebase">; protected declare config: ILakebaseConfig; private pool: Pool | null = null; diff --git a/packages/appkit/src/plugins/server/index.ts b/packages/appkit/src/plugins/server/index.ts index 4bd8ce1a..a3156a61 100644 --- a/packages/appkit/src/plugins/server/index.ts +++ b/packages/appkit/src/plugins/server/index.ts @@ -42,10 +42,7 @@ export class ServerPlugin extends Plugin { }; /** Plugin manifest declaring metadata and resource requirements */ - static manifest = manifest as PluginManifest; - - static override readonly name = "server"; - public name = "server" as const; + static manifest = manifest as PluginManifest<"server">; private serverApplication: express.Application; private server: HTTPServer | null; private viteDevServer?: ViteDevServer; @@ -351,8 +348,7 @@ export class ServerPlugin extends Plugin { } } -const EXCLUDED_PLUGINS = [ServerPlugin.name]; - +const EXCLUDED_PLUGINS: string[] = [ServerPlugin.manifest.name]; /** * @internal */ diff --git a/packages/appkit/src/plugins/server/tests/server.integration.test.ts b/packages/appkit/src/plugins/server/tests/server.integration.test.ts index 70675540..c3a646ea 100644 --- a/packages/appkit/src/plugins/server/tests/server.integration.test.ts +++ b/packages/appkit/src/plugins/server/tests/server.integration.test.ts @@ -6,6 +6,7 @@ import { afterAll, beforeAll, describe, expect, test } from "vitest"; process.env.DATABRICKS_APP_PORT = "8000"; process.env.FLASK_RUN_HOST = "0.0.0.0"; +import type { PluginManifest } from "shared"; import { ServiceContext } from "../../../context/service-context"; import { createApp } from "../../../core"; import { Plugin, toPlugin } from "../../../plugin"; @@ -98,14 +99,12 @@ describe("ServerPlugin with custom plugin", () => { // Create a simple test plugin class TestPlugin extends Plugin { - static override readonly name = "test-plugin"; static manifest = { name: "test-plugin", displayName: "Test Plugin", description: "Test plugin for integration tests", resources: { required: [], optional: [] }, - }; - name = "test-plugin" as const; + } satisfies PluginManifest<"test-plugin">; injectRoutes(router: any) { router.get("/echo", (_req: any, res: any) => { diff --git a/packages/appkit/src/registry/manifest-loader.ts b/packages/appkit/src/registry/manifest-loader.ts index 5c58f1fd..f1bfc79a 100644 --- a/packages/appkit/src/registry/manifest-loader.ts +++ b/packages/appkit/src/registry/manifest-loader.ts @@ -59,7 +59,7 @@ function normalizeResource(r: LooseResource): ResourceRequirement { * @throws {ConfigurationError} If the manifest is missing, invalid, or has invalid resource type/permission */ export function getPluginManifest(plugin: PluginConstructor): PluginManifest { - const pluginName = plugin.name || "unknown"; + const pluginName = plugin.manifest?.name || plugin.name || "unknown"; if (!plugin.manifest) { throw new ConfigurationError( diff --git a/packages/appkit/src/registry/types.ts b/packages/appkit/src/registry/types.ts index e220276e..272b5021 100644 --- a/packages/appkit/src/registry/types.ts +++ b/packages/appkit/src/registry/types.ts @@ -143,9 +143,9 @@ export type ConfigSchema = JSONSchema7; * Plugin manifest that declares metadata and resource requirements. * Attached to plugin classes as a static property. */ -export interface PluginManifest { - /** Plugin identifier (matches plugin.name) */ - name: string; +export interface PluginManifest { + /** Plugin identifier — the single source of truth for the plugin's name */ + name: TName; /** Human-readable display name for UI/CLI */ displayName: string; diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts index f99e4e97..7283cb89 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts @@ -100,7 +100,6 @@ describe("scaffold", () => { "utf-8", ); expect(pluginTs).toContain("class MyPlugin"); - expect(pluginTs).toContain('static override readonly name = "my-plugin"'); expect(pluginTs).toContain("export const myPlugin = toPlugin(MyPlugin)"); expect(pluginTs).toContain('import manifest from "./manifest.json"'); expect(pluginTs).toContain("manifest as PluginManifest"); @@ -119,7 +118,6 @@ describe("scaffold", () => { ); expect(indexTs).toContain("MyPlugin"); expect(indexTs).toContain("myPlugin"); - expect(indexTs).toContain("manifest"); }); it("includes resources in manifest when provided", () => { diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.ts index f44353c8..16b7f376 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.ts @@ -124,10 +124,7 @@ export function scaffoldPlugin( import manifest from "./manifest.json"; export class ${className} extends Plugin { - static override readonly name = "${answers.name}"; - name = "${answers.name}"; - - static manifest = manifest as PluginManifest; + static manifest = manifest as PluginManifest<"${answers.name}">; injectRoutes(router: IAppRouter): void { // Add your routes here, e.g.: diff --git a/packages/shared/src/plugin.ts b/packages/shared/src/plugin.ts index 8079d75b..465f8a64 100644 --- a/packages/shared/src/plugin.ts +++ b/packages/shared/src/plugin.ts @@ -73,8 +73,8 @@ export type PluginConstructor< * Manifest declaration for plugins (imported from registry types). * Re-exported here to avoid circular dependencies. */ -export interface PluginManifest { - name: string; +export interface PluginManifest { + name: TName; displayName: string; description: string; resources: { From 5c6b222f36b265d3300efd4f9b3dfb1847b06692 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 10 Mar 2026 10:52:39 +0100 Subject: [PATCH 3/5] chore: fixup --- apps/dev-playground/server/lakebase-examples-plugin.ts | 2 +- apps/dev-playground/tsconfig.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/dev-playground/server/lakebase-examples-plugin.ts b/apps/dev-playground/server/lakebase-examples-plugin.ts index d8200210..57533af4 100644 --- a/apps/dev-playground/server/lakebase-examples-plugin.ts +++ b/apps/dev-playground/server/lakebase-examples-plugin.ts @@ -34,7 +34,7 @@ export class LakebaseExamplesPlugin extends Plugin { required: [], optional: [], }, - } satisfies PluginManifest; + } satisfies PluginManifest<"lakebase-examples">; async setup() { // Check if Lakebase is configured diff --git a/apps/dev-playground/tsconfig.json b/apps/dev-playground/tsconfig.json index 986d7131..e0c499b8 100644 --- a/apps/dev-playground/tsconfig.json +++ b/apps/dev-playground/tsconfig.json @@ -3,6 +3,8 @@ "compilerOptions": { "baseUrl": ".", "outDir": "./build", + "declaration": false, + "declarationMap": false, "experimentalDecorators": true, "emitDecoratorMetadata": true }, From 77441db7981e749956719b1f99cd15e97b3175d3 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 10 Mar 2026 11:07:00 +0100 Subject: [PATCH 4/5] chore: fixup --- .../server/telemetry-example-plugin.ts | 2 +- docs/docs/api/appkit/Class.Plugin.md | 4 ++-- docs/docs/plugins/custom-plugins.md | 12 +++--------- packages/appkit/src/plugins/server/index.ts | 1 + 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/dev-playground/server/telemetry-example-plugin.ts b/apps/dev-playground/server/telemetry-example-plugin.ts index 576e56ac..7f74b20e 100644 --- a/apps/dev-playground/server/telemetry-example-plugin.ts +++ b/apps/dev-playground/server/telemetry-example-plugin.ts @@ -25,7 +25,7 @@ class TelemetryExamples extends Plugin { required: [], optional: [], }, - } as PluginManifest<"telemetry-examples">; + } satisfies PluginManifest<"telemetry-examples">; private requestCounter: Counter; private durationHistogram: Histogram; diff --git a/docs/docs/api/appkit/Class.Plugin.md b/docs/docs/api/appkit/Class.Plugin.md index 005b8d35..b79994aa 100644 --- a/docs/docs/api/appkit/Class.Plugin.md +++ b/docs/docs/api/appkit/Class.Plugin.md @@ -43,7 +43,7 @@ interface MyConfig extends BasePluginConfig { enableCaching?: boolean; } -const myManifest: PluginManifest = { +const myManifest: PluginManifest<"myPlugin"> = { name: 'myPlugin', resources: { required: [ @@ -57,7 +57,7 @@ const myManifest: PluginManifest = { }; class MyPlugin extends Plugin { - static manifest = myManifest<"myPlugin">; + static manifest = myManifest; // Runtime method: converts optional resources to required based on config static getResourceRequirements(config: MyConfig) { diff --git a/docs/docs/plugins/custom-plugins.md b/docs/docs/plugins/custom-plugins.md index 3ed8fef6..8ccd58b4 100644 --- a/docs/docs/plugins/custom-plugins.md +++ b/docs/docs/plugins/custom-plugins.md @@ -17,13 +17,10 @@ For a deeper understanding of the plugin structure, read on. Extend the [`Plugin`](../api/appkit/Class.Plugin.md) class and export with `toPlugin()`: ```typescript -import { Plugin, toPlugin } from "@databricks/appkit"; +import { Plugin, toPlugin, type PluginManifest } from "@databricks/appkit"; import type express from "express"; class MyPlugin extends Plugin { - static override readonly name = "myPlugin"; - name = "myPlugin"; - static manifest = { name: "myPlugin", displayName: "My Plugin", @@ -44,7 +41,7 @@ class MyPlugin extends Plugin { ], optional: [] } - }; + } satisfies PluginManifest<"myPlugin">; async setup() { // Initialize your plugin @@ -80,9 +77,6 @@ interface MyPluginConfig extends BasePluginConfig { } class MyPlugin extends Plugin { - static override readonly name = "myPlugin"; - name = "myPlugin"; - static manifest = { name: "myPlugin", displayName: "My Plugin", @@ -96,7 +90,7 @@ class MyPlugin extends Plugin { { type: "database", alias: "cache", resourceKey: "cache", description: "Query result caching (if enabled)", permission: "CAN_CONNECT_AND_CREATE", fields: { instance_name: { env: "DATABRICKS_CACHE_INSTANCE" }, database_name: { env: "DATABRICKS_CACHE_DB" } } } ] } - }; + } satisfies PluginManifest<"myPlugin">; // Runtime: Convert optional resources to required based on config static getResourceRequirements(config: MyPluginConfig) { diff --git a/packages/appkit/src/plugins/server/index.ts b/packages/appkit/src/plugins/server/index.ts index a3156a61..e999a620 100644 --- a/packages/appkit/src/plugins/server/index.ts +++ b/packages/appkit/src/plugins/server/index.ts @@ -349,6 +349,7 @@ export class ServerPlugin extends Plugin { } const EXCLUDED_PLUGINS: string[] = [ServerPlugin.manifest.name]; + /** * @internal */ From b656f80a9ecbe934951c3b243a1f6db209af40cd Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 10 Mar 2026 11:44:31 +0100 Subject: [PATCH 5/5] chore: fixup --- docs/docs/api/appkit/Class.Plugin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/api/appkit/Class.Plugin.md b/docs/docs/api/appkit/Class.Plugin.md index b79994aa..005b8d35 100644 --- a/docs/docs/api/appkit/Class.Plugin.md +++ b/docs/docs/api/appkit/Class.Plugin.md @@ -43,7 +43,7 @@ interface MyConfig extends BasePluginConfig { enableCaching?: boolean; } -const myManifest: PluginManifest<"myPlugin"> = { +const myManifest: PluginManifest = { name: 'myPlugin', resources: { required: [ @@ -57,7 +57,7 @@ const myManifest: PluginManifest<"myPlugin"> = { }; class MyPlugin extends Plugin { - static manifest = myManifest; + static manifest = myManifest<"myPlugin">; // Runtime method: converts optional resources to required based on config static getResourceRequirements(config: MyConfig) {