committed by
Fredrik Adelöw
parent
046e04a7cf
commit
d7585d4003
@@ -54,6 +54,7 @@ import { initialize } from 'express-openapi';
|
||||
import yaml from 'js-yaml';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import OpenAPIBackend from 'openapi-backend';
|
||||
|
||||
class ParsingError extends Error {
|
||||
toString() {
|
||||
@@ -104,6 +105,23 @@ export async function createRouter(
|
||||
logger.info('Catalog is running in readonly mode');
|
||||
}
|
||||
|
||||
// create api with your definition file or object
|
||||
const api = new OpenAPIBackend({ definition: './petstore.yml' });
|
||||
|
||||
// register your framework specific request handlers here
|
||||
api.register({
|
||||
getPets: (c, req, res) => {
|
||||
return res.status(200).json({ result: 'ok' });
|
||||
},
|
||||
getPetById: (c, req, res) => res.status(200).json({ result: 'ok' }),
|
||||
validationFail: (c, req, res) =>
|
||||
res.status(400).json({ err: c.validation.errors }),
|
||||
notFound: (c, req, res) => res.status(404).json({ err: 'not found' }),
|
||||
});
|
||||
|
||||
// initalize the backend
|
||||
api.init();
|
||||
|
||||
const validateDependency = (
|
||||
dependency: any,
|
||||
next: (req: express.Request, res: express.Response) => any,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,5 @@
|
||||
# @backstage/plugin-openapi-router-common
|
||||
|
||||
Welcome to the common package for the openapi-router plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@backstage/plugin-openapi-router",
|
||||
"description": "OpenAPI router with type completions.",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"json-schema-to-ts": "^2.6.2",
|
||||
"openapi-types": "^12.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common functionalities for the openapi-router plugin.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './router';
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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 { OpenAPIV3_1 } from 'openapi-types';
|
||||
import { FieldValues } from './types/fields';
|
||||
import {
|
||||
AppendNonBlankKey,
|
||||
FieldPath,
|
||||
FieldPathValue,
|
||||
Path,
|
||||
} from './types/path';
|
||||
import { IRouter, Router, IRouterMatcher } from 'express';
|
||||
import core, {
|
||||
ParamsDictionary,
|
||||
RequestHandler,
|
||||
} from 'express-serve-static-core';
|
||||
import { FromSchema } from 'json-schema-to-ts';
|
||||
|
||||
type RouterFn<TFieldValues extends FieldValues> = <
|
||||
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>(
|
||||
name: TFieldName,
|
||||
) => null;
|
||||
|
||||
const doc = {
|
||||
openapi: '3.1.0',
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: 'Swagger Petstore',
|
||||
license: {
|
||||
name: 'MIT',
|
||||
url: 'https://opensource.org/licenses/MIT',
|
||||
},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: 'http://petstore.swagger.io/v1',
|
||||
},
|
||||
],
|
||||
paths: {
|
||||
'/pets': {
|
||||
get: {
|
||||
summary: 'List all pets',
|
||||
operationId: 'listPets',
|
||||
tags: ['pets'],
|
||||
parameters: [
|
||||
{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'How many items to return at one time (max 100)',
|
||||
required: false,
|
||||
schema: {
|
||||
type: 'integer',
|
||||
format: 'int32',
|
||||
},
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'A paged array of pets',
|
||||
headers: {
|
||||
'x-next': {
|
||||
description: 'A link to the next page of responses',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Pets',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: 'Create a pet',
|
||||
operationId: 'createPets',
|
||||
tags: ['pets'],
|
||||
responses: {
|
||||
'201': {
|
||||
description: 'Null response',
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/pets/{petId}': {
|
||||
get: {
|
||||
summary: 'Info for a specific pet',
|
||||
operationId: 'showPetById',
|
||||
tags: ['pets'],
|
||||
parameters: [
|
||||
{
|
||||
name: 'petId',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: 'The id of the pet to retrieve',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Expected response to a valid request',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Pet',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
Pet: {
|
||||
type: 'object',
|
||||
required: ['id', 'name'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
format: 'int64',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
tag: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
Pets: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Pet',
|
||||
},
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
required: ['code', 'message'],
|
||||
properties: {
|
||||
code: {
|
||||
type: 'integer',
|
||||
format: 'int32',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
const isUndefined = (a: any): a is undefined => a === undefined;
|
||||
const isNull = (a: any): a is null => a === null;
|
||||
|
||||
const isNullOrUndefined = (a: any): a is undefined | null =>
|
||||
isUndefined(a) || isNull(a);
|
||||
|
||||
const isDateObject = (value: unknown): value is Date => value instanceof Date;
|
||||
|
||||
const isObjectType = (value: unknown) => typeof value === 'object';
|
||||
|
||||
const isObject = <T extends object>(value: unknown): value is T =>
|
||||
!isNullOrUndefined(value) &&
|
||||
!Array.isArray(value) &&
|
||||
isObjectType(value) &&
|
||||
!isDateObject(value);
|
||||
|
||||
const compact = <TValue>(value: TValue[]) =>
|
||||
Array.isArray(value) ? value.filter(Boolean) : [];
|
||||
|
||||
export const resolve = <T>(
|
||||
obj: T,
|
||||
path: string,
|
||||
defaultValue?: unknown,
|
||||
): any => {
|
||||
if (!path || !isObject(obj)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const result = compact(path.split(/[,[\].]+?/)).reduce(
|
||||
(result, key) =>
|
||||
isNullOrUndefined(result) ? result : result[key as keyof {}],
|
||||
obj,
|
||||
);
|
||||
if (result === undefined || result === obj) {
|
||||
return obj[path as keyof T] === undefined
|
||||
? defaultValue
|
||||
: obj[path as keyof T];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* We want this to input path and have
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
const x: RouterFn<typeof doc.paths> = name => {
|
||||
console.log(resolve(doc, name));
|
||||
return null;
|
||||
};
|
||||
|
||||
x('/pets/{petId}');
|
||||
|
||||
type RemoveTail<
|
||||
S extends string,
|
||||
Tail extends string,
|
||||
> = S extends `${infer P}${Tail}` ? P : S;
|
||||
type GetRouteParameter<S extends string> = RemoveTail<
|
||||
RemoveTail<RemoveTail<S, `/${string}`>, `-${string}`>,
|
||||
`.${string}`
|
||||
>;
|
||||
export type RouteParameters<Route extends string> = string extends Route
|
||||
? ParamsDictionary
|
||||
: Route extends `${string}(${string}`
|
||||
? ParamsDictionary
|
||||
: Route extends `${string}:${infer Rest}`
|
||||
? (GetRouteParameter<Rest> extends never
|
||||
? ParamsDictionary
|
||||
: GetRouteParameter<Rest> extends `${infer ParamName}?`
|
||||
? { [P in ParamName]?: string }
|
||||
: { [P in GetRouteParameter<Rest>]: string }) &
|
||||
(Rest extends `${GetRouteParameter<Rest>}${infer Next}`
|
||||
? RouteParameters<Next>
|
||||
: unknown)
|
||||
: {};
|
||||
interface ParsedQs {
|
||||
[key: string]: undefined | string | string[] | ParsedQs | ParsedQs[];
|
||||
}
|
||||
interface ApiRouterMatcher<
|
||||
TFieldValues extends FieldValues,
|
||||
T,
|
||||
Method extends
|
||||
| 'all'
|
||||
| 'get'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'delete'
|
||||
| 'patch'
|
||||
| 'options'
|
||||
| 'head',
|
||||
> {
|
||||
<
|
||||
TFieldName extends Path<TFieldValues> = Path<TFieldValues>,
|
||||
P = RouteParameters<TFieldName>,
|
||||
ResBody = any,
|
||||
ReqBody = any,
|
||||
ReqQuery = ParsedQs,
|
||||
Locals extends Record<string, any> = Record<string, any>,
|
||||
>(
|
||||
// (it's used as the default type parameter for P)
|
||||
path: TFieldName,
|
||||
// (This generic is meant to be passed explicitly.)
|
||||
...handlers: Array<RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals>>
|
||||
): T;
|
||||
}
|
||||
|
||||
type path = ApiRouterMatcher<typeof doc.paths, any, 'get'>;
|
||||
const test: path = a => {
|
||||
console.log(a);
|
||||
};
|
||||
test('/pets/{petId}');
|
||||
|
||||
export interface IApiRouter<ApiSpec, PathSpec, T> extends IRouter {
|
||||
all: ApiRouterMatcher<PathSpec, T, 'all'>;
|
||||
get: ApiRouterMatcher<PathSpec, T, 'get'>;
|
||||
}
|
||||
|
||||
export default class ApiRouter<
|
||||
ApiSpec,
|
||||
PathSpec extends OpenAPIV3_1.Document,
|
||||
T,
|
||||
> implements IApiRouter<ApiSpec, PathSpec, T>
|
||||
{
|
||||
private _router = Router();
|
||||
|
||||
constructor(private spec: OpenAPIV3_1.Document) {}
|
||||
|
||||
static fromSpec<ApiSpec, PathSpec, T>(spec: OpenAPIV3_1.Document) {
|
||||
return new ApiRouter<ApiSpec, PathSpec, T>(spec);
|
||||
}
|
||||
|
||||
get<
|
||||
TFieldValues extends FieldValues = PathSpec,
|
||||
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>(
|
||||
path: TFieldName,
|
||||
...handlers: core.RequestHandler<
|
||||
core.ParamsDictionary,
|
||||
any,
|
||||
FieldPathValue<FieldPathValue<TFieldValues, TFieldName>, 'get'>,
|
||||
ParsedQs,
|
||||
Record<string, string>
|
||||
>[]
|
||||
) {
|
||||
console.log(path);
|
||||
return this._router.get(path, ...handlers);
|
||||
}
|
||||
}
|
||||
|
||||
const router = ApiRouter.fromSpec<typeof doc, typeof doc.paths, any>(doc);
|
||||
|
||||
router.get('/pets', (req, res) => {
|
||||
req.body.tags;
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export {};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 { IsFlatObject, Noop } from './utils';
|
||||
|
||||
export type InternalFieldName = string;
|
||||
|
||||
export type FieldName<TFieldValues extends FieldValues> =
|
||||
IsFlatObject<TFieldValues> extends true
|
||||
? Extract<keyof TFieldValues, string>
|
||||
: string;
|
||||
|
||||
export type CustomElement<TFieldValues extends FieldValues> = {
|
||||
name: FieldName<TFieldValues>;
|
||||
type?: string;
|
||||
value?: any;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
options?: HTMLOptionsCollection;
|
||||
files?: FileList | null;
|
||||
focus?: Noop;
|
||||
};
|
||||
|
||||
export type FieldValue<TFieldValues extends FieldValues> =
|
||||
TFieldValues[InternalFieldName];
|
||||
|
||||
export type FieldValues = Record<string, any>;
|
||||
|
||||
export type NativeFieldValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| unknown[];
|
||||
|
||||
export type FieldElement<TFieldValues extends FieldValues = FieldValues> =
|
||||
| HTMLInputElement
|
||||
| HTMLSelectElement
|
||||
| HTMLTextAreaElement
|
||||
| CustomElement<TFieldValues>;
|
||||
|
||||
export type Ref = FieldElement;
|
||||
|
||||
export type Field = {
|
||||
_f: {
|
||||
ref: Ref;
|
||||
name: InternalFieldName;
|
||||
refs?: HTMLInputElement[];
|
||||
mount?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type FieldRefs = Partial<Record<InternalFieldName, Field>>;
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* 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 { FieldValues } from './fields';
|
||||
import {
|
||||
BrowserNativeObject,
|
||||
IsAny,
|
||||
IsNever,
|
||||
IsEqual,
|
||||
Primitive,
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Type alias to `string` which describes a lodash-like path through an object.
|
||||
* E.g. `'foo.bar.0.baz'`
|
||||
*/
|
||||
export type PathString = string;
|
||||
|
||||
/**
|
||||
* Type which can be traversed through with a {@link PathString}.
|
||||
* I.e. objects, arrays, and tuples
|
||||
*/
|
||||
export type Traversable = object;
|
||||
|
||||
/**
|
||||
* Type to query whether an array type T is a tuple type.
|
||||
* @typeParam T - type which may be an array or tuple
|
||||
* @example
|
||||
* ```
|
||||
* IsTuple<[number]> = true
|
||||
* IsTuple<number[]> = false
|
||||
* ```
|
||||
*/
|
||||
export type IsTuple<T extends ReadonlyArray<any>> = number extends T['length']
|
||||
? false
|
||||
: true;
|
||||
|
||||
/**
|
||||
* Type which can be used to index an array or tuple type.
|
||||
*/
|
||||
export type ArrayKey = number;
|
||||
|
||||
/**
|
||||
* Type which can be used to index an object.
|
||||
*/
|
||||
export type Key = string;
|
||||
|
||||
/**
|
||||
* Type to assert that a type is a {@link Key}.
|
||||
* @typeParam T - type which may be a {@link Key}
|
||||
*/
|
||||
export type AsKey<T> = Extract<T, Key>;
|
||||
|
||||
/**
|
||||
* Type to convert a type to a {@link Key}.
|
||||
* @typeParam T - type which may be converted to a {@link Key}
|
||||
*/
|
||||
export type ToKey<T> = T extends ArrayKey ? `${T}` : AsKey<T>;
|
||||
|
||||
/**
|
||||
* Type which describes a path through an object
|
||||
* as a list of individual {@link Key}s.
|
||||
*/
|
||||
export type PathTuple = Key[];
|
||||
|
||||
/**
|
||||
* Type to assert that a type is a {@link PathTuple}.
|
||||
* @typeParam T - type which may be a {@link PathTuple}
|
||||
*/
|
||||
export type AsPathTuple<T> = Extract<T, PathTuple>;
|
||||
|
||||
/**
|
||||
* Type to intersect a union type.
|
||||
* See https://fettblog.eu/typescript-union-to-intersection/
|
||||
* @typeParam U - union
|
||||
* @example
|
||||
* ```
|
||||
* UnionToIntersection<{ foo: string } | { bar: number }>
|
||||
* = { foo: string; bar: number }
|
||||
* ```
|
||||
*/
|
||||
export type UnionToIntersection<U> = (
|
||||
U extends any ? (_: U) => any : never
|
||||
) extends (_: infer I) => any
|
||||
? I
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Type which appends a {@link Key} to the {@link PathTuple} only if it is not
|
||||
* blank, i.e. not the empty string.
|
||||
* @typeParam PT - path
|
||||
* @typeParam K - key
|
||||
* @example
|
||||
* ```
|
||||
* AppendNonBlankKey<['foo'], 'bar'> = ['foo', 'bar']
|
||||
* AppendNonBlankKey<['foo'], ''> = ['foo']
|
||||
* ```
|
||||
*/
|
||||
export type AppendNonBlankKey<
|
||||
PT extends PathTuple,
|
||||
K extends Key,
|
||||
> = K extends '' ? PT : [...PT, K];
|
||||
|
||||
/**
|
||||
* Type to implement {@link SplitPathString} tail recursively.
|
||||
* @typeParam PS - remaining {@link PathString} which should be split into its
|
||||
* individual {@link Key}s
|
||||
* @typeParam PT - accumulator of the {@link Key}s which have been split from
|
||||
* the original {@link PathString} already
|
||||
*/
|
||||
type SplitPathStringImpl<
|
||||
PS extends PathString,
|
||||
PT extends PathTuple,
|
||||
> = PS extends `${infer K}.${infer R}`
|
||||
? SplitPathStringImpl<R, AppendNonBlankKey<PT, K>>
|
||||
: AppendNonBlankKey<PT, PS>;
|
||||
|
||||
/**
|
||||
* Type to split a {@link PathString} into a {@link PathTuple}.
|
||||
* The individual {@link Key}s may be empty strings.
|
||||
* @typeParam PS - {@link PathString} which should be split into its
|
||||
* individual {@link Key}s
|
||||
* @example
|
||||
* ```
|
||||
* SplitPathString<'foo'> = ['foo']
|
||||
* SplitPathString<'foo.bar.0.baz'> = ['foo', 'bar', '0', 'baz']
|
||||
* SplitPathString<'.'> = []
|
||||
* ```
|
||||
*/
|
||||
export type SplitPathString<PS extends PathString> = SplitPathStringImpl<
|
||||
PS,
|
||||
[]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type to implement {@link JoinPathTuple} tail-recursively.
|
||||
* @typeParam PT - remaining {@link Key}s which needs to be joined
|
||||
* @typeParam PS - accumulator of the already joined {@link Key}s
|
||||
*/
|
||||
type JoinPathTupleImpl<
|
||||
PT extends PathTuple,
|
||||
PS extends PathString,
|
||||
> = PT extends [infer K, ...infer R]
|
||||
? JoinPathTupleImpl<AsPathTuple<R>, `${PS}.${AsKey<K>}`>
|
||||
: PS;
|
||||
|
||||
/**
|
||||
* Type to join a {@link PathTuple} to a {@link PathString}.
|
||||
* @typeParam PT - {@link PathTuple} which should be joined.
|
||||
* @example
|
||||
* ```
|
||||
* JoinPathTuple<['foo']> = 'foo'
|
||||
* JoinPathTuple<['foo', 'bar', '0', 'baz']> = 'foo.bar.0.baz'
|
||||
* JoinPathTuple<[]> = never
|
||||
* ```
|
||||
*/
|
||||
export type JoinPathTuple<PT extends PathTuple> = PT extends [
|
||||
infer K,
|
||||
...infer R,
|
||||
]
|
||||
? JoinPathTupleImpl<AsPathTuple<R>, AsKey<K>>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Type which converts all keys of an object to {@link Key}s.
|
||||
* @typeParam T - object type
|
||||
* @example
|
||||
* ```
|
||||
* MapKeys<{0: string}> = {'0': string}
|
||||
* ```
|
||||
*/
|
||||
type MapKeys<T> = { [K in keyof T as ToKey<K>]: T[K] };
|
||||
|
||||
/**
|
||||
* Type to access a type by a key.
|
||||
* - Returns undefined if it can't be indexed by that key.
|
||||
* - Returns null if the type is null.
|
||||
* - Returns undefined if the type is not traversable.
|
||||
* @typeParam T - type which is indexed by the key
|
||||
* @typeParam K - key into the type
|
||||
* ```
|
||||
* TryAccess<{foo: string}, 'foo'> = string
|
||||
* TryAccess<{foo: string}, 'bar'> = undefined
|
||||
* TryAccess<null, 'foo'> = null
|
||||
* TryAccess<string, 'foo'> = undefined
|
||||
* ```
|
||||
*/
|
||||
type TryAccess<T, K> = K extends keyof T
|
||||
? T[K]
|
||||
: T extends null
|
||||
? null
|
||||
: undefined;
|
||||
|
||||
/**
|
||||
* Type to access an array type by a key.
|
||||
* Returns undefined if the key is non-numeric.
|
||||
* @typeParam T - type which is indexed by the key
|
||||
* @typeParam K - key into the type
|
||||
* ```
|
||||
* TryAccessArray<string[], '0'> = string
|
||||
* TryAccessArray<string[], 'foo'> = undefined
|
||||
* ```
|
||||
*/
|
||||
type TryAccessArray<
|
||||
T extends ReadonlyArray<any>,
|
||||
K extends Key,
|
||||
> = K extends `${ArrayKey}` ? T[number] : TryAccess<T, K>;
|
||||
|
||||
/**
|
||||
* Type to evaluate the type which the given key points to.
|
||||
* @typeParam T - type which is indexed by the key
|
||||
* @typeParam K - key into the type
|
||||
* @example
|
||||
* ```
|
||||
* EvaluateKey<{foo: string}, 'foo'> = string
|
||||
* EvaluateKey<[number, string], '1'> = string
|
||||
* EvaluateKey<string[], '1'> = string
|
||||
* ```
|
||||
*/
|
||||
export type EvaluateKey<T, K extends Key> = T extends ReadonlyArray<any>
|
||||
? IsTuple<T> extends true
|
||||
? TryAccess<T, K>
|
||||
: TryAccessArray<T, K>
|
||||
: TryAccess<MapKeys<T>, K>;
|
||||
|
||||
/**
|
||||
* Type to evaluate the type which the given path points to.
|
||||
* @typeParam T - deeply nested type which is indexed by the path
|
||||
* @typeParam PT - path into the deeply nested type
|
||||
* @example
|
||||
* ```
|
||||
* EvaluatePath<{foo: {bar: string}}, ['foo', 'bar']> = string
|
||||
* EvaluatePath<[number, string], ['1']> = string
|
||||
* EvaluatePath<number, []> = number
|
||||
* EvaluatePath<number, ['foo']> = undefined
|
||||
* ```
|
||||
*/
|
||||
export type EvaluatePath<T, PT extends PathTuple> = PT extends [
|
||||
infer K,
|
||||
...infer R,
|
||||
]
|
||||
? EvaluatePath<EvaluateKey<T, AsKey<K>>, AsPathTuple<R>>
|
||||
: T;
|
||||
|
||||
/**
|
||||
* Type which given a tuple type returns its own keys, i.e. only its indices.
|
||||
* @typeParam T - tuple type
|
||||
* @example
|
||||
* ```
|
||||
* TupleKeys<[number, string]> = '0' | '1'
|
||||
* ```
|
||||
*/
|
||||
export type TupleKeys<T extends ReadonlyArray<any>> = Exclude<
|
||||
keyof T,
|
||||
keyof any[]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type which extracts all numeric keys from an object.
|
||||
* @typeParam T - type
|
||||
* @example
|
||||
* ```
|
||||
* NumericObjectKeys<{0: string, '1': string, foo: string}> = '0' | '1'
|
||||
* ```
|
||||
*/
|
||||
type NumericObjectKeys<T extends Traversable> = ToKey<
|
||||
Extract<keyof T, ArrayKey | `${ArrayKey}`>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type which extracts all numeric keys from an object, tuple, or array.
|
||||
* If a union is passed, it evaluates to the overlapping numeric keys.
|
||||
* @typeParam T - type
|
||||
* @example
|
||||
* ```
|
||||
* NumericKeys<{0: string, '1': string, foo: string}> = '0' | '1'
|
||||
* NumericKeys<number[]> = `${number}`
|
||||
* NumericKeys<[string, number]> = '0' | '1'
|
||||
* NumericKeys<{0: string, '1': string} | [number] | number[]> = '0'
|
||||
* ```
|
||||
*/
|
||||
export type NumericKeys<T extends Traversable> = UnionToIntersection<
|
||||
T extends ReadonlyArray<any>
|
||||
? IsTuple<T> extends true
|
||||
? [TupleKeys<T>]
|
||||
: [ToKey<ArrayKey>]
|
||||
: [NumericObjectKeys<T>]
|
||||
>[never];
|
||||
|
||||
/**
|
||||
* Type which extracts all keys from an object.
|
||||
* If a union is passed, it evaluates to the overlapping keys.
|
||||
* @typeParam T - object type
|
||||
* @example
|
||||
* ```
|
||||
* ObjectKeys<{foo: string, bar: string}, string> = 'foo' | 'bar'
|
||||
* ObjectKeys<{foo: string, bar: number}, string> = 'foo'
|
||||
* ```
|
||||
*/
|
||||
export type ObjectKeys<T extends Traversable> = Exclude<
|
||||
ToKey<keyof T>,
|
||||
`${string}.${string}` | ''
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type to check whether a type's property matches the constraint type
|
||||
* and return its key. Converts the key to a {@link Key}.
|
||||
* @typeParam T - type whose property should be checked
|
||||
* @typeParam K - key of the property
|
||||
* @typeParam U - constraint type
|
||||
* @example
|
||||
* ```
|
||||
* CheckKeyConstraint<{foo: string}, 'foo', string> = 'foo'
|
||||
* CheckKeyConstraint<{foo: string}, 'foo', number> = never
|
||||
* CheckKeyConstraint<string[], number, string> = `${number}`
|
||||
* ```
|
||||
*/
|
||||
export type CheckKeyConstraint<T, K extends Key, U> = K extends any
|
||||
? EvaluateKey<T, K> extends U
|
||||
? K
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Type which evaluates to true when the type is an array or tuple or is a union
|
||||
* which contains an array or tuple.
|
||||
* @typeParam T - type
|
||||
* @example
|
||||
* ```
|
||||
* ContainsIndexable<{foo: string}> = false
|
||||
* ContainsIndexable<{foo: string} | number[]> = true
|
||||
* ```
|
||||
*/
|
||||
export type ContainsIndexable<T> = IsNever<
|
||||
Extract<T, ReadonlyArray<any>>
|
||||
> extends true
|
||||
? false
|
||||
: true;
|
||||
|
||||
/**
|
||||
* Type to implement {@link Keys} for non-nullable values.
|
||||
* @typeParam T - non-nullable type whose property should be checked
|
||||
*/
|
||||
type KeysImpl<T> = [T] extends [Traversable]
|
||||
? ContainsIndexable<T> extends true
|
||||
? NumericKeys<T>
|
||||
: ObjectKeys<T>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Type to find all properties of a type that match the constraint type
|
||||
* and return their keys.
|
||||
* If a union is passed, it evaluates to the overlapping keys.
|
||||
* @typeParam T - type whose property should be checked
|
||||
* @typeParam U - constraint type
|
||||
* @example
|
||||
* ```
|
||||
* Keys<{foo: string, bar: string}, string> = 'foo' | 'bar'
|
||||
* Keys<{foo?: string, bar?: string}> = 'foo' | 'bar'
|
||||
* Keys<{foo: string, bar: number}, string> = 'foo'
|
||||
* Keys<[string, number], string> = '0'
|
||||
* Keys<string[], string> = `${number}`
|
||||
* Keys<{0: string, '1': string} | [number] | number[]> = '0'
|
||||
* ```
|
||||
*/
|
||||
export type Keys<T, U = unknown> = IsAny<T> extends true
|
||||
? Key
|
||||
: IsNever<T> extends true
|
||||
? Key
|
||||
: IsNever<NonNullable<T>> extends true
|
||||
? never
|
||||
: CheckKeyConstraint<T, KeysImpl<NonNullable<T>>, U>;
|
||||
|
||||
/**
|
||||
* Type to check whether a {@link Key} is present in a type.
|
||||
* If a union of {@link Key}s is passed, all {@link Key}s have to be present
|
||||
* in the type.
|
||||
* @typeParam T - type which is introspected
|
||||
* @typeParam K - key
|
||||
* @example
|
||||
* ```
|
||||
* HasKey<{foo: string}, 'foo'> = true
|
||||
* HasKey<{foo: string}, 'bar'> = false
|
||||
* HasKey<{foo: string}, 'foo' | 'bar'> = false
|
||||
* ```
|
||||
*/
|
||||
export type HasKey<T, K extends Key> = IsNever<Exclude<K, Keys<T>>>;
|
||||
|
||||
/**
|
||||
* Type to implement {@link ValidPathPrefix} tail recursively.
|
||||
* @typeParam T - type which the path should be checked against
|
||||
* @typeParam PT - path which should exist within the given type
|
||||
* @typeParam VPT - accumulates the prefix of {@link Key}s which have been
|
||||
* confirmed to exist already
|
||||
*/
|
||||
type ValidPathPrefixImpl<
|
||||
T,
|
||||
PT extends PathTuple,
|
||||
VPT extends PathTuple,
|
||||
> = PT extends [infer K, ...infer R]
|
||||
? HasKey<T, AsKey<K>> extends true
|
||||
? ValidPathPrefixImpl<
|
||||
EvaluateKey<T, AsKey<K>>,
|
||||
AsPathTuple<R>,
|
||||
AsPathTuple<[...VPT, K]>
|
||||
>
|
||||
: VPT
|
||||
: VPT;
|
||||
|
||||
/**
|
||||
* Type to find the longest path prefix which is still valid,
|
||||
* i.e. exists within the given type.
|
||||
* @typeParam T - type which the path should be checked against
|
||||
* @typeParam PT - path which should exist within the given type
|
||||
* @example
|
||||
* ```
|
||||
* ValidPathPrefix<{foo: {bar: string}}, ['foo', 'bar']> = ['foo', 'bar']
|
||||
* ValidPathPrefix<{foo: {bar: string}}, ['foo', 'ba']> = ['foo']
|
||||
* ```
|
||||
*/
|
||||
export type ValidPathPrefix<T, PT extends PathTuple> = ValidPathPrefixImpl<
|
||||
T,
|
||||
PT,
|
||||
[]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type to check whether a path through a type exists.
|
||||
* @typeParam T - type which the path should be checked against
|
||||
* @typeParam PT - path which should exist within the given type
|
||||
* @example
|
||||
* ```
|
||||
* HasPath<{foo: {bar: string}}, ['foo', 'bar']> = true
|
||||
* HasPath<{foo: {bar: string}}, ['foo', 'ba']> = false
|
||||
* ```
|
||||
*/
|
||||
export type HasPath<T, PT extends PathTuple> = ValidPathPrefix<T, PT> extends PT
|
||||
? true
|
||||
: false;
|
||||
|
||||
/**
|
||||
* Helper function to break apart T1 and check if any are equal to T2
|
||||
*
|
||||
* See {@link IsEqual}
|
||||
*/
|
||||
type AnyIsEqual<T1, T2> = T1 extends T2
|
||||
? IsEqual<T1, T2> extends true
|
||||
? true
|
||||
: never
|
||||
: never;
|
||||
|
||||
type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'all';
|
||||
|
||||
/**
|
||||
* Helper type for recursively constructing paths through a type.
|
||||
* This actually constructs the strings and recurses into nested
|
||||
* object types.
|
||||
*
|
||||
* See {@link Path}
|
||||
*/
|
||||
type PathImpl<K extends string | number, V, TraversedTypes> = V extends
|
||||
| Primitive
|
||||
| BrowserNativeObject
|
||||
? `${K}`
|
||||
: // Check so that we don't recurse into the same type
|
||||
// by ensuring that the types are mutually assignable
|
||||
// mutually required to avoid false positives of subtypes
|
||||
true extends AnyIsEqual<TraversedTypes, V>
|
||||
? `${K}`
|
||||
: true extends AnyIsEqual<HttpMethod, K>
|
||||
? ``
|
||||
: `${K}` | `${K}.${PathInternal<V, TraversedTypes | V>}`;
|
||||
|
||||
/**
|
||||
* Helper type for recursively constructing paths through a type.
|
||||
* This obsucres the internal type param TraversedTypes from exported contract.
|
||||
*
|
||||
* See {@link Path}
|
||||
*/
|
||||
type PathInternal<T, TraversedTypes = T> = T extends ReadonlyArray<infer V>
|
||||
? IsTuple<T> extends true
|
||||
? {
|
||||
[K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes>;
|
||||
}[TupleKeys<T>]
|
||||
: PathImpl<ArrayKey, V, TraversedTypes>
|
||||
: {
|
||||
[K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes>;
|
||||
}[keyof T];
|
||||
|
||||
/**
|
||||
* Type which eagerly collects all paths through a type
|
||||
* @typeParam T - type which should be introspected
|
||||
* @example
|
||||
* ```
|
||||
* Path<{foo: {bar: string}}> = 'foo' | 'foo.bar'
|
||||
* ```
|
||||
*/
|
||||
// We want to explode the union type and process each individually
|
||||
// so assignable types don't leak onto the stack from the base.
|
||||
export type Path<T> = T extends any ? PathInternal<T> : never;
|
||||
|
||||
/**
|
||||
* See {@link Path}
|
||||
*/
|
||||
export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>;
|
||||
|
||||
/**
|
||||
* Helper type for recursively constructing paths through a type.
|
||||
* This actually constructs the strings and recurses into nested
|
||||
* object types.
|
||||
*
|
||||
* See {@link ArrayPath}
|
||||
*/
|
||||
type ArrayPathImpl<K extends string | number, V, TraversedTypes> = V extends
|
||||
| Primitive
|
||||
| BrowserNativeObject
|
||||
? IsAny<V> extends true
|
||||
? string
|
||||
: never
|
||||
: V extends ReadonlyArray<infer U>
|
||||
? U extends Primitive | BrowserNativeObject
|
||||
? IsAny<V> extends true
|
||||
? string
|
||||
: never
|
||||
: // Check so that we don't recurse into the same type
|
||||
// by ensuring that the types are mutually assignable
|
||||
// mutually required to avoid false positives of subtypes
|
||||
true extends AnyIsEqual<TraversedTypes, V>
|
||||
? never
|
||||
: `${K}` | `${K}.${ArrayPathInternal<V, TraversedTypes | V>}`
|
||||
: true extends AnyIsEqual<TraversedTypes, V>
|
||||
? never
|
||||
: `${K}.${ArrayPathInternal<V, TraversedTypes | V>}`;
|
||||
|
||||
/**
|
||||
* Helper type for recursively constructing paths through a type.
|
||||
* This obsucres the internal type param TraversedTypes from exported contract.
|
||||
*
|
||||
* See {@link ArrayPath}
|
||||
*/
|
||||
type ArrayPathInternal<T, TraversedTypes = T> = T extends ReadonlyArray<infer V>
|
||||
? IsTuple<T> extends true
|
||||
? {
|
||||
[K in TupleKeys<T>]-?: ArrayPathImpl<K & string, T[K], TraversedTypes>;
|
||||
}[TupleKeys<T>]
|
||||
: ArrayPathImpl<ArrayKey, V, TraversedTypes>
|
||||
: {
|
||||
[K in keyof T]-?: ArrayPathImpl<K & string, T[K], TraversedTypes>;
|
||||
}[keyof T];
|
||||
|
||||
/**
|
||||
* Type which eagerly collects all paths through a type which point to an array
|
||||
* type.
|
||||
* @typeParam T - type which should be introspected.
|
||||
* @example
|
||||
* ```
|
||||
* Path<{foo: {bar: string[], baz: number[]}}> = 'foo.bar' | 'foo.baz'
|
||||
* ```
|
||||
*/
|
||||
// We want to explode the union type and process each individually
|
||||
// so assignable types don't leak onto the stack from the base.
|
||||
export type ArrayPath<T> = T extends any ? ArrayPathInternal<T> : never;
|
||||
|
||||
/**
|
||||
* See {@link ArrayPath}
|
||||
*/
|
||||
export type FieldArrayPath<TFieldValues extends FieldValues> =
|
||||
ArrayPath<TFieldValues>;
|
||||
|
||||
/**
|
||||
* Type to evaluate the type which the given path points to.
|
||||
* @typeParam T - deeply nested type which is indexed by the path
|
||||
* @typeParam P - path into the deeply nested type
|
||||
* @example
|
||||
* ```
|
||||
* PathValue<{foo: {bar: string}}, 'foo.bar'> = string
|
||||
* PathValue<[number, string], '1'> = string
|
||||
* ```
|
||||
*/
|
||||
export type PathValue<T, P extends Path<T> | ArrayPath<T>> = T extends any
|
||||
? P extends `${infer K}.${infer R}`
|
||||
? K extends keyof T
|
||||
? R extends Path<T[K]>
|
||||
? PathValue<T[K], R>
|
||||
: never
|
||||
: K extends `${ArrayKey}`
|
||||
? T extends ReadonlyArray<infer V>
|
||||
? PathValue<V, R & Path<V>>
|
||||
: never
|
||||
: never
|
||||
: P extends keyof T
|
||||
? T[P]
|
||||
: P extends `${ArrayKey}`
|
||||
? T extends ReadonlyArray<infer V>
|
||||
? V
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* See {@link PathValue}
|
||||
*/
|
||||
export type FieldPathValue<
|
||||
TFieldValues extends FieldValues,
|
||||
TFieldPath extends FieldPath<TFieldValues>,
|
||||
> = PathValue<TFieldValues, TFieldPath>;
|
||||
|
||||
/**
|
||||
* See {@link PathValue}
|
||||
*/
|
||||
export type FieldArrayPathValue<
|
||||
TFieldValues extends FieldValues,
|
||||
TFieldArrayPath extends FieldArrayPath<TFieldValues>,
|
||||
> = PathValue<TFieldValues, TFieldArrayPath>;
|
||||
|
||||
/**
|
||||
* Type to evaluate the type which the given paths point to.
|
||||
* @typeParam TFieldValues - field values which are indexed by the paths
|
||||
* @typeParam TPath - paths into the deeply nested field values
|
||||
* @example
|
||||
* ```
|
||||
* FieldPathValues<{foo: {bar: string}}, ['foo', 'foo.bar']>
|
||||
* = [{bar: string}, string]
|
||||
* ```
|
||||
*/
|
||||
export type FieldPathValues<
|
||||
TFieldValues extends FieldValues,
|
||||
TPath extends FieldPath<TFieldValues>[] | readonly FieldPath<TFieldValues>[],
|
||||
> = {} & {
|
||||
[K in keyof TPath]: FieldPathValue<
|
||||
TFieldValues,
|
||||
TPath[K] & FieldPath<TFieldValues>
|
||||
>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type which eagerly collects all paths through a fieldType that matches a give type
|
||||
* @typeParam TFieldValues - field values which are indexed by the paths
|
||||
* @typeParam TValue - the value you want to match into each type
|
||||
* @example
|
||||
* ```typescript
|
||||
* FieldPathByValue<{foo: {bar: number}, baz: number, bar: string}, number>
|
||||
* = 'foo.bar' | 'baz'
|
||||
* ```
|
||||
*/
|
||||
export type FieldPathByValue<TFieldValues extends FieldValues, TValue> = {
|
||||
[FieldKey in FieldPath<TFieldValues>]: FieldPathValue<
|
||||
TFieldValues,
|
||||
FieldKey
|
||||
> extends TValue
|
||||
? FieldKey
|
||||
: never;
|
||||
}[FieldPath<TFieldValues>];
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
declare const $NestedValue: unique symbol;
|
||||
|
||||
/**
|
||||
* @deprecated to be removed in the next major version
|
||||
*/
|
||||
export type NestedValue<TValue extends object = object> = {
|
||||
[$NestedValue]: never;
|
||||
} & TValue;
|
||||
|
||||
/*
|
||||
Projects that React Hook Form installed don't include the DOM library need these interfaces to compile.
|
||||
React Native applications is no DOM available. The JavaScript runtime is ES6/ES2015 only.
|
||||
These definitions allow such projects to compile with only --lib ES6.
|
||||
|
||||
Warning: all of these interfaces are empty.
|
||||
If you want type definitions for various properties, you need to add `--lib DOM` (via command line or tsconfig.json).
|
||||
*/
|
||||
|
||||
export type Noop = () => void;
|
||||
|
||||
interface File extends Blob {
|
||||
readonly lastModified: number;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
interface FileList {
|
||||
readonly length: number;
|
||||
item(index: number): File | null;
|
||||
[index: number]: File;
|
||||
}
|
||||
|
||||
export type Primitive =
|
||||
| null
|
||||
| undefined
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| symbol
|
||||
| bigint;
|
||||
|
||||
export type BrowserNativeObject = Date | FileList | File;
|
||||
|
||||
export type EmptyObject = { [K in string | number]: never };
|
||||
|
||||
export type NonUndefined<T> = T extends undefined ? never : T;
|
||||
|
||||
export type LiteralUnion<T extends U, U extends Primitive> =
|
||||
| T
|
||||
| (U & { _?: never });
|
||||
|
||||
export type DeepPartial<T> = T extends BrowserNativeObject | NestedValue
|
||||
? T
|
||||
: { [K in keyof T]?: DeepPartial<T[K]> };
|
||||
|
||||
export type DeepPartialSkipArrayKey<T> = T extends
|
||||
| BrowserNativeObject
|
||||
| NestedValue
|
||||
? T
|
||||
: T extends ReadonlyArray<any>
|
||||
? { [K in keyof T]: DeepPartialSkipArrayKey<T[K]> }
|
||||
: { [K in keyof T]?: DeepPartialSkipArrayKey<T[K]> };
|
||||
|
||||
/**
|
||||
* Checks whether the type is any
|
||||
* See {@link https://stackoverflow.com/a/49928360/3406963}
|
||||
* @typeParam T - type which may be any
|
||||
* ```
|
||||
* IsAny<any> = true
|
||||
* IsAny<string> = false
|
||||
* ```
|
||||
*/
|
||||
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
||||
|
||||
/**
|
||||
* Checks whether the type is never
|
||||
* @typeParam T - type which may be never
|
||||
* ```
|
||||
* IsAny<never> = true
|
||||
* IsAny<string> = false
|
||||
* ```
|
||||
*/
|
||||
export type IsNever<T> = [T] extends [never] ? true : false;
|
||||
|
||||
/**
|
||||
* Checks whether T1 can be exactly (mutually) assigned to T2
|
||||
* @typeParam T1 - type to check
|
||||
* @typeParam T2 - type to check against
|
||||
* ```
|
||||
* IsEqual<string, string> = true
|
||||
* IsEqual<'foo', 'foo'> = true
|
||||
* IsEqual<string, number> = false
|
||||
* IsEqual<string, number> = false
|
||||
* IsEqual<string, 'foo'> = false
|
||||
* IsEqual<'foo', string> = false
|
||||
* IsEqual<'foo' | 'bar', 'foo'> = boolean // 'foo' is assignable, but 'bar' is not (true | false) -> boolean
|
||||
* ```
|
||||
*/
|
||||
export type IsEqual<T1, T2> = T1 extends T2
|
||||
? (<G>() => G extends T1 ? 1 : 2) extends <G>() => G extends T2 ? 1 : 2
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
|
||||
export type DeepMap<T, TValue> = IsAny<T> extends true
|
||||
? any
|
||||
: T extends BrowserNativeObject | NestedValue
|
||||
? TValue
|
||||
: T extends object
|
||||
? { [K in keyof T]: DeepMap<NonUndefined<T[K]>, TValue> }
|
||||
: TValue;
|
||||
|
||||
export type IsFlatObject<T extends object> = Extract<
|
||||
Exclude<T[keyof T], NestedValue | Date | FileList>,
|
||||
any[] | object
|
||||
> extends never
|
||||
? true
|
||||
: false;
|
||||
|
||||
export type Merge<A, B> = {
|
||||
[K in keyof A | keyof B]?: K extends keyof A & keyof B
|
||||
? [A[K], B[K]] extends [object, object]
|
||||
? Merge<A[K], B[K]>
|
||||
: A[K] | B[K]
|
||||
: K extends keyof A
|
||||
? A[K]
|
||||
: K extends keyof B
|
||||
? B[K]
|
||||
: never;
|
||||
};
|
||||
@@ -3342,7 +3342,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.20.13
|
||||
resolution: "@babel/runtime@npm:7.20.13"
|
||||
dependencies:
|
||||
@@ -7446,6 +7446,17 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-openapi-router@workspace:plugins/openapi-router-common":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-openapi-router@workspace:plugins/openapi-router-common"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
express: ^4.18.2
|
||||
json-schema-to-ts: ^2.6.2
|
||||
openapi-types: ^12.1.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-org-react@workspace:plugins/org-react":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-org-react@workspace:plugins/org-react"
|
||||
@@ -23777,7 +23788,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1":
|
||||
"express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1, express@npm:^4.18.2":
|
||||
version: 4.18.2
|
||||
resolution: "express@npm:4.18.2"
|
||||
dependencies:
|
||||
@@ -28324,6 +28335,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json-schema-to-ts@npm:^2.6.2":
|
||||
version: 2.6.2
|
||||
resolution: "json-schema-to-ts@npm:2.6.2"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.18.3
|
||||
"@types/json-schema": ^7.0.9
|
||||
ts-algebra: ^1.1.1
|
||||
ts-toolbelt: ^9.6.0
|
||||
checksum: e408f8d3d32dda1a001f194cf5514fea7ef0a4dc4ed4f5448b9ea02df3a071119a0517aaffb639de725f4a12102406dfdf7743455e0e30317e231bb551362039
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json-schema-traverse@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "json-schema-traverse@npm:0.4.1"
|
||||
@@ -38580,6 +38603,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-algebra@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "ts-algebra@npm:1.1.1"
|
||||
dependencies:
|
||||
ts-toolbelt: ^9.6.0
|
||||
checksum: 29fe27215863520377a94a14ccf1e78e921be6a5c514a0a8250dab09e325b006a9571ef1f366875d58e495b59ba880620718a1dd38b6af598cc6ea6de82d4f66
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-easing@npm:^0.2.0":
|
||||
version: 0.2.0
|
||||
resolution: "ts-easing@npm:0.2.0"
|
||||
@@ -38672,6 +38704,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-toolbelt@npm:^9.6.0":
|
||||
version: 9.6.0
|
||||
resolution: "ts-toolbelt@npm:9.6.0"
|
||||
checksum: 9f35fd95d895a5d32ea9fd2e532a695b0bae6cbff6832b77292efa188a0ed1ed6e54f63f74a8920390f3d909a7a3adb20a144686372a8e78b420246a9bd3d58a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfig-paths@npm:^3.14.1":
|
||||
version: 3.14.1
|
||||
resolution: "tsconfig-paths@npm:3.14.1"
|
||||
|
||||
Reference in New Issue
Block a user