@nospt/plugin-dev-ai-hub-common 0.3.1 → 1.0.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/toResourceSummary.cjs.js +31 -0
- package/dist/toResourceSummary.cjs.js.map +1 -0
- package/dist/toResourceSummary.esm.js +29 -0
- package/dist/toResourceSummary.esm.js.map +1 -0
- package/package.json +5 -4
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @nospt/plugin-dev-ai-hub-common
|
|
2
|
+
|
|
3
|
+
Isomorphic contracts shared by the **Dev AI Hub** frontend and backend plugins.
|
|
4
|
+
|
|
5
|
+
> **Requires Backstage 1.51 or later.** The `AiResource` kind comes from Backstage's own alpha catalog module, which is not available before that.
|
|
6
|
+
|
|
7
|
+
**You do not install this package directly.** It arrives as a dependency of [`@nospt/plugin-dev-ai-hub`](https://www.npmjs.com/package/@nospt/plugin-dev-ai-hub) and [`@nospt/plugin-dev-ai-hub-backend`](https://www.npmjs.com/package/@nospt/plugin-dev-ai-hub-backend), and the three publish in lockstep at the same version.
|
|
8
|
+
|
|
9
|
+
## Why it exists
|
|
10
|
+
|
|
11
|
+
A contract duplicated across two packages is a contract that will drift. Anything the frontend and backend must agree on lives here exactly once, and neither plugin imports the other.
|
|
12
|
+
|
|
13
|
+
It is genuinely isomorphic — no Node-only and no browser-only code — which is why the same package can back both halves.
|
|
14
|
+
|
|
15
|
+
## The main contract
|
|
16
|
+
|
|
17
|
+
`ResourceSummary` is the flat shape the backend serves and the frontend consumes. The frontend never sees a raw catalog `Entity`; this is the whole of what it knows about a resource.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface ResourceSummary {
|
|
21
|
+
entityRef: string; // "airesource:default/azure-devops-cli"
|
|
22
|
+
name: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
tags: string[];
|
|
26
|
+
type: ResourceType;
|
|
27
|
+
lifecycle: string;
|
|
28
|
+
owner?: string;
|
|
29
|
+
sourceLocation?: string; // absent → browsable, not installable
|
|
30
|
+
frameworks: string[]; // "claude-code", "github-copilot", …
|
|
31
|
+
version?: string;
|
|
32
|
+
kind: string;
|
|
33
|
+
childCount?: number; // unpopulated until containment lands
|
|
34
|
+
helpText?: string;
|
|
35
|
+
annotations: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`toResourceSummary(entity)` is the `Entity` → `ResourceSummary` mapping, exported here so the backend router and the test fixtures cannot disagree about it.
|
|
40
|
+
|
|
41
|
+
## Also in here
|
|
42
|
+
|
|
43
|
+
- **The `ResourceType` vocabulary** — `skill`, `agent`, `hook`, `mcp-config`, `plugin`, `marketplace` — and the rules derived from it: which body shape a type renders as (`mcp-config` is JSON, the rest markdown), whether its body is a downloadable artifact or merely a pointer, and its per-framework install paths.
|
|
44
|
+
- **Framework tokens and normalisation**, so `claude-code` means the same thing on both sides.
|
|
45
|
+
- **Telemetry schemas** for the event contract.
|
|
46
|
+
|
|
47
|
+
## Full documentation
|
|
48
|
+
|
|
49
|
+
See the [repository README](https://github.com/nosportugal/backstage-plugin-dev-ai-hub#readme) for setup, and [`docs/AIRESOURCE-SPEC.md`](https://github.com/nosportugal/backstage-plugin-dev-ai-hub/blob/main-nos/docs/AIRESOURCE-SPEC.md) for the full `AiResource` entity spec.
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
Apache-2.0
|
package/dist/index.cjs.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var resources = require('./resources.cjs.js');
|
|
4
4
|
var telemetry = require('./telemetry.cjs.js');
|
|
5
|
+
var toResourceSummary = require('./toResourceSummary.cjs.js');
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
|
|
@@ -36,4 +37,5 @@ exports.normalizeFramework = resources.normalizeFramework;
|
|
|
36
37
|
exports.resolveCapableFrameworks = resources.resolveCapableFrameworks;
|
|
37
38
|
exports.TelemetryActionEnum = telemetry.TelemetryActionEnum;
|
|
38
39
|
exports.TelemetryEventInputSchema = telemetry.TelemetryEventInputSchema;
|
|
40
|
+
exports.toResourceSummary = toResourceSummary.toResourceSummary;
|
|
39
41
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { Entity } from '@backstage/catalog-model';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The v2 read contract: AiResource vocabulary, the flat ResourceSummary
|
|
@@ -318,5 +319,19 @@ type TelemetryEventInput = z.infer<typeof TelemetryEventInputSchema>;
|
|
|
318
319
|
/** Raw per-action counts for one resource, as returned by `GET /telemetry/:ref`. */
|
|
319
320
|
type TelemetryCounts = Record<TelemetryAction, number>;
|
|
320
321
|
|
|
321
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Map a catalog AiResource entity to the flat contract served to the
|
|
324
|
+
* frontend. Returns undefined for entities with an unsupported `spec.type`,
|
|
325
|
+
* which are silently dropped (ADR-0003).
|
|
326
|
+
*
|
|
327
|
+
* Lives in `common` rather than the backend because it is pure and
|
|
328
|
+
* isomorphic — `Entity` in, `ResourceSummary` out — and both the backend
|
|
329
|
+
* router and the frontend's e2e fixtures need it to agree. The e2e suite
|
|
330
|
+
* builds its mock `GET /resources` payload by running the real examples in
|
|
331
|
+
* `examples/catalog/` through this function, so a change to the mapping
|
|
332
|
+
* cannot silently diverge from what the tests assert against.
|
|
333
|
+
*/
|
|
334
|
+
declare function toResourceSummary(entity: Entity): ResourceSummary | undefined;
|
|
335
|
+
|
|
336
|
+
export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, ANNOTATION_VERSION, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MCP_LINK_CAPABLE_FRAMEWORKS, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, TelemetryActionEnum, TelemetryEventInputSchema, expandInstallFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isResourceType, normalizeFramework, resolveCapableFrameworks, toResourceSummary };
|
|
322
337
|
export type { AiResourceEntityLike, BodyShape, FrameworkToken, InstallMode, MarketplaceAddCommand, ResourceDeepLink, ResourceInstallStep, ResourceInstallTarget, ResourceListResponse, ResourceSummary, ResourceType, ResourceTypeIcon, ResourceTypeInfo, TelemetryAction, TelemetryCounts, TelemetryEventInput };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, ANNOTATION_VERSION, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MCP_LINK_CAPABLE_FRAMEWORKS, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, expandInstallFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isResourceType, normalizeFramework, resolveCapableFrameworks } from './resources.esm.js';
|
|
2
2
|
export { TelemetryActionEnum, TelemetryEventInputSchema } from './telemetry.esm.js';
|
|
3
|
+
export { toResourceSummary } from './toResourceSummary.esm.js';
|
|
3
4
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
+
var resources = require('./resources.cjs.js');
|
|
5
|
+
|
|
6
|
+
function toResourceSummary(entity) {
|
|
7
|
+
const type = entity.spec?.type;
|
|
8
|
+
if (!resources.isResourceType(type)) {
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
const annotations = entity.metadata.annotations ?? {};
|
|
12
|
+
return {
|
|
13
|
+
entityRef: catalogModel.stringifyEntityRef(entity),
|
|
14
|
+
name: entity.metadata.name,
|
|
15
|
+
title: entity.metadata.title,
|
|
16
|
+
description: entity.metadata.description,
|
|
17
|
+
tags: entity.metadata.tags ?? [],
|
|
18
|
+
type,
|
|
19
|
+
lifecycle: typeof entity.spec?.lifecycle === "string" ? entity.spec.lifecycle : "",
|
|
20
|
+
owner: typeof entity.spec?.owner === "string" ? entity.spec.owner : void 0,
|
|
21
|
+
sourceLocation: annotations[catalogModel.ANNOTATION_SOURCE_LOCATION],
|
|
22
|
+
frameworks: resources.getFrameworks(entity),
|
|
23
|
+
version: annotations[resources.ANNOTATION_VERSION],
|
|
24
|
+
kind: entity.kind,
|
|
25
|
+
helpText: annotations[resources.ANNOTATION_HELP],
|
|
26
|
+
annotations
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
exports.toResourceSummary = toResourceSummary;
|
|
31
|
+
//# sourceMappingURL=toResourceSummary.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toResourceSummary.cjs.js","sources":["../src/toResourceSummary.ts"],"sourcesContent":["import {\n ANNOTATION_SOURCE_LOCATION,\n stringifyEntityRef,\n type Entity,\n} from '@backstage/catalog-model';\nimport {\n ANNOTATION_HELP,\n ANNOTATION_VERSION,\n getFrameworks,\n isResourceType,\n type ResourceSummary,\n} from './resources';\n\n/**\n * Map a catalog AiResource entity to the flat contract served to the\n * frontend. Returns undefined for entities with an unsupported `spec.type`,\n * which are silently dropped (ADR-0003).\n *\n * Lives in `common` rather than the backend because it is pure and\n * isomorphic — `Entity` in, `ResourceSummary` out — and both the backend\n * router and the frontend's e2e fixtures need it to agree. The e2e suite\n * builds its mock `GET /resources` payload by running the real examples in\n * `examples/catalog/` through this function, so a change to the mapping\n * cannot silently diverge from what the tests assert against.\n */\nexport function toResourceSummary(entity: Entity): ResourceSummary | undefined {\n const type = entity.spec?.type;\n if (!isResourceType(type)) {\n return undefined;\n }\n\n const annotations = entity.metadata.annotations ?? {};\n\n return {\n entityRef: stringifyEntityRef(entity),\n name: entity.metadata.name,\n title: entity.metadata.title,\n description: entity.metadata.description,\n tags: entity.metadata.tags ?? [],\n type,\n lifecycle:\n typeof entity.spec?.lifecycle === 'string' ? entity.spec.lifecycle : '',\n owner:\n typeof entity.spec?.owner === 'string' ? entity.spec.owner : undefined,\n sourceLocation: annotations[ANNOTATION_SOURCE_LOCATION],\n frameworks: getFrameworks(entity),\n version: annotations[ANNOTATION_VERSION],\n kind: entity.kind,\n helpText: annotations[ANNOTATION_HELP],\n annotations,\n };\n}\n"],"names":["isResourceType","stringifyEntityRef","ANNOTATION_SOURCE_LOCATION","getFrameworks","ANNOTATION_VERSION","ANNOTATION_HELP"],"mappings":";;;;;AAyBO,SAAS,kBAAkB,MAAA,EAA6C;AAC7E,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,EAAM,IAAA;AAC1B,EAAA,IAAI,CAACA,wBAAA,CAAe,IAAI,CAAA,EAAG;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAEpD,EAAA,OAAO;AAAA,IACL,SAAA,EAAWC,gCAAmB,MAAM,CAAA;AAAA,IACpC,IAAA,EAAM,OAAO,QAAA,CAAS,IAAA;AAAA,IACtB,KAAA,EAAO,OAAO,QAAA,CAAS,KAAA;AAAA,IACvB,WAAA,EAAa,OAAO,QAAA,CAAS,WAAA;AAAA,IAC7B,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,IAAA,IAAQ,EAAC;AAAA,IAC/B,IAAA;AAAA,IACA,SAAA,EACE,OAAO,MAAA,CAAO,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA,CAAO,KAAK,SAAA,GAAY,EAAA;AAAA,IACvE,KAAA,EACE,OAAO,MAAA,CAAO,IAAA,EAAM,UAAU,QAAA,GAAW,MAAA,CAAO,KAAK,KAAA,GAAQ,MAAA;AAAA,IAC/D,cAAA,EAAgB,YAAYC,uCAA0B,CAAA;AAAA,IACtD,UAAA,EAAYC,wBAAc,MAAM,CAAA;AAAA,IAChC,OAAA,EAAS,YAAYC,4BAAkB,CAAA;AAAA,IACvC,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,QAAA,EAAU,YAAYC,yBAAe,CAAA;AAAA,IACrC;AAAA,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ANNOTATION_SOURCE_LOCATION, stringifyEntityRef } from '@backstage/catalog-model';
|
|
2
|
+
import { isResourceType, ANNOTATION_HELP, ANNOTATION_VERSION, getFrameworks } from './resources.esm.js';
|
|
3
|
+
|
|
4
|
+
function toResourceSummary(entity) {
|
|
5
|
+
const type = entity.spec?.type;
|
|
6
|
+
if (!isResourceType(type)) {
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
const annotations = entity.metadata.annotations ?? {};
|
|
10
|
+
return {
|
|
11
|
+
entityRef: stringifyEntityRef(entity),
|
|
12
|
+
name: entity.metadata.name,
|
|
13
|
+
title: entity.metadata.title,
|
|
14
|
+
description: entity.metadata.description,
|
|
15
|
+
tags: entity.metadata.tags ?? [],
|
|
16
|
+
type,
|
|
17
|
+
lifecycle: typeof entity.spec?.lifecycle === "string" ? entity.spec.lifecycle : "",
|
|
18
|
+
owner: typeof entity.spec?.owner === "string" ? entity.spec.owner : void 0,
|
|
19
|
+
sourceLocation: annotations[ANNOTATION_SOURCE_LOCATION],
|
|
20
|
+
frameworks: getFrameworks(entity),
|
|
21
|
+
version: annotations[ANNOTATION_VERSION],
|
|
22
|
+
kind: entity.kind,
|
|
23
|
+
helpText: annotations[ANNOTATION_HELP],
|
|
24
|
+
annotations
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { toResourceSummary };
|
|
29
|
+
//# sourceMappingURL=toResourceSummary.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toResourceSummary.esm.js","sources":["../src/toResourceSummary.ts"],"sourcesContent":["import {\n ANNOTATION_SOURCE_LOCATION,\n stringifyEntityRef,\n type Entity,\n} from '@backstage/catalog-model';\nimport {\n ANNOTATION_HELP,\n ANNOTATION_VERSION,\n getFrameworks,\n isResourceType,\n type ResourceSummary,\n} from './resources';\n\n/**\n * Map a catalog AiResource entity to the flat contract served to the\n * frontend. Returns undefined for entities with an unsupported `spec.type`,\n * which are silently dropped (ADR-0003).\n *\n * Lives in `common` rather than the backend because it is pure and\n * isomorphic — `Entity` in, `ResourceSummary` out — and both the backend\n * router and the frontend's e2e fixtures need it to agree. The e2e suite\n * builds its mock `GET /resources` payload by running the real examples in\n * `examples/catalog/` through this function, so a change to the mapping\n * cannot silently diverge from what the tests assert against.\n */\nexport function toResourceSummary(entity: Entity): ResourceSummary | undefined {\n const type = entity.spec?.type;\n if (!isResourceType(type)) {\n return undefined;\n }\n\n const annotations = entity.metadata.annotations ?? {};\n\n return {\n entityRef: stringifyEntityRef(entity),\n name: entity.metadata.name,\n title: entity.metadata.title,\n description: entity.metadata.description,\n tags: entity.metadata.tags ?? [],\n type,\n lifecycle:\n typeof entity.spec?.lifecycle === 'string' ? entity.spec.lifecycle : '',\n owner:\n typeof entity.spec?.owner === 'string' ? entity.spec.owner : undefined,\n sourceLocation: annotations[ANNOTATION_SOURCE_LOCATION],\n frameworks: getFrameworks(entity),\n version: annotations[ANNOTATION_VERSION],\n kind: entity.kind,\n helpText: annotations[ANNOTATION_HELP],\n annotations,\n };\n}\n"],"names":[],"mappings":";;;AAyBO,SAAS,kBAAkB,MAAA,EAA6C;AAC7E,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,EAAM,IAAA;AAC1B,EAAA,IAAI,CAAC,cAAA,CAAe,IAAI,CAAA,EAAG;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAEpD,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,mBAAmB,MAAM,CAAA;AAAA,IACpC,IAAA,EAAM,OAAO,QAAA,CAAS,IAAA;AAAA,IACtB,KAAA,EAAO,OAAO,QAAA,CAAS,KAAA;AAAA,IACvB,WAAA,EAAa,OAAO,QAAA,CAAS,WAAA;AAAA,IAC7B,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,IAAA,IAAQ,EAAC;AAAA,IAC/B,IAAA;AAAA,IACA,SAAA,EACE,OAAO,MAAA,CAAO,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA,CAAO,KAAK,SAAA,GAAY,EAAA;AAAA,IACvE,KAAA,EACE,OAAO,MAAA,CAAO,IAAA,EAAM,UAAU,QAAA,GAAW,MAAA,CAAO,KAAK,KAAA,GAAQ,MAAA;AAAA,IAC/D,cAAA,EAAgB,YAAY,0BAA0B,CAAA;AAAA,IACtD,UAAA,EAAY,cAAc,MAAM,CAAA;AAAA,IAChC,OAAA,EAAS,YAAY,kBAAkB,CAAA;AAAA,IACvC,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,QAAA,EAAU,YAAY,eAAe,CAAA;AAAA,IACrC;AAAA,GACF;AACF;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nospt/plugin-dev-ai-hub-common",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-rc1",
|
|
4
4
|
"description": "Common types and schemas for Dev AI Hub",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"pluginPackages": [
|
|
35
35
|
"@nospt/plugin-dev-ai-hub",
|
|
36
36
|
"@nospt/plugin-dev-ai-hub-backend",
|
|
37
|
-
"@nospt/plugin-dev-ai-hub-common"
|
|
38
|
-
"@nospt/plugin-dev-ai-hub-node"
|
|
37
|
+
"@nospt/plugin-dev-ai-hub-common"
|
|
39
38
|
]
|
|
40
39
|
},
|
|
41
40
|
"sideEffects": false,
|
|
42
41
|
"files": [
|
|
43
|
-
"dist"
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "backstage-cli package build",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"tsc": "tsc --noEmit"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
+
"@backstage/catalog-model": "^1.9.0",
|
|
55
56
|
"zod": "^3.22.0"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|