Merge pull request #26144 from backstage/rugvip/sparklng
frontend-plugin-api: clean up internal createExtension{,Input,Blueprint} types
This commit is contained in:
@@ -365,50 +365,6 @@ export function createExtension<
|
||||
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
|
||||
name: string | undefined extends TName ? undefined : TName;
|
||||
}
|
||||
>;
|
||||
export function createExtension<
|
||||
const TKind extends string | undefined,
|
||||
const TNamespace extends string | undefined,
|
||||
const TName extends string | undefined,
|
||||
UOutput extends AnyExtensionDataRef,
|
||||
TInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
>(
|
||||
options: CreateExtensionOptions<
|
||||
TKind,
|
||||
TNamespace,
|
||||
TName,
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>,
|
||||
): ExtensionDefinition<
|
||||
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]>;
|
||||
}>
|
||||
>,
|
||||
UOutput,
|
||||
TInputs,
|
||||
{
|
||||
kind: TKind;
|
||||
namespace: TNamespace;
|
||||
name: TName;
|
||||
}
|
||||
> {
|
||||
const schemaDeclaration = options.config?.schema;
|
||||
const configSchema =
|
||||
@@ -447,76 +403,7 @@ export function createExtension<
|
||||
parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`);
|
||||
return `ExtensionDefinition{${parts.join(',')}}`;
|
||||
},
|
||||
override: <
|
||||
TExtensionConfigSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
UOverrideFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
UNewOutput extends AnyExtensionDataRef,
|
||||
TExtraInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
>(overrideOptions: {
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs;
|
||||
output?: Array<UNewOutput>;
|
||||
config?: {
|
||||
schema: TExtensionConfigSchema;
|
||||
};
|
||||
factory(
|
||||
originalFactory: (context?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: ResolveInputValueOverrides<TInputs>;
|
||||
}) => ExtensionDataContainer<UOutput>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
|
||||
},
|
||||
): Iterable<UOverrideFactoryOutput>;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[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]>;
|
||||
}
|
||||
>
|
||||
>,
|
||||
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
|
||||
TInputs & TExtraInputs,
|
||||
{
|
||||
kind: TKind;
|
||||
namespace: TNamespace;
|
||||
name: TName;
|
||||
}
|
||||
> => {
|
||||
override(overrideOptions) {
|
||||
if (!Array.isArray(options.output)) {
|
||||
throw new Error(
|
||||
'Cannot override an extension that is not declared using the new format with outputs as an array',
|
||||
@@ -531,12 +418,6 @@ export function createExtension<
|
||||
TConfigSchema,
|
||||
UFactoryOutput
|
||||
>;
|
||||
const overrideNewConfigSchema = overrideOptions.config?.schema;
|
||||
|
||||
const schema = {
|
||||
...newOptions.config?.schema,
|
||||
...overrideNewConfigSchema,
|
||||
} as TConfigSchema & TExtensionConfigSchema;
|
||||
|
||||
return createExtension({
|
||||
kind: newOptions.kind,
|
||||
@@ -545,75 +426,74 @@ export function createExtension<
|
||||
attachTo: overrideOptions.attachTo ?? newOptions.attachTo,
|
||||
disabled: overrideOptions.disabled ?? newOptions.disabled,
|
||||
inputs: { ...overrideOptions.inputs, ...newOptions.inputs },
|
||||
output: overrideOptions.output ?? newOptions.output,
|
||||
config: Object.keys(schema).length === 0 ? undefined : { schema },
|
||||
output: (overrideOptions.output ??
|
||||
newOptions.output) as AnyExtensionDataRef[],
|
||||
config:
|
||||
newOptions.config || overrideOptions.config
|
||||
? {
|
||||
schema: {
|
||||
...newOptions.config?.schema,
|
||||
...overrideOptions.config?.schema,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
factory: ({ node, config, inputs }) => {
|
||||
if (!overrideOptions.factory) {
|
||||
return newOptions.factory({
|
||||
node,
|
||||
config,
|
||||
inputs: inputs as unknown as Expand<
|
||||
ResolvedExtensionInputs<TInputs>
|
||||
>,
|
||||
config: config as any,
|
||||
inputs: inputs as any,
|
||||
});
|
||||
}
|
||||
const parentResult = overrideOptions.factory(
|
||||
(innerContext?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: ResolveInputValueOverrides<TInputs>;
|
||||
}): ExtensionDataContainer<UOutput> => {
|
||||
(innerContext): ExtensionDataContainer<UOutput> => {
|
||||
return createExtensionDataContainer<UOutput>(
|
||||
newOptions.factory({
|
||||
node,
|
||||
config: innerContext?.config ?? config,
|
||||
config: (innerContext?.config ?? config) as any,
|
||||
inputs: resolveInputOverrides(
|
||||
newOptions.inputs,
|
||||
inputs,
|
||||
innerContext?.inputs,
|
||||
) as any, // TODO: Might be able to improve this once legacy inputs are gone
|
||||
) as any,
|
||||
}) as Iterable<any>,
|
||||
newOptions.output,
|
||||
);
|
||||
},
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
config: config as any,
|
||||
inputs: inputs as any,
|
||||
},
|
||||
);
|
||||
|
||||
const deduplicatedResult = new Map<string, UOverrideFactoryOutput>();
|
||||
const deduplicatedResult = new Map<
|
||||
string,
|
||||
ExtensionDataValue<any, any>
|
||||
>();
|
||||
for (const item of parentResult) {
|
||||
deduplicatedResult.set(item.id, item);
|
||||
}
|
||||
|
||||
return deduplicatedResult.values() as Iterable<UOverrideFactoryOutput>;
|
||||
return deduplicatedResult.values();
|
||||
},
|
||||
} as CreateExtensionOptions<TKind, TNamespace, TName, AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UOverrideFactoryOutput>);
|
||||
}) as ExtensionDefinition<any>;
|
||||
},
|
||||
} as InternalExtensionDefinition<
|
||||
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]>;
|
||||
}>
|
||||
>,
|
||||
{
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>,
|
||||
UOutput,
|
||||
TInputs,
|
||||
{
|
||||
kind: TKind;
|
||||
namespace: TNamespace;
|
||||
name: TName;
|
||||
kind: string | undefined extends TKind ? undefined : TKind;
|
||||
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
|
||||
name: string | undefined extends TName ? undefined : TName;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import { AppNode } from '../apis';
|
||||
import { Expand } from '../types';
|
||||
import {
|
||||
CreateExtensionOptions,
|
||||
ExtensionDefinition,
|
||||
ResolvedExtensionInputs,
|
||||
VerifyExtensionFactoryOutput,
|
||||
@@ -211,193 +210,6 @@ export interface ExtensionBlueprint<
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ExtensionBlueprintImpl<
|
||||
TKind extends string,
|
||||
TNamespace extends string | undefined,
|
||||
TName extends string | undefined,
|
||||
TParams,
|
||||
UOutput extends AnyExtensionDataRef,
|
||||
TInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
|
||||
TDataRefs extends { [name in string]: AnyExtensionDataRef },
|
||||
> {
|
||||
constructor(
|
||||
private readonly options: CreateExtensionBlueprintOptions<
|
||||
TKind,
|
||||
TNamespace,
|
||||
TName,
|
||||
TParams,
|
||||
UOutput,
|
||||
TInputs,
|
||||
TConfigSchema,
|
||||
any,
|
||||
TDataRefs
|
||||
>,
|
||||
) {
|
||||
this.dataRefs = options.dataRefs!;
|
||||
}
|
||||
|
||||
dataRefs: TDataRefs;
|
||||
|
||||
public makeWithOverrides<
|
||||
TExtensionConfigSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
UFactoryOutput extends ExtensionDataValue<any, any>,
|
||||
UNewOutput extends AnyExtensionDataRef,
|
||||
TExtraInputs extends {
|
||||
[inputName in string]: ExtensionInput<
|
||||
AnyExtensionDataRef,
|
||||
{ optional: boolean; singleton: boolean }
|
||||
>;
|
||||
},
|
||||
TNewNamespace extends string | undefined = undefined,
|
||||
TNewName extends string | undefined = undefined,
|
||||
>(args: {
|
||||
namespace?: TNewNamespace;
|
||||
name?: TNewName;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TExtraInputs;
|
||||
output?: Array<UNewOutput>;
|
||||
config?: {
|
||||
schema: TExtensionConfigSchema;
|
||||
};
|
||||
factory(
|
||||
originalFactory: (
|
||||
params: TParams,
|
||||
context?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: ResolveInputValueOverrides<TInputs>;
|
||||
},
|
||||
) => ExtensionDataContainer<UOutput>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
|
||||
},
|
||||
): Iterable<UFactoryOutput>;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
ReturnType<TExtensionConfigSchema[key]>
|
||||
>;
|
||||
} & {
|
||||
[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,
|
||||
...args.config?.schema,
|
||||
} as TConfigSchema & TExtensionConfigSchema;
|
||||
|
||||
return createExtension({
|
||||
kind: this.options.kind,
|
||||
namespace: args.namespace ?? this.options.namespace,
|
||||
name: args.name ?? this.options.name,
|
||||
attachTo: args.attachTo ?? this.options.attachTo,
|
||||
disabled: args.disabled ?? this.options.disabled,
|
||||
inputs: { ...args.inputs, ...this.options.inputs },
|
||||
output: args.output ?? this.options.output,
|
||||
config: Object.keys(schema).length === 0 ? undefined : { schema },
|
||||
factory: ({ node, config, inputs }) => {
|
||||
return args.factory(
|
||||
(
|
||||
innerParams: TParams,
|
||||
innerContext?: {
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<
|
||||
ReturnType<TConfigSchema[key]>
|
||||
>;
|
||||
};
|
||||
inputs?: ResolveInputValueOverrides;
|
||||
},
|
||||
): ExtensionDataContainer<UOutput> => {
|
||||
return createExtensionDataContainer<UOutput>(
|
||||
this.options.factory(innerParams, {
|
||||
node,
|
||||
config: innerContext?.config ?? config,
|
||||
inputs: resolveInputOverrides(
|
||||
this.options.inputs,
|
||||
inputs,
|
||||
innerContext?.inputs,
|
||||
) as any, // TODO: Might be able to improve this once legacy inputs are gone
|
||||
}),
|
||||
this.options.output,
|
||||
);
|
||||
},
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
},
|
||||
);
|
||||
},
|
||||
} as CreateExtensionOptions<TKind, string | undefined extends TNewNamespace ? TNamespace : TNewNamespace, string | undefined extends TNewName ? TName : TNewName, AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UFactoryOutput>);
|
||||
}
|
||||
|
||||
public make<
|
||||
TNewNamespace extends string | undefined = undefined,
|
||||
TNewName extends string | undefined = undefined,
|
||||
>(args: {
|
||||
namespace?: TNewNamespace;
|
||||
name?: TNewName;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
params: TParams;
|
||||
}): ExtensionDefinition<
|
||||
{
|
||||
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
|
||||
},
|
||||
z.input<
|
||||
z.ZodObject<{
|
||||
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
|
||||
}>
|
||||
>
|
||||
> {
|
||||
return createExtension({
|
||||
kind: this.options.kind,
|
||||
namespace: args.namespace ?? this.options.namespace,
|
||||
name: args.name ?? this.options.name,
|
||||
attachTo: args.attachTo ?? this.options.attachTo,
|
||||
disabled: args.disabled ?? this.options.disabled,
|
||||
inputs: this.options.inputs,
|
||||
output: this.options.output,
|
||||
config: this.options.config,
|
||||
factory: ctx => this.options.factory(args.params, ctx),
|
||||
} as CreateExtensionOptions<TKind, string | undefined extends TNewNamespace ? TNamespace : TNewNamespace, string | undefined extends TNewName ? TName : TNewName, UOutput, TInputs, TConfigSchema, any>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simpler replacement for wrapping up `createExtension` inside a kind or type. This allows for a cleaner API for creating
|
||||
* types and instances of those types.
|
||||
@@ -452,7 +264,68 @@ export function createExtensionBlueprint<
|
||||
>,
|
||||
TDataRefs
|
||||
> {
|
||||
return new ExtensionBlueprintImpl(options) as ExtensionBlueprint<
|
||||
return {
|
||||
dataRefs: options.dataRefs,
|
||||
make(args) {
|
||||
return createExtension({
|
||||
kind: options.kind,
|
||||
namespace: args.namespace ?? options.namespace,
|
||||
name: args.name ?? options.name,
|
||||
attachTo: args.attachTo ?? options.attachTo,
|
||||
disabled: args.disabled ?? options.disabled,
|
||||
inputs: options.inputs,
|
||||
output: options.output as AnyExtensionDataRef[],
|
||||
config: options.config,
|
||||
factory: ctx =>
|
||||
options.factory(args.params, ctx) as Iterable<
|
||||
ExtensionDataValue<any, any>
|
||||
>,
|
||||
});
|
||||
},
|
||||
makeWithOverrides(args) {
|
||||
return createExtension({
|
||||
kind: options.kind,
|
||||
namespace: args.namespace ?? options.namespace,
|
||||
name: args.name ?? options.name,
|
||||
attachTo: args.attachTo ?? options.attachTo,
|
||||
disabled: args.disabled ?? options.disabled,
|
||||
inputs: { ...args.inputs, ...options.inputs },
|
||||
output: (args.output ?? options.output) as AnyExtensionDataRef[],
|
||||
config:
|
||||
options.config || args.config
|
||||
? {
|
||||
schema: {
|
||||
...options.config?.schema,
|
||||
...args.config?.schema,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
factory: ({ node, config, inputs }) => {
|
||||
return args.factory(
|
||||
(innerParams, innerContext) => {
|
||||
return createExtensionDataContainer<UOutput>(
|
||||
options.factory(innerParams, {
|
||||
node,
|
||||
config: (innerContext?.config ?? config) as any,
|
||||
inputs: resolveInputOverrides(
|
||||
options.inputs,
|
||||
inputs,
|
||||
innerContext?.inputs,
|
||||
) as any,
|
||||
}) as Iterable<any>,
|
||||
options.output,
|
||||
);
|
||||
},
|
||||
{
|
||||
node,
|
||||
config: config as any,
|
||||
inputs: inputs as any,
|
||||
},
|
||||
) as Iterable<ExtensionDataValue<any, any>>;
|
||||
},
|
||||
}) as ExtensionDefinition<any>;
|
||||
},
|
||||
} as ExtensionBlueprint<
|
||||
{
|
||||
kind: TKind;
|
||||
namespace: TNamespace;
|
||||
|
||||
@@ -39,19 +39,6 @@ export function createExtensionInput<
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
}
|
||||
>;
|
||||
export function createExtensionInput<
|
||||
TExtensionData extends ExtensionDataRef<unknown, string, { optional?: true }>,
|
||||
TConfig extends { singleton?: boolean; optional?: boolean },
|
||||
>(
|
||||
extensionData: Array<TExtensionData>,
|
||||
config?: TConfig,
|
||||
): ExtensionInput<
|
||||
TExtensionData,
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
}
|
||||
> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (Array.isArray(extensionData)) {
|
||||
@@ -85,7 +72,7 @@ export function createExtensionInput<
|
||||
: false,
|
||||
},
|
||||
} as ExtensionInput<
|
||||
TExtensionData,
|
||||
UExtensionData,
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
|
||||
Reference in New Issue
Block a user