From 0bc1ce919e76983282a189b8724483a5c3a07996 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 19 Nov 2025 17:42:16 +0100 Subject: [PATCH] frontend-plugin-api: fix input incompatibility crash Signed-off-by: Patrik Oldsberg --- .changeset/flat-pillows-rush.md | 5 +++++ .../src/wiring/InternalExtensionInput.ts | 2 +- .../src/wiring/createExtension.test.ts | 22 +++++++++++++++++++ .../src/wiring/createExtension.ts | 4 ++-- .../src/wiring/createExtensionInput.test.ts | 2 +- .../wiring/resolveExtensionDefinition.test.ts | 14 ++++++------ 6 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 .changeset/flat-pillows-rush.md diff --git a/.changeset/flat-pillows-rush.md b/.changeset/flat-pillows-rush.md new file mode 100644 index 0000000000..bb64013f62 --- /dev/null +++ b/.changeset/flat-pillows-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Fixed a versioning conflict that could result in a `.withContext` is not a function error. diff --git a/packages/frontend-internal/src/wiring/InternalExtensionInput.ts b/packages/frontend-internal/src/wiring/InternalExtensionInput.ts index b816890f1a..9090023db9 100644 --- a/packages/frontend-internal/src/wiring/InternalExtensionInput.ts +++ b/packages/frontend-internal/src/wiring/InternalExtensionInput.ts @@ -28,7 +28,7 @@ export const OpaqueExtensionInput = OpaqueType.create<{ versions: { readonly version: undefined; readonly context?: ExtensionInputContext; - withContext(context: ExtensionInputContext): ExtensionInput; + withContext?(context: ExtensionInputContext): ExtensionInput; }; }>({ type: '@backstage/ExtensionInput', diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 4996fbc535..989dcf4468 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -1318,4 +1318,26 @@ describe('createExtension', () => { ); }); }); + + it('should old inputs without context support', () => { + const legacyInput = createExtensionInput([numberDataRef]); + + // old API without context + delete (legacyInput as any).context; + delete (legacyInput as any).withContext; + + const extension = createExtension({ + attachTo: { id: 'root', input: 'default' }, + output: [stringDataRef], + inputs: { + foo: legacyInput, + }, + factory({ inputs }) { + unused(inputs.foo); + return [stringDataRef('output')]; + }, + }); + + expect(extension.inputs.foo).toBe(legacyInput); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 0268cadf51..9b55505056 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -368,11 +368,11 @@ function bindInputs( return Object.fromEntries( Object.entries(inputs).map(([inputName, input]) => [ inputName, - OpaqueExtensionInput.toInternal(input).withContext({ + OpaqueExtensionInput.toInternal(input).withContext?.({ kind, name, input: inputName, - }), + }) ?? input, ]), ); } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts index 8511f79b4e..279878542a 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts @@ -60,7 +60,7 @@ describe('createExtensionInput', () => { const input = createExtensionInput([stringDataRef, numberDataRef]); const context = { input: 'test1', kind: 'test2', name: 'test3' }; const inputWithContext = - OpaqueExtensionInput.toInternal(input).withContext(context); + OpaqueExtensionInput.toInternal(input).withContext?.(context); expect(inputWithContext).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts index d8b90d69f3..79749af77c 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts @@ -80,7 +80,7 @@ describe('resolveExtensionDefinition', () => { resolveExtensionDefinition( OpaqueExtensionDefinition.toInternal({ ...baseDef, - attachTo: baseInpuf.withContext({ + attachTo: baseInpuf.withContext?.({ kind: 'parent', name: 'example', input: 'children', @@ -97,7 +97,7 @@ describe('resolveExtensionDefinition', () => { resolveExtensionDefinition( OpaqueExtensionDefinition.toInternal({ ...baseDef, - attachTo: baseInpuf.withContext({ + attachTo: baseInpuf.withContext?.({ name: 'example', input: 'children', }), @@ -113,7 +113,7 @@ describe('resolveExtensionDefinition', () => { resolveExtensionDefinition( OpaqueExtensionDefinition.toInternal({ ...baseDef, - attachTo: baseInpuf.withContext({ + attachTo: baseInpuf.withContext?.({ kind: 'parent', input: 'children', }), @@ -129,7 +129,7 @@ describe('resolveExtensionDefinition', () => { resolveExtensionDefinition( OpaqueExtensionDefinition.toInternal({ ...baseDef, - attachTo: baseInpuf.withContext({ + attachTo: baseInpuf.withContext?.({ input: 'children', }), }), @@ -145,15 +145,15 @@ describe('resolveExtensionDefinition', () => { OpaqueExtensionDefinition.toInternal({ ...baseDef, attachTo: [ - baseInpuf.withContext({ + baseInpuf.withContext?.({ kind: 'k1', input: 'children', }), - baseInpuf.withContext({ + baseInpuf.withContext?.({ kind: 'k2', input: 'children', }), - baseInpuf.withContext({ + baseInpuf.withContext?.({ kind: 'k3', input: 'children', }),