Merge branch 'backstage:master' into master

This commit is contained in:
Bogdan Nechyporenko
2023-10-04 11:00:30 +02:00
committed by GitHub
30 changed files with 550 additions and 298 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
The `HostDiscovery` export has been deprecated, import it from `@backstage/backend-app-api` instead.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/backend-openapi-utils': patch
---
Adds new public utility types for common OpenAPI values, like request and response shapes and parameters available on an endpoint.
**deprecated** `internal` namespace
The internal namespace will continue to be exported but now uses OpenAPI format for path parameters. You should use the new utility types.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
Updated to import `HostDiscovery` from `@backstage/backend-app-api`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-app-api': patch
---
Moved `HostDiscovery` from `@backstage/backend-common`.
+1 -1
View File
@@ -123,7 +123,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Contentful](https://www.contentful.com) | [James Bourne](https://github.com/jamesmbourne) | Centralized documentation of service ownership, APIs, and documentation, and new service creation with a custom scaffolder - [full case study with Roadie](https://roadie.io/case-studies/maintaining-velocity-through-hypergrowth-contentful/). |
| [Back Market](https://www.backmarket.com) | [Sami Farhat](https://github.com/skfarhat) | Internal Developer Portal featuring catalog, tech-radar, ownership management, component creation (scaffolder) and centralized infrastructure management -- probably more to come. |
| [Avalia Systems](https://avalia.io) | [Olivier Liechti](https://github.com/wasadigi), [Fabio Velloso](https://github.com/fabiovelloso) | Innersource, software analytics, knowledge base for 360 software assessments, collaborative applications, hub for tracking and sharing IP assets. |
| [Albert Heijn](https://ah.technology) | [Joost Hofman](https://github.com/joosthofman), [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. |
| [Albert Heijn](https://ah.technology) | [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. |
| [Wise, formerly TransferWise](https://wise.com) | [Andrew Beveridge](https://github.com/beveradb) | It's early days for us, we're trying to start small with catalog, tech docs and unified search. Future ambitious vision includes scaffolder for one-click component addition, building out integrations with CI/CD tooling, kubernetes clusters, monitoring/alerting tooling etc. and aiming for a frictionless "golden path" for engineers! 🚀 |
| [Happy Money](http://happymoney.com/) | [Akshit Lomash](mailto:alomash@happymoney.com) | We are moving from a monolith to microservices-based architecture. We are developing a developer portal based on Backstage to create a service catalog for our new services. All the services created are onboarded Backstage and engineering teams are using a cookie-cutter-based template from backstage to initiate a new service. |
| [Lightspeed](http://lightspeedhq.com/) | [Marcus Crane](mailto:marcus.crane@lightspeedhq.com) | We use it within our X-Series division (https://vendhq.com) to catalog ~100+ systems and ~350 components! |
+16 -2
View File
@@ -10,6 +10,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api';
import { CacheClient } from '@backstage/backend-common';
import { Config } from '@backstage/config';
import { CorsOptions } from 'cors';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import { ErrorRequestHandler } from 'express';
import { Express as Express_2 } from 'express';
import { Format } from 'logform';
@@ -25,7 +26,6 @@ import { LoadConfigOptionsRemote } from '@backstage/config-loader';
import { LoggerService } from '@backstage/backend-plugin-api';
import { PermissionsService } from '@backstage/backend-plugin-api';
import { PluginDatabaseManager } from '@backstage/backend-common';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { RemoteConfigSourceOptions } from '@backstage/config-loader';
import { RequestHandler } from 'express';
import { RequestListener } from 'http';
@@ -114,7 +114,7 @@ export interface DefaultRootHttpRouterOptions {
// @public (undocumented)
export const discoveryServiceFactory: () => ServiceFactory<
PluginEndpointDiscovery,
DiscoveryService,
'plugin'
>;
@@ -128,6 +128,20 @@ export interface ExtendedHttpServer extends http.Server {
stop(): Promise<void>;
}
// @public
export class HostDiscovery implements DiscoveryService {
static fromConfig(
config: Config,
options?: {
basePath?: string;
},
): HostDiscovery;
// (undocumented)
getBaseUrl(pluginId: string): Promise<string>;
// (undocumented)
getExternalBaseUrl(pluginId: string): Promise<string>;
}
// @public (undocumented)
export interface HttpRouterFactoryOptions {
getPath?(pluginId: string): string;
+37
View File
@@ -0,0 +1,37 @@
/*
* 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 interface Config {
/** Discovery options. */
discovery?: {
/**
* Endpoints
*
* A list of target baseUrls and the associated plugins.
*/
endpoints: {
/**
* The target baseUrl to use for the plugin
*
* Can be either a string or an object with internal and external keys.
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
*/
target: string | { internal: string; external: string };
/** Array of plugins which use the target baseUrl. */
plugins: string[];
}[];
};
}
+2
View File
@@ -90,8 +90,10 @@
"mock-fs": "^5.2.0",
"supertest": "^6.1.3"
},
"configSchema": "config.d.ts",
"files": [
"dist",
"config.d.ts",
"alpha"
]
}
@@ -0,0 +1,132 @@
/*
* 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 { Config } from '@backstage/config';
import { readHttpServerOptions } from '@backstage/backend-app-api';
import { DiscoveryService } from '@backstage/backend-plugin-api';
type Target = string | { internal: string; external: string };
/**
* HostDiscovery is a basic PluginEndpointDiscovery implementation
* that can handle plugins that are hosted in a single or multiple deployments.
*
* The deployment may be scaled horizontally, as long as the external URL
* is the same for all instances. However, internal URLs will always be
* resolved to the same host, so there won't be any balancing of internal traffic.
*
* @public
*/
export class HostDiscovery implements DiscoveryService {
/**
* Creates a new HostDiscovery discovery instance by reading
* from the `backend` config section, specifically the `.baseUrl` for
* discovering the external URL, and the `.listen` and `.https` config
* for the internal one.
*
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
* eg.
* ```yaml
* discovery:
* endpoints:
* - target: https://internal.example.com/internal-catalog
* plugins: [catalog]
* - target: https://internal.example.com/secure/api/{{pluginId}}
* plugins: [auth, permission]
* - target:
* internal: https://internal.example.com/search
* external: https://example.com/search
* plugins: [search]
* ```
*
* The basePath defaults to `/api`, meaning the default full internal
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
*/
static fromConfig(config: Config, options?: { basePath?: string }) {
const basePath = options?.basePath ?? '/api';
const externalBaseUrl = config
.getString('backend.baseUrl')
.replace(/\/+$/, '');
const {
listen: { host: listenHost = '::', port: listenPort },
} = readHttpServerOptions(config.getConfig('backend'));
const protocol = config.has('backend.https') ? 'https' : 'http';
// Translate bind-all to localhost, and support IPv6
let host = listenHost;
if (host === '::' || host === '') {
// We use localhost instead of ::1, since IPv6-compatible systems should default
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
// things will still work.
host = 'localhost';
} else if (host === '0.0.0.0') {
host = '127.0.0.1';
}
if (host.includes(':')) {
host = `[${host}]`;
}
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
return new HostDiscovery(
internalBaseUrl + basePath,
externalBaseUrl + basePath,
config.getOptionalConfig('discovery'),
);
}
private constructor(
private readonly internalBaseUrl: string,
private readonly externalBaseUrl: string,
private readonly discoveryConfig: Config | undefined,
) {}
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
const target = endpoints
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
?.get<Target>('target');
if (!target) {
const baseUrl =
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
}
if (typeof target === 'string') {
return target.replace(
/\{\{\s*pluginId\s*\}\}/g,
encodeURIComponent(pluginId),
);
}
return target[type].replace(
/\{\{\s*pluginId\s*\}\}/g,
encodeURIComponent(pluginId),
);
}
async getBaseUrl(pluginId: string): Promise<string> {
return this.getTargetFromConfig(pluginId, 'internal');
}
async getExternalBaseUrl(pluginId: string): Promise<string> {
return this.getTargetFromConfig(pluginId, 'external');
}
}
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import { HostDiscovery } from '@backstage/backend-common';
import {
coreServices,
createServiceFactory,
} from '@backstage/backend-plugin-api';
import { HostDiscovery } from './HostDiscovery';
/** @public */
export const discoveryServiceFactory = createServiceFactory({
@@ -15,3 +15,4 @@
*/
export { discoveryServiceFactory } from './discoveryServiceFactory';
export { HostDiscovery } from './HostDiscovery';
+3 -13
View File
@@ -28,6 +28,7 @@ import { GiteaIntegration } from '@backstage/integration';
import { GithubCredentialsProvider } from '@backstage/integration';
import { GithubIntegration } from '@backstage/integration';
import { GitLabIntegration } from '@backstage/integration';
import { HostDiscovery as HostDiscovery_2 } from '@backstage/backend-app-api';
import { IdentityService } from '@backstage/backend-plugin-api';
import { isChildPath } from '@backstage/cli-common';
import { Knex } from 'knex';
@@ -479,18 +480,7 @@ export class GitlabUrlReader implements UrlReader {
}
// @public
export class HostDiscovery implements PluginEndpointDiscovery {
static fromConfig(
config: Config,
options?: {
basePath?: string;
},
): HostDiscovery;
// (undocumented)
getBaseUrl(pluginId: string): Promise<string>;
// (undocumented)
getExternalBaseUrl(pluginId: string): Promise<string>;
}
export const HostDiscovery: typeof HostDiscovery_2;
export { isChildPath };
@@ -751,7 +741,7 @@ export type ServiceBuilder = {
export function setRootLogger(newLogger: winston.Logger): void;
// @public @deprecated
export const SingleHostDiscovery: typeof HostDiscovery;
export const SingleHostDiscovery: typeof HostDiscovery_2;
// @public
export type StatusCheck = () => Promise<any>;
-20
View File
@@ -216,24 +216,4 @@ export interface Config {
*/
csp?: { [policyId: string]: string[] | false };
};
/** Discovery options. */
discovery?: {
/**
* Endpoints
*
* A list of target baseUrls and the associated plugins.
*/
endpoints: {
/**
* The target baseUrl to use for the plugin
*
* Can be either a string or an object with internal and external keys.
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
*/
target: string | { internal: string; external: string };
/** Array of plugins which use the target baseUrl. */
plugins: string[];
}[];
};
}
@@ -14,11 +14,9 @@
* limitations under the License.
*/
import { Config } from '@backstage/config';
import { PluginEndpointDiscovery } from './types';
import { readHttpServerOptions } from '@backstage/backend-app-api';
import { HostDiscovery as _HostDiscovery } from '@backstage/backend-app-api';
type Target = string | { internal: string; external: string };
export type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
/**
* HostDiscovery is a basic PluginEndpointDiscovery implementation
@@ -30,106 +28,7 @@ type Target = string | { internal: string; external: string };
*
* @public
*/
export class HostDiscovery implements PluginEndpointDiscovery {
/**
* Creates a new HostDiscovery discovery instance by reading
* from the `backend` config section, specifically the `.baseUrl` for
* discovering the external URL, and the `.listen` and `.https` config
* for the internal one.
*
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
* eg.
* ```yaml
* discovery:
* endpoints:
* - target: https://internal.example.com/internal-catalog
* plugins: [catalog]
* - target: https://internal.example.com/secure/api/{{pluginId}}
* plugins: [auth, permission]
* - target:
* internal: https://internal.example.com/search
* external: https://example.com/search
* plugins: [search]
* ```
*
* The basePath defaults to `/api`, meaning the default full internal
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
*/
static fromConfig(config: Config, options?: { basePath?: string }) {
const basePath = options?.basePath ?? '/api';
const externalBaseUrl = config
.getString('backend.baseUrl')
.replace(/\/+$/, '');
const {
listen: { host: listenHost = '::', port: listenPort },
} = readHttpServerOptions(config.getConfig('backend'));
const protocol = config.has('backend.https') ? 'https' : 'http';
// Translate bind-all to localhost, and support IPv6
let host = listenHost;
if (host === '::' || host === '') {
// We use localhost instead of ::1, since IPv6-compatible systems should default
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
// things will still work.
host = 'localhost';
} else if (host === '0.0.0.0') {
host = '127.0.0.1';
}
if (host.includes(':')) {
host = `[${host}]`;
}
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
return new HostDiscovery(
internalBaseUrl + basePath,
externalBaseUrl + basePath,
config.getOptionalConfig('discovery'),
);
}
private constructor(
private readonly internalBaseUrl: string,
private readonly externalBaseUrl: string,
private readonly discoveryConfig: Config | undefined,
) {}
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
const target = endpoints
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
?.get<Target>('target');
if (!target) {
const baseUrl =
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
}
if (typeof target === 'string') {
return target.replace(
/\{\{\s*pluginId\s*\}\}/g,
encodeURIComponent(pluginId),
);
}
return target[type].replace(
/\{\{\s*pluginId\s*\}\}/g,
encodeURIComponent(pluginId),
);
}
async getBaseUrl(pluginId: string): Promise<string> {
return this.getTargetFromConfig(pluginId, 'internal');
}
async getExternalBaseUrl(pluginId: string): Promise<string> {
return this.getTargetFromConfig(pluginId, 'external');
}
}
export const HostDiscovery = _HostDiscovery;
/**
* SingleHostDiscovery is a basic PluginEndpointDiscovery implementation
@@ -142,4 +41,4 @@ export class HostDiscovery implements PluginEndpointDiscovery {
* @public
* @deprecated Use {@link HostDiscovery} instead
*/
export const SingleHostDiscovery = HostDiscovery;
export const SingleHostDiscovery = _HostDiscovery;
@@ -13,5 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { HostDiscovery, SingleHostDiscovery } from './HostDiscovery';
export type { PluginEndpointDiscovery } from './types';
export {
HostDiscovery,
SingleHostDiscovery,
type PluginEndpointDiscovery,
} from './HostDiscovery';
@@ -1,17 +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.
*/
export type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
+6
View File
@@ -61,6 +61,12 @@ export function createRouter() {
}
```
## FAQs
### Why am I getting `unknown` as the type for a response?
This can happen when you have a `charset` defined in your `response.content` section. Something like `response.content[ 'application/json; charset=utf-8:']` will cause this issue.
## INTERNAL
### Limitations
+118 -57
View File
@@ -75,11 +75,18 @@ interface CookieObject extends ParameterObject {
}
// @public (undocumented)
type CookieSchema<
export type CookieParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = CookieSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
// @public (undocumented)
type CookieSchema<
Doc extends RequiredDoc,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableCookieObject>;
// @public
export function createValidatedOpenApiRouter<T extends RequiredDoc>(
@@ -99,16 +106,16 @@ type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = Extract<
// @public (undocumented)
type DocOperation<
Doc extends RequiredDoc,
Path extends keyof Doc['paths'],
Path extends DocPath<Doc>,
Method extends keyof Doc['paths'][Path],
> = Doc['paths'][Path][Method];
// @public (undocumented)
type DocParameter<
Doc extends RequiredDoc,
Path extends Extract<keyof Doc['paths'], string>,
Method extends keyof Doc['paths'][Path],
Parameter extends keyof Doc['paths'][Path][Method]['parameters'],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
Parameter extends keyof DocOperation<Doc, Path, Method>['parameters'],
> = DocOperation<
Doc,
Path,
@@ -142,32 +149,28 @@ type DocParameters<
};
// @public
type DocPath<
Doc extends PathDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
> = ValueOf<{
[Template in Extract<
keyof Doc['paths'],
string
>]: Path extends PathTemplate<Template> ? Template : never;
}>;
type DocPath<Doc extends PathDoc> = Extract<keyof Doc['paths'], string>;
// @public (undocumented)
type DocPathMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPathTemplate<Doc>,
> = keyof Doc['paths'][DocPath<Doc, Path>];
Path extends DocPath<Doc>,
> = keyof Doc['paths'][Path];
// @public (undocumented)
type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
Extract<keyof Doc['paths'], string>
>;
type DocPathTemplate<Doc extends PathDoc> = PathTemplate<DocPath<Doc>>;
// @public (undocumented)
type DocPathTemplateMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPathTemplate<Doc>,
> = keyof Doc['paths'][TemplateToDocPath<Doc, Path>];
// @public
type DocRequestHandler<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Method extends keyof Doc['paths'][Path],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = core.RequestHandler<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
@@ -179,8 +182,8 @@ type DocRequestHandler<
// @public
type DocRequestHandlerParams<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Method extends keyof Doc['paths'][Path],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = core.RequestHandlerParams<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
@@ -204,14 +207,30 @@ interface DocRequestMatcher<
| 'head',
> {
// (undocumented)
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
<
Path extends MethodAwareDocPath<
Doc,
PathTemplate<Extract<keyof Doc['paths'], string>>,
Method
>,
>(
path: Path,
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
...handlers: Array<
DocRequestHandler<Doc, TemplateToDocPath<Doc, Path>, Method>
>
): T;
// (undocumented)
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
<
Path extends MethodAwareDocPath<
Doc,
PathTemplate<Extract<keyof Doc['paths'], string>>,
Method
>,
>(
path: Path,
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
...handlers: Array<
DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>
>
): T;
}
@@ -238,11 +257,18 @@ interface HeaderObject extends ParameterObject {
}
// @public (undocumented)
type HeaderSchema<
export type HeaderParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = HeaderSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
// @public (undocumented)
type HeaderSchema<
Doc extends RequiredDoc,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableHeaderObject>;
// @public
type Immutable<T> = T extends
@@ -304,10 +330,12 @@ declare namespace internal {
RequiredDoc,
PathDoc,
ValueOf,
PathTemplate,
DocPath,
PathTemplate,
TemplateToDocPath,
DocPathTemplate,
DocPathMethod,
DocPathTemplateMethod,
MethodAwareDocPath,
DocOperation,
ComponentTypes,
@@ -362,7 +390,7 @@ declare namespace internal {
RequestBody,
RequestBodySchema,
RequestBodyToJsonSchema,
Response_2 as Response,
Response_3 as Response,
ResponseSchemas,
ResponseBodyToJsonSchema,
};
@@ -394,14 +422,11 @@ type MapToSchema<
// @public (undocumented)
type MethodAwareDocPath<
Doc extends PathDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends keyof Doc['paths'][Path],
Path extends DocPathTemplate<Doc>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = ValueOf<{
[Template in Extract<
keyof Doc['paths'],
string
>]: Path extends PathTemplate<Template>
? Method extends DocPathMethod<Doc, Path>
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Method extends DocPathTemplateMethod<Doc, Path>
? PathTemplate<Template>
: never
: never;
@@ -466,11 +491,18 @@ interface PathObject extends ParameterObject {
}
// @public (undocumented)
type PathSchema<
export type PathParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = PathSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
// @public (undocumented)
type PathSchema<
Doc extends RequiredDoc,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
> = ParametersSchema<Doc, Path, Method, ImmutablePathObject>;
// @public
type PathTemplate<Path extends string> =
@@ -508,11 +540,26 @@ interface QueryObject extends ParameterObject {
}
// @public (undocumented)
type QuerySchema<
export type QueryParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = QuerySchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
// @public (undocumented)
type QuerySchema<
Doc extends RequiredDoc,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableQueryObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableQueryObject>;
// @public (undocumented)
type Request_2<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = RequestBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
export { Request_2 as Request };
// @public (undocumented)
type RequestBody<
@@ -536,20 +583,16 @@ type RequestBody<
// @public (undocumented)
type RequestBodySchema<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = RequestBody<
Doc,
DocPath<Doc, Path>,
Method
> extends ImmutableRequestBodyObject
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
> = RequestBody<Doc, Path, Method> extends ImmutableRequestBodyObject
? ObjectWithContentSchema<Doc, RequestBody<Doc, Path, Method>>
: never;
// @public
type RequestBodyToJsonSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
@@ -568,8 +611,16 @@ type RequiredMap<
// @public (undocumented)
type Response_2<
Doc extends RequiredDoc,
Path extends keyof Doc['paths'],
Method extends keyof Doc['paths'][Path],
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = ResponseBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
export { Response_2 as Response };
// @public (undocumented)
type Response_3<
Doc extends RequiredDoc,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
StatusCode extends keyof DocOperation<Doc, Path, Method>['responses'],
> = DocOperation<
Doc,
@@ -588,27 +639,27 @@ type Response_2<
// @public
type ResponseBodyToJsonSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
// @public (undocumented)
type ResponseSchemas<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = {
[StatusCode in keyof DocOperation<
Doc,
Path,
Method
>['responses']]: Response_2<
>['responses']]: Response_3<
Doc,
Path,
Method,
StatusCode
> extends ImmutableResponseObject
? ObjectWithContentSchema<Doc, Response_2<Doc, Path, Method, StatusCode>>
? ObjectWithContentSchema<Doc, Response_3<Doc, Path, Method, StatusCode>>
: never;
};
@@ -625,6 +676,16 @@ type SchemaRef<Doc extends RequiredDoc, Schema> = Schema extends {
[Key in keyof Schema]: SchemaRef<Doc, Schema[Key]>;
};
// @public
type TemplateToDocPath<
Doc extends PathDoc,
Path extends DocPathTemplate<Doc>,
> = ValueOf<{
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Template
: never;
}>;
// @public (undocumented)
type ToTypeSafe<T> = UnknownIfNever<ConvertAll<TuplifyUnion<T>>[number]>;
@@ -22,5 +22,13 @@
import * as internal from './types';
export { internal };
export type {
Request,
Response,
QueryParameters,
HeaderParameters,
CookieParameters,
PathParameters,
} from './utility';
export type { ApiRouter } from './router';
export { createValidatedOpenApiRouter } from './stub';
@@ -18,15 +18,23 @@ import { createValidatedOpenApiRouter } from './stub';
import express from 'express';
import request from 'supertest';
import singlePathSpec from './___fixtures__/single-path';
import { Response } from './utility';
describe('createRouter', () => {
const pet: Response<typeof singlePathSpec, '/pet/:petId', 'get'> = {
id: 1,
name: 'rover',
status: 'available',
photoUrls: [],
};
it('does NOT override originalUrl and basePath after execution', async () => {
expect.assertions(2);
const router = createValidatedOpenApiRouter(singlePathSpec);
router.get('/pet/:petId', (req, res) => {
expect(req.baseUrl).toBe('/pet-store');
expect(req.originalUrl).toBe(`/pet-store/pet/${req.params.petId}`);
res.send('');
res.status(400).send();
});
const appRouter = express();
@@ -41,7 +49,7 @@ describe('createRouter', () => {
const routerGetFn = jest.fn();
router.get('/pet/:petId', (_, res) => {
routerGetFn();
res.send('');
res.json(pet);
});
const apiRouter = express.Router();
@@ -58,7 +66,7 @@ describe('createRouter', () => {
const router = createValidatedOpenApiRouter(singlePathSpec);
router.get('/pet/:petId', (req, res) => {
expect(typeof req.params.petId).toBe('integer');
res.send('');
res.json(pet);
});
const apiRouter = express.Router();
@@ -42,6 +42,13 @@ export type PathDoc = Pick<ImmutableOpenAPIObject, 'paths'>;
*/
export type ValueOf<T> = T[keyof T];
/**
* All paths for a given doc,
* @example `/pet/{petId}` | `/pet`
* @public
*/
export type DocPath<Doc extends PathDoc> = Extract<keyof Doc['paths'], string>;
/**
* Validate a string against OpenAPI path template, {@link https://spec.openapis.org/oas/v3.1.0#path-templating-matching}.
*
@@ -74,44 +81,46 @@ export type PathTemplate<Path extends string> =
*
* @public
*/
export type DocPath<
export type TemplateToDocPath<
Doc extends PathDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPathTemplate<Doc>,
> = ValueOf<{
[Template in Extract<
keyof Doc['paths'],
string
>]: Path extends PathTemplate<Template> ? Template : never;
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Template
: never;
}>;
/**
* @public
*/
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
Extract<keyof Doc['paths'], string>
>;
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<DocPath<Doc>>;
/**
* @public
*/
export type DocPathMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPath<Doc>,
> = keyof Doc['paths'][Path];
/**
* @public
*/
export type DocPathTemplateMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPathTemplate<Doc>,
> = keyof Doc['paths'][DocPath<Doc, Path>];
> = keyof Doc['paths'][TemplateToDocPath<Doc, Path>];
/**
* @public
*/
export type MethodAwareDocPath<
Doc extends PathDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends keyof Doc['paths'][Path],
Path extends DocPathTemplate<Doc>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = ValueOf<{
[Template in Extract<
keyof Doc['paths'],
string
>]: Path extends PathTemplate<Template>
? Method extends DocPathMethod<Doc, Path>
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Method extends DocPathTemplateMethod<Doc, Path>
? PathTemplate<Template>
: never
: never;
@@ -122,7 +131,7 @@ export type MethodAwareDocPath<
*/
export type DocOperation<
Doc extends RequiredDoc,
Path extends keyof Doc['paths'],
Path extends DocPath<Doc>,
Method extends keyof Doc['paths'][Path],
> = Doc['paths'][Path][Method];
@@ -14,7 +14,14 @@
* limitations under the License.
*/
import type core from 'express-serve-static-core';
import { DocPathTemplate, MethodAwareDocPath, RequiredDoc } from './common';
import {
DocPath,
DocPathMethod,
MethodAwareDocPath,
PathTemplate,
RequiredDoc,
TemplateToDocPath,
} from './common';
import { PathSchema, QuerySchema } from './params';
import { RequestBodyToJsonSchema } from './requests';
import { ResponseBodyToJsonSchema } from './responses';
@@ -25,8 +32,8 @@ import { ResponseBodyToJsonSchema } from './responses';
*/
export type DocRequestHandler<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Method extends keyof Doc['paths'][Path],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = core.RequestHandler<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
@@ -41,8 +48,8 @@ export type DocRequestHandler<
*/
export type DocRequestHandlerParams<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Method extends keyof Doc['paths'][Path],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = core.RequestHandlerParams<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
@@ -68,12 +75,28 @@ export interface DocRequestMatcher<
| 'options'
| 'head',
> {
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
<
Path extends MethodAwareDocPath<
Doc,
PathTemplate<Extract<keyof Doc['paths'], string>>,
Method
>,
>(
path: Path,
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
...handlers: Array<
DocRequestHandler<Doc, TemplateToDocPath<Doc, Path>, Method>
>
): T;
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
<
Path extends MethodAwareDocPath<
Doc,
PathTemplate<Extract<keyof Doc['paths'], string>>,
Method
>,
>(
path: Path,
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
...handlers: Array<
DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>
>
): T;
}
@@ -32,7 +32,6 @@ import {
Filter,
FullMap,
MapDiscriminatedUnion,
PathTemplate,
RequiredDoc,
SchemaRef,
ValueOf,
@@ -44,9 +43,9 @@ import { FromSchema, JSONSchema7 } from 'json-schema-to-ts';
*/
export type DocParameter<
Doc extends RequiredDoc,
Path extends Extract<keyof Doc['paths'], string>,
Method extends keyof Doc['paths'][Path],
Parameter extends keyof Doc['paths'][Path][Method]['parameters'],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
Parameter extends keyof DocOperation<Doc, Path, Method>['parameters'],
> = DocOperation<
Doc,
Path,
@@ -138,33 +137,33 @@ export type ParametersSchema<
*/
export type HeaderSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableHeaderObject>;
/**
* @public
*/
export type CookieSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableCookieObject>;
/**
* @public
*/
export type PathSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
> = ParametersSchema<Doc, Path, Method, ImmutablePathObject>;
/**
* @public
*/
export type QuerySchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableQueryObject>;
> = ParametersSchema<Doc, Path, Method, ImmutableQueryObject>;
@@ -26,8 +26,6 @@ import type {
DocOperation,
DocPath,
DocPathMethod,
DocPathTemplate,
PathTemplate,
ToTypeSafe,
} from './common';
import {
@@ -61,14 +59,10 @@ export type RequestBody<
*/
export type RequestBodySchema<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = RequestBody<
Doc,
DocPath<Doc, Path>,
Method
> extends ImmutableRequestBodyObject
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
> = RequestBody<Doc, Path, Method> extends ImmutableRequestBodyObject
? ObjectWithContentSchema<Doc, RequestBody<Doc, Path, Method>>
: never;
/**
@@ -77,6 +71,6 @@ export type RequestBodySchema<
*/
export type RequestBodyToJsonSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
@@ -25,10 +25,9 @@ import type {
RequiredDoc,
DocOperation,
DocPathMethod,
DocPathTemplate,
PathTemplate,
ToTypeSafe,
ValueOf,
DocPath,
} from './common';
import { ImmutableReferenceObject, ImmutableResponseObject } from './immutable';
@@ -37,8 +36,8 @@ import { ImmutableReferenceObject, ImmutableResponseObject } from './immutable';
*/
export type Response<
Doc extends RequiredDoc,
Path extends keyof Doc['paths'],
Method extends keyof Doc['paths'][Path],
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
StatusCode extends keyof DocOperation<Doc, Path, Method>['responses'],
> = DocOperation<
Doc,
@@ -59,7 +58,7 @@ export type Response<
*/
export type ResponseSchemas<
Doc extends RequiredDoc,
Path extends DocPathTemplate<Doc>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = {
[StatusCode in keyof DocOperation<Doc, Path, Method>['responses']]: Response<
@@ -78,6 +77,6 @@ export type ResponseSchemas<
*/
export type ResponseBodyToJsonSchema<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
@@ -0,0 +1,81 @@
/*
* Copyright 2023 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 {
CookieSchema,
DocPathTemplateMethod,
HeaderSchema,
PathSchema,
PathTemplate,
QuerySchema,
RequestBodyToJsonSchema,
RequiredDoc,
ResponseBodyToJsonSchema,
TemplateToDocPath,
} from './types';
/**
* @public
*/
export type Response<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = ResponseBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
/**
* @public
*/
export type Request<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = RequestBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
/**
* @public
*/
export type HeaderParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = HeaderSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
/**
* @public
*/
export type CookieParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = CookieSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
/**
* @public
*/
export type PathParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = PathSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
/**
* @public
*/
export type QueryParameters<
Doc extends RequiredDoc,
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = QuerySchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
@@ -20,9 +20,9 @@ import {
MiddlewareFactory,
createHttpServer,
ExtendedHttpServer,
HostDiscovery,
DefaultRootHttpRouter,
} from '@backstage/backend-app-api';
import { HostDiscovery } from '@backstage/backend-common';
import {
createServiceFactory,
BackendFeature,
@@ -24,10 +24,10 @@ import { parseEntityFilterParams } from './parseEntityFilterParams';
import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';
import { parseEntityTransformParams } from './parseEntityTransformParams';
import { spec } from '../../schema/openapi.generated';
import { internal } from '@backstage/backend-openapi-utils';
import { QueryParameters } from '@backstage/backend-openapi-utils';
export function parseQueryEntitiesParams(
params: internal.QuerySchema<typeof spec, '/entities/by-query', 'get'>,
params: QueryParameters<typeof spec, '/entities/by-query', 'get'>,
): Omit<QueryEntitiesRequest, 'authorizationToken' | 'limit'> {
const fields = parseEntityTransformParams(params);
+19 -19
View File
@@ -18158,9 +18158,9 @@ __metadata:
linkType: hard
"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.3, @types/prop-types@npm:^15.7.5":
version: 15.7.7
resolution: "@types/prop-types@npm:15.7.7"
checksum: 023b95f7dd82e1c594f51dcb93ec4c382600cef6eeee29a2ac7b782b92c0882eab8da16d4cbd6e18b39e85ac8d94ebf4ca02c6e248ce5b5fb4b16dbab5d82861
version: 15.7.8
resolution: "@types/prop-types@npm:15.7.8"
checksum: 61dfad79da8b1081c450bab83b77935df487ae1cdd4660ec7df6be8e74725c15fa45cf486ce057addc956ca4ae78300b97091e2a25061133d1b9a1440bc896ae
languageName: node
linkType: hard
@@ -18299,13 +18299,13 @@ __metadata:
linkType: hard
"@types/react@npm:^17":
version: 17.0.66
resolution: "@types/react@npm:17.0.66"
version: 17.0.67
resolution: "@types/react@npm:17.0.67"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: b364a98670ccee222dbd21ddd6cd1be78ad22bca40c7e1c073fff908121fd51fe1de0e9afc3d4ef8dfb6ceedfa28c5257dde9f23af0096f4d350e60c4b2f8858
checksum: eb4915fc7d24e19fe6e98dfe98ade377423b1263e7dc001beda41b0298152fb5a410a55dde5def5ec0838156239a48e778d522da03d319ae5ecea80d455da604
languageName: node
linkType: hard
@@ -18989,9 +18989,9 @@ __metadata:
languageName: node
linkType: hard
"@uiw/codemirror-extensions-basic-setup@npm:4.21.13":
version: 4.21.13
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.13"
"@uiw/codemirror-extensions-basic-setup@npm:4.21.18":
version: 4.21.18
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.18"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/commands": ^6.0.0
@@ -19008,19 +19008,19 @@ __metadata:
"@codemirror/search": ">=6.0.0"
"@codemirror/state": ">=6.0.0"
"@codemirror/view": ">=6.0.0"
checksum: 94a1ef335ed12fd1eb201e3d94892824d16a01d5a531c1962ed3b9a2d379dc3261abb4647208254a5a69763467b8a4f677ae0201fc8e67e5b60b1da9b12bae34
checksum: c643eebc45e2067080c46e28fdb4ac40043e5890f4862bd45fe5bf816894b6c118ea901ca9ba1dce8862877622ebd793db6ec220491506355ed562a0dc0c4507
languageName: node
linkType: hard
"@uiw/react-codemirror@npm:^4.9.3":
version: 4.21.13
resolution: "@uiw/react-codemirror@npm:4.21.13"
version: 4.21.18
resolution: "@uiw/react-codemirror@npm:4.21.18"
dependencies:
"@babel/runtime": ^7.18.6
"@codemirror/commands": ^6.1.0
"@codemirror/state": ^6.1.1
"@codemirror/theme-one-dark": ^6.0.0
"@uiw/codemirror-extensions-basic-setup": 4.21.13
"@uiw/codemirror-extensions-basic-setup": 4.21.18
codemirror: ^6.0.0
peerDependencies:
"@babel/runtime": ">=7.11.0"
@@ -19030,7 +19030,7 @@ __metadata:
codemirror: ">=6.0.0"
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 5e8dafa3837eeb4a6909f463f92ac2dd4143a5db79bf03b3d1782759940e7de6fc459ac60d9f01c9ce2e37771e83d4894e236810a788b38eebf1f312659f2764
checksum: ef3897e9901b98fd981d4b9db2ae8702aa7ffda4cf5cab3286963580606a6dde38e77a4314c12574b6f5f251d62b62e59f0a1a445009b5927b8500f4b5fd795c
languageName: node
linkType: hard
@@ -27462,7 +27462,7 @@ __metadata:
languageName: node
linkType: hard
"graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0":
"graphql@npm:^16.0.0, graphql@npm:^16.8.1":
version: 16.8.1
resolution: "graphql@npm:16.8.1"
checksum: 8d304b7b6f708c8c5cc164b06e92467dfe36aff6d4f2cf31dd19c4c2905a0e7b89edac4b7e225871131fd24e21460836b369de0c06532644d15b461d55b1ccc0
@@ -33183,8 +33183,8 @@ __metadata:
linkType: hard
"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1, msw@npm:^1.2.3, msw@npm:^1.3.1":
version: 1.3.1
resolution: "msw@npm:1.3.1"
version: 1.3.2
resolution: "msw@npm:1.3.2"
dependencies:
"@mswjs/cookies": ^0.2.2
"@mswjs/interceptors": ^0.17.10
@@ -33194,7 +33194,7 @@ __metadata:
chalk: ^4.1.1
chokidar: ^3.4.2
cookie: ^0.4.2
graphql: ^15.0.0 || ^16.0.0
graphql: ^16.8.1
headers-polyfill: 3.2.5
inquirer: ^8.2.0
is-node-process: ^1.2.0
@@ -33212,7 +33212,7 @@ __metadata:
optional: true
bin:
msw: cli/index.js
checksum: 5e3c9d2efff025ecaa55c48b0392179fb07253f26212fe884cca7cc4b5d62cebbfa937dc7249a91d432b1f64ebcce2a1e4938056264f277da62d62d072dfb883
checksum: c2d4f7747f5806f0fd8d8cc3ca250ee1c2a7a6cd608de43f95bd072ba1fb13cdce0b52932ce9bf8f4a21b194d2815db535501e224ec8f7052593447fe1c0cb70
languageName: node
linkType: hard