@anyshift/backstage-plugin-anyshift 0.1.0-beta.2 → 0.1.0-beta.21

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/LICENSE CHANGED
@@ -174,3 +174,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
174
174
  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
175
175
  License for the specific language governing permissions and limitations under
176
176
  the License.
177
+
package/README.md CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  New-frontend-system plugin for the Anyshift infrastructure graph. Installing the package in an app
4
4
  created with `@backstage/frontend-defaults` and configured with `app.packages: all` discovers its
5
- dashboard, navigation item, API factory, and entity content automatically.
5
+ dashboard, Cloud inventory page, navigation items, API factory, and entity content automatically.
6
+
7
+ The Cloud page lists provider-managed infrastructure Resources with searchable type, account,
8
+ region, and ownership metadata. Cloud Resource entities also receive a generic read-only page for
9
+ their ARN, provider console link, and catalog relationships, independently of whether the resource
10
+ supports an Anyshift service brief.
6
11
 
7
12
  The package has no legacy frontend entry point. Add the corresponding
8
13
  `@anyshift/backstage-plugin-anyshift-backend` package to the Backstage backend and configure one
@@ -47,11 +47,14 @@ class AnyshiftClient {
47
47
  body: JSON.stringify({ sql })
48
48
  });
49
49
  }
50
+ async reconcileCatalog() {
51
+ return this.request("/anyshift/reconcile", { method: "POST" }, "catalog");
52
+ }
50
53
  async get(path) {
51
54
  return this.request(path, { method: "GET" });
52
55
  }
53
- async request(path, init) {
54
- const baseUrl = await this.discoveryApi.getBaseUrl("anyshift");
56
+ async request(path, init, pluginId = "anyshift") {
57
+ const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);
55
58
  const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);
56
59
  const text = await response.text();
57
60
  let payload;
@@ -1 +1 @@
1
- {"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n CatalogSummary,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(): Promise<EstateInsights> {\n return this.get('/insights');\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(path: string, init: RequestInit): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl('anyshift');\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: any;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n throw new Error(\n payload?.error?.message ||\n `Anyshift request failed (${response.status})`,\n );\n }\n return payload as T;\n }\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n CatalogSummary,\n CatalogSyncReport,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n reconcileCatalog(): Promise<CatalogSyncAccepted>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n}\n\nexport interface CatalogSyncAccepted {\n accepted: true;\n acceptedAt: string;\n previous?: CatalogSyncReport;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(): Promise<EstateInsights> {\n return this.get('/insights');\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n async reconcileCatalog(): Promise<CatalogSyncAccepted> {\n return this.request('/anyshift/reconcile', { method: 'POST' }, 'catalog');\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(\n path: string,\n init: RequestInit,\n pluginId = 'anyshift',\n ): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: any;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n throw new Error(\n payload?.error?.message ||\n `Anyshift request failed (${response.status})`,\n );\n }\n return payload as T;\n }\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useState, useEffect, useMemo } from 'react';
3
+ import { Typography, Grid } from '@material-ui/core';
4
+ import { Page, Header, Content, ContentHeader, Progress, InfoCard, Table } from '@backstage/core-components';
5
+ import { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
6
+ import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
7
+ import { useApi } from '@backstage/frontend-plugin-api';
8
+
9
+ function isCloudResource(entity) {
10
+ return entity.kind.toLocaleLowerCase("en-US") === "resource" && Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]);
11
+ }
12
+ function cloudResourceRows(entities) {
13
+ return entities.filter(isCloudResource).map((entity) => {
14
+ const annotations = entity.metadata.annotations ?? {};
15
+ return {
16
+ entity,
17
+ name: entity.metadata.title || entity.metadata.name,
18
+ provider: annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]?.toUpperCase() || "Unknown",
19
+ resourceType: annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] || String(entity.spec?.type || "resource"),
20
+ region: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || "Global",
21
+ account: annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION] || "Not available",
22
+ owner: typeof entity.spec?.owner === "string" ? entity.spec.owner : "Not assigned"
23
+ };
24
+ }).sort((a, b) => a.name.localeCompare(b.name));
25
+ }
26
+ const columns = [
27
+ {
28
+ title: "Resource",
29
+ field: "name",
30
+ render: (row) => /* @__PURE__ */ jsx(EntityRefLink, { entityRef: row.entity, title: row.name })
31
+ },
32
+ { title: "Type", field: "resourceType" },
33
+ { title: "Provider", field: "provider" },
34
+ { title: "Region", field: "region" },
35
+ { title: "Account", field: "account" },
36
+ { title: "Owner", field: "owner" }
37
+ ];
38
+ const CloudPage = () => {
39
+ const catalogApi = useApi(catalogApiRef);
40
+ const [entities, setEntities] = useState();
41
+ const [error, setError] = useState();
42
+ useEffect(() => {
43
+ catalogApi.getEntities({ filter: { kind: "Resource" } }).then((response) => setEntities(response.items)).catch(
44
+ (cause) => setError(cause instanceof Error ? cause.message : String(cause))
45
+ );
46
+ }, [catalogApi]);
47
+ const rows = useMemo(() => cloudResourceRows(entities ?? []), [entities]);
48
+ const summary = useMemo(
49
+ () => ({
50
+ resources: rows.length,
51
+ types: new Set(rows.map((row) => row.resourceType)).size,
52
+ accounts: new Set(
53
+ rows.map((row) => row.account).filter((account) => account !== "Not available")
54
+ ).size,
55
+ regions: new Set(
56
+ rows.map((row) => row.region).filter((region) => region !== "Global")
57
+ ).size
58
+ }),
59
+ [rows]
60
+ );
61
+ return /* @__PURE__ */ jsxs(Page, { themeId: "tool", children: [
62
+ /* @__PURE__ */ jsx(
63
+ Header,
64
+ {
65
+ title: "Cloud",
66
+ subtitle: "Read-only cloud inventory synchronized through Anyshift"
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsxs(Content, { children: [
70
+ /* @__PURE__ */ jsx(ContentHeader, { title: "Cloud inventory" }),
71
+ error ? /* @__PURE__ */ jsx(Typography, { color: "error", children: error }) : !entities ? /* @__PURE__ */ jsx(Progress, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ /* @__PURE__ */ jsx(InfoCard, { children: /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 2, children: [
73
+ /* @__PURE__ */ jsx(Summary, { label: "Resources", value: summary.resources }),
74
+ /* @__PURE__ */ jsx(Summary, { label: "Types", value: summary.types }),
75
+ /* @__PURE__ */ jsx(Summary, { label: "Accounts", value: summary.accounts }),
76
+ /* @__PURE__ */ jsx(Summary, { label: "Regions", value: summary.regions })
77
+ ] }) }),
78
+ /* @__PURE__ */ jsx(
79
+ Table,
80
+ {
81
+ title: "Cloud resources",
82
+ columns,
83
+ data: rows,
84
+ options: {
85
+ filtering: true,
86
+ paging: true,
87
+ pageSize: 20,
88
+ pageSizeOptions: [20, 50, 100],
89
+ search: true
90
+ },
91
+ emptyContent: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No cloud resources have been synchronized yet." })
92
+ }
93
+ )
94
+ ] })
95
+ ] })
96
+ ] });
97
+ };
98
+ const Summary = ({ label, value }) => /* @__PURE__ */ jsxs(Grid, { item: true, xs: 6, md: 3, children: [
99
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: value }),
100
+ /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: label })
101
+ ] });
102
+
103
+ export { CloudPage, cloudResourceRows, isCloudResource };
104
+ //# sourceMappingURL=CloudPage.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudPage.esm.js","sources":["../../../src/components/cloud/CloudPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Grid, Typography } from '@material-ui/core';\nimport {\n Content,\n ContentHeader,\n Header,\n InfoCard,\n Page,\n Progress,\n Table,\n type TableColumn,\n} from '@backstage/core-components';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_CLOUD_REGION_ANNOTATION,\n ANYSHIFT_RESOURCE_TYPE_ANNOTATION,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';\nimport { useApi } from '@backstage/frontend-plugin-api';\n\nexport interface CloudResourceRow {\n entity: Entity;\n name: string;\n provider: string;\n resourceType: string;\n region: string;\n account: string;\n owner: string;\n}\n\nexport function isCloudResource(entity: Entity): boolean {\n return (\n entity.kind.toLocaleLowerCase('en-US') === 'resource' &&\n Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION])\n );\n}\n\nexport function cloudResourceRows(entities: Entity[]): CloudResourceRow[] {\n return entities\n .filter(isCloudResource)\n .map(entity => {\n const annotations = entity.metadata.annotations ?? {};\n return {\n entity,\n name: entity.metadata.title || entity.metadata.name,\n provider:\n annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]?.toUpperCase() ||\n 'Unknown',\n resourceType:\n annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] ||\n String(entity.spec?.type || 'resource'),\n region: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || 'Global',\n account:\n annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION] || 'Not available',\n owner:\n typeof entity.spec?.owner === 'string'\n ? entity.spec.owner\n : 'Not assigned',\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\nconst columns: TableColumn<CloudResourceRow>[] = [\n {\n title: 'Resource',\n field: 'name',\n render: row => <EntityRefLink entityRef={row.entity} title={row.name} />,\n },\n { title: 'Type', field: 'resourceType' },\n { title: 'Provider', field: 'provider' },\n { title: 'Region', field: 'region' },\n { title: 'Account', field: 'account' },\n { title: 'Owner', field: 'owner' },\n];\n\nexport const CloudPage = () => {\n const catalogApi = useApi(catalogApiRef);\n const [entities, setEntities] = useState<Entity[]>();\n const [error, setError] = useState<string>();\n\n useEffect(() => {\n catalogApi\n .getEntities({ filter: { kind: 'Resource' } })\n .then(response => setEntities(response.items))\n .catch(cause =>\n setError(cause instanceof Error ? cause.message : String(cause)),\n );\n }, [catalogApi]);\n\n const rows = useMemo(() => cloudResourceRows(entities ?? []), [entities]);\n const summary = useMemo(\n () => ({\n resources: rows.length,\n types: new Set(rows.map(row => row.resourceType)).size,\n accounts: new Set(\n rows\n .map(row => row.account)\n .filter(account => account !== 'Not available'),\n ).size,\n regions: new Set(\n rows.map(row => row.region).filter(region => region !== 'Global'),\n ).size,\n }),\n [rows],\n );\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Cloud\"\n subtitle=\"Read-only cloud inventory synchronized through Anyshift\"\n />\n <Content>\n <ContentHeader title=\"Cloud inventory\" />\n {error ? (\n <Typography color=\"error\">{error}</Typography>\n ) : !entities ? (\n <Progress />\n ) : (\n <>\n <InfoCard>\n <Grid container spacing={2}>\n <Summary label=\"Resources\" value={summary.resources} />\n <Summary label=\"Types\" value={summary.types} />\n <Summary label=\"Accounts\" value={summary.accounts} />\n <Summary label=\"Regions\" value={summary.regions} />\n </Grid>\n </InfoCard>\n <Table\n title=\"Cloud resources\"\n columns={columns}\n data={rows}\n options={{\n filtering: true,\n paging: true,\n pageSize: 20,\n pageSizeOptions: [20, 50, 100],\n search: true,\n }}\n emptyContent={\n <Typography color=\"textSecondary\">\n No cloud resources have been synchronized yet.\n </Typography>\n }\n />\n </>\n )}\n </Content>\n </Page>\n );\n};\n\nconst Summary = ({ label, value }: { label: string; value: number }) => (\n <Grid item xs={6} md={3}>\n <Typography variant=\"h5\">{value}</Typography>\n <Typography color=\"textSecondary\">{label}</Typography>\n </Grid>\n);\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { Grid, Typography, Chip } from '@material-ui/core';
3
+ import { InfoCard, Link } from '@backstage/core-components';
4
+ import { ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
5
+ import { useEntity, EntityRefLink } from '@backstage/plugin-catalog-react';
6
+
7
+ const CloudResourceContent = () => {
8
+ const { entity } = useEntity();
9
+ const annotations = entity.metadata.annotations ?? {};
10
+ const relationships = (entity.relations ?? []).filter(
11
+ (relation) => ["dependencyOf", "dependsOn"].includes(relation.type)
12
+ );
13
+ const consoleLink = entity.metadata.links?.find(
14
+ (link) => link.url.startsWith("https://console.aws.amazon.com/")
15
+ );
16
+ return /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
17
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Cloud identity", children: [
18
+ /* @__PURE__ */ jsx(
19
+ Definition,
20
+ {
21
+ label: "Provider",
22
+ value: annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]
23
+ }
24
+ ),
25
+ /* @__PURE__ */ jsx(
26
+ Definition,
27
+ {
28
+ label: "Resource type",
29
+ value: annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] || String(entity.spec?.type || "")
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsx(
33
+ Definition,
34
+ {
35
+ label: "Region",
36
+ value: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || "Global"
37
+ }
38
+ ),
39
+ /* @__PURE__ */ jsx(
40
+ Definition,
41
+ {
42
+ label: "Account",
43
+ value: annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION]
44
+ }
45
+ ),
46
+ consoleLink && /* @__PURE__ */ jsx(Link, { to: consoleLink.url, children: "Open in AWS Console" })
47
+ ] }) }),
48
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Catalog context", children: [
49
+ /* @__PURE__ */ jsx(
50
+ Definition,
51
+ {
52
+ label: "Owner",
53
+ value: typeof entity.spec?.owner === "string" ? entity.spec.owner : void 0
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx(
57
+ Definition,
58
+ {
59
+ label: "Namespace",
60
+ value: entity.metadata.namespace || "default"
61
+ }
62
+ ),
63
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Relationships" }),
64
+ relationships.length === 0 ? /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No component or resource relationships are available yet." }) : /* @__PURE__ */ jsx("ul", { children: relationships.map((relation) => /* @__PURE__ */ jsxs("li", { children: [
65
+ /* @__PURE__ */ jsx(
66
+ Chip,
67
+ {
68
+ size: "small",
69
+ label: relation.type,
70
+ style: { marginRight: 8 }
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsx(EntityRefLink, { entityRef: relation.targetRef })
74
+ ] }, `${relation.type}:${relation.targetRef}`)) })
75
+ ] }) }),
76
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(InfoCard, { title: "Cloud resource ARN", children: /* @__PURE__ */ jsx(
77
+ Typography,
78
+ {
79
+ component: "code",
80
+ style: { overflowWrap: "anywhere", whiteSpace: "pre-wrap" },
81
+ children: annotations[ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION] || "No ARN is available for this resource."
82
+ }
83
+ ) }) })
84
+ ] });
85
+ };
86
+ const Definition = ({ label, value }) => /* @__PURE__ */ jsxs(Typography, { paragraph: true, children: [
87
+ /* @__PURE__ */ jsxs("strong", { children: [
88
+ label,
89
+ ":"
90
+ ] }),
91
+ " ",
92
+ value || "Not available"
93
+ ] });
94
+
95
+ export { CloudResourceContent };
96
+ //# sourceMappingURL=CloudResourceContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudResourceContent.esm.js","sources":["../../../src/components/cloud/CloudResourceContent.tsx"],"sourcesContent":["import { Chip, Grid, Typography } from '@material-ui/core';\nimport { InfoCard, Link } from '@backstage/core-components';\nimport {\n ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_CLOUD_REGION_ANNOTATION,\n ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION,\n ANYSHIFT_RESOURCE_TYPE_ANNOTATION,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { EntityRefLink, useEntity } from '@backstage/plugin-catalog-react';\n\nexport const CloudResourceContent = () => {\n const { entity } = useEntity();\n const annotations = entity.metadata.annotations ?? {};\n const relationships = (entity.relations ?? []).filter(relation =>\n ['dependencyOf', 'dependsOn'].includes(relation.type),\n );\n const consoleLink = entity.metadata.links?.find(link =>\n link.url.startsWith('https://console.aws.amazon.com/'),\n );\n\n return (\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Cloud identity\">\n <Definition\n label=\"Provider\"\n value={annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]}\n />\n <Definition\n label=\"Resource type\"\n value={\n annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] ||\n String(entity.spec?.type || '')\n }\n />\n <Definition\n label=\"Region\"\n value={annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || 'Global'}\n />\n <Definition\n label=\"Account\"\n value={annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION]}\n />\n {consoleLink && <Link to={consoleLink.url}>Open in AWS Console</Link>}\n </InfoCard>\n </Grid>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Catalog context\">\n <Definition\n label=\"Owner\"\n value={\n typeof entity.spec?.owner === 'string'\n ? entity.spec.owner\n : undefined\n }\n />\n <Definition\n label=\"Namespace\"\n value={entity.metadata.namespace || 'default'}\n />\n <Typography variant=\"subtitle2\">Relationships</Typography>\n {relationships.length === 0 ? (\n <Typography color=\"textSecondary\">\n No component or resource relationships are available yet.\n </Typography>\n ) : (\n <ul>\n {relationships.map(relation => (\n <li key={`${relation.type}:${relation.targetRef}`}>\n <Chip\n size=\"small\"\n label={relation.type}\n style={{ marginRight: 8 }}\n />\n <EntityRefLink entityRef={relation.targetRef} />\n </li>\n ))}\n </ul>\n )}\n </InfoCard>\n </Grid>\n <Grid item xs={12}>\n <InfoCard title=\"Cloud resource ARN\">\n <Typography\n component=\"code\"\n style={{ overflowWrap: 'anywhere', whiteSpace: 'pre-wrap' }}\n >\n {annotations[ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION] ||\n 'No ARN is available for this resource.'}\n </Typography>\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n\nconst Definition = ({ label, value }: { label: string; value?: string }) => (\n <Typography paragraph>\n <strong>{label}:</strong> {value || 'Not available'}\n </Typography>\n);\n"],"names":[],"mappings":"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"}
@@ -1,16 +1,17 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { useState, useEffect } from 'react';
3
- import { Typography, Grid, TextField, Button } from '@material-ui/core';
3
+ import { Typography, Grid, Button, TextField } from '@material-ui/core';
4
4
  import { Page, Header, Content, Progress, ContentHeader, InfoCard } from '@backstage/core-components';
5
5
  import { useApi } from '@backstage/frontend-plugin-api';
6
6
  import { usePermission } from '@backstage/plugin-permission-react';
7
- import { anyshiftQueryPermission } from '@anyshift/backstage-plugin-anyshift-common';
7
+ import { anyshiftQueryPermission, anyshiftCatalogSyncPermission, ANYSHIFT_CATALOG_SYNC_ERROR_ANNOTATION, ANYSHIFT_CATALOG_SYNC_FINISHED_AT_ANNOTATION, ANYSHIFT_CATALOG_SYNC_TRIGGER_ANNOTATION, ANYSHIFT_CATALOG_SYNC_STATUS_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
8
8
  import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
9
9
  import { anyshiftApiRef } from '../../api/AnyshiftApi.esm.js';
10
10
  import { TimelinePresenter, ImpactPresenter } from '../entity/SectionPresenters.esm.js';
11
11
  import { DatadogOperationsRadar } from './DatadogOperationsRadar.esm.js';
12
12
 
13
13
  const DEFAULT_QUERY = "SELECT * FROM resources WHERE type = deployment LIMIT 10";
14
+ const CATALOG_SYNC_STATUS_ENTITY = "anyshift-catalog-sync";
14
15
  function isDeterministicSelect(value) {
15
16
  const sql = value.trim();
16
17
  return /^SELECT\b/i.test(sql) && sql.length <= 4096 && !sql.includes(";") && !sql.includes("--") && !sql.includes("/*");
@@ -49,10 +50,22 @@ function summarizeCatalogSync(entities) {
49
50
  (entity) => entity.metadata.annotations?.["anyshift.io/observed-service"]
50
51
  ).length,
51
52
  managedResources: entities.filter(
52
- (entity) => entity.kind.toLowerCase() === "resource" && entity.metadata.annotations?.["anyshift.io/managed-by"] === "catalog-provider"
53
+ (entity) => entity.kind.toLowerCase() === "resource" && entity.metadata.name !== CATALOG_SYNC_STATUS_ENTITY && entity.metadata.annotations?.["anyshift.io/managed-by"] === "catalog-provider"
53
54
  ).length
54
55
  };
55
56
  }
57
+ function catalogSyncStatus(entities) {
58
+ const entity = entities.find(
59
+ (candidate) => candidate.kind.toLowerCase() === "resource" && candidate.metadata.name === CATALOG_SYNC_STATUS_ENTITY
60
+ );
61
+ const annotations = entity?.metadata.annotations ?? {};
62
+ return {
63
+ status: annotations[ANYSHIFT_CATALOG_SYNC_STATUS_ANNOTATION] ?? "waiting",
64
+ trigger: annotations[ANYSHIFT_CATALOG_SYNC_TRIGGER_ANNOTATION],
65
+ finishedAt: annotations[ANYSHIFT_CATALOG_SYNC_FINISHED_AT_ANNOTATION],
66
+ error: annotations[ANYSHIFT_CATALOG_SYNC_ERROR_ANNOTATION]
67
+ };
68
+ }
56
69
  const EstateInsightsGrid = ({
57
70
  insights
58
71
  }) => /* @__PURE__ */ jsx(Fragment, { children: insights.sections.map((item) => /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(InfoCard, { title: item.title, children: item.section.state === "error" ? /* @__PURE__ */ jsx(Typography, { color: "error", children: item.section.message }) : item.section.state === "empty" || item.section.state === "unsupported" || item.section.data === void 0 ? /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: item.section.message || "No insight evidence is available." }) : item.id === "recent-change" ? /* @__PURE__ */ jsx(
@@ -68,6 +81,9 @@ const AnyshiftExplorerPage = () => {
68
81
  const queryPermission = usePermission({
69
82
  permission: anyshiftQueryPermission
70
83
  });
84
+ const catalogSyncPermission = usePermission({
85
+ permission: anyshiftCatalogSyncPermission
86
+ });
71
87
  const [queryEnabled, setQueryEnabled] = useState(false);
72
88
  const [insights, setInsights] = useState();
73
89
  const [insightError, setInsightError] = useState();
@@ -80,6 +96,9 @@ const AnyshiftExplorerPage = () => {
80
96
  const [sql, setSql] = useState(DEFAULT_QUERY);
81
97
  const [queryResult, setQueryResult] = useState();
82
98
  const [queryError, setQueryError] = useState();
99
+ const [requestingSync, setRequestingSync] = useState(false);
100
+ const [syncRequestMessage, setSyncRequestMessage] = useState();
101
+ const [syncRequestError, setSyncRequestError] = useState();
83
102
  useEffect(() => {
84
103
  api.capabilities().then((capabilities) => setQueryEnabled(capabilities.queryEnabled)).catch(() => setQueryEnabled(false));
85
104
  }, [api]);
@@ -104,6 +123,7 @@ const AnyshiftExplorerPage = () => {
104
123
  (entity) => entity.metadata.annotations?.["anyshift.io/target"]
105
124
  );
106
125
  const syncSummary = summarizeCatalogSync(catalogEntities);
126
+ const syncStatus = catalogSyncStatus(catalogEntities);
107
127
  const matchingEntities = anyshiftEntities.filter((entity) => matchesCatalogEntity(entity, catalogQuery)).slice(0, 12);
108
128
  const runQuery = async () => {
109
129
  if (!isDeterministicSelect(sql)) {
@@ -128,6 +148,23 @@ const AnyshiftExplorerPage = () => {
128
148
  setRefreshingDatadog(false);
129
149
  }
130
150
  };
151
+ const requestCatalogSync = async () => {
152
+ setRequestingSync(true);
153
+ setSyncRequestError(void 0);
154
+ setSyncRequestMessage(void 0);
155
+ try {
156
+ const accepted = await api.reconcileCatalog();
157
+ setSyncRequestMessage(
158
+ `Reconciliation accepted ${formatTimestamp(accepted.acceptedAt)}.`
159
+ );
160
+ } catch (error) {
161
+ setSyncRequestError(
162
+ error instanceof Error ? error.message : String(error)
163
+ );
164
+ } finally {
165
+ setRequestingSync(false);
166
+ }
167
+ };
131
168
  return /* @__PURE__ */ jsxs(Page, { themeId: "tool", children: [
132
169
  /* @__PURE__ */ jsx(
133
170
  Header,
@@ -168,6 +205,31 @@ const AnyshiftExplorerPage = () => {
168
205
  /* @__PURE__ */ jsxs(Grid, { item: true, xs: 6, md: 2, children: [
169
206
  /* @__PURE__ */ jsx(Typography, { variant: "h5", children: syncSummary.managedResources }),
170
207
  /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "Managed resources" })
208
+ ] }),
209
+ /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
210
+ /* @__PURE__ */ jsxs(Typography, { children: [
211
+ "Last reconciliation: ",
212
+ /* @__PURE__ */ jsx("strong", { children: syncStatus.status }),
213
+ syncStatus.finishedAt ? ` \xB7 ${formatTimestamp(syncStatus.finishedAt)}` : "",
214
+ syncStatus.trigger ? ` \xB7 ${syncStatus.trigger}` : ""
215
+ ] }),
216
+ syncStatus.error && /* @__PURE__ */ jsxs(Typography, { color: "error", children: [
217
+ "Last failure: ",
218
+ syncStatus.error
219
+ ] }),
220
+ catalogSyncPermission.allowed && /* @__PURE__ */ jsx(
221
+ Button,
222
+ {
223
+ variant: "outlined",
224
+ color: "primary",
225
+ disabled: requestingSync,
226
+ onClick: requestCatalogSync,
227
+ style: { marginTop: 12 },
228
+ children: requestingSync ? "Requesting\u2026" : "Reconcile now"
229
+ }
230
+ ),
231
+ syncRequestMessage && /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: syncRequestMessage }),
232
+ syncRequestError && /* @__PURE__ */ jsx(Typography, { color: "error", children: syncRequestError })
171
233
  ] })
172
234
  ] }) }),
173
235
  /* @__PURE__ */ jsx(ContentHeader, { title: "Find a service" }),
@@ -235,6 +297,10 @@ const AnyshiftExplorerPage = () => {
235
297
  ] })
236
298
  ] });
237
299
  };
300
+ function formatTimestamp(value) {
301
+ const parsed = Date.parse(value);
302
+ return Number.isNaN(parsed) ? value : new Date(parsed).toLocaleString();
303
+ }
238
304
 
239
- export { AnyshiftExplorerPage, EstateInsightsGrid, isDeterministicSelect, matchesCatalogEntity, summarizeCatalogSync };
305
+ export { AnyshiftExplorerPage, EstateInsightsGrid, catalogSyncStatus, isDeterministicSelect, matchesCatalogEntity, summarizeCatalogSync };
240
306
  //# sourceMappingURL=AnyshiftExplorerPage.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnyshiftExplorerPage.esm.js","sources":["../../../src/components/explorer/AnyshiftExplorerPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Button, Grid, TextField, Typography } from '@material-ui/core';\nimport {\n Content,\n ContentHeader,\n Header,\n InfoCard,\n Page,\n Progress,\n} from '@backstage/core-components';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport { anyshiftQueryPermission } from '@anyshift/backstage-plugin-anyshift-common';\nimport type { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';\nimport { anyshiftApiRef } from '../../api/AnyshiftApi';\nimport type {\n DatadogSignals,\n EstateInsights,\n QueryResult,\n} from '../../api/types';\nimport {\n ImpactPresenter,\n TimelinePresenter,\n} from '../entity/SectionPresenters';\nimport { DatadogOperationsRadar } from './DatadogOperationsRadar';\n\nconst DEFAULT_QUERY =\n 'SELECT * FROM resources WHERE type = deployment LIMIT 10';\n\nexport function isDeterministicSelect(value: string): boolean {\n const sql = value.trim();\n return (\n /^SELECT\\b/i.test(sql) &&\n sql.length <= 4_096 &&\n !sql.includes(';') &&\n !sql.includes('--') &&\n !sql.includes('/*')\n );\n}\n\nexport function matchesCatalogEntity(entity: Entity, value: string): boolean {\n const query = value.trim().toLocaleLowerCase();\n if (!query) return true;\n const searchable = [\n entity.metadata.name,\n entity.metadata.title,\n entity.metadata.description,\n entity.metadata.annotations?.['anyshift.io/target'],\n typeof entity.spec?.owner === 'string' ? entity.spec.owner : undefined,\n ];\n return searchable.some(item => item?.toLocaleLowerCase().includes(query));\n}\n\nexport function summarizeCatalogSync(entities: Entity[]) {\n const components = entities.filter(\n entity => entity.kind.toLowerCase() === 'component',\n );\n const configured = components.filter(\n entity => entity.metadata.annotations?.['anyshift.io/target'],\n );\n const excluded = components.filter(\n entity =>\n entity.metadata.annotations?.[\n 'anyshift.io/catalog-sync'\n ]?.toLowerCase() === 'excluded',\n );\n return {\n components: components.length,\n configured: configured.length,\n excluded: excluded.length,\n unclassified: Math.max(\n 0,\n components.length - configured.length - excluded.length,\n ),\n observed: configured.filter(\n entity => entity.metadata.annotations?.['anyshift.io/observed-service'],\n ).length,\n managedResources: entities.filter(\n entity =>\n entity.kind.toLowerCase() === 'resource' &&\n entity.metadata.annotations?.['anyshift.io/managed-by'] ===\n 'catalog-provider',\n ).length,\n };\n}\n\nexport const EstateInsightsGrid = ({\n insights,\n}: {\n insights: EstateInsights;\n}) => (\n <>\n {insights.sections.map(item => (\n <Grid item xs={12} md={6} key={item.id}>\n <InfoCard title={item.title}>\n {item.section.state === 'error' ? (\n <Typography color=\"error\">{item.section.message}</Typography>\n ) : item.section.state === 'empty' ||\n item.section.state === 'unsupported' ||\n item.section.data === undefined ? (\n <Typography color=\"textSecondary\">\n {item.section.message || 'No insight evidence is available.'}\n </Typography>\n ) : item.id === 'recent-change' ? (\n <TimelinePresenter\n data={item.section.data}\n fallbackLabel=\"Deployment\"\n />\n ) : (\n <ImpactPresenter data={item.section.data} />\n )}\n </InfoCard>\n </Grid>\n ))}\n </>\n);\n\nexport const AnyshiftExplorerPage = () => {\n const api = useApi(anyshiftApiRef);\n const catalogApi = useApi(catalogApiRef);\n const queryPermission = usePermission({\n permission: anyshiftQueryPermission,\n });\n const [queryEnabled, setQueryEnabled] = useState(false);\n const [insights, setInsights] = useState<EstateInsights>();\n const [insightError, setInsightError] = useState<string>();\n const [datadogSignals, setDatadogSignals] = useState<DatadogSignals>();\n const [datadogError, setDatadogError] = useState<string>();\n const [refreshingDatadog, setRefreshingDatadog] = useState(false);\n const [catalogEntities, setCatalogEntities] = useState<Entity[]>([]);\n const [catalogError, setCatalogError] = useState<string>();\n const [catalogQuery, setCatalogQuery] = useState('');\n const [sql, setSql] = useState(DEFAULT_QUERY);\n const [queryResult, setQueryResult] = useState<QueryResult>();\n const [queryError, setQueryError] = useState<string>();\n\n useEffect(() => {\n api\n .capabilities()\n .then(capabilities => setQueryEnabled(capabilities.queryEnabled))\n .catch(() => setQueryEnabled(false));\n }, [api]);\n\n useEffect(() => {\n api\n .datadogSignals()\n .then(setDatadogSignals)\n .catch(error => {\n setDatadogError(error instanceof Error ? error.message : String(error));\n });\n }, [api]);\n\n useEffect(() => {\n api\n .insights()\n .then(setInsights)\n .catch(error => {\n setInsightError(error instanceof Error ? error.message : String(error));\n });\n }, [api]);\n\n useEffect(() => {\n catalogApi\n .getEntities({\n filter: [{ kind: 'Component' }, { kind: 'Resource' }],\n })\n .then(response => setCatalogEntities(response.items))\n .catch(error => {\n setCatalogError(error instanceof Error ? error.message : String(error));\n });\n }, [catalogApi]);\n\n const anyshiftEntities = catalogEntities.filter(\n entity => entity.metadata.annotations?.['anyshift.io/target'],\n );\n const syncSummary = summarizeCatalogSync(catalogEntities);\n const matchingEntities = anyshiftEntities\n .filter(entity => matchesCatalogEntity(entity, catalogQuery))\n .slice(0, 12);\n\n const runQuery = async () => {\n if (!isDeterministicSelect(sql)) {\n setQueryError('Enter one deterministic SELECT query.');\n return;\n }\n setQueryError(undefined);\n try {\n setQueryResult(await api.query(sql.trim()));\n } catch (error) {\n setQueryError(error instanceof Error ? error.message : String(error));\n }\n };\n\n const refreshDatadog = async () => {\n setRefreshingDatadog(true);\n setDatadogError(undefined);\n try {\n setDatadogSignals(await api.datadogSignals({ refresh: true }));\n } catch (error) {\n setDatadogError(error instanceof Error ? error.message : String(error));\n } finally {\n setRefreshingDatadog(false);\n }\n };\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Anyshift\"\n subtitle=\"Live infrastructure context for your software catalog\"\n />\n <Content>\n {datadogSignals ? (\n <DatadogOperationsRadar\n signals={datadogSignals}\n catalogEntities={anyshiftEntities}\n onRefresh={refreshDatadog}\n refreshing={refreshingDatadog}\n error={datadogError}\n />\n ) : datadogError ? (\n <Typography color=\"error\">{datadogError}</Typography>\n ) : (\n <Progress />\n )}\n <ContentHeader title=\"Catalog synchronization\" />\n <InfoCard subheader=\"GitHub ownership stays authoritative; Anyshift adds observed infrastructure context\">\n <Grid container spacing={2}>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.configured}</Typography>\n <Typography color=\"textSecondary\">Configured</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.excluded}</Typography>\n <Typography color=\"textSecondary\">Excluded</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.unclassified}</Typography>\n <Typography color=\"textSecondary\">Unclassified</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.observed}</Typography>\n <Typography color=\"textSecondary\">Active matches</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">\n {syncSummary.managedResources}\n </Typography>\n <Typography color=\"textSecondary\">Managed resources</Typography>\n </Grid>\n </Grid>\n </InfoCard>\n <ContentHeader title=\"Find a service\" />\n <InfoCard subheader=\"Anyshift-annotated Components and Resources in the Backstage catalog\">\n <TextField\n fullWidth\n variant=\"outlined\"\n label=\"Search services and resources\"\n value={catalogQuery}\n onChange={event => setCatalogQuery(event.target.value)}\n />\n {catalogError && (\n <Typography color=\"error\">{catalogError}</Typography>\n )}\n {!catalogError && matchingEntities.length === 0 ? (\n <Typography color=\"textSecondary\" style={{ marginTop: 12 }}>\n No matching Anyshift catalog entities.\n </Typography>\n ) : (\n <ul>\n {matchingEntities.map(entity => (\n <li\n key={`${entity.kind}:${\n entity.metadata.namespace ?? 'default'\n }/${entity.metadata.name}`}\n >\n <EntityRefLink entityRef={entity} />\n {entity.metadata.description\n ? ` - ${entity.metadata.description}`\n : null}\n </li>\n ))}\n </ul>\n )}\n </InfoCard>\n <ContentHeader title=\"Estate insights\" />\n {insightError && <Typography color=\"error\">{insightError}</Typography>}\n {!insights && !insightError ? <Progress /> : null}\n <Grid container spacing={3}>\n {insights && <EstateInsightsGrid insights={insights} />}\n {queryEnabled && queryPermission.allowed && (\n <Grid item xs={12}>\n <InfoCard\n title=\"Advanced query\"\n subheader=\"Deterministic Graph API SELECT queries only\"\n >\n <TextField\n fullWidth\n multiline\n minRows={2}\n variant=\"outlined\"\n value={sql}\n onChange={event => setSql(event.target.value)}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={runQuery}\n style={{ marginTop: 12 }}\n >\n Run query\n </Button>\n {queryError && (\n <Typography color=\"error\">{queryError}</Typography>\n )}\n {queryResult && (\n <pre style={{ whiteSpace: 'pre-wrap', fontSize: 12 }}>\n {JSON.stringify(queryResult, null, 2)}\n </pre>\n )}\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnyshiftExplorerPage.esm.js","sources":["../../../src/components/explorer/AnyshiftExplorerPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Button, Grid, TextField, Typography } from '@material-ui/core';\nimport {\n Content,\n ContentHeader,\n Header,\n InfoCard,\n Page,\n Progress,\n} from '@backstage/core-components';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n ANYSHIFT_CATALOG_SYNC_ERROR_ANNOTATION,\n ANYSHIFT_CATALOG_SYNC_FINISHED_AT_ANNOTATION,\n ANYSHIFT_CATALOG_SYNC_STATUS_ANNOTATION,\n ANYSHIFT_CATALOG_SYNC_TRIGGER_ANNOTATION,\n anyshiftCatalogSyncPermission,\n anyshiftQueryPermission,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport type { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';\nimport { anyshiftApiRef } from '../../api/AnyshiftApi';\nimport type {\n DatadogSignals,\n EstateInsights,\n QueryResult,\n} from '../../api/types';\nimport {\n ImpactPresenter,\n TimelinePresenter,\n} from '../entity/SectionPresenters';\nimport { DatadogOperationsRadar } from './DatadogOperationsRadar';\n\nconst DEFAULT_QUERY =\n 'SELECT * FROM resources WHERE type = deployment LIMIT 10';\nconst CATALOG_SYNC_STATUS_ENTITY = 'anyshift-catalog-sync';\n\nexport function isDeterministicSelect(value: string): boolean {\n const sql = value.trim();\n return (\n /^SELECT\\b/i.test(sql) &&\n sql.length <= 4_096 &&\n !sql.includes(';') &&\n !sql.includes('--') &&\n !sql.includes('/*')\n );\n}\n\nexport function matchesCatalogEntity(entity: Entity, value: string): boolean {\n const query = value.trim().toLocaleLowerCase();\n if (!query) return true;\n const searchable = [\n entity.metadata.name,\n entity.metadata.title,\n entity.metadata.description,\n entity.metadata.annotations?.['anyshift.io/target'],\n typeof entity.spec?.owner === 'string' ? entity.spec.owner : undefined,\n ];\n return searchable.some(item => item?.toLocaleLowerCase().includes(query));\n}\n\nexport function summarizeCatalogSync(entities: Entity[]) {\n const components = entities.filter(\n entity => entity.kind.toLowerCase() === 'component',\n );\n const configured = components.filter(\n entity => entity.metadata.annotations?.['anyshift.io/target'],\n );\n const excluded = components.filter(\n entity =>\n entity.metadata.annotations?.[\n 'anyshift.io/catalog-sync'\n ]?.toLowerCase() === 'excluded',\n );\n return {\n components: components.length,\n configured: configured.length,\n excluded: excluded.length,\n unclassified: Math.max(\n 0,\n components.length - configured.length - excluded.length,\n ),\n observed: configured.filter(\n entity => entity.metadata.annotations?.['anyshift.io/observed-service'],\n ).length,\n managedResources: entities.filter(\n entity =>\n entity.kind.toLowerCase() === 'resource' &&\n entity.metadata.name !== CATALOG_SYNC_STATUS_ENTITY &&\n entity.metadata.annotations?.['anyshift.io/managed-by'] ===\n 'catalog-provider',\n ).length,\n };\n}\n\nexport function catalogSyncStatus(entities: Entity[]) {\n const entity = entities.find(\n candidate =>\n candidate.kind.toLowerCase() === 'resource' &&\n candidate.metadata.name === CATALOG_SYNC_STATUS_ENTITY,\n );\n const annotations = entity?.metadata.annotations ?? {};\n return {\n status: annotations[ANYSHIFT_CATALOG_SYNC_STATUS_ANNOTATION] ?? 'waiting',\n trigger: annotations[ANYSHIFT_CATALOG_SYNC_TRIGGER_ANNOTATION],\n finishedAt: annotations[ANYSHIFT_CATALOG_SYNC_FINISHED_AT_ANNOTATION],\n error: annotations[ANYSHIFT_CATALOG_SYNC_ERROR_ANNOTATION],\n };\n}\n\nexport const EstateInsightsGrid = ({\n insights,\n}: {\n insights: EstateInsights;\n}) => (\n <>\n {insights.sections.map(item => (\n <Grid item xs={12} md={6} key={item.id}>\n <InfoCard title={item.title}>\n {item.section.state === 'error' ? (\n <Typography color=\"error\">{item.section.message}</Typography>\n ) : item.section.state === 'empty' ||\n item.section.state === 'unsupported' ||\n item.section.data === undefined ? (\n <Typography color=\"textSecondary\">\n {item.section.message || 'No insight evidence is available.'}\n </Typography>\n ) : item.id === 'recent-change' ? (\n <TimelinePresenter\n data={item.section.data}\n fallbackLabel=\"Deployment\"\n />\n ) : (\n <ImpactPresenter data={item.section.data} />\n )}\n </InfoCard>\n </Grid>\n ))}\n </>\n);\n\nexport const AnyshiftExplorerPage = () => {\n const api = useApi(anyshiftApiRef);\n const catalogApi = useApi(catalogApiRef);\n const queryPermission = usePermission({\n permission: anyshiftQueryPermission,\n });\n const catalogSyncPermission = usePermission({\n permission: anyshiftCatalogSyncPermission,\n });\n const [queryEnabled, setQueryEnabled] = useState(false);\n const [insights, setInsights] = useState<EstateInsights>();\n const [insightError, setInsightError] = useState<string>();\n const [datadogSignals, setDatadogSignals] = useState<DatadogSignals>();\n const [datadogError, setDatadogError] = useState<string>();\n const [refreshingDatadog, setRefreshingDatadog] = useState(false);\n const [catalogEntities, setCatalogEntities] = useState<Entity[]>([]);\n const [catalogError, setCatalogError] = useState<string>();\n const [catalogQuery, setCatalogQuery] = useState('');\n const [sql, setSql] = useState(DEFAULT_QUERY);\n const [queryResult, setQueryResult] = useState<QueryResult>();\n const [queryError, setQueryError] = useState<string>();\n const [requestingSync, setRequestingSync] = useState(false);\n const [syncRequestMessage, setSyncRequestMessage] = useState<string>();\n const [syncRequestError, setSyncRequestError] = useState<string>();\n\n useEffect(() => {\n api\n .capabilities()\n .then(capabilities => setQueryEnabled(capabilities.queryEnabled))\n .catch(() => setQueryEnabled(false));\n }, [api]);\n\n useEffect(() => {\n api\n .datadogSignals()\n .then(setDatadogSignals)\n .catch(error => {\n setDatadogError(error instanceof Error ? error.message : String(error));\n });\n }, [api]);\n\n useEffect(() => {\n api\n .insights()\n .then(setInsights)\n .catch(error => {\n setInsightError(error instanceof Error ? error.message : String(error));\n });\n }, [api]);\n\n useEffect(() => {\n catalogApi\n .getEntities({\n filter: [{ kind: 'Component' }, { kind: 'Resource' }],\n })\n .then(response => setCatalogEntities(response.items))\n .catch(error => {\n setCatalogError(error instanceof Error ? error.message : String(error));\n });\n }, [catalogApi]);\n\n const anyshiftEntities = catalogEntities.filter(\n entity => entity.metadata.annotations?.['anyshift.io/target'],\n );\n const syncSummary = summarizeCatalogSync(catalogEntities);\n const syncStatus = catalogSyncStatus(catalogEntities);\n const matchingEntities = anyshiftEntities\n .filter(entity => matchesCatalogEntity(entity, catalogQuery))\n .slice(0, 12);\n\n const runQuery = async () => {\n if (!isDeterministicSelect(sql)) {\n setQueryError('Enter one deterministic SELECT query.');\n return;\n }\n setQueryError(undefined);\n try {\n setQueryResult(await api.query(sql.trim()));\n } catch (error) {\n setQueryError(error instanceof Error ? error.message : String(error));\n }\n };\n\n const refreshDatadog = async () => {\n setRefreshingDatadog(true);\n setDatadogError(undefined);\n try {\n setDatadogSignals(await api.datadogSignals({ refresh: true }));\n } catch (error) {\n setDatadogError(error instanceof Error ? error.message : String(error));\n } finally {\n setRefreshingDatadog(false);\n }\n };\n\n const requestCatalogSync = async () => {\n setRequestingSync(true);\n setSyncRequestError(undefined);\n setSyncRequestMessage(undefined);\n try {\n const accepted = await api.reconcileCatalog();\n setSyncRequestMessage(\n `Reconciliation accepted ${formatTimestamp(accepted.acceptedAt)}.`,\n );\n } catch (error) {\n setSyncRequestError(\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n setRequestingSync(false);\n }\n };\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Anyshift\"\n subtitle=\"Live infrastructure context for your software catalog\"\n />\n <Content>\n {datadogSignals ? (\n <DatadogOperationsRadar\n signals={datadogSignals}\n catalogEntities={anyshiftEntities}\n onRefresh={refreshDatadog}\n refreshing={refreshingDatadog}\n error={datadogError}\n />\n ) : datadogError ? (\n <Typography color=\"error\">{datadogError}</Typography>\n ) : (\n <Progress />\n )}\n <ContentHeader title=\"Catalog synchronization\" />\n <InfoCard subheader=\"GitHub ownership stays authoritative; Anyshift adds observed infrastructure context\">\n <Grid container spacing={2}>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.configured}</Typography>\n <Typography color=\"textSecondary\">Configured</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.excluded}</Typography>\n <Typography color=\"textSecondary\">Excluded</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.unclassified}</Typography>\n <Typography color=\"textSecondary\">Unclassified</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">{syncSummary.observed}</Typography>\n <Typography color=\"textSecondary\">Active matches</Typography>\n </Grid>\n <Grid item xs={6} md={2}>\n <Typography variant=\"h5\">\n {syncSummary.managedResources}\n </Typography>\n <Typography color=\"textSecondary\">Managed resources</Typography>\n </Grid>\n <Grid item xs={12}>\n <Typography>\n Last reconciliation: <strong>{syncStatus.status}</strong>\n {syncStatus.finishedAt\n ? ` · ${formatTimestamp(syncStatus.finishedAt)}`\n : ''}\n {syncStatus.trigger ? ` · ${syncStatus.trigger}` : ''}\n </Typography>\n {syncStatus.error && (\n <Typography color=\"error\">\n Last failure: {syncStatus.error}\n </Typography>\n )}\n {catalogSyncPermission.allowed && (\n <Button\n variant=\"outlined\"\n color=\"primary\"\n disabled={requestingSync}\n onClick={requestCatalogSync}\n style={{ marginTop: 12 }}\n >\n {requestingSync ? 'Requesting…' : 'Reconcile now'}\n </Button>\n )}\n {syncRequestMessage && (\n <Typography color=\"textSecondary\">\n {syncRequestMessage}\n </Typography>\n )}\n {syncRequestError && (\n <Typography color=\"error\">{syncRequestError}</Typography>\n )}\n </Grid>\n </Grid>\n </InfoCard>\n <ContentHeader title=\"Find a service\" />\n <InfoCard subheader=\"Anyshift-annotated Components and Resources in the Backstage catalog\">\n <TextField\n fullWidth\n variant=\"outlined\"\n label=\"Search services and resources\"\n value={catalogQuery}\n onChange={event => setCatalogQuery(event.target.value)}\n />\n {catalogError && (\n <Typography color=\"error\">{catalogError}</Typography>\n )}\n {!catalogError && matchingEntities.length === 0 ? (\n <Typography color=\"textSecondary\" style={{ marginTop: 12 }}>\n No matching Anyshift catalog entities.\n </Typography>\n ) : (\n <ul>\n {matchingEntities.map(entity => (\n <li\n key={`${entity.kind}:${\n entity.metadata.namespace ?? 'default'\n }/${entity.metadata.name}`}\n >\n <EntityRefLink entityRef={entity} />\n {entity.metadata.description\n ? ` - ${entity.metadata.description}`\n : null}\n </li>\n ))}\n </ul>\n )}\n </InfoCard>\n <ContentHeader title=\"Estate insights\" />\n {insightError && <Typography color=\"error\">{insightError}</Typography>}\n {!insights && !insightError ? <Progress /> : null}\n <Grid container spacing={3}>\n {insights && <EstateInsightsGrid insights={insights} />}\n {queryEnabled && queryPermission.allowed && (\n <Grid item xs={12}>\n <InfoCard\n title=\"Advanced query\"\n subheader=\"Deterministic Graph API SELECT queries only\"\n >\n <TextField\n fullWidth\n multiline\n minRows={2}\n variant=\"outlined\"\n value={sql}\n onChange={event => setSql(event.target.value)}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={runQuery}\n style={{ marginTop: 12 }}\n >\n Run query\n </Button>\n {queryError && (\n <Typography color=\"error\">{queryError}</Typography>\n )}\n {queryResult && (\n <pre style={{ whiteSpace: 'pre-wrap', fontSize: 12 }}>\n {JSON.stringify(queryResult, null, 2)}\n </pre>\n )}\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n\nfunction formatTimestamp(value: string): string {\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? value : new Date(parsed).toLocaleString();\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,105 +1,9 @@
1
- import * as _backstage_catalog_model from '@backstage/catalog-model';
2
- import * as react from 'react';
3
- import * as _backstage_filter_predicates from '@backstage/filter-predicates';
4
1
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
5
- import { AnyshiftCapabilities, ServiceBrief, BriefSectionName, BriefSectionResponse, EstateInsights, DatadogSignals, CatalogSummary, QueryResult } from '@anyshift/backstage-plugin-anyshift-common';
6
- export { ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, BriefSection, CatalogSummary, EstateInsights, GITHUB_PROJECT_SLUG_ANNOTATION, QueryResult, ServiceBrief, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
2
+ import { FrontendPlugin } from '@backstage/frontend-plugin-api';
3
+ import { AnyshiftCapabilities, ServiceBrief, BriefSectionName, BriefSectionResponse, EstateInsights, DatadogSignals, CatalogSummary, QueryResult, CatalogSyncReport } from '@anyshift/backstage-plugin-anyshift-common';
4
+ export { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, BriefSection, CatalogSummary, EstateInsights, GITHUB_PROJECT_SLUG_ANNOTATION, QueryResult, ServiceBrief, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
7
5
 
8
- declare const anyshiftPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
9
- root: _backstage_frontend_plugin_api.RouteRef<undefined>;
10
- }, {}, {
11
- "api:anyshift": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
12
- kind: "api";
13
- name: undefined;
14
- config: {};
15
- configInput: {};
16
- output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.AnyApiFactory, "core.api.factory", {}>;
17
- inputs: {};
18
- params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
19
- }>;
20
- "entity-content:anyshift/entity-content": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
21
- kind: "entity-content";
22
- name: "entity-content";
23
- config: {
24
- path: string | undefined;
25
- title: string | undefined;
26
- filter: _backstage_filter_predicates.FilterPredicate | undefined;
27
- group: string | false | undefined;
28
- icon: string | undefined;
29
- };
30
- configInput: {
31
- path?: string | undefined;
32
- title?: string | undefined;
33
- filter?: _backstage_filter_predicates.FilterPredicate | undefined;
34
- group?: string | false | undefined;
35
- icon?: string | undefined;
36
- };
37
- output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
38
- optional: true;
39
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
40
- optional: true;
41
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
42
- optional: true;
43
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-group", {
44
- optional: true;
45
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<string | react.ReactElement<any, string | react.JSXElementConstructor<any>>, "catalog.entity-content-icon", {
46
- optional: true;
47
- }>;
48
- inputs: {};
49
- params: {
50
- defaultPath?: [Error: `Use the 'path' param instead`];
51
- path: string;
52
- defaultTitle?: [Error: `Use the 'title' param instead`];
53
- title: string;
54
- defaultGroup?: [Error: `Use the 'group' param instead`];
55
- group?: ("development" | "deployment" | "overview" | "documentation" | "operation" | "observability") | (string & {});
56
- icon?: string | react.ReactElement;
57
- loader: () => Promise<JSX.Element>;
58
- routeRef?: _backstage_frontend_plugin_api.RouteRef;
59
- filter?: string | _backstage_filter_predicates.FilterPredicate | ((entity: _backstage_catalog_model.Entity) => boolean);
60
- };
61
- }>;
62
- "page:anyshift": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
63
- kind: "page";
64
- name: undefined;
65
- config: {
66
- path: string | undefined;
67
- title: string | undefined;
68
- };
69
- configInput: {
70
- path?: string | undefined;
71
- title?: string | undefined;
72
- };
73
- output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
74
- optional: true;
75
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
76
- optional: true;
77
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
78
- optional: true;
79
- }>;
80
- inputs: {
81
- pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
82
- optional: true;
83
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
84
- optional: true;
85
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
86
- optional: true;
87
- }>, {
88
- singleton: false;
89
- optional: false;
90
- internal: false;
91
- }>;
92
- };
93
- params: {
94
- path: string;
95
- title?: string;
96
- icon?: _backstage_frontend_plugin_api.IconElement;
97
- loader?: () => Promise<react.JSX.Element>;
98
- routeRef?: _backstage_frontend_plugin_api.RouteRef;
99
- noHeader?: boolean;
100
- };
101
- }>;
102
- }>;
6
+ declare const anyshiftPlugin: FrontendPlugin;
103
7
 
104
8
  interface AnyshiftApi {
105
9
  capabilities(): Promise<AnyshiftCapabilities>;
@@ -114,11 +18,17 @@ interface AnyshiftApi {
114
18
  offset?: number;
115
19
  }): Promise<CatalogSummary>;
116
20
  query(sql: string): Promise<QueryResult>;
21
+ reconcileCatalog(): Promise<CatalogSyncAccepted>;
117
22
  }
118
23
  interface BriefRequestOptions {
119
24
  namespace?: string;
120
25
  refresh?: boolean;
121
26
  }
27
+ interface CatalogSyncAccepted {
28
+ accepted: true;
29
+ acceptedAt: string;
30
+ previous?: CatalogSyncReport;
31
+ }
122
32
  declare const anyshiftApiRef: _backstage_frontend_plugin_api.ApiRef<AnyshiftApi> & {
123
33
  readonly $$type: "@backstage/ApiRef";
124
34
  };
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { anyshiftPlugin, anyshiftPlugin as default } from './plugin.esm.js';
2
2
  export { anyshiftApiRef } from './api/AnyshiftApi.esm.js';
3
- export { ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
3
+ export { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
4
4
  //# sourceMappingURL=index.esm.js.map
@@ -1,15 +1,15 @@
1
1
  var name = "@anyshift/backstage-plugin-anyshift";
2
- var version = "0.1.0-beta.2";
2
+ var version = "0.1.0-beta.21";
3
3
  var description = "Read-only Anyshift infrastructure context for Backstage";
4
4
  var main = "src/index.ts";
5
5
  var types = "src/index.ts";
6
6
  var license = "Apache-2.0";
7
7
  var repository = {
8
8
  type: "git",
9
- url: "https://github.com/anyshift-io/graph-api",
10
- directory: "integrations/backstage/plugins/anyshift"
9
+ url: "https://github.com/anyshift-io/backstage-plugins",
10
+ directory: "plugins/anyshift"
11
11
  };
12
- var homepage = "https://github.com/anyshift-io/graph-api/tree/main/integrations/backstage#readme";
12
+ var homepage = "https://github.com/anyshift-io/backstage-plugins#readme";
13
13
  var keywords = [
14
14
  "backstage",
15
15
  "anyshift",
@@ -1,11 +1,12 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import DeviceHubIcon from '@material-ui/icons/DeviceHub';
3
+ import CloudQueueIcon from '@material-ui/icons/CloudQueue';
3
4
  import { ApiBlueprint, fetchApiRef, discoveryApiRef, PageBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
4
5
  import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
5
- import { anyshiftViewPermission, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
6
+ import { anyshiftViewPermission, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, isAnyshiftResourceKind, ANYSHIFT_MANAGED_BY_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
6
7
  import { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary.esm.js';
7
8
  import { AnyshiftClient, anyshiftApiRef } from './api/AnyshiftApi.esm.js';
8
- import { rootRouteRef } from './routes.esm.js';
9
+ import { rootRouteRef, cloudRouteRef } from './routes.esm.js';
9
10
 
10
11
  const anyshiftApi = ApiBlueprint.make({
11
12
  params: (defineParams) => defineParams({
@@ -24,26 +25,64 @@ const anyshiftPage = PageBlueprint.make({
24
25
  loader: () => import('./components/explorer/AnyshiftExplorerPage.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.AnyshiftExplorerPage, {}) }))
25
26
  }
26
27
  });
28
+ const cloudPage = PageBlueprint.make({
29
+ name: "cloud",
30
+ params: {
31
+ path: "/cloud",
32
+ title: "Cloud",
33
+ icon: /* @__PURE__ */ jsx(CloudQueueIcon, { fontSize: "inherit" }),
34
+ routeRef: cloudRouteRef,
35
+ noHeader: true,
36
+ loader: () => import('./components/cloud/CloudPage.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.CloudPage, {}) }))
37
+ }
38
+ });
27
39
  const anyshiftEntityContent = EntityContentBlueprint.make({
28
40
  name: "entity-content",
29
41
  params: {
30
42
  path: "/anyshift",
31
43
  title: "Anyshift",
32
44
  group: "observability",
33
- filter: (entity) => Boolean(
34
- entity.metadata.annotations?.[ANYSHIFT_TARGET_ANNOTATION] || entity.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION]
35
- ),
45
+ filter: isAnyshiftEntityContentAvailable,
36
46
  loader: () => import('./components/entity/AnyshiftEntityContent.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.AnyshiftEntityContent, {}) }))
37
47
  }
38
48
  });
49
+ const cloudEntityContent = EntityContentBlueprint.make({
50
+ name: "cloud-resource-content",
51
+ params: {
52
+ path: "/cloud",
53
+ title: "Cloud",
54
+ group: "observability",
55
+ filter: isCloudResourceContentAvailable,
56
+ loader: () => import('./components/cloud/CloudResourceContent.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.CloudResourceContent, {}) }))
57
+ }
58
+ });
59
+ function isAnyshiftEntityContentAvailable(entity) {
60
+ const annotations = entity.metadata.annotations ?? {};
61
+ const hasTarget = Boolean(
62
+ annotations[ANYSHIFT_TARGET_ANNOTATION] || annotations[GITHUB_PROJECT_SLUG_ANNOTATION]
63
+ );
64
+ if (!hasTarget) return false;
65
+ const kind = annotations[ANYSHIFT_KIND_ANNOTATION];
66
+ if (kind) return isAnyshiftResourceKind(kind);
67
+ return annotations[ANYSHIFT_MANAGED_BY_ANNOTATION] !== "catalog-provider";
68
+ }
69
+ function isCloudResourceContentAvailable(entity) {
70
+ return entity.kind.toLocaleLowerCase("en-US") === "resource" && Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]);
71
+ }
39
72
  const anyshiftPlugin = createFrontendPlugin({
40
73
  pluginId: "anyshift",
41
74
  info: {
42
75
  packageJson: () => import('./package.json.esm.js')
43
76
  },
44
- routes: { root: rootRouteRef },
45
- extensions: [anyshiftApi, anyshiftPage, anyshiftEntityContent]
77
+ routes: { root: rootRouteRef, cloud: cloudRouteRef },
78
+ extensions: [
79
+ anyshiftApi,
80
+ anyshiftPage,
81
+ cloudPage,
82
+ anyshiftEntityContent,
83
+ cloudEntityContent
84
+ ]
46
85
  });
47
86
 
48
- export { anyshiftPlugin };
87
+ export { anyshiftPlugin, isAnyshiftEntityContentAvailable, isCloudResourceContentAvailable };
49
88
  //# sourceMappingURL=plugin.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import DeviceHubIcon from '@material-ui/icons/DeviceHub';\nimport {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport {\n ANYSHIFT_TARGET_ANNOTATION,\n GITHUB_PROJECT_SLUG_ANNOTATION,\n anyshiftViewPermission,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary';\nimport { anyshiftApiRef, AnyshiftClient } from './api/AnyshiftApi';\nimport { rootRouteRef } from './routes';\n\nconst anyshiftApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: anyshiftApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new AnyshiftClient({ discoveryApi, fetchApi }),\n }),\n});\n\nconst anyshiftPage = PageBlueprint.make({\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n icon: <DeviceHubIcon fontSize=\"inherit\" />,\n routeRef: rootRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/explorer/AnyshiftExplorerPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftExplorerPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst anyshiftEntityContent = EntityContentBlueprint.make({\n name: 'entity-content',\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n group: 'observability',\n filter: entity =>\n Boolean(\n entity.metadata.annotations?.[ANYSHIFT_TARGET_ANNOTATION] ||\n entity.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION],\n ),\n loader: () =>\n import('./components/entity/AnyshiftEntityContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftEntityContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nexport const anyshiftPlugin = createFrontendPlugin({\n pluginId: 'anyshift',\n info: {\n packageJson: () => import('../package.json'),\n },\n routes: { root: rootRouteRef },\n extensions: [anyshiftApi, anyshiftPage, anyshiftEntityContent],\n});\n\nexport default anyshiftPlugin;\n"],"names":[],"mappings":";;;;;;;;;AAkBA,MAAM,WAAA,GAAc,aAAa,IAAA,CAAK;AAAA,EACpC,MAAA,EAAQ,kBACN,YAAA,CAAa;AAAA,IACX,GAAA,EAAK,cAAA;AAAA,IACL,IAAA,EAAM,EAAE,YAAA,EAAc,eAAA,EAAiB,UAAU,WAAA,EAAY;AAAA,IAC7D,OAAA,EAAS,CAAC,EAAE,YAAA,EAAc,QAAA,EAAS,KACjC,IAAI,cAAA,CAAe,EAAE,YAAA,EAAc,QAAA,EAAU;AAAA,GAChD;AACL,CAAC,CAAA;AAED,MAAM,YAAA,GAAe,cAAc,IAAA,CAAK;AAAA,EACtC,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,WAAA;AAAA,IACN,KAAA,EAAO,UAAA;AAAA,IACP,IAAA,kBAAM,GAAA,CAAC,aAAA,EAAA,EAAc,QAAA,EAAS,SAAA,EAAU,CAAA;AAAA,IACxC,QAAA,EAAU,YAAA;AAAA,IACV,QAAA,EAAU,IAAA;AAAA,IACV,QAAQ,MACN,OAAO,mDAA4C,CAAA,CAAE,KAAK,CAAA,MAAA,qBACxD,GAAA,CAAC,0BAAA,EAAA,EAA2B,UAAA,EAAY,wBACtC,QAAA,kBAAA,GAAA,CAAC,MAAA,CAAO,oBAAA,EAAP,EAA4B,GAC/B,CACD;AAAA;AAEP,CAAC,CAAA;AAED,MAAM,qBAAA,GAAwB,uBAAuB,IAAA,CAAK;AAAA,EACxD,IAAA,EAAM,gBAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,WAAA;AAAA,IACN,KAAA,EAAO,UAAA;AAAA,IACP,KAAA,EAAO,eAAA;AAAA,IACP,QAAQ,CAAA,MAAA,KACN,OAAA;AAAA,MACE,MAAA,CAAO,SAAS,WAAA,GAAc,0BAA0B,KACtD,MAAA,CAAO,QAAA,CAAS,cAAc,8BAA8B;AAAA,KAChE;AAAA,IACF,QAAQ,MACN,OAAO,kDAA2C,CAAA,CAAE,KAAK,CAAA,MAAA,qBACvD,GAAA,CAAC,0BAAA,EAAA,EAA2B,UAAA,EAAY,wBACtC,QAAA,kBAAA,GAAA,CAAC,MAAA,CAAO,qBAAA,EAAP,EAA6B,GAChC,CACD;AAAA;AAEP,CAAC,CAAA;AAEM,MAAM,iBAAiB,oBAAA,CAAqB;AAAA,EACjD,QAAA,EAAU,UAAA;AAAA,EACV,IAAA,EAAM;AAAA,IACJ,WAAA,EAAa,MAAM,OAAO,uBAAiB;AAAA,GAC7C;AAAA,EACA,MAAA,EAAQ,EAAE,IAAA,EAAM,YAAA,EAAa;AAAA,EAC7B,UAAA,EAAY,CAAC,WAAA,EAAa,YAAA,EAAc,qBAAqB;AAC/D,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import DeviceHubIcon from '@material-ui/icons/DeviceHub';\nimport CloudQueueIcon from '@material-ui/icons/CloudQueue';\nimport {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport type { FrontendPlugin } from '@backstage/frontend-plugin-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_KIND_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_MANAGED_BY_ANNOTATION,\n ANYSHIFT_TARGET_ANNOTATION,\n GITHUB_PROJECT_SLUG_ANNOTATION,\n anyshiftViewPermission,\n isAnyshiftResourceKind,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary';\nimport { anyshiftApiRef, AnyshiftClient } from './api/AnyshiftApi';\nimport { cloudRouteRef, rootRouteRef } from './routes';\n\nconst anyshiftApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: anyshiftApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new AnyshiftClient({ discoveryApi, fetchApi }),\n }),\n});\n\nconst anyshiftPage = PageBlueprint.make({\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n icon: <DeviceHubIcon fontSize=\"inherit\" />,\n routeRef: rootRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/explorer/AnyshiftExplorerPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftExplorerPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst cloudPage = PageBlueprint.make({\n name: 'cloud',\n params: {\n path: '/cloud',\n title: 'Cloud',\n icon: <CloudQueueIcon fontSize=\"inherit\" />,\n routeRef: cloudRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/cloud/CloudPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.CloudPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst anyshiftEntityContent = EntityContentBlueprint.make({\n name: 'entity-content',\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n group: 'observability',\n filter: isAnyshiftEntityContentAvailable,\n loader: () =>\n import('./components/entity/AnyshiftEntityContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftEntityContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst cloudEntityContent = EntityContentBlueprint.make({\n name: 'cloud-resource-content',\n params: {\n path: '/cloud',\n title: 'Cloud',\n group: 'observability',\n filter: isCloudResourceContentAvailable,\n loader: () =>\n import('./components/cloud/CloudResourceContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.CloudResourceContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nexport function isAnyshiftEntityContentAvailable(entity: Entity): boolean {\n const annotations = entity.metadata.annotations ?? {};\n const hasTarget = Boolean(\n annotations[ANYSHIFT_TARGET_ANNOTATION] ||\n annotations[GITHUB_PROJECT_SLUG_ANNOTATION],\n );\n if (!hasTarget) return false;\n\n const kind = annotations[ANYSHIFT_KIND_ANNOTATION];\n if (kind) return isAnyshiftResourceKind(kind);\n\n return annotations[ANYSHIFT_MANAGED_BY_ANNOTATION] !== 'catalog-provider';\n}\n\nexport function isCloudResourceContentAvailable(entity: Entity): boolean {\n return (\n entity.kind.toLocaleLowerCase('en-US') === 'resource' &&\n Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION])\n );\n}\n\nexport const anyshiftPlugin: FrontendPlugin = createFrontendPlugin({\n pluginId: 'anyshift',\n info: {\n packageJson: () => import('../package.json'),\n },\n routes: { root: rootRouteRef, cloud: cloudRouteRef },\n extensions: [\n anyshiftApi,\n anyshiftPage,\n cloudPage,\n anyshiftEntityContent,\n cloudEntityContent,\n ],\n});\n\nexport default anyshiftPlugin;\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { createRouteRef } from '@backstage/frontend-plugin-api';
2
2
 
3
3
  const rootRouteRef = createRouteRef();
4
+ const cloudRouteRef = createRouteRef();
4
5
 
5
- export { rootRouteRef };
6
+ export { cloudRouteRef, rootRouteRef };
6
7
  //# sourceMappingURL=routes.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/frontend-plugin-api';\n\nexport const rootRouteRef = createRouteRef();\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAA;;;;"}
1
+ {"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/frontend-plugin-api';\n\nexport const rootRouteRef = createRouteRef();\nexport const cloudRouteRef = createRouteRef();\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAA;AACrB,MAAM,gBAAgB,cAAA;;;;"}
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@anyshift/backstage-plugin-anyshift",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.21",
4
4
  "description": "Read-only Anyshift infrastructure context for Backstage",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/anyshift-io/graph-api",
11
- "directory": "integrations/backstage/plugins/anyshift"
10
+ "url": "https://github.com/anyshift-io/backstage-plugins",
11
+ "directory": "plugins/anyshift"
12
12
  },
13
- "homepage": "https://github.com/anyshift-io/graph-api/tree/main/integrations/backstage#readme",
13
+ "homepage": "https://github.com/anyshift-io/backstage-plugins#readme",
14
14
  "keywords": [
15
15
  "backstage",
16
16
  "anyshift",
@@ -45,7 +45,7 @@
45
45
  "clean": "backstage-cli package clean"
46
46
  },
47
47
  "dependencies": {
48
- "@anyshift/backstage-plugin-anyshift-common": "^0.1.0-beta.2",
48
+ "@anyshift/backstage-plugin-anyshift-common": "workspace:^",
49
49
  "@backstage/catalog-model": "^1.9.0",
50
50
  "@backstage/core-components": "^0.18.11",
51
51
  "@backstage/frontend-plugin-api": "^0.17.2",
@@ -81,4 +81,4 @@
81
81
  }
82
82
  },
83
83
  "module": "./dist/index.esm.js"
84
- }
84
+ }
package/CHANGELOG.md DELETED
@@ -1,11 +0,0 @@
1
- # Changelog
2
-
3
- ## 0.1.0-beta.2
4
-
5
- - Coordinate the frontend package with the trusted catalog synchronization beta.
6
- - Show configured, excluded, active, unclassified, and managed catalog coverage.
7
-
8
- ## 0.1.0-beta.1
9
-
10
- - Add discoverable dashboard, navigation, API, and entity-content extensions.
11
- - Add permission-aware rendering for estate data and deterministic queries.