diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 4ec1ed050d..86fcdc56ee 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -20,7 +20,7 @@ import { Expand } from '../types'; import { ResolveInputValueOverrides, resolveInputOverrides, -} from './createExtensionBlueprint'; +} from './resolveInputOverrides'; import { ExtensionDataContainer, createExtensionDataContainer, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 78a2b5283d..d1fc75bca6 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -19,7 +19,6 @@ import { Expand } from '../types'; import { CreateExtensionOptions, ExtensionDefinition, - ResolvedExtensionInput, ResolvedExtensionInputs, VerifyExtensionFactoryOutput, createExtension, @@ -28,13 +27,16 @@ import { z } from 'zod'; import { ExtensionInput } from './createExtensionInput'; import { AnyExtensionDataRef, - ExtensionDataRefToValue, ExtensionDataValue, } from './createExtensionDataRef'; import { ExtensionDataContainer, createExtensionDataContainer, } from './createExtensionDataContainer'; +import { + ResolveInputValueOverrides, + resolveInputOverrides, +} from './resolveInputOverrides'; /** * @public @@ -79,61 +81,6 @@ export type CreateExtensionBlueprintOptions< dataRefs?: TDataRefs; } & VerifyExtensionFactoryOutput; -/** @public */ -export type ResolveInputValueOverrides< - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - } = { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, -> = Expand< - { - [KName in keyof TInputs as TInputs[KName] extends ExtensionInput< - any, - { - optional: infer IOptional extends boolean; - singleton: boolean; - } - > - ? IOptional extends true - ? never - : KName - : never]: TInputs[KName] extends ExtensionInput< - infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } - > - ? ISingleton extends true - ? Iterable> - : Array>> - : never; - } & { - [KName in keyof TInputs as TInputs[KName] extends ExtensionInput< - any, - { - optional: infer IOptional extends boolean; - singleton: boolean; - } - > - ? IOptional extends true - ? KName - : never - : never]?: TInputs[KName] extends ExtensionInput< - infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } - > - ? ISingleton extends true - ? Iterable> - : Array>> - : never; - } ->; - /** * @public */ @@ -254,84 +201,6 @@ export interface ExtensionBlueprint< >; } -function expectArray(value: T | T[]): T[] { - return value as T[]; -} -function expectItem(value: T | T[]): T { - return value as T; -} - -/** @internal */ -export function resolveInputOverrides( - declaredInputs?: { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - inputs?: { - [KName in string]?: - | ({ node: AppNode } & ExtensionDataContainer) - | Array<{ node: AppNode } & ExtensionDataContainer>; - }, - inputOverrides?: ResolveInputValueOverrides, -) { - if (!declaredInputs || !inputs || !inputOverrides) { - return inputs; - } - - const newInputs: typeof inputs = {}; - for (const name in declaredInputs) { - if (!Object.hasOwn(declaredInputs, name)) { - continue; - } - const declaredInput = declaredInputs[name]; - const providedData = inputOverrides[name]; - if (declaredInput.config.singleton) { - const originalInput = expectItem(inputs[name]); - if (providedData) { - const providedContainer = createExtensionDataContainer( - providedData as Iterable>, - declaredInput.extensionData, - ); - if (!originalInput) { - throw new Error( - `attempted to override data of input '${name}' but it is not present in the original inputs`, - ); - } - newInputs[name] = Object.assign(providedContainer, { - name: (originalInput as ResolvedExtensionInput).node, - }) as any; - } - } else { - const originalInput = expectArray(inputs[name]); - if (!Array.isArray(providedData)) { - throw new Error( - `override data provided for input '${name}' must be an array`, - ); - } - if ( - originalInput.length !== providedData.length && - providedData.length > 0 - ) { - throw new Error( - `override data provided for input '${name}' must match the length of the original inputs`, - ); - } - newInputs[name] = providedData.map((data, i) => { - const providedContainer = createExtensionDataContainer( - data as Iterable>, - declaredInput.extensionData, - ); - return Object.assign(providedContainer, { - name: (originalInput[i] as ResolvedExtensionInput).node, - }) as any; - }); - } - } - return newInputs; -} - /** * @internal */ diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 5e56e5a2d7..e3389593e9 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -56,7 +56,7 @@ export { } from './types'; export { type CreateExtensionBlueprintOptions, - type ResolveInputValueOverrides, type ExtensionBlueprint, createExtensionBlueprint, } from './createExtensionBlueprint'; +export { type ResolveInputValueOverrides } from './resolveInputOverrides'; diff --git a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts new file mode 100644 index 0000000000..06143549bb --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts @@ -0,0 +1,162 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppNode } from '../apis'; +import { Expand } from '../types'; +import { ResolvedExtensionInput } from './createExtension'; +import { + ExtensionDataContainer, + createExtensionDataContainer, +} from './createExtensionDataContainer'; +import { + AnyExtensionDataRef, + ExtensionDataRefToValue, + ExtensionDataValue, +} from './createExtensionDataRef'; +import { ExtensionInput } from './createExtensionInput'; + +/** @public */ +export type ResolveInputValueOverrides< + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + } = { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, +> = Expand< + { + [KName in keyof TInputs as TInputs[KName] extends ExtensionInput< + any, + { + optional: infer IOptional extends boolean; + singleton: boolean; + } + > + ? IOptional extends true + ? never + : KName + : never]: TInputs[KName] extends ExtensionInput< + infer IDataRefs, + { optional: boolean; singleton: infer ISingleton extends boolean } + > + ? ISingleton extends true + ? Iterable> + : Array>> + : never; + } & { + [KName in keyof TInputs as TInputs[KName] extends ExtensionInput< + any, + { + optional: infer IOptional extends boolean; + singleton: boolean; + } + > + ? IOptional extends true + ? KName + : never + : never]?: TInputs[KName] extends ExtensionInput< + infer IDataRefs, + { optional: boolean; singleton: infer ISingleton extends boolean } + > + ? ISingleton extends true + ? Iterable> + : Array>> + : never; + } +>; + +function expectArray(value: T | T[]): T[] { + return value as T[]; +} +function expectItem(value: T | T[]): T { + return value as T; +} + +/** @internal */ +export function resolveInputOverrides( + declaredInputs?: { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + inputs?: { + [KName in string]?: + | ({ node: AppNode } & ExtensionDataContainer) + | Array<{ node: AppNode } & ExtensionDataContainer>; + }, + inputOverrides?: ResolveInputValueOverrides, +) { + if (!declaredInputs || !inputs || !inputOverrides) { + return inputs; + } + + const newInputs: typeof inputs = {}; + for (const name in declaredInputs) { + if (!Object.hasOwn(declaredInputs, name)) { + continue; + } + const declaredInput = declaredInputs[name]; + const providedData = inputOverrides[name]; + if (declaredInput.config.singleton) { + const originalInput = expectItem(inputs[name]); + if (providedData) { + const providedContainer = createExtensionDataContainer( + providedData as Iterable>, + declaredInput.extensionData, + ); + if (!originalInput) { + throw new Error( + `attempted to override data of input '${name}' but it is not present in the original inputs`, + ); + } + newInputs[name] = Object.assign(providedContainer, { + name: (originalInput as ResolvedExtensionInput).node, + }) as any; + } + } else { + const originalInput = expectArray(inputs[name]); + if (!Array.isArray(providedData)) { + throw new Error( + `override data provided for input '${name}' must be an array`, + ); + } + if ( + originalInput.length !== providedData.length && + providedData.length > 0 + ) { + throw new Error( + `override data provided for input '${name}' must match the length of the original inputs`, + ); + } + newInputs[name] = providedData.map((data, i) => { + const providedContainer = createExtensionDataContainer( + data as Iterable>, + declaredInput.extensionData, + ); + return Object.assign(providedContainer, { + name: (originalInput[i] as ResolvedExtensionInput).node, + }) as any; + }); + } + } + return newInputs; +}