diff --git a/.changeset/afraid-flies-try.md b/.changeset/afraid-flies-try.md new file mode 100644 index 0000000000..15d2e3942c --- /dev/null +++ b/.changeset/afraid-flies-try.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +Move cost-insights data specific API types (non react) into an @backstage/plugin-cost-insights-common +isomorphic package. This allows these types to be shared in any backend packages or other cost-insight +modules. diff --git a/.changeset/light-hornets-eat.md b/.changeset/light-hornets-eat.md new file mode 100644 index 0000000000..795e51fb67 --- /dev/null +++ b/.changeset/light-hornets-eat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights-common': minor +--- + +Introduces a new isomorphic @backstage/plugin-cost-insight-common package to contain shared types across all other cost insights packages and modules. diff --git a/.changeset/search-boats-double.md b/.changeset/search-boats-double.md new file mode 100644 index 0000000000..92fd372cb8 --- /dev/null +++ b/.changeset/search-boats-double.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Fix search pagination to reset page cursor also when a term is cleared. diff --git a/plugins/cost-insights-common/.eslintrc.js b/plugins/cost-insights-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/cost-insights-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/cost-insights-common/CHANGELOG.md b/plugins/cost-insights-common/CHANGELOG.md new file mode 100644 index 0000000000..d958aaa41b --- /dev/null +++ b/plugins/cost-insights-common/CHANGELOG.md @@ -0,0 +1 @@ +# @backstage/plugin-cost-insights-common diff --git a/plugins/cost-insights-common/README.md b/plugins/cost-insights-common/README.md new file mode 100644 index 0000000000..5140701987 --- /dev/null +++ b/plugins/cost-insights-common/README.md @@ -0,0 +1,8 @@ +# Cost Insights Common + +Shared isomorphic code for the cost-insights plugin. + +## Links + +- [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/cost-insights) +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/cost-insights-common/api-report.md b/plugins/cost-insights-common/api-report.md new file mode 100644 index 0000000000..a5f7045854 --- /dev/null +++ b/plugins/cost-insights-common/api-report.md @@ -0,0 +1,92 @@ +## API Report File for "@backstage/plugin-cost-insights-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// @public (undocumented) +export interface ChangeStatistic { + amount: number; + ratio?: number; +} + +// @public (undocumented) +export interface Cost { + // (undocumented) + aggregation: DateAggregation[]; + // (undocumented) + change?: ChangeStatistic; + // (undocumented) + groupedCosts?: Record; + // (undocumented) + id: string; + // (undocumented) + trendline?: Trendline; +} + +// @public (undocumented) +export type DateAggregation = { + date: string; + amount: number; +}; + +// @public +export interface Entity { + // (undocumented) + aggregation: [number, number]; + // (undocumented) + change: ChangeStatistic; + // (undocumented) + entities: Record; + // (undocumented) + id: Maybe; +} + +// @public (undocumented) +export type Group = { + id: string; +}; + +// @public (undocumented) +export type Maybe = T | null; + +// @public (undocumented) +export type Metric = { + kind: string; + name: string; + default: boolean; +}; + +// @public (undocumented) +export interface MetricData { + // (undocumented) + aggregation: DateAggregation[]; + // (undocumented) + change: ChangeStatistic; + // (undocumented) + format: 'number' | 'currency'; + // (undocumented) + id: string; +} + +// @public (undocumented) +export interface Product { + // (undocumented) + kind: string; + // (undocumented) + name: string; +} + +// @public (undocumented) +export interface Project { + // (undocumented) + id: string; + // (undocumented) + name?: string; +} + +// @public (undocumented) +export type Trendline = { + slope: number; + intercept: number; +}; +``` diff --git a/plugins/cost-insights-common/package.json b/plugins/cost-insights-common/package.json new file mode 100644 index 0000000000..c15f063fd2 --- /dev/null +++ b/plugins/cost-insights-common/package.json @@ -0,0 +1,42 @@ +{ + "name": "@backstage/plugin-cost-insights-common", + "description": "Common functionalities for the cost-insights plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/cost-insights-common" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": {}, + "devDependencies": { + "@backstage/cli": "^0.18.0-next.1" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/cost-insights-common/src/index.ts b/plugins/cost-insights-common/src/index.ts new file mode 100644 index 0000000000..c3c76ccf17 --- /dev/null +++ b/plugins/cost-insights-common/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Provides shared objects useful for interacting with the cost-insights plugin. + * + * @packageDocumentation + */ + +export * from './types'; diff --git a/plugins/cost-insights-common/src/setupTests.ts b/plugins/cost-insights-common/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/cost-insights-common/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/plugins/cost-insights-common/src/types/ChangeStatistic.ts b/plugins/cost-insights-common/src/types/ChangeStatistic.ts new file mode 100644 index 0000000000..8d64fb47f9 --- /dev/null +++ b/plugins/cost-insights-common/src/types/ChangeStatistic.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @public + */ +export interface ChangeStatistic { + /** + * The ratio of change from one duration to another, expressed as: (newSum - oldSum) / oldSum + * If a ratio cannot be calculated - such as when a new or old sum is zero, + * the ratio can be omitted and where applicable, ∞ or -∞ will display based on amount. + */ + ratio?: number; + + /** + * The actual USD change between time periods (can be negative if costs decreased) + */ + amount: number; +} diff --git a/plugins/cost-insights/src/types/Cost.ts b/plugins/cost-insights-common/src/types/Cost.ts similarity index 97% rename from plugins/cost-insights/src/types/Cost.ts rename to plugins/cost-insights-common/src/types/Cost.ts index 2ae4755bc7..90ccbe17c0 100644 --- a/plugins/cost-insights/src/types/Cost.ts +++ b/plugins/cost-insights-common/src/types/Cost.ts @@ -18,6 +18,9 @@ import { DateAggregation } from './DateAggregation'; import { ChangeStatistic } from './ChangeStatistic'; import { Trendline } from './Trendline'; +/** + * @public + */ export interface Cost { id: string; aggregation: DateAggregation[]; diff --git a/plugins/cost-insights/src/types/DateAggregation.ts b/plugins/cost-insights-common/src/types/DateAggregation.ts similarity index 85% rename from plugins/cost-insights/src/types/DateAggregation.ts rename to plugins/cost-insights-common/src/types/DateAggregation.ts index 72a2514006..edc867a7bf 100644 --- a/plugins/cost-insights/src/types/DateAggregation.ts +++ b/plugins/cost-insights-common/src/types/DateAggregation.ts @@ -14,7 +14,15 @@ * limitations under the License. */ +/** + * @public + */ export type DateAggregation = { - date: string; // YYYY-MM-DD + /** + * The date aggregation as string. + * @example YYYY-MM-DD + */ + date: string; + amount: number; }; diff --git a/plugins/cost-insights-common/src/types/Entity.ts b/plugins/cost-insights-common/src/types/Entity.ts new file mode 100644 index 0000000000..9a6d6746aa --- /dev/null +++ b/plugins/cost-insights-common/src/types/Entity.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ChangeStatistic } from './ChangeStatistic'; +import { Maybe } from './Maybe'; + +/** + * + * An entity is a tree-like structure that represents any unique + * product or service that generates cost over a fixed period of time. + * An entity could be atomic or composite. An atomic entity is indivisible + * and cannot be broken into sub-entities. + * + * A composite entity is divided into sub-entities that account for portions + * of the total cost **over the same time period**. The root entity is + * expected to only have _one_ Record consisting of the sub-entities to display + * in the product panel (keyed by the entity type, such as "service" for + * compute entities). + * + * The root sub-entities may have multiple breakdowns - for example, a + * breakdown of an entity cost by SKU vs deployment environment. The sum + * aggregated cost of each keyed breakdown should equal the sub-entity's cost. + * + * Entities with null ids are considered "unlabeled" - costs without attribution. + * If an entity is a composite, it may only have one (1) null child but may have any number of + * null grandchildren. + * + * @public + * + * @example + * Here's an example composite entity: + * ``` + * const compositeEntity = { + * id: 'product', + * aggregation: [0, 200], + * change: { + * ratio: 2000, + * amount: 200 + * }, + * entities: { + * service: [ + * { + * id: 'service-a', + * aggregation: [0, 100], + * change: { + * ratio: 100, + * amount: 100 + * }, + * entities: {} + * }, + * { + * id: 'service-b', + * aggregation: [0, 100], + * change: { + * ratio: 100, + * amount: 100 + * }, + * entities: { + * SKU: [ + * { + * id: 'service-b-sku-a', + * aggregation: [0, 25], + * change: { + * ratio: 25, + * amount: 25 + * }, + * entities: {} + * }, + * { + * id: null, // Unlabeled cost for service-b + * aggregation: [0, 75], + * change: { + * ratio: 75, + * amount: 75 + * }, + * entities: {} + * }, + * ], + * deployment: [ + * { + * id: 'service-b-env-a', + * aggregation: [0, 50], + * change: { + * ratio: 50, + * amount: 50 + * }, + * entities: {} + * }, + * { + * id: 'service-b-env-b', + * aggregation: [0, 50], + * change: { + * ratio: 50, + * amount: 50 + * }, + * entities: {} + * }, + * ] + * } + * }, + * ] + * } + * } + * ``` + */ +export interface Entity { + id: Maybe; + aggregation: [number, number]; + entities: Record; + change: ChangeStatistic; +} diff --git a/plugins/cost-insights/src/types/Group.ts b/plugins/cost-insights-common/src/types/Group.ts similarity index 97% rename from plugins/cost-insights/src/types/Group.ts rename to plugins/cost-insights-common/src/types/Group.ts index 8e0c768924..b34cac083f 100644 --- a/plugins/cost-insights/src/types/Group.ts +++ b/plugins/cost-insights-common/src/types/Group.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export type Group = { id: string; }; diff --git a/plugins/cost-insights/src/types/Maybe.ts b/plugins/cost-insights-common/src/types/Maybe.ts similarity index 97% rename from plugins/cost-insights/src/types/Maybe.ts rename to plugins/cost-insights-common/src/types/Maybe.ts index 461c31f6a5..824c64c334 100644 --- a/plugins/cost-insights/src/types/Maybe.ts +++ b/plugins/cost-insights-common/src/types/Maybe.ts @@ -14,4 +14,7 @@ * limitations under the License. */ +/** + * @public + */ export type Maybe = T | null; diff --git a/plugins/cost-insights/src/types/Metric.ts b/plugins/cost-insights-common/src/types/Metric.ts similarity index 97% rename from plugins/cost-insights/src/types/Metric.ts rename to plugins/cost-insights-common/src/types/Metric.ts index 107513b80d..901d40f106 100644 --- a/plugins/cost-insights/src/types/Metric.ts +++ b/plugins/cost-insights-common/src/types/Metric.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export type Metric = { kind: string; name: string; diff --git a/plugins/cost-insights/src/types/MetricData.ts b/plugins/cost-insights-common/src/types/MetricData.ts similarity index 97% rename from plugins/cost-insights/src/types/MetricData.ts rename to plugins/cost-insights-common/src/types/MetricData.ts index 225da185f8..56742e76e9 100644 --- a/plugins/cost-insights/src/types/MetricData.ts +++ b/plugins/cost-insights-common/src/types/MetricData.ts @@ -17,6 +17,9 @@ import { DateAggregation } from './DateAggregation'; import { ChangeStatistic } from './ChangeStatistic'; +/** + * @public + */ export interface MetricData { id: string; format: 'number' | 'currency'; diff --git a/plugins/cost-insights/src/types/Product.ts b/plugins/cost-insights-common/src/types/Product.ts similarity index 97% rename from plugins/cost-insights/src/types/Product.ts rename to plugins/cost-insights-common/src/types/Product.ts index 9920841223..7ed49c18da 100644 --- a/plugins/cost-insights/src/types/Product.ts +++ b/plugins/cost-insights-common/src/types/Product.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export interface Product { kind: string; name: string; diff --git a/plugins/cost-insights/src/types/Project.ts b/plugins/cost-insights-common/src/types/Project.ts similarity index 97% rename from plugins/cost-insights/src/types/Project.ts rename to plugins/cost-insights-common/src/types/Project.ts index c12a3f1fa2..6cad92ed46 100644 --- a/plugins/cost-insights/src/types/Project.ts +++ b/plugins/cost-insights-common/src/types/Project.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export interface Project { id: string; name?: string; diff --git a/plugins/cost-insights/src/types/Trendline.ts b/plugins/cost-insights-common/src/types/Trendline.ts similarity index 97% rename from plugins/cost-insights/src/types/Trendline.ts rename to plugins/cost-insights-common/src/types/Trendline.ts index 64405678f1..3a7e372880 100644 --- a/plugins/cost-insights/src/types/Trendline.ts +++ b/plugins/cost-insights-common/src/types/Trendline.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** + * @public + */ export type Trendline = { slope: number; intercept: number; diff --git a/plugins/cost-insights-common/src/types/index.ts b/plugins/cost-insights-common/src/types/index.ts new file mode 100644 index 0000000000..86e7af33aa --- /dev/null +++ b/plugins/cost-insights-common/src/types/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './ChangeStatistic'; +export * from './Cost'; +export * from './DateAggregation'; +export * from './Entity'; +export * from './Group'; +export * from './Maybe'; +export * from './MetricData'; +export * from './Metric'; +export * from './Product'; +export * from './Project'; +export * from './Trendline'; diff --git a/plugins/cost-insights/api-report.md b/plugins/cost-insights/api-report.md index 75e1315502..a646cddeab 100644 --- a/plugins/cost-insights/api-report.md +++ b/plugins/cost-insights/api-report.md @@ -9,9 +9,12 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePalette } from '@backstage/theme'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; +import { ChangeStatistic as ChangeStatistic_2 } from '@backstage/plugin-cost-insights-common'; +import * as common from '@backstage/plugin-cost-insights-common'; import { ContentRenderer } from 'recharts'; import { Dispatch } from 'react'; import { ForwardRefExoticComponent } from 'react'; +import { Maybe as Maybe_2 } from '@backstage/plugin-cost-insights-common'; import { PaletteOptions } from '@material-ui/core/styles/createPalette'; import { PropsWithChildren } from 'react'; import { ReactNode } from 'react'; @@ -32,9 +35,9 @@ export type Alert = { status?: AlertStatus; url?: string; buttonText?: string; - SnoozeForm?: Maybe; - AcceptForm?: Maybe; - DismissForm?: Maybe; + SnoozeForm?: Maybe_2; + AcceptForm?: Maybe_2; + DismissForm?: Maybe_2; onSnoozed?(options: AlertOptions): Promise; onAccepted?(options: AlertOptions): Promise; onDismissed?(options: AlertOptions): Promise; @@ -55,9 +58,9 @@ export interface AlertCost { // @public (undocumented) export interface AlertDismissFormData { // (undocumented) - feedback: Maybe; + feedback: Maybe_2; // (undocumented) - other: Maybe; + other: Maybe_2; // (undocumented) reason: AlertDismissReason; } @@ -269,15 +272,11 @@ export type BarChartTooltipProps = { actions?: ReactNode; }; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "ChangeStatistic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface ChangeStatistic { - // (undocumented) - amount: number; - // (undocumented) - ratio?: number; -} +// @public @deprecated (undocumented) +export type ChangeStatistic = common.ChangeStatistic; // Warning: (ae-missing-release-tag) "ChangeThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -289,8 +288,6 @@ export enum ChangeThreshold { upper = 0.05, } -// Warning: (ae-missing-release-tag) "ChartData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type ChartData = { date: number; @@ -299,21 +296,11 @@ export type ChartData = { [key: string]: number; }; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Cost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface Cost { - // (undocumented) - aggregation: DateAggregation[]; - // (undocumented) - change?: ChangeStatistic; - // (undocumented) - groupedCosts?: Record; - // (undocumented) - id: string; - // (undocumented) - trendline?: Trendline; -} +// @public @deprecated (undocumented) +export type Cost = common.Cost; // Warning: (ae-missing-release-tag) "CostGrowth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -423,8 +410,6 @@ export interface CostInsightsThemeOptions extends PaletteOptions { palette: CostInsightsPaletteOptions; } -// Warning: (ae-missing-release-tag) "Currency" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface Currency { // (undocumented) @@ -465,21 +450,17 @@ export enum DataKey { Previous = 'previous', } +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "DateAggregation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type DateAggregation = { - date: string; - amount: number; -}; +// @public @deprecated (undocumented) +export type DateAggregation = common.DateAggregation; // Warning: (ae-missing-release-tag) "DEFAULT_DATE_FORMAT" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd'; -// Warning: (ae-missing-release-tag) "Duration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export enum Duration { // (undocumented) @@ -497,19 +478,11 @@ export enum Duration { // @public (undocumented) export const EngineerThreshold = 0.5; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Entity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface Entity { - // (undocumented) - aggregation: [number, number]; - // (undocumented) - change: ChangeStatistic; - // (undocumented) - entities: Record; - // (undocumented) - id: Maybe; -} +// @public @deprecated (undocumented) +export type Entity = common.Entity; // Warning: (ae-missing-release-tag) "ExampleCostInsightsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -533,12 +506,11 @@ export class ExampleCostInsightsClient implements CostInsightsApi { getUserGroups(userId: string): Promise; } +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Group" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Group = { - id: string; -}; +// @public @deprecated (undocumented) +export type Group = common.Group; // Warning: (ae-missing-release-tag) "GrowthType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -602,33 +574,23 @@ export type LegendItemProps = { // @public (undocumented) export type Loading = Record; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Maybe" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Maybe = T | null; +// @public @deprecated (undocumented) +export type Maybe = common.Maybe; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Metric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Metric = { - kind: string; - name: string; - default: boolean; -}; +// @public @deprecated (undocumented) +export type Metric = common.Metric; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "MetricData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface MetricData { - // (undocumented) - aggregation: DateAggregation[]; - // (undocumented) - change: ChangeStatistic; - // (undocumented) - format: 'number' | 'currency'; - // (undocumented) - id: string; -} +// @public @deprecated (undocumented) +export type MetricData = common.MetricData; // Warning: (ae-forgotten-export) The symbol "MockConfigProviderProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "MockConfigProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -648,32 +610,24 @@ export const MockCurrencyProvider: ({ ...context }: MockCurrencyProviderProps) => JSX.Element; -// Warning: (ae-missing-release-tag) "PageFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface PageFilters { // (undocumented) duration: Duration; // (undocumented) - group: Maybe; + group: Maybe_2; // (undocumented) metric: string | null; // (undocumented) - project: Maybe; + project: Maybe_2; } +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Product" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface Product { - // (undocumented) - kind: string; - // (undocumented) - name: string; -} +// @public @deprecated (undocumented) +export type Product = common.Product; -// Warning: (ae-missing-release-tag) "ProductFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type ProductFilters = Array; @@ -687,8 +641,6 @@ export type ProductInsightsOptions = { project: Maybe; }; -// Warning: (ae-missing-release-tag) "ProductPeriod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface ProductPeriod { // (undocumented) @@ -697,15 +649,11 @@ export interface ProductPeriod { productType: string; } +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Project" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface Project { - // (undocumented) - id: string; - // (undocumented) - name?: string; -} +// @public @deprecated (undocumented) +export type Project = common.Project; // Warning: (ae-missing-release-tag) "ProjectGrowthAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -731,7 +679,7 @@ export interface ProjectGrowthData { // (undocumented) aggregation: [number, number]; // (undocumented) - change: ChangeStatistic; + change: ChangeStatistic_2; // (undocumented) periodEnd: string; // (undocumented) @@ -749,7 +697,7 @@ export interface ResourceData { // (undocumented) current: number; // (undocumented) - name: Maybe; + name: Maybe_2; // (undocumented) previous: number; } @@ -763,13 +711,11 @@ export type TooltipItem = { value: string; }; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // Warning: (ae-missing-release-tag) "Trendline" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Trendline = { - slope: number; - intercept: number; -}; +// @public @deprecated (undocumented) +export type Trendline = common.Trendline; // Warning: (ae-missing-release-tag) "UnlabeledDataflowAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/cost-insights/contrib/aws-cost-explorer-api.md b/plugins/cost-insights/contrib/aws-cost-explorer-api.md index caa7b72e10..1e3f4e9de6 100644 --- a/plugins/cost-insights/contrib/aws-cost-explorer-api.md +++ b/plugins/cost-insights/contrib/aws-cost-explorer-api.md @@ -77,7 +77,7 @@ We can use the data provided by the Cost Explorer API to implement CostInsightsA ### 1. [getGroupDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L93) -The `getGroupDailyCost` method is expected to return daily cost aggregations for a given group and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts). +The `getGroupDailyCost` method is expected to return daily cost aggregations for a given group and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Cost.ts). #### Total Daily cost @@ -208,13 +208,13 @@ Sample `groupedCosts` based on the response: ### 2. [getProjectDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111) -The `getProjectDailyCost` method is expected to return daily cost aggregations for a given billing entity and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts). +The `getProjectDailyCost` method is expected to return daily cost aggregations for a given billing entity and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Cost.ts). This should be similar to the `getGroupDailyCost` method implementation, but with an updated `LINKED_ACCOUNT` filter to get narrower cost data for a lower-level linked account. ### 3. [getProductInsights](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111) -The `getProductInsights` method is expected to return cost aggregations for a particular cloud product and interval time frame as an [`Entity`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Entity.ts). +The `getProductInsights` method is expected to return cost aggregations for a particular cloud product and interval time frame as an [`Entity`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Entity.ts). #### Cloud product cost by resource diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index e2555d961f..d7a04eddbc 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -38,6 +38,7 @@ "@backstage/config": "^1.0.1", "@backstage/core-components": "^0.9.6-next.1", "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-cost-insights-common": "^0.0.0", "@backstage/theme": "^0.2.16-next.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/cost-insights/src/types/Alert.ts b/plugins/cost-insights/src/types/Alert.ts index 2f220be0e6..483b9a654d 100644 --- a/plugins/cost-insights/src/types/Alert.ts +++ b/plugins/cost-insights/src/types/Alert.ts @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { ForwardRefExoticComponent, RefAttributes } from 'react'; -import { ChangeStatistic } from './ChangeStatistic'; +import { ChangeStatistic, Maybe } from '@backstage/plugin-cost-insights-common'; import { Duration } from './Duration'; -import { Maybe } from './Maybe'; /** * Generic alert type with required fields for display. The `element` field will be rendered in diff --git a/plugins/cost-insights/src/types/ChangeStatistic.ts b/plugins/cost-insights/src/types/ChangeStatistic.ts index 6e85335a67..8264b23c8d 100644 --- a/plugins/cost-insights/src/types/ChangeStatistic.ts +++ b/plugins/cost-insights/src/types/ChangeStatistic.ts @@ -14,15 +14,6 @@ * limitations under the License. */ -export interface ChangeStatistic { - // The ratio of change from one duration to another, expressed as: (newSum - oldSum) / oldSum - // If a ratio cannot be calculated - such as when a new or old sum is zero, - // the ratio can be omitted and where applicable, ∞ or -∞ will display based on amount. - ratio?: number; - // The actual USD change between time periods (can be negative if costs decreased) - amount: number; -} - export const EngineerThreshold = 0.5; export enum ChangeThreshold { diff --git a/plugins/cost-insights/src/types/ChartData.tsx b/plugins/cost-insights/src/types/ChartData.ts similarity index 97% rename from plugins/cost-insights/src/types/ChartData.tsx rename to plugins/cost-insights/src/types/ChartData.ts index 9de8af496d..f4ae204a22 100644 --- a/plugins/cost-insights/src/types/ChartData.tsx +++ b/plugins/cost-insights/src/types/ChartData.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export type ChartData = { date: number; trend: number; diff --git a/plugins/cost-insights/src/types/Currency.ts b/plugins/cost-insights/src/types/Currency.ts index 5cc5756dfc..4bd1490a69 100644 --- a/plugins/cost-insights/src/types/Currency.ts +++ b/plugins/cost-insights/src/types/Currency.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * @public + */ export interface Currency { kind: string | null; label: string; @@ -21,10 +24,3 @@ export interface Currency { prefix?: string; rate?: number; } - -export enum CurrencyType { - USD = 'USD', - CarbonOffsetTons = 'CARBON_OFFSET_TONS', - Beers = 'BEERS', - IceCream = 'PINTS_OF_ICE_CREAM', -} diff --git a/plugins/cost-insights/src/types/CurrencyType.ts b/plugins/cost-insights/src/types/CurrencyType.ts new file mode 100644 index 0000000000..0316e8f90e --- /dev/null +++ b/plugins/cost-insights/src/types/CurrencyType.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum CurrencyType { + USD = 'USD', + CarbonOffsetTons = 'CARBON_OFFSET_TONS', + Beers = 'BEERS', + IceCream = 'PINTS_OF_ICE_CREAM', +} diff --git a/plugins/cost-insights/src/types/DateFormat.ts b/plugins/cost-insights/src/types/DateFormat.ts new file mode 100644 index 0000000000..c5d9c83f6e --- /dev/null +++ b/plugins/cost-insights/src/types/DateFormat.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd'; diff --git a/plugins/cost-insights/src/types/Duration.ts b/plugins/cost-insights/src/types/Duration.ts index 94297ac00a..dbb226df24 100644 --- a/plugins/cost-insights/src/types/Duration.ts +++ b/plugins/cost-insights/src/types/Duration.ts @@ -19,6 +19,8 @@ * 'last completed quarter', and P30D/P90D to be '[month|quarter] relative to today'. So if * it's September 15, P3M represents costs for Q2 and P30D represents August 16 - * September 15. + * + * @public */ export enum Duration { P7D = 'P7D', @@ -26,5 +28,3 @@ export enum Duration { P90D = 'P90D', P3M = 'P3M', } - -export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd'; diff --git a/plugins/cost-insights/src/types/Entity.ts b/plugins/cost-insights/src/types/Entity.ts deleted file mode 100644 index ccfea6667e..0000000000 --- a/plugins/cost-insights/src/types/Entity.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ChangeStatistic } from './ChangeStatistic'; -import { Maybe } from './Maybe'; - -export interface Entity { - id: Maybe; - aggregation: [number, number]; - entities: Record; - change: ChangeStatistic; -} - -/* - An entity is a tree-like structure that represents any unique - product or service that generates cost over a fixed period of time. - An entity could be atomic or composite. An atomic entity is indivisible - and cannot be broken into sub-entities. - - A composite entity is divided into sub-entities that account for portions - of the total cost **over the same time period**. The root entity is - expected to only have _one_ Record consisting of the sub-entities to display - in the product panel (keyed by the entity type, such as "service" for - compute entities). - - The root sub-entities may have multiple breakdowns - for example, a - breakdown of an entity cost by SKU vs deployment environment. The sum - aggregated cost of each keyed breakdown should equal the sub-entity's cost. - - Entities with null ids are considered "unlabeled" - costs without attribution. - If an entity is a composite, it may only have one (1) null child but may have any number of - null grandchildren. - - { - id: 'product', - aggregation: [0, 200], - change: { - ratio: 2000, - amount: 200 - }, - entities: { - service: [ - { - id: 'service-a', - aggregation: [0, 100], - change: { - ratio: 100, - amount: 100 - }, - entities: {} - }, - { - id: 'service-b', - aggregation: [0, 100], - change: { - ratio: 100, - amount: 100 - }, - entities: { - SKU: [ - { - id: 'service-b-sku-a', - aggregation: [0, 25], - change: { - ratio: 25, - amount: 25 - }, - entities: {} - }, - { - id: null, // Unlabeled cost for service-b - aggregation: [0, 75], - change: { - ratio: 75, - amount: 75 - }, - entities: {} - }, - ], - deployment: [ - { - id: 'service-b-env-a', - aggregation: [0, 50], - change: { - ratio: 50, - amount: 50 - }, - entities: {} - }, - { - id: 'service-b-env-b', - aggregation: [0, 50], - change: { - ratio: 50, - amount: 50 - }, - entities: {} - }, - ] - } - }, - ] - } - } -*/ diff --git a/plugins/cost-insights/src/types/Filters.ts b/plugins/cost-insights/src/types/Filters.ts index 6beef5cacc..d307243ad0 100644 --- a/plugins/cost-insights/src/types/Filters.ts +++ b/plugins/cost-insights/src/types/Filters.ts @@ -14,9 +14,12 @@ * limitations under the License. */ -import { Maybe } from './Maybe'; +import { Maybe } from '@backstage/plugin-cost-insights-common'; import { Duration } from './Duration'; +/** + * @public + */ export interface PageFilters { group: Maybe; project: Maybe; @@ -24,8 +27,14 @@ export interface PageFilters { metric: string | null; } +/** + * @public + */ export type ProductFilters = Array; +/** + * @public + */ export interface ProductPeriod { duration: Duration; productType: string; diff --git a/plugins/cost-insights/src/types/index.ts b/plugins/cost-insights/src/types/index.ts index 5f090cc7eb..57711b4eee 100644 --- a/plugins/cost-insights/src/types/index.ts +++ b/plugins/cost-insights/src/types/index.ts @@ -14,22 +14,40 @@ * limitations under the License. */ +import * as common from '@backstage/plugin-cost-insights-common'; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type ChangeStatistic = common.ChangeStatistic; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Cost = common.Cost; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type DateAggregation = common.DateAggregation; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Entity = common.Entity; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Group = common.Group; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Maybe = common.Maybe; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Metric = common.Metric; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type MetricData = common.MetricData; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Product = common.Product; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Project = common.Project; +/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */ +export type Trendline = common.Trendline; + +// TODO: Split some of these up into `@backstage/plugin-cost-insights-react` for presentation types +// and `@backstage/plugin-cost-insights-common` for data transfer object types. export * from './Alert'; export * from './ChangeStatistic'; export * from './ChartData'; -export * from './Cost'; -export * from './DateAggregation'; -export * from './Duration'; export * from './Currency'; -export * from './Entity'; -export * from './Icon'; +export * from './CurrencyType'; +export * from './DateFormat'; +export * from './Duration'; export * from './Filters'; -export * from './Group'; +export * from './Icon'; export * from './Loading'; -export * from './Maybe'; -export * from './MetricData'; -export * from './Metric'; -export * from './Product'; -export * from './Project'; export * from './Theme'; -export * from './Trendline'; diff --git a/plugins/search-react/src/context/SearchContext.test.tsx b/plugins/search-react/src/context/SearchContext.test.tsx index 0272176150..68c79d3a5e 100644 --- a/plugins/search-react/src/context/SearchContext.test.tsx +++ b/plugins/search-react/src/context/SearchContext.test.tsx @@ -108,40 +108,58 @@ describe('SearchContext', () => { expect(result.current).toEqual(expect.objectContaining(initialState)); }); - it('Resets cursor when term is set (and different from previous)', async () => { - const { result, waitForNextUpdate } = renderHook(() => useSearch(), { - wrapper, - initialProps: { - initialState: { - ...initialState, - pageCursor: 'SOMEPAGE', + describe('Resets cursor', () => { + it('When term is cleared', async () => { + const { result, waitForNextUpdate } = renderHook(() => useSearch(), { + wrapper, + initialProps: { + initialState: { + ...initialState, + term: 'first term', + pageCursor: 'SOMEPAGE', + }, }, - }, + }); + + await waitForNextUpdate(); + + expect(result.current.term).toEqual('first term'); + expect(result.current.pageCursor).toEqual('SOMEPAGE'); + + act(() => { + result.current.setTerm(''); + }); + + await waitForNextUpdate(); + + expect(result.current.pageCursor).toBeUndefined(); }); - await waitForNextUpdate(); + it('When term is set (and different from previous)', async () => { + const { result, waitForNextUpdate } = renderHook(() => useSearch(), { + wrapper, + initialProps: { + initialState: { + ...initialState, + term: 'first term', + pageCursor: 'SOMEPAGE', + }, + }, + }); - expect(result.current.pageCursor).toEqual('SOMEPAGE'); + await waitForNextUpdate(); - act(() => { - result.current.setTerm('first term'); + expect(result.current.term).toEqual('first term'); + expect(result.current.pageCursor).toEqual('SOMEPAGE'); + + act(() => { + result.current.setTerm('second term'); + }); + + await waitForNextUpdate(); + + expect(result.current.pageCursor).toBeUndefined(); }); - - act(() => { - result.current.setPageCursor('OTHERPAGE'); - }); - - await waitForNextUpdate(); - - expect(result.current.pageCursor).toEqual('OTHERPAGE'); - - act(() => { - result.current.setTerm('second term'); - }); - - await waitForNextUpdate(); - - expect(result.current.pageCursor).toEqual(undefined); }); describe('Performs search (and sets results)', () => { diff --git a/plugins/search-react/src/context/SearchContext.tsx b/plugins/search-react/src/context/SearchContext.tsx index 8a0f962121..faad50f37a 100644 --- a/plugins/search-react/src/context/SearchContext.tsx +++ b/plugins/search-react/src/context/SearchContext.tsx @@ -150,10 +150,11 @@ export const SearchContextProvider = (props: SearchContextProviderProps) => { useEffect(() => { // Any time a term is reset, we want to start from page 0. - if (term && prevTerm && term !== prevTerm) { + // Only reset the term if it has been modified by the user at least once, the initial state must not reset the term. + if (prevTerm !== undefined && term !== prevTerm) { setPageCursor(undefined); } - }, [term, prevTerm, initialState.pageCursor]); + }, [term, prevTerm, setPageCursor]); const value: SearchContextValue = { result, diff --git a/yarn.lock b/yarn.lock index 4082736f9c..7e4eda9c3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5263,9 +5263,9 @@ "@react-hookz/deep-equal" "^1.0.1" "@react-hookz/web@^14.0.0": - version "14.6.0" - resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.6.0.tgz#8b3824841ee679b23516aa9b316e819e55590a1c" - integrity sha512-4ZQU4wsNd4QsMe4LkF9t1y4NS2cCp1N/JfG9TYw1dsWLOJhZ0w+4ebp7cydN5BLeceIaILcfQ7fBYUvrBJN6EA== + version "14.7.0" + resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.7.0.tgz#3154b7cf38bf464e7b60417eae7f3348ce19e6f6" + integrity sha512-9AN0W3QH3Aj5/LXHKy2XiysjgvaNRLQwEVyCaU3AOMW9c1XlL5hSYc67ExubmzQRw2kAYx2Jpgl8wKCIINxsxQ== dependencies: "@react-hookz/deep-equal" "^1.0.2" @@ -6787,7 +6787,7 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/react-dom@*", "@types/react-dom@<18.0.0": +"@types/react-dom@*", "@types/react-dom@<18.0.0", "@types/react-dom@^17": version "17.0.17" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== @@ -26313,7 +26313,7 @@ ws@^7.3.1, ws@^7.4.6: resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== -ws@^8.3.0: +ws@^8.0.0, ws@^8.3.0: version "8.8.0" resolved "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==