diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index dfdbfface6..5f1324fe7c 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -384,4 +384,70 @@ describe('createExtensionBlueprint', () => { expect(true).toBe(true); }); + + it('should allow merging of inputs', () => { + const blueprint = createExtensionBlueprint({ + kind: 'test-extension', + attachTo: { id: 'test', input: 'default' }, + inputs: { + test: createExtensionInput([coreExtensionData.routeRef], { + singleton: true, + }), + }, + output: [coreExtensionData.reactElement.optional()], + factory(_params: { x?: string }, { inputs }) { + const ref: RouteRef = inputs.test.get(coreExtensionData.routeRef); + + unused(ref); + return []; + }, + }); + + blueprint.make({ + inputs: { + test2: createExtensionInput([coreExtensionData.reactElement], { + singleton: true, + }), + }, + factory(origFactory, { inputs }) { + const ref: RouteRef = inputs.test.get(coreExtensionData.routeRef); + + const el: JSX.Element = inputs.test2.get( + coreExtensionData.reactElement, + ); + + unused(ref, el); + + return origFactory({}); + }, + }); + + expect(true).toBe(true); + }); + + it('should not allow overriding inputs', () => { + const blueprint = createExtensionBlueprint({ + kind: 'test-extension', + attachTo: { id: 'test', input: 'default' }, + inputs: { + test: createExtensionInput([coreExtensionData.routeRef]), + }, + output: [coreExtensionData.reactElement.optional()], + factory() { + return []; + }, + }); + + blueprint.make({ + inputs: { + // @ts-expect-error + test: createExtensionInput([]), // Overrides are not allowed + }, + factory(origFactory) { + return origFactory({}); + }, + }); + + expect(true).toBe(true); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 46db278bf8..90736703a0 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -83,7 +83,6 @@ export interface ExtensionBlueprint< { optional: boolean; singleton: boolean } >; }, - UExtraOutput extends AnyExtensionDataRef, TConfig extends { [key in string]: unknown }, TConfigInput extends { [key in string]: unknown }, TDataRefs extends { [name in string]: AnyExtensionDataRef }, @@ -101,13 +100,23 @@ export interface ExtensionBlueprint< [key in string]: (zImpl: typeof z) => z.ZodType; }, UFactoryOutput extends ExtensionDataValue, + UExtraOutput extends AnyExtensionDataRef, + TExtraInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, >( args: { namespace?: string; name?: string; attachTo?: { id: string; input: string }; disabled?: boolean; - inputs?: TInputs; + inputs?: TExtraInputs & { + [KName in keyof TInputs]?: `Error: Input '${KName & + string}' is already defined in parent definition`; + }; output?: Array; config?: { schema: TExtensionConfigSchema & { @@ -132,7 +141,7 @@ export interface ExtensionBlueprint< ReturnType >; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable; } & VerifyExtensionFactoryOutput< @@ -172,7 +181,6 @@ class ExtensionBlueprintImpl< { optional: boolean; singleton: boolean } >; }, - UExtraOutput extends AnyExtensionDataRef, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, TDataRefs extends { [name in string]: AnyExtensionDataRef }, > { @@ -196,12 +204,22 @@ class ExtensionBlueprintImpl< [key in string]: (zImpl: typeof z) => z.ZodType; }, UFactoryOutput extends ExtensionDataValue, + UExtraOutput extends AnyExtensionDataRef, + TExtraInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, >(args: { namespace?: string; name?: string; attachTo?: { id: string; input: string }; disabled?: boolean; - inputs?: TInputs; + inputs?: TExtraInputs & { + [KName in keyof TInputs]?: `Error: Input '${KName & + string}' is already defined in parent definition`; + }; output?: Array; params?: TParams; config?: { @@ -228,7 +246,7 @@ class ExtensionBlueprintImpl< } & { [key in keyof TConfigSchema]: z.infer>; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable; }): ExtensionDefinition< @@ -277,7 +295,7 @@ class ExtensionBlueprintImpl< name: args.name ?? name, attachTo: args.attachTo ?? this.options.attachTo, disabled: args.disabled ?? this.options.disabled, - inputs: args.inputs ?? this.options.inputs, + inputs: { ...args.inputs, ...this.options.inputs }, output: [...(args.output ?? []), ...this.options.output], config: Object.keys(schema).length === 0 ? undefined : { schema }, factory: ({ node, config, inputs }) => { @@ -297,7 +315,7 @@ class ExtensionBlueprintImpl< return this.options.factory(innerParams, { node, config: innerContext?.config ?? config, - inputs: innerContext?.inputs ?? inputs, + inputs: (innerContext?.inputs ?? inputs) as any, // TODO: Fix the way input values are overridden }); }, { @@ -317,7 +335,7 @@ class ExtensionBlueprintImpl< }, } as CreateExtensionOptions< UOutput, - TInputs, + TInputs & TExtraInputs, { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType @@ -357,7 +375,6 @@ export function createExtensionBlueprint< { optional: boolean; singleton: boolean } >; }, - UExtraOutput extends AnyExtensionDataRef, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, TDataRefs extends { [name in string]: AnyExtensionDataRef } = never, @@ -374,7 +391,6 @@ export function createExtensionBlueprint< TParams, UOutput, TInputs, - UExtraOutput, string extends keyof TConfigSchema ? {} : { [key in keyof TConfigSchema]: z.infer> }, @@ -391,7 +407,6 @@ export function createExtensionBlueprint< TParams, UOutput, TInputs, - UExtraOutput, string extends keyof TConfigSchema ? {} : {