From a185d7e0f8b74f1df8cecee5fcc3e33226be386c Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 19 May 2026 12:14:25 +0200 Subject: [PATCH] feat: restore per-type guards for union narrowing Signed-off-by: benjdlambert --- packages/catalog-model/report-alpha.api.md | 10 ++++ packages/catalog-model/src/alpha.ts | 2 + .../kinds/AiResourceEntityV1alpha1.test.ts | 56 +++++++++++++++++++ .../src/kinds/AiResourceEntityV1alpha1.ts | 20 +++++++ 4 files changed, 88 insertions(+) diff --git a/packages/catalog-model/report-alpha.api.md b/packages/catalog-model/report-alpha.api.md index 4d264e6793..d1f5e4ef61 100644 --- a/packages/catalog-model/report-alpha.api.md +++ b/packages/catalog-model/report-alpha.api.md @@ -485,6 +485,16 @@ export const isAiResourceEntity: ( entity: Entity, ) => entity is AiResourceEntityV1alpha1; +// @alpha +export const isRuleAiResourceEntity: ( + entity: Entity, +) => entity is RuleAiResourceEntityV1alpha1; + +// @alpha +export const isSkillAiResourceEntity: ( + entity: Entity, +) => entity is SkillAiResourceEntityV1alpha1; + // @public export type KindValidator = { check(entity: Entity): Promise; diff --git a/packages/catalog-model/src/alpha.ts b/packages/catalog-model/src/alpha.ts index 1d8f1c01af..219513f167 100644 --- a/packages/catalog-model/src/alpha.ts +++ b/packages/catalog-model/src/alpha.ts @@ -38,6 +38,8 @@ export { skillAiResourceEntityV1alpha1Validator, ruleAiResourceEntityV1alpha1Validator, isAiResourceEntity, + isSkillAiResourceEntity, + isRuleAiResourceEntity, aiResourceEntityModel, } from './kinds/AiResourceEntityV1alpha1'; export * from './model'; diff --git a/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.test.ts index 688f25f5c6..be295c81dd 100644 --- a/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.test.ts @@ -23,6 +23,8 @@ import { skillAiResourceEntityV1alpha1Validator as skillValidator, ruleAiResourceEntityV1alpha1Validator as ruleValidator, isAiResourceEntity, + isSkillAiResourceEntity, + isRuleAiResourceEntity, } from './AiResourceEntityV1alpha1'; describe('AiResourceV1alpha1 default validator', () => { @@ -301,3 +303,57 @@ describe('isAiResourceEntity', () => { expect(isAiResourceEntity(entity)).toBe(false); }); }); + +describe('isSkillAiResourceEntity', () => { + it('returns true for a skill AiResource', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'AiResource', + metadata: { name: 'test' }, + spec: { type: 'skill' }, + }; + expect(isSkillAiResourceEntity(entity)).toBe(true); + }); + + it('returns false for a non-skill AiResource', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'AiResource', + metadata: { name: 'test' }, + spec: { type: 'rule' }, + }; + expect(isSkillAiResourceEntity(entity)).toBe(false); + }); + + it('returns false for a different kind', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'test' }, + spec: { type: 'skill' }, + }; + expect(isSkillAiResourceEntity(entity)).toBe(false); + }); +}); + +describe('isRuleAiResourceEntity', () => { + it('returns true for a rule AiResource', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'AiResource', + metadata: { name: 'test' }, + spec: { type: 'rule' }, + }; + expect(isRuleAiResourceEntity(entity)).toBe(true); + }); + + it('returns false for a non-rule AiResource', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'AiResource', + metadata: { name: 'test' }, + spec: { type: 'skill' }, + }; + expect(isRuleAiResourceEntity(entity)).toBe(false); + }); +}); diff --git a/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.ts index 109712c5ec..8ee9374a45 100644 --- a/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/AiResourceEntityV1alpha1.ts @@ -125,6 +125,26 @@ export const isAiResourceEntity = ( ): entity is AiResourceEntityV1alpha1 => entity.apiVersion === 'backstage.io/v1alpha1' && entity.kind === 'AiResource'; +/** + * Type guard for {@link SkillAiResourceEntityV1alpha1}. + * + * @alpha + */ +export const isSkillAiResourceEntity = ( + entity: Entity, +): entity is SkillAiResourceEntityV1alpha1 => + isAiResourceEntity(entity) && entity.spec?.type === 'skill'; + +/** + * Type guard for {@link RuleAiResourceEntityV1alpha1}. + * + * @alpha + */ +export const isRuleAiResourceEntity = ( + entity: Entity, +): entity is RuleAiResourceEntityV1alpha1 => + isAiResourceEntity(entity) && entity.spec?.type === 'rule'; + const ruleValidator = entityKindSchemaValidator(ruleJsonSchema); /**