Merge pull request #25765 from backstage/blam/output-types
NFS: Added config input type to the Extensions
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/frontend-test-utils': patch
|
||||
'@backstage/frontend-app-api': patch
|
||||
'@backstage/core-compat-api': patch
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-user-settings': patch
|
||||
'@backstage/plugin-search-react': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-search': patch
|
||||
---
|
||||
|
||||
Added config input type to the extensions
|
||||
@@ -101,7 +101,7 @@ function visitRouteChildren(options: {
|
||||
parentExtensionId: string;
|
||||
context: {
|
||||
pluginId: string;
|
||||
extensions: ExtensionDefinition<unknown>[];
|
||||
extensions: ExtensionDefinition<any, any>[];
|
||||
getUniqueName: () => string;
|
||||
discoverPlugin: (plugin: LegacyBackstagePlugin) => void;
|
||||
};
|
||||
@@ -154,7 +154,7 @@ export function collectLegacyRoutes(
|
||||
): BackstagePlugin[] {
|
||||
const pluginExtensions = new Map<
|
||||
LegacyBackstagePlugin,
|
||||
ExtensionDefinition<unknown>[]
|
||||
ExtensionDefinition<any, any>[]
|
||||
>();
|
||||
|
||||
const getUniqueName = (() => {
|
||||
|
||||
@@ -70,7 +70,7 @@ function createTestExtension(options: {
|
||||
});
|
||||
}
|
||||
|
||||
function routeInfoFromExtensions(extensions: ExtensionDefinition<unknown>[]) {
|
||||
function routeInfoFromExtensions(extensions: ExtensionDefinition<any, any>[]) {
|
||||
const plugin = createPlugin({
|
||||
id: 'test',
|
||||
extensions,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { instantiateAppNodeTree } from './instantiateAppNodeTree';
|
||||
/** @internal */
|
||||
export interface CreateAppTreeOptions {
|
||||
features: FrontendFeature[];
|
||||
builtinExtensions: Extension<unknown>[];
|
||||
builtinExtensions: Extension<any, any>[];
|
||||
config: Config;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,22 +59,22 @@ const simpleExtension = resolveExtensionDefinition(
|
||||
}),
|
||||
);
|
||||
|
||||
function makeSpec<TConfig>(
|
||||
extension: Extension<TConfig>,
|
||||
function makeSpec<TConfig, TConfigInput>(
|
||||
extension: Extension<TConfig, TConfigInput>,
|
||||
spec?: Partial<AppNodeSpec>,
|
||||
): AppNodeSpec {
|
||||
return {
|
||||
id: extension.id,
|
||||
attachTo: extension.attachTo,
|
||||
disabled: extension.disabled,
|
||||
extension,
|
||||
extension: extension as Extension<unknown, unknown>,
|
||||
source: undefined,
|
||||
...spec,
|
||||
};
|
||||
}
|
||||
|
||||
function makeNode<TConfig>(
|
||||
extension: Extension<TConfig>,
|
||||
function makeNode<TConfig, TConfigInput>(
|
||||
extension: Extension<TConfig, TConfigInput>,
|
||||
spec?: Partial<AppNodeSpec>,
|
||||
): AppNode {
|
||||
return {
|
||||
@@ -85,8 +85,8 @@ function makeNode<TConfig>(
|
||||
};
|
||||
}
|
||||
|
||||
function makeInstanceWithId<TConfig>(
|
||||
extension: Extension<TConfig>,
|
||||
function makeInstanceWithId<TConfig, TConfigInput>(
|
||||
extension: Extension<TConfig, TConfigInput>,
|
||||
config?: TConfig,
|
||||
): AppNode {
|
||||
const node = makeNode(extension, { config });
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createExtension } from '@backstage/frontend-plugin-api';
|
||||
import { createExtension, Extension } from '@backstage/frontend-plugin-api';
|
||||
import { resolveAppTree } from './resolveAppTree';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
|
||||
@@ -26,7 +26,7 @@ const extension = resolveExtensionDefinition(
|
||||
output: {},
|
||||
factory: () => ({}),
|
||||
}),
|
||||
);
|
||||
) as Extension<unknown, unknown>;
|
||||
|
||||
const baseSpec = {
|
||||
extension,
|
||||
|
||||
@@ -230,7 +230,7 @@ export interface AppNodeSpec {
|
||||
// (undocumented)
|
||||
readonly disabled: boolean;
|
||||
// (undocumented)
|
||||
readonly extension: Extension<unknown>;
|
||||
readonly extension: Extension<unknown, unknown>;
|
||||
// (undocumented)
|
||||
readonly id: string;
|
||||
// (undocumented)
|
||||
@@ -391,7 +391,7 @@ export function createApiExtension<
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
inputs?: TInputs;
|
||||
},
|
||||
): ExtensionDefinition<TConfig>;
|
||||
): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createApiExtension {
|
||||
@@ -487,7 +487,7 @@ export function createComponentExtension<
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => ComponentType<TProps>;
|
||||
};
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createComponentExtension {
|
||||
@@ -512,12 +512,34 @@ export function createExtension<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
>(
|
||||
options: CreateExtensionOptions<TOutput, TInputs, TConfig, TConfigSchema>,
|
||||
): ExtensionDefinition<TConfig>;
|
||||
options: CreateExtensionOptions<
|
||||
TOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
>;
|
||||
|
||||
// @public
|
||||
export function createExtensionBlueprint<
|
||||
@@ -545,6 +567,13 @@ export function createExtensionBlueprint<
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
TDataRefs
|
||||
>;
|
||||
|
||||
@@ -627,6 +656,7 @@ export interface CreateExtensionOptions<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends {
|
||||
[key: string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
@@ -641,15 +671,20 @@ export interface CreateExtensionOptions<
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
// @deprecated (undocumented)
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig & {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
};
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
@@ -702,9 +737,14 @@ export function createNavItemExtension(options: {
|
||||
routeRef: RouteRef<undefined>;
|
||||
title: string;
|
||||
icon: IconComponent_2;
|
||||
}): ExtensionDefinition<{
|
||||
title: string;
|
||||
}>;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
title: string;
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createNavItemExtension {
|
||||
@@ -726,7 +766,7 @@ export function createNavLogoExtension(options: {
|
||||
namespace?: string;
|
||||
logoIcon: JSX.Element;
|
||||
logoFull: JSX.Element;
|
||||
}): ExtensionDefinition<unknown>;
|
||||
}): ExtensionDefinition<{}, {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createNavLogoExtension {
|
||||
@@ -837,7 +877,7 @@ export namespace createRouterExtension {
|
||||
// @public @deprecated (undocumented)
|
||||
export function createSchemaFromZod<TOutput, TInput>(
|
||||
schemaCreator: (zImpl: typeof z) => ZodSchema<TOutput, ZodTypeDef, TInput>,
|
||||
): PortableSchema<TOutput>;
|
||||
): PortableSchema<TOutput, TInput>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createSignInPageExtension<
|
||||
@@ -881,7 +921,7 @@ export function createSubRouteRef<
|
||||
// @public (undocumented)
|
||||
export function createThemeExtension(
|
||||
theme: AppTheme,
|
||||
): ExtensionDefinition<unknown>;
|
||||
): ExtensionDefinition<{}, {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createThemeExtension {
|
||||
@@ -897,7 +937,7 @@ export namespace createThemeExtension {
|
||||
export function createTranslationExtension(options: {
|
||||
name?: string;
|
||||
resource: TranslationResource | TranslationMessages;
|
||||
}): ExtensionDefinition<unknown>;
|
||||
}): ExtensionDefinition<{}, {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace createTranslationExtension {
|
||||
@@ -935,7 +975,7 @@ export { ErrorApiErrorContext };
|
||||
export { errorApiRef };
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Extension<TConfig> {
|
||||
export interface Extension<TConfig, TConfigInput = TConfig> {
|
||||
// (undocumented)
|
||||
$$type: '@backstage/Extension';
|
||||
// (undocumented)
|
||||
@@ -944,7 +984,7 @@ export interface Extension<TConfig> {
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
readonly configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
// (undocumented)
|
||||
readonly disabled: boolean;
|
||||
// (undocumented)
|
||||
@@ -959,6 +999,9 @@ export interface ExtensionBlueprint<
|
||||
TConfig extends {
|
||||
[key in string]: unknown;
|
||||
},
|
||||
TConfigInput extends {
|
||||
[key in string]: unknown;
|
||||
},
|
||||
TDataRefs extends AnyExtensionDataMap,
|
||||
> {
|
||||
// (undocumented)
|
||||
@@ -1014,7 +1057,15 @@ export interface ExtensionBlueprint<
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & TConfig
|
||||
} & TConfig,
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
}>
|
||||
> &
|
||||
TConfigInput
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -1062,7 +1113,7 @@ export type ExtensionDataValues<TExtensionData extends AnyExtensionDataMap> = {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionDefinition<TConfig> {
|
||||
export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
// (undocumented)
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
// (undocumented)
|
||||
@@ -1071,7 +1122,7 @@ export interface ExtensionDefinition<TConfig> {
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
readonly configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
// (undocumented)
|
||||
readonly disabled: boolean;
|
||||
// (undocumented)
|
||||
@@ -1107,7 +1158,7 @@ export interface ExtensionOverrides {
|
||||
// @public (undocumented)
|
||||
export interface ExtensionOverridesOptions {
|
||||
// (undocumented)
|
||||
extensions: ExtensionDefinition<unknown>[];
|
||||
extensions: ExtensionDefinition<any, any>[];
|
||||
// (undocumented)
|
||||
featureFlags?: FeatureFlagConfig[];
|
||||
}
|
||||
@@ -1170,7 +1221,14 @@ export const IconBundleBlueprint: ExtensionBlueprint<
|
||||
{}
|
||||
>;
|
||||
},
|
||||
{},
|
||||
{
|
||||
icons: string;
|
||||
test: string;
|
||||
},
|
||||
{
|
||||
test: string;
|
||||
icons?: string | undefined;
|
||||
},
|
||||
{
|
||||
icons: ConfigurableExtensionDataRef<
|
||||
'core.icons',
|
||||
@@ -1235,7 +1293,7 @@ export interface PluginOptions<
|
||||
ExternalRoutes extends AnyExternalRoutes,
|
||||
> {
|
||||
// (undocumented)
|
||||
extensions?: ExtensionDefinition<unknown>[];
|
||||
extensions?: ExtensionDefinition<any, any>[];
|
||||
// (undocumented)
|
||||
externalRoutes?: ExternalRoutes;
|
||||
// (undocumented)
|
||||
@@ -1247,8 +1305,8 @@ export interface PluginOptions<
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type PortableSchema<TOutput> = {
|
||||
parse: (input: unknown) => TOutput;
|
||||
export type PortableSchema<TOutput, TInput = TOutput> = {
|
||||
parse: (input: TInput) => TOutput;
|
||||
schema: JsonObject;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import { BackstagePlugin, Extension, ExtensionDataRef } from '../../wiring';
|
||||
export interface AppNodeSpec {
|
||||
readonly id: string;
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly extension: Extension<unknown>;
|
||||
readonly extension: Extension<unknown, unknown>;
|
||||
readonly disabled: boolean;
|
||||
readonly config?: unknown;
|
||||
readonly source?: BackstagePlugin;
|
||||
|
||||
@@ -28,6 +28,12 @@ export const IconBundleBlueprint = createExtensionBlueprint({
|
||||
output: {
|
||||
icons: iconsDataRef,
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
icons: z => z.string().default('blob'),
|
||||
test: z => z.string(),
|
||||
},
|
||||
},
|
||||
factory: (params: { icons: { [key in string]: IconComponent } }) => params,
|
||||
dataRefs: {
|
||||
icons: iconsDataRef,
|
||||
|
||||
@@ -25,10 +25,19 @@ describe('createSchemaFromZod', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(() => parse({ derp: { bar: 'derp' } })).toThrow(
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
return parse({ derp: { bar: 'derp' } });
|
||||
}).toThrow(
|
||||
`Missing required value at 'foo'; Expected number, received string at 'derp.bar'`,
|
||||
);
|
||||
expect(() => parse(undefined)).toThrow(`Missing required value`);
|
||||
expect(() => parse('derp')).toThrow(`Expected object, received string`);
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
return parse(undefined);
|
||||
}).toThrow(`Missing required value`);
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
return parse('derp');
|
||||
}).toThrow(`Expected object, received string`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import { PortableSchema } from './types';
|
||||
*/
|
||||
export function createSchemaFromZod<TOutput, TInput>(
|
||||
schemaCreator: (zImpl: typeof z) => ZodSchema<TOutput, ZodTypeDef, TInput>,
|
||||
): PortableSchema<TOutput> {
|
||||
): PortableSchema<TOutput, TInput> {
|
||||
const schema = schemaCreator(z);
|
||||
return {
|
||||
// TODO: Types allow z.array etc here but it will break stuff
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export type PortableSchema<TOutput> = {
|
||||
parse: (input: unknown) => TOutput;
|
||||
export type PortableSchema<TOutput, TInput = TOutput> = {
|
||||
parse: (input: TInput) => TOutput;
|
||||
schema: JsonObject;
|
||||
};
|
||||
|
||||
@@ -334,6 +334,7 @@ describe('createExtension', () => {
|
||||
foo: 'x',
|
||||
bar: 'y',
|
||||
baz: 'z',
|
||||
// @ts-expect-error
|
||||
qux: 'w',
|
||||
}),
|
||||
).toEqual({
|
||||
@@ -349,8 +350,9 @@ describe('createExtension', () => {
|
||||
foo: 'x',
|
||||
bar: 'bar',
|
||||
});
|
||||
expect(() => extension.configSchema?.parse({})).toThrow(
|
||||
"Missing required value at 'foo'",
|
||||
);
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
return extension.configSchema?.parse({});
|
||||
}).toThrow("Missing required value at 'foo'");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ export interface CreateExtensionOptions<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
> {
|
||||
kind?: string;
|
||||
@@ -92,33 +93,38 @@ export interface CreateExtensionOptions<
|
||||
inputs?: TInputs;
|
||||
output: TOutput;
|
||||
/** @deprecated - use `config.schema` instead */
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
factory(context: {
|
||||
node: AppNode;
|
||||
config: TConfig & {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
};
|
||||
config: TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
});
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionDefinition<TConfig> {
|
||||
export interface ExtensionDefinition<TConfig, TConfigInput = TConfig> {
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
readonly kind?: string;
|
||||
readonly namespace?: string;
|
||||
readonly name?: string;
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly disabled: boolean;
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
readonly configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface InternalExtensionDefinition<TConfig>
|
||||
extends ExtensionDefinition<TConfig> {
|
||||
export interface InternalExtensionDefinition<TConfig, TConfigInput>
|
||||
extends ExtensionDefinition<TConfig, TConfigInput> {
|
||||
readonly version: 'v1';
|
||||
readonly inputs: AnyExtensionInputMap;
|
||||
readonly output: AnyExtensionDataMap;
|
||||
@@ -130,10 +136,13 @@ export interface InternalExtensionDefinition<TConfig>
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function toInternalExtensionDefinition<TConfig>(
|
||||
overrides: ExtensionDefinition<TConfig>,
|
||||
): InternalExtensionDefinition<TConfig> {
|
||||
const internal = overrides as InternalExtensionDefinition<TConfig>;
|
||||
export function toInternalExtensionDefinition<TConfig, TConfigInput>(
|
||||
overrides: ExtensionDefinition<TConfig, TConfigInput>,
|
||||
): InternalExtensionDefinition<TConfig, TConfigInput> {
|
||||
const internal = overrides as InternalExtensionDefinition<
|
||||
TConfig,
|
||||
TConfigInput
|
||||
>;
|
||||
if (internal.$$type !== '@backstage/ExtensionDefinition') {
|
||||
throw new Error(
|
||||
`Invalid extension definition instance, bad type '${internal.$$type}'`,
|
||||
@@ -152,10 +161,32 @@ export function createExtension<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
>(
|
||||
options: CreateExtensionOptions<TOutput, TInputs, TConfig, TConfigSchema>,
|
||||
): ExtensionDefinition<TConfig> {
|
||||
options: CreateExtensionOptions<
|
||||
TOutput,
|
||||
TInputs,
|
||||
TConfig,
|
||||
TConfigInput,
|
||||
TConfigSchema
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
> {
|
||||
const newConfigSchema = options.config?.schema;
|
||||
if (newConfigSchema && options.configSchema) {
|
||||
throw new Error(`Cannot provide both configSchema and config.schema`);
|
||||
@@ -205,5 +236,22 @@ export function createExtension<
|
||||
parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`);
|
||||
return `ExtensionDefinition{${parts.join(',')}}`;
|
||||
},
|
||||
} as InternalExtensionDefinition<TConfig>;
|
||||
} as InternalExtensionDefinition<
|
||||
TConfig &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
}),
|
||||
TConfigInput &
|
||||
(string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>)
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface ExtensionBlueprint<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig extends { [key in string]: unknown },
|
||||
TConfigInput extends { [key in string]: unknown },
|
||||
TDataRefs extends AnyExtensionDataMap,
|
||||
> {
|
||||
dataRefs: TDataRefs;
|
||||
@@ -125,7 +126,15 @@ export interface ExtensionBlueprint<
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & TConfig
|
||||
} & TConfig,
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
}>
|
||||
> &
|
||||
TConfigInput
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -199,7 +208,18 @@ class ExtensionBlueprintImpl<
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
}
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: ReturnType<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}
|
||||
>
|
||||
>
|
||||
> {
|
||||
const schema = {
|
||||
...this.options.config?.schema,
|
||||
@@ -279,6 +299,13 @@ export function createExtensionBlueprint<
|
||||
string extends keyof TConfigSchema
|
||||
? {}
|
||||
: { [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>> },
|
||||
string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
TDataRefs
|
||||
> {
|
||||
return new ExtensionBlueprintImpl(options) as ExtensionBlueprint<
|
||||
@@ -290,6 +317,13 @@ export function createExtensionBlueprint<
|
||||
: {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
string extends keyof TConfigSchema
|
||||
? {}
|
||||
: z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
TDataRefs
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ExtensionOverrides, FeatureFlagConfig } from './types';
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionOverridesOptions {
|
||||
extensions: ExtensionDefinition<unknown>[];
|
||||
extensions: ExtensionDefinition<any, any>[];
|
||||
featureFlags?: FeatureFlagConfig[];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export interface PluginOptions<
|
||||
id: string;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
extensions?: ExtensionDefinition<unknown>[];
|
||||
extensions?: ExtensionDefinition<any, any>[];
|
||||
featureFlags?: FeatureFlagConfig[];
|
||||
}
|
||||
|
||||
|
||||
@@ -26,16 +26,17 @@ import {
|
||||
import { PortableSchema } from '../schema';
|
||||
|
||||
/** @public */
|
||||
export interface Extension<TConfig> {
|
||||
export interface Extension<TConfig, TConfigInput = TConfig> {
|
||||
$$type: '@backstage/Extension';
|
||||
readonly id: string;
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly disabled: boolean;
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
readonly configSchema?: PortableSchema<TConfig, TConfigInput>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface InternalExtension<TConfig> extends Extension<TConfig> {
|
||||
export interface InternalExtension<TConfig, TConfigInput>
|
||||
extends Extension<TConfig, TConfigInput> {
|
||||
readonly version: 'v1';
|
||||
readonly inputs: AnyExtensionInputMap;
|
||||
readonly output: AnyExtensionDataMap;
|
||||
@@ -47,10 +48,10 @@ export interface InternalExtension<TConfig> extends Extension<TConfig> {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function toInternalExtension<TConfig>(
|
||||
overrides: Extension<TConfig>,
|
||||
): InternalExtension<TConfig> {
|
||||
const internal = overrides as InternalExtension<TConfig>;
|
||||
export function toInternalExtension<TConfig, TConfigInput>(
|
||||
overrides: Extension<TConfig, TConfigInput>,
|
||||
): InternalExtension<TConfig, TConfigInput> {
|
||||
const internal = overrides as InternalExtension<TConfig, TConfigInput>;
|
||||
if (internal.$$type !== '@backstage/Extension') {
|
||||
throw new Error(
|
||||
`Invalid extension instance, bad type '${internal.$$type}'`,
|
||||
@@ -65,10 +66,10 @@ export function toInternalExtension<TConfig>(
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function resolveExtensionDefinition<TConfig>(
|
||||
definition: ExtensionDefinition<TConfig>,
|
||||
export function resolveExtensionDefinition<TConfig, TConfigInput>(
|
||||
definition: ExtensionDefinition<TConfig, TConfigInput>,
|
||||
context?: { namespace?: string },
|
||||
): Extension<TConfig> {
|
||||
): Extension<TConfig, TConfigInput> {
|
||||
const internalDefinition = toInternalExtensionDefinition(definition);
|
||||
const { name, kind, namespace: _, ...rest } = internalDefinition;
|
||||
const namespace = internalDefinition.namespace ?? context?.namespace;
|
||||
@@ -91,5 +92,5 @@ export function resolveExtensionDefinition<TConfig>(
|
||||
toString() {
|
||||
return `Extension{id=${id}}`;
|
||||
},
|
||||
} as Extension<TConfig>;
|
||||
} as Extension<TConfig, TConfigInput>;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ export { ErrorWithContext };
|
||||
// @public (undocumented)
|
||||
export class ExtensionTester {
|
||||
// (undocumented)
|
||||
add<TConfig>(
|
||||
extension: ExtensionDefinition<TConfig>,
|
||||
add<TConfig, TConfigInput>(
|
||||
extension: ExtensionDefinition<TConfig, TConfigInput>,
|
||||
options?: {
|
||||
config?: TConfig;
|
||||
config?: TConfigInput;
|
||||
},
|
||||
): ExtensionTester;
|
||||
// (undocumented)
|
||||
|
||||
@@ -88,9 +88,9 @@ const TestAppNavExtension = createExtension({
|
||||
/** @public */
|
||||
export class ExtensionTester {
|
||||
/** @internal */
|
||||
static forSubject<TConfig>(
|
||||
subject: ExtensionDefinition<TConfig>,
|
||||
options?: { config?: TConfig },
|
||||
static forSubject<TConfig, TConfigInput>(
|
||||
subject: ExtensionDefinition<TConfig, TConfigInput>,
|
||||
options?: { config?: TConfigInput },
|
||||
): ExtensionTester {
|
||||
const tester = new ExtensionTester();
|
||||
const { output, factory, ...rest } = toInternalExtensionDefinition(subject);
|
||||
@@ -107,7 +107,7 @@ export class ExtensionTester {
|
||||
path: '/',
|
||||
}),
|
||||
});
|
||||
tester.add(extension, options);
|
||||
tester.add(extension, options as TConfigInput & {});
|
||||
return tester;
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ export class ExtensionTester {
|
||||
config?: JsonValue;
|
||||
}>();
|
||||
|
||||
add<TConfig>(
|
||||
extension: ExtensionDefinition<TConfig>,
|
||||
options?: { config?: TConfig },
|
||||
add<TConfig, TConfigInput>(
|
||||
extension: ExtensionDefinition<TConfig, TConfigInput>,
|
||||
options?: { config?: TConfigInput },
|
||||
): ExtensionTester {
|
||||
const { name, namespace } = extension;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ export function createEntityCardExtension<
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createEntityContentExtension<
|
||||
@@ -138,11 +138,18 @@ export function createEntityContentExtension<
|
||||
loader: (options: {
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<{
|
||||
title: string;
|
||||
path: string;
|
||||
filter?: string | undefined;
|
||||
}>;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
title: string;
|
||||
path: string;
|
||||
filter?: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
title?: string | undefined;
|
||||
path?: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export function isOwnerOf(owner: Entity, entity: Entity): boolean;
|
||||
|
||||
@@ -99,14 +99,14 @@ export const catalogTranslationRef: TranslationRef<
|
||||
// @alpha (undocumented)
|
||||
export function createCatalogFilterExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig = never,
|
||||
TConfig,
|
||||
>(options: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
inputs?: TInputs;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
loader: (options: { config: TConfig }) => Promise<JSX.Element>;
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
}): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
const _default: BackstagePlugin<
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
/** @alpha */
|
||||
export function createCatalogFilterExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig = never,
|
||||
TConfig,
|
||||
>(options: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
|
||||
@@ -25,7 +25,7 @@ export function createSearchResultListItemExtension<
|
||||
},
|
||||
>(
|
||||
options: SearchResultItemExtensionOptions<TConfig>,
|
||||
): ExtensionDefinition<TConfig>;
|
||||
): ExtensionDefinition<TConfig & {}, TConfig & {}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export namespace createSearchResultListItemExtension {
|
||||
|
||||
@@ -17,18 +17,29 @@ const _default: BackstagePlugin<
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchApi: ExtensionDefinition<{}>;
|
||||
export const searchApi: ExtensionDefinition<{}, {}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchNavItem: ExtensionDefinition<{
|
||||
title: string;
|
||||
}>;
|
||||
export const searchNavItem: ExtensionDefinition<
|
||||
{
|
||||
title: string;
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchPage: ExtensionDefinition<{
|
||||
path: string;
|
||||
noTrack: boolean;
|
||||
}>;
|
||||
export const searchPage: ExtensionDefinition<
|
||||
{
|
||||
path: string;
|
||||
noTrack: boolean;
|
||||
},
|
||||
{
|
||||
path: string;
|
||||
noTrack: boolean;
|
||||
}
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -23,13 +23,22 @@ const _default: BackstagePlugin<
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const techDocsSearchResultListItemExtension: ExtensionDefinition<{
|
||||
lineClamp: number;
|
||||
noTrack: boolean;
|
||||
asListItem: boolean;
|
||||
asLink: boolean;
|
||||
title?: string | undefined;
|
||||
}>;
|
||||
export const techDocsSearchResultListItemExtension: ExtensionDefinition<
|
||||
{
|
||||
lineClamp: number;
|
||||
noTrack: boolean;
|
||||
asListItem: boolean;
|
||||
asLink: boolean;
|
||||
title?: string | undefined;
|
||||
},
|
||||
{
|
||||
lineClamp: number;
|
||||
noTrack: boolean;
|
||||
asListItem: boolean;
|
||||
asLink: boolean;
|
||||
title?: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -18,9 +18,14 @@ const _default: BackstagePlugin<
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const settingsNavItem: ExtensionDefinition<{
|
||||
title: string;
|
||||
}>;
|
||||
export const settingsNavItem: ExtensionDefinition<
|
||||
{
|
||||
title: string;
|
||||
},
|
||||
{
|
||||
title?: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const userSettingsTranslationRef: TranslationRef<
|
||||
|
||||
Reference in New Issue
Block a user