diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 8ce3dab4d5..88f3a6e9dd 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -297,6 +297,81 @@ export function createDataContainer( } as ExtensionDataContainer; } +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 = createDataContainer( + providedData as Iterable>, + declaredInput.extensionData, + ); + if (!originalInput) { + throw new Error( + `A data override was provided for input '${name}', but no original input was present.`, + ); + } + 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( + `Invalid override provided for input '${name}', expected an array`, + ); + } + if (originalInput.length !== providedData.length) { + throw new Error( + `Invalid override provided for input '${name}', when overriding the input data the length must match the original input data`, + ); + } + newInputs[name] = providedData.map((data, i) => { + const providedContainer = createDataContainer( + data as Iterable>, + declaredInput.extensionData, + ); + return Object.assign(providedContainer, { + name: (originalInput[i] as ResolvedExtensionInput).node, + }) as any; + }); + } + } + return newInputs; +} + /** * @internal */ @@ -428,82 +503,15 @@ class ExtensionBlueprintImpl< inputs?: ResolveInputValueOverrides; }, ): ExtensionDataContainer => { - const overrideInputs = innerContext?.inputs; - const declaredInputs = this.options.inputs; - - let forwardedInputs = inputs; - if (overrideInputs && declaredInputs) { - const newInputs = {} as { - [KName in keyof TInputs]?: - | ({ node: AppNode } & ExtensionDataContainer) - | Array<{ node: AppNode } & ExtensionDataContainer>; - }; - forwardedInputs = newInputs as typeof inputs; - - for (const name in declaredInputs) { - if (!Object.hasOwn(declaredInputs, name)) { - continue; - } - const declaredInput = declaredInputs[name]; - const providedData = overrideInputs[name]; - if (declaredInput.config.singleton) { - // We know the passed in input will match the declared inputs, so this case is safe - const originalInput = ( - inputs as Record< - string, - { node: AppNode } & ExtensionDataContainer - > - )[name]; - if (providedData) { - const providedContainer = createDataContainer( - providedData as Iterable>, - declaredInput.extensionData, - ); - if (!originalInput) { - throw new Error( - `A data override was provided for input '${name}', but no original input was present.`, - ); - } - newInputs[name] = Object.assign(providedContainer, { - name: (originalInput as ResolvedExtensionInput).node, - }) as any; - } - } else { - // We know the passed in input will match the declared inputs, so this case is safe - const originalInput = ( - inputs as Record< - string, - Array<{ node: AppNode } & ExtensionDataContainer> - > - )[name]; - if (!Array.isArray(providedData)) { - throw new Error( - `Invalid override provided for input '${name}', expected an array`, - ); - } - if (originalInput.length !== providedData.length) { - throw new Error( - `Invalid override provided for input '${name}', when overriding the input data the length must match the original input data`, - ); - } - newInputs[name] = providedData.map((data, i) => { - const providedContainer = createDataContainer( - data as Iterable>, - declaredInput.extensionData, - ); - return Object.assign(providedContainer, { - name: (originalInput[i] as ResolvedExtensionInput) - .node, - }) as any; - }); - } - } - } return createDataContainer( this.options.factory(innerParams, { node, config: innerContext?.config ?? config, - inputs: forwardedInputs as any, // TODO: Might be able to improve this once legacy inputs are gone + 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, );