From d27c0327768917a1dbeb7b5b2ab0aef77b22a44d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 7 Sep 2025 11:45:40 +0200 Subject: [PATCH] frontend-app-api: refine app error types Signed-off-by: Patrik Oldsberg --- .../src/tree/instantiateAppNodeTree.test.ts | 56 +++++++++---------- .../src/tree/instantiateAppNodeTree.ts | 22 ++++---- .../src/tree/resolveAppNodeSpecs.test.ts | 6 +- .../src/tree/resolveAppNodeSpecs.ts | 16 +++--- .../src/tree/resolveAppTree.test.ts | 15 ++--- .../src/tree/resolveAppTree.ts | 13 +---- .../src/wiring/createErrorCollector.ts | 39 +++++-------- .../src/wiring/createSpecializedApp.tsx | 2 +- 8 files changed, 74 insertions(+), 95 deletions(-) diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 4b086b58b5..4f1589d3a3 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -411,7 +411,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'INVALID_CONFIGURATION', + code: 'EXTENSION_CONFIGURATION_INVALID', message: "Invalid configuration for extension 'app/test'; caused by Error: Expected number, received string at 'other'", context: { node }, @@ -443,7 +443,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test'; caused by NopeError: NOPE", context: { node }, @@ -476,7 +476,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', duplicate extension data 'test' received via output 'test2'", context: { node }, @@ -508,7 +508,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', unknown output provided via 'nonexistent'", context: { node }, @@ -544,7 +544,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', input 'singleton' is required but was not received", context: { node }, @@ -669,7 +669,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', expected exactly one 'singleton' input but received multiple: 'app/test', 'app/test'", context: { node }, @@ -713,7 +713,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', expected at most one 'singleton' input but received multiple: 'app/test', 'app/test'", context: { node }, @@ -751,7 +751,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test', extension 'app/test' could not be attached because its output data ('test', 'other') does not match what the input 'singleton' requires ('other')", context: { node }, @@ -1073,7 +1073,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'INVALID_CONFIGURATION', + code: 'EXTENSION_CONFIGURATION_INVALID', message: "Invalid configuration for extension 'app/test'; caused by Error: Expected number, received string at 'other'", context: { node }, @@ -1121,7 +1121,7 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - code: 'EXTENSION_FACTORY_INVALID_OUTPUT', + code: 'EXTENSION_INVALID', message: 'extension factory did not provide an iterable object', context: { node: expect.anything() }, }, @@ -1138,8 +1138,8 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - // TODO: This should probably be EXTENSION_FACTORY_INVALID_OUTPUT - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + // TODO: This should probably be EXTENSION_INVALID + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'test', extension factory override did not provide an iterable object", context: { node: expect.anything() }, @@ -1157,8 +1157,8 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - // TODO: This should probably be EXTENSION_FACTORY_INVALID_OUTPUT - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + // TODO: This should probably be EXTENSION_INVALID + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'test', extension factory middleware did not provide an iterable object", context: { node: expect.anything() }, @@ -1175,7 +1175,7 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - code: 'EXTENSION_FACTORY_INVALID_OUTPUT', + code: 'EXTENSION_INVALID', message: 'extension factory did not provide an iterable object', context: { node: expect.anything() }, }, @@ -1194,7 +1194,7 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - code: 'EXTENSION_FACTORY_INVALID_OUTPUT', + code: 'EXTENSION_INVALID', message: 'extension factory did not provide an iterable object', context: { node: expect.anything() }, }, @@ -1214,8 +1214,8 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - // TODO: This should probably be EXTENSION_FACTORY_INVALID_OUTPUT - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + // TODO: This should probably be EXTENSION_INVALID + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'test:test', extension factory did not provide an iterable object", context: { node: expect.anything() }, @@ -1236,8 +1236,8 @@ describe('instantiateAppNodeTree', () => { ), ).toEqual([ { - // TODO: This should probably be EXTENSION_FACTORY_INVALID_OUTPUT - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + // TODO: This should probably be EXTENSION_INVALID + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'test:test', original blueprint factory did not provide an iterable object", context: { node: expect.anything() }, @@ -1271,7 +1271,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: "Failed to instantiate extension 'app/test'; caused by NopeError: NOPE", context: { node }, @@ -1303,7 +1303,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_FACTORY_DUPLICATE_OUTPUT', + code: 'EXTENSION_OUTPUT_CONFLICT', message: "extension factory output duplicate data 'test'", context: { dataRefId: 'test', node }, }, @@ -1335,7 +1335,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_FACTORY_MISSING_REQUIRED_OUTPUT', + code: 'EXTENSION_OUTPUT_MISSING', message: "missing required extension data output 'test'", context: { dataRefId: 'test', @@ -1370,7 +1370,7 @@ describe('instantiateAppNodeTree', () => { ).toBeDefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_FACTORY_UNEXPECTED_OUTPUT', + code: 'EXTENSION_OUTPUT_IGNORED', message: "unexpected output 'test'", context: { dataRefId: 'test', node }, }, @@ -1404,7 +1404,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_MISSING_REQUIRED_INPUT', + code: 'EXTENSION_ATTACHMENT_MISSING', message: "input 'singleton' is required but was not received", context: { inputName: 'singleton', node }, }, @@ -1529,7 +1529,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_TOO_MANY_ATTACHMENTS', + code: 'EXTENSION_ATTACHMENT_CONFLICT', message: "expected exactly one 'singleton' input but received multiple: 'app/test', 'app/test'", context: { inputName: 'singleton', node }, @@ -1573,7 +1573,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_TOO_MANY_ATTACHMENTS', + code: 'EXTENSION_ATTACHMENT_CONFLICT', message: "expected at most one 'singleton' input but received multiple: 'app/test', 'app/test'", context: { inputName: 'singleton', node }, @@ -1610,7 +1610,7 @@ describe('instantiateAppNodeTree', () => { ).toBeUndefined(); expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_MISSING_INPUT_DATA', + code: 'EXTENSION_INPUT_DATA_MISSING', message: "extension 'app/test' could not be attached because its output data ('test') does not match what the input 'singleton' requires ('other')", context: { node, inputName: 'singleton' }, diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 212cb19bc5..62bf64ebc7 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -102,7 +102,7 @@ function resolveInputDataContainer( mapWithFailures(extensionData, ref => { if (dataMap.has(ref.id)) { collector.report({ - code: 'EXTENSION_DUPLICATE_INPUT', + code: 'EXTENSION_INPUT_DATA_IGNORED', message: `Unexpected duplicate input data '${ref.id}'`, }); return; @@ -120,7 +120,7 @@ function resolveInputDataContainer( .join(', '); collector.report({ - code: 'EXTENSION_MISSING_INPUT_DATA', + code: 'EXTENSION_INPUT_DATA_MISSING', message: `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`, }); throw INSTANTIATION_FAILED; @@ -265,7 +265,7 @@ function resolveV2Inputs( if (attachedNodes.length > 1) { const attachedNodeIds = attachedNodes.map(e => e.spec.id).join("', '"); collector.report({ - code: 'EXTENSION_TOO_MANY_ATTACHMENTS', + code: 'EXTENSION_ATTACHMENT_CONFLICT', message: `expected ${ input.config.optional ? 'at most' : 'exactly' } one '${inputName}' input but received multiple: '${attachedNodeIds}'`, @@ -274,7 +274,7 @@ function resolveV2Inputs( } else if (attachedNodes.length === 0) { if (!input.config.optional) { collector.report({ - code: 'EXTENSION_MISSING_REQUIRED_INPUT', + code: 'EXTENSION_ATTACHMENT_MISSING', message: `input '${inputName}' is required but was not received`, }); throw INSTANTIATION_FAILED; @@ -326,7 +326,7 @@ export function createAppNodeInstance(options: { }; } catch (e) { collector.report({ - code: 'INVALID_CONFIGURATION', + code: 'EXTENSION_CONFIGURATION_INVALID', message: `Invalid configuration for extension '${id}'; caused by ${e}`, }); return undefined; @@ -393,7 +393,7 @@ export function createAppNodeInstance(options: { !outputDataValues?.[Symbol.iterator] ) { collector.report({ - code: 'EXTENSION_FACTORY_INVALID_OUTPUT', + code: 'EXTENSION_INVALID', message: 'extension factory did not provide an iterable object', }); throw INSTANTIATION_FAILED; @@ -403,7 +403,7 @@ export function createAppNodeInstance(options: { mapWithFailures(outputDataValues, value => { if (outputDataMap.has(value.id)) { collector.report({ - code: 'EXTENSION_FACTORY_DUPLICATE_OUTPUT', + code: 'EXTENSION_OUTPUT_CONFLICT', message: `extension factory output duplicate data '${value.id}'`, context: { dataRefId: value.id, @@ -421,7 +421,7 @@ export function createAppNodeInstance(options: { if (value === undefined) { if (!ref.config.optional) { collector.report({ - code: 'EXTENSION_FACTORY_MISSING_REQUIRED_OUTPUT', + code: 'EXTENSION_OUTPUT_MISSING', message: `missing required extension data output '${ref.id}'`, context: { dataRefId: ref.id, @@ -439,7 +439,7 @@ export function createAppNodeInstance(options: { for (const dataRefId of outputDataMap.keys()) { // TODO: Make this a warning collector.report({ - code: 'EXTENSION_FACTORY_UNEXPECTED_OUTPUT', + code: 'EXTENSION_OUTPUT_IGNORED', message: `unexpected output '${dataRefId}'`, context: { dataRefId: dataRefId, @@ -449,7 +449,7 @@ export function createAppNodeInstance(options: { } } else { collector.report({ - code: 'UNEXPECTED_EXTENSION_VERSION', + code: 'EXTENSION_INVALID', message: `unexpected extension version '${ (internalExtension as any).version }'`, @@ -459,7 +459,7 @@ export function createAppNodeInstance(options: { } catch (e) { if (e !== INSTANTIATION_FAILED) { collector.report({ - code: 'FAILED_TO_INSTANTIATE_EXTENSION', + code: 'EXTENSION_FACTORY_ERROR', message: `Failed to instantiate extension '${id}'${ e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}` }`, diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index 23217f697a..2eacd0ae67 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -440,7 +440,7 @@ describe('resolveAppNodeSpecs', () => { expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_FORBIDDEN', + code: 'EXTENSION_IGNORED', message: "It is forbidden to override the 'test/forbidden' extension, attempted by the 'test' plugin", context: { @@ -473,7 +473,7 @@ describe('resolveAppNodeSpecs', () => { expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_FORBIDDEN', + code: 'EXTENSION_IGNORED', message: "It is forbidden to override the 'forbidden' extension, attempted by the 'forbidden' plugin", context: { @@ -496,7 +496,7 @@ describe('resolveAppNodeSpecs', () => { expect(collector.collectErrors()).toEqual([ { - code: 'EXTENSION_CONFIG_FORBIDDEN', + code: 'INVALID_EXTENSION_CONFIG_KEY', message: "Configuration of the 'forbidden' extension is forbidden", context: { extensionId: 'forbidden', diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 23a1847b43..d3b4ad6723 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -56,7 +56,7 @@ export function resolveAppNodeSpecs(options: { ) => { if (forbidden.has(extension.id)) { collector.report({ - code: 'EXTENSION_FORBIDDEN', + code: 'EXTENSION_IGNORED', message: `It is forbidden to override the '${extension.id}' extension, attempted by the '${extension.plugin.id}' plugin`, context: { plugin: extension.plugin, @@ -153,13 +153,13 @@ export function resolveAppNodeSpecs(options: { } } - const seendExtensionIds = new Set(); + const seenExtensionIds = new Set(); const deduplicatedExtensions = configuredExtensions.filter( ({ extension, params }) => { - if (seendExtensionIds.has(extension.id)) { + if (seenExtensionIds.has(extension.id)) { collector.report({ - code: 'EXTENSION_DUPLICATED', - message: `The extension '${extension.id}' is duplicated`, + code: 'EXTENSION_IGNORED', + message: `The '${extension.id}' extension from the '${params.plugin.id}' plugin is a duplicate and will be ignored`, context: { plugin: params.plugin, extensionId: extension.id, @@ -167,7 +167,7 @@ export function resolveAppNodeSpecs(options: { }); return false; } - seendExtensionIds.add(extension.id); + seenExtensionIds.add(extension.id); return true; }, ); @@ -178,7 +178,7 @@ export function resolveAppNodeSpecs(options: { if (forbidden.has(extensionId)) { collector.report({ - code: 'EXTENSION_CONFIG_FORBIDDEN', + code: 'INVALID_EXTENSION_CONFIG_KEY', message: `Configuration of the '${extensionId}' extension is forbidden`, context: { extensionId, @@ -206,7 +206,7 @@ export function resolveAppNodeSpecs(options: { order.set(extensionId, existing); } else { collector.report({ - code: 'EXTENSION_CONFIG_UNKNOWN_EXTENSION', + code: 'INVALID_EXTENSION_CONFIG_KEY', message: `Extension ${extensionId} does not exist`, context: { extensionId, diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index 79bdf81126..bffc156509 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -238,7 +238,7 @@ describe('buildAppTree', () => { `); }); - it('emits an error when duplicated extensions are detected', () => { + it('ignores duplicate extensions', () => { const tree = resolveAppTree( 'a', [ @@ -248,13 +248,6 @@ describe('buildAppTree', () => { collector, ); expect(Array.from(tree.nodes.keys())).toEqual(['a']); - expect(collector.collectErrors()).toEqual([ - { - code: 'DUPLICATE_EXTENSION_ID', - message: "Unexpected duplicate extension id 'a'", - context: { spec: { ...baseSpec, id: 'a' } }, - }, - ]); }); describe('redirects', () => { @@ -300,11 +293,13 @@ describe('buildAppTree', () => { expect(collector.collectErrors()).toEqual([ { - code: 'DUPLICATE_REDIRECT_TARGET', + code: 'EXTENSION_INPUT_REDIRECT_CONFLICT', message: "Duplicate redirect target for input 'test' in extension 'b'", context: { - spec: { ...baseSpec, id: 'b', extension: e2 }, + node: expect.objectContaining({ + spec: { ...baseSpec, id: 'b', extension: e2 }, + }), inputName: 'test', }, }, diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.ts b/packages/frontend-app-api/src/tree/resolveAppTree.ts index fc2c8f845e..f2f31f8950 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.ts @@ -122,15 +122,8 @@ export function resolveAppTree( const redirectTargetsByKey = new Map(); for (const spec of specs) { - // The main check with a more helpful error message happens in resolveAppNodeSpecs + // The main check with a helpful error message happens in resolveAppNodeSpecs if (nodes.has(spec.id)) { - errorCollector.report({ - code: 'DUPLICATE_EXTENSION_ID', - message: `Unexpected duplicate extension id '${spec.id}'`, - context: { - spec, - }, - }); continue; } @@ -144,10 +137,10 @@ export function resolveAppTree( const key = makeRedirectKey(replace); if (redirectTargetsByKey.has(key)) { errorCollector.report({ - code: 'DUPLICATE_REDIRECT_TARGET', + code: 'EXTENSION_INPUT_REDIRECT_CONFLICT', message: `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`, context: { - spec, + node, inputName, }, }); diff --git a/packages/frontend-app-api/src/wiring/createErrorCollector.ts b/packages/frontend-app-api/src/wiring/createErrorCollector.ts index cc51f146ce..fd028f1f18 100644 --- a/packages/frontend-app-api/src/wiring/createErrorCollector.ts +++ b/packages/frontend-app-api/src/wiring/createErrorCollector.ts @@ -14,41 +14,32 @@ * limitations under the License. */ -import { - AppNode, - AppNodeSpec, - FrontendPlugin, -} from '@backstage/frontend-plugin-api'; +import { AppNode, FrontendPlugin } from '@backstage/frontend-plugin-api'; import { Expand } from '@backstage/types'; type AppErrorTypes = { // resolveAppNodeSpecs - EXTENSION_FORBIDDEN: 'plugin' | 'extensionId'; - EXTENSION_DUPLICATED: 'plugin' | 'extensionId'; - EXTENSION_CONFIG_FORBIDDEN: 'extensionId'; - EXTENSION_CONFIG_UNKNOWN_EXTENSION: 'extensionId'; + EXTENSION_IGNORED: 'plugin' | 'extensionId'; + INVALID_EXTENSION_CONFIG_KEY: 'extensionId'; // resolveAppTree - DUPLICATE_EXTENSION_ID: 'spec'; - DUPLICATE_REDIRECT_TARGET: 'spec' | 'inputName'; + EXTENSION_INPUT_REDIRECT_CONFLICT: 'node' | 'inputName'; // instantiateAppNodeTree - EXTENSION_DUPLICATE_INPUT: 'node' | 'inputName'; - EXTENSION_MISSING_INPUT_DATA: 'node' | 'inputName'; - EXTENSION_TOO_MANY_ATTACHMENTS: 'node' | 'inputName'; - EXTENSION_MISSING_REQUIRED_INPUT: 'node' | 'inputName'; - INVALID_CONFIGURATION: 'node'; - EXTENSION_FACTORY_INVALID_OUTPUT: 'node'; - EXTENSION_FACTORY_DUPLICATE_OUTPUT: 'node' | 'dataRefId'; - EXTENSION_FACTORY_MISSING_REQUIRED_OUTPUT: 'node' | 'dataRefId'; - EXTENSION_FACTORY_UNEXPECTED_OUTPUT: 'node' | 'dataRefId'; - UNEXPECTED_EXTENSION_VERSION: 'node'; - FAILED_TO_INSTANTIATE_EXTENSION: 'node'; + EXTENSION_INPUT_DATA_IGNORED: 'node' | 'inputName'; + EXTENSION_INPUT_DATA_MISSING: 'node' | 'inputName'; + EXTENSION_ATTACHMENT_CONFLICT: 'node' | 'inputName'; + EXTENSION_ATTACHMENT_MISSING: 'node' | 'inputName'; + EXTENSION_CONFIGURATION_INVALID: 'node'; + EXTENSION_INVALID: 'node'; + EXTENSION_OUTPUT_CONFLICT: 'node' | 'dataRefId'; + EXTENSION_OUTPUT_MISSING: 'node' | 'dataRefId'; + EXTENSION_OUTPUT_IGNORED: 'node' | 'dataRefId'; + EXTENSION_FACTORY_ERROR: 'node'; // createSpecializedApp - NO_API_FACTORY: 'node'; + API_EXTENSION_INVALID: 'node'; }; type AppErrorContext = { node?: AppNode; - spec?: AppNodeSpec; plugin?: FrontendPlugin; extensionId?: string; dataRefId?: string; diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index bcb57ce7e7..a9b7e6864a 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -394,7 +394,7 @@ function createApiFactories(options: { factories.push(apiFactory); } else { options.collector.report({ - code: 'NO_API_FACTORY', + code: 'API_EXTENSION_INVALID', message: `API extension '${apiNode.spec.id}' did not output an API factory`, context: { node: apiNode,