├── .prettierignore ├── .gitignore ├── src ├── api │ ├── index.ts │ ├── generated │ │ ├── api │ │ │ ├── index.ts │ │ │ └── resources │ │ │ │ ├── imdb │ │ │ │ ├── service │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ImdbService.ts │ │ │ │ ├── errors │ │ │ │ │ ├── index.ts │ │ │ │ │ └── MovieDoesNotExistError.ts │ │ │ │ ├── index.ts │ │ │ │ └── types │ │ │ │ │ ├── MovieId.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── CreateMovieRequest.ts │ │ │ │ │ ├── Movie.ts │ │ │ │ │ └── Genre.ts │ │ │ │ └── index.ts │ │ ├── serialization │ │ │ ├── index.ts │ │ │ └── resources │ │ │ │ ├── imdb │ │ │ │ ├── index.ts │ │ │ │ └── types │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── MovieId.ts │ │ │ │ │ ├── Genre.ts │ │ │ │ │ ├── Movie.ts │ │ │ │ │ └── CreateMovieRequest.ts │ │ │ │ └── index.ts │ │ ├── core │ │ │ ├── index.ts │ │ │ └── schemas │ │ │ │ ├── builders │ │ │ │ ├── date │ │ │ │ │ ├── index.ts │ │ │ │ │ └── date.ts │ │ │ │ ├── list │ │ │ │ │ ├── index.ts │ │ │ │ │ └── list.ts │ │ │ │ ├── set │ │ │ │ │ ├── index.ts │ │ │ │ │ └── set.ts │ │ │ │ ├── enum │ │ │ │ │ ├── index.ts │ │ │ │ │ └── enum.ts │ │ │ │ ├── record │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── record.ts │ │ │ │ ├── literals │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── stringLiteral.ts │ │ │ │ │ └── booleanLiteral.ts │ │ │ │ ├── lazy │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── lazyObject.ts │ │ │ │ │ └── lazy.ts │ │ │ │ ├── object-like │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── getObjectLikeUtils.ts │ │ │ │ ├── primitives │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── any.ts │ │ │ │ │ ├── unknown.ts │ │ │ │ │ ├── number.ts │ │ │ │ │ ├── string.ts │ │ │ │ │ └── boolean.ts │ │ │ │ ├── schema-utils │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── stringifyValidationErrors.ts │ │ │ │ │ ├── JsonError.ts │ │ │ │ │ ├── ParseError.ts │ │ │ │ │ └── getSchemaUtils.ts │ │ │ │ ├── undiscriminated-union │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── undiscriminatedUnion.ts │ │ │ │ ├── union │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── discriminant.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── union.ts │ │ │ │ ├── index.ts │ │ │ │ └── object │ │ │ │ │ ├── property.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── objectWithoutOptionalProperties.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── object.ts │ │ │ │ ├── utils │ │ │ │ ├── MaybePromise.ts │ │ │ │ ├── keys.ts │ │ │ │ ├── entries.ts │ │ │ │ ├── partition.ts │ │ │ │ ├── filterObject.ts │ │ │ │ ├── addQuestionMarksToNullableProperties.ts │ │ │ │ ├── isPlainObject.ts │ │ │ │ ├── getErrorMessageForIncorrectType.ts │ │ │ │ ├── createIdentitySchemaCreator.ts │ │ │ │ └── maybeSkipValidation.ts │ │ │ │ ├── index.ts │ │ │ │ └── Schema.ts │ │ ├── errors │ │ │ ├── index.ts │ │ │ └── FernApiError.ts │ │ ├── index.ts │ │ └── register.ts │ └── README.md ├── server.ts └── services │ └── imdb.ts ├── fern ├── fern.config.json ├── definition │ ├── api.yml │ └── imdb.yml └── generators.yml ├── .vscode ├── extensions.json └── settings.json ├── assets └── missing-getMovie.png ├── .github ├── dependabot.yml └── workflows │ ├── check.yml │ └── dependabot.yml ├── tsconfig.json ├── .eslintrc.cjs ├── package.json ├── README.md └── yarn.lock /.prettierignore: -------------------------------------------------------------------------------- 1 | src/api/generated -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | .DS_Store 3 | node_modules -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generated"; 2 | -------------------------------------------------------------------------------- /src/api/generated/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resources"; 2 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/service/index.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /src/api/generated/serialization/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resources"; 2 | -------------------------------------------------------------------------------- /src/api/generated/core/index.ts: -------------------------------------------------------------------------------- 1 | export * as serialization from "./schemas"; 2 | -------------------------------------------------------------------------------- /fern/fern.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "organization": "fern", 3 | "version": "0.40.4" 4 | } -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/date/index.ts: -------------------------------------------------------------------------------- 1 | export { date } from "./date"; 2 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/list/index.ts: -------------------------------------------------------------------------------- 1 | export { list } from "./list"; 2 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/set/index.ts: -------------------------------------------------------------------------------- 1 | export { set } from "./set"; 2 | -------------------------------------------------------------------------------- /src/api/generated/errors/index.ts: -------------------------------------------------------------------------------- 1 | export { FernApiError } from "./FernApiError"; 2 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /fern/definition/api.yml: -------------------------------------------------------------------------------- 1 | name: api 2 | error-discrimination: 3 | strategy: status-code 4 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/enum/index.ts: -------------------------------------------------------------------------------- 1 | export { enum_ } from "./enum"; 2 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MovieDoesNotExistError"; 2 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/MaybePromise.ts: -------------------------------------------------------------------------------- 1 | export type MaybePromise = T | Promise; 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /assets/missing-getMovie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fern-api/express-starter/HEAD/assets/missing-getMovie.png -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/index.ts: -------------------------------------------------------------------------------- 1 | export * as imdb from "./imdb"; 2 | export * from "./imdb/types"; 3 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./service"; 3 | export * from "./errors"; 4 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/index.ts: -------------------------------------------------------------------------------- 1 | export * as imdb from "./imdb"; 2 | export * from "./imdb/types"; 3 | export * from "./imdb/errors"; 4 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./builders"; 2 | export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema"; 3 | -------------------------------------------------------------------------------- /src/api/generated/index.ts: -------------------------------------------------------------------------------- 1 | export * as FernApi from "./api"; 2 | export { register } from "./register"; 3 | export { FernApiError } from "./errors"; 4 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/keys.ts: -------------------------------------------------------------------------------- 1 | export function keys(object: T): (keyof T)[] { 2 | return Object.keys(object) as (keyof T)[]; 3 | } 4 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/record/index.ts: -------------------------------------------------------------------------------- 1 | export { record } from "./record"; 2 | export type { BaseRecordSchema, RecordSchema } from "./types"; 3 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/literals/index.ts: -------------------------------------------------------------------------------- 1 | export { stringLiteral } from "./stringLiteral"; 2 | export { booleanLiteral } from "./booleanLiteral"; 3 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/types/MovieId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | export type MovieId = string; 6 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MovieId"; 2 | export * from "./Movie"; 3 | export * from "./CreateMovieRequest"; 4 | export * from "./Genre"; 5 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/lazy/index.ts: -------------------------------------------------------------------------------- 1 | export { lazy } from "./lazy"; 2 | export type { SchemaGetter } from "./lazy"; 3 | export { lazyObject } from "./lazyObject"; 4 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/entries.ts: -------------------------------------------------------------------------------- 1 | export function entries(object: T): [keyof T, T[keyof T]][] { 2 | return Object.entries(object) as [keyof T, T[keyof T]][]; 3 | } 4 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MovieId"; 2 | export * from "./Movie"; 3 | export * from "./CreateMovieRequest"; 4 | export * from "./Genre"; 5 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object-like/index.ts: -------------------------------------------------------------------------------- 1 | export { getObjectLikeUtils, withParsedProperties } from "./getObjectLikeUtils"; 2 | export type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" # See documentation for possible values 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/index.ts: -------------------------------------------------------------------------------- 1 | export { any } from "./any"; 2 | export { boolean } from "./boolean"; 3 | export { number } from "./number"; 4 | export { string } from "./string"; 5 | export { unknown } from "./unknown"; 6 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/schema-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { getSchemaUtils, optional, transform } from "./getSchemaUtils"; 2 | export type { SchemaUtils } from "./getSchemaUtils"; 3 | export { JsonError } from "./JsonError"; 4 | export { ParseError } from "./ParseError"; 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/.yarn": true, 4 | "**/.pnp.*": true 5 | }, 6 | "editor.formatOnSave": true, 7 | "[typescript]": { 8 | "editor.codeActionsOnSave": { 9 | "source.organizeImports": "explicit" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/any.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | 4 | export const any = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ ok: true, value })); 5 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/undiscriminated-union/index.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | inferParsedUnidiscriminatedUnionSchema, 3 | inferRawUnidiscriminatedUnionSchema, 4 | UndiscriminatedUnionSchema, 5 | } from "./types"; 6 | export { undiscriminatedUnion } from "./undiscriminatedUnion"; 7 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/unknown.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | 4 | export const unknown = createIdentitySchemaCreator(SchemaType.UNKNOWN, (value) => ({ ok: true, value })); 5 | -------------------------------------------------------------------------------- /fern/generators.yml: -------------------------------------------------------------------------------- 1 | default-group: local 2 | groups: 3 | local: 4 | generators: 5 | - name: fernapi/fern-typescript-express 6 | version: 0.17.3 7 | output: 8 | location: local-file-system 9 | path: ../src/api/generated 10 | config: 11 | outputSourceFiles: true 12 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/types/CreateMovieRequest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as FernApi from "../../../index"; 6 | 7 | export interface CreateMovieRequest { 8 | title: string; 9 | rating: number; 10 | genre: FernApi.Genre; 11 | } 12 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/types/Movie.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as FernApi from "../../../index"; 6 | 7 | export interface Movie { 8 | id: FernApi.MovieId; 9 | title: string; 10 | /** The rating scale is one to five stars */ 11 | rating: number; 12 | } 13 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/schema-utils/stringifyValidationErrors.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from "../../Schema"; 2 | 3 | export function stringifyValidationError(error: ValidationError): string { 4 | if (error.path.length === 0) { 5 | return error.message; 6 | } 7 | return `${error.path.join(" -> ")}: ${error.message}`; 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "esModuleInterop": true, 5 | "target": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "rootDir": "src", 9 | "outDir": "lib", 10 | "allowSyntheticDefaultImports": true, 11 | "allowJs": true 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /src/api/README.md: -------------------------------------------------------------------------------- 1 | ## Generated code 2 | 3 | This directory contains the code that's generated by [Fern](https://github.com/fern-api/fern). 4 | The definition lives in the [fern](../../fern/api//definition/) directory. 5 | 6 | You can re-generate the `generated/` directory using the Fern CLI: 7 | 8 | ``` 9 | npm install -g fern-api 10 | fern generate 11 | ``` 12 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/union/index.ts: -------------------------------------------------------------------------------- 1 | export { discriminant } from "./discriminant"; 2 | export type { Discriminant } from "./discriminant"; 3 | export type { 4 | inferParsedDiscriminant, 5 | inferParsedUnion, 6 | inferRawDiscriminant, 7 | inferRawUnion, 8 | UnionSubtypes, 9 | } from "./types"; 10 | export { union } from "./union"; 11 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/types/Genre.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | export type Genre = 6 | /** 7 | * This is for a horror movie */ 8 | "HORROR" | "COMEDY" | ""; 9 | 10 | export const Genre = { 11 | Horror: "HORROR", 12 | Comedy: "COMEDY", 13 | GreaterThan: "", 14 | } as const; 15 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import cors from "cors"; 2 | import express from "express"; 3 | import { register } from "./api/generated"; 4 | import imdb from "./services/imdb"; 5 | 6 | const PORT = process.env.PORT ?? 8080; 7 | 8 | const app = express(); 9 | 10 | //enable cors 11 | app.use(cors()); 12 | 13 | register(app, { 14 | imdb, 15 | }); 16 | 17 | app.listen(PORT); 18 | console.log(`🎉 Listening on port ${PORT}...`); 19 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/schema-utils/JsonError.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from "../../Schema"; 2 | import { stringifyValidationError } from "./stringifyValidationErrors"; 3 | 4 | export class JsonError extends Error { 5 | constructor(public readonly errors: ValidationError[]) { 6 | super(errors.map(stringifyValidationError).join("; ")); 7 | Object.setPrototypeOf(this, JsonError.prototype); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/schema-utils/ParseError.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from "../../Schema"; 2 | import { stringifyValidationError } from "./stringifyValidationErrors"; 3 | 4 | export class ParseError extends Error { 5 | constructor(public readonly errors: ValidationError[]) { 6 | super(errors.map(stringifyValidationError).join("; ")); 7 | Object.setPrototypeOf(this, ParseError.prototype); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./date"; 2 | export * from "./enum"; 3 | export * from "./lazy"; 4 | export * from "./list"; 5 | export * from "./literals"; 6 | export * from "./object"; 7 | export * from "./object-like"; 8 | export * from "./primitives"; 9 | export * from "./record"; 10 | export * from "./schema-utils"; 11 | export * from "./set"; 12 | export * from "./undiscriminated-union"; 13 | export * from "./union"; 14 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/partition.ts: -------------------------------------------------------------------------------- 1 | export function partition(items: readonly T[], predicate: (item: T) => boolean): [T[], T[]] { 2 | const trueItems: T[] = [], 3 | falseItems: T[] = []; 4 | for (const item of items) { 5 | if (predicate(item)) { 6 | trueItems.push(item); 7 | } else { 8 | falseItems.push(item); 9 | } 10 | } 11 | return [trueItems, falseItems]; 12 | } 13 | -------------------------------------------------------------------------------- /src/api/generated/errors/FernApiError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import express from "express"; 6 | 7 | export abstract class FernApiError extends Error { 8 | constructor(public readonly errorName?: string) { 9 | super(); 10 | Object.setPrototypeOf(this, FernApiError.prototype); 11 | } 12 | 13 | public abstract send(res: express.Response): Promise; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/generated/register.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import express from "express"; 6 | import { ImdbService } from "./api/resources/imdb/service/ImdbService"; 7 | 8 | export function register( 9 | expressApp: express.Express | express.Router, 10 | services: { 11 | imdb: ImdbService; 12 | } 13 | ): void { 14 | (expressApp as any).use("/movies", services.imdb.toRouter()); 15 | } 16 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object-like/types.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, Schema } from "../../Schema"; 2 | 3 | export type ObjectLikeSchema = Schema & 4 | BaseSchema & 5 | ObjectLikeUtils; 6 | 7 | export interface ObjectLikeUtils { 8 | withParsedProperties: >(properties: { 9 | [K in keyof T]: T[K] | ((parsed: Parsed) => T[K]); 10 | }) => ObjectLikeSchema; 11 | } 12 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/types/MovieId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as serializers from "../../../index"; 6 | import * as FernApi from "../../../../api/index"; 7 | import * as core from "../../../../core"; 8 | 9 | export const MovieId: core.serialization.Schema = core.serialization.string(); 10 | 11 | export declare namespace MovieId { 12 | type Raw = string; 13 | } 14 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/filterObject.ts: -------------------------------------------------------------------------------- 1 | export function filterObject(obj: T, keysToInclude: K[]): Pick { 2 | const keysToIncludeSet = new Set(keysToInclude); 3 | return Object.entries(obj).reduce((acc, [key, value]) => { 4 | if (keysToIncludeSet.has(key as K)) { 5 | acc[key as K] = value; 6 | } 7 | return acc; 8 | // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter 9 | }, {} as Pick); 10 | } 11 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/addQuestionMarksToNullableProperties.ts: -------------------------------------------------------------------------------- 1 | export type addQuestionMarksToNullableProperties = { 2 | [K in OptionalKeys]?: T[K]; 3 | } & Pick>; 4 | 5 | export type OptionalKeys = { 6 | [K in keyof T]-?: undefined extends T[K] 7 | ? K 8 | : null extends T[K] 9 | ? K 10 | : 1 extends (any extends T[K] ? 0 : 1) 11 | ? never 12 | : K; 13 | }[keyof T]; 14 | 15 | export type RequiredKeys = Exclude>; 16 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/undiscriminated-union/types.ts: -------------------------------------------------------------------------------- 1 | import { inferParsed, inferRaw, Schema } from "../../Schema"; 2 | 3 | export type UndiscriminatedUnionSchema = Schema< 4 | inferRawUnidiscriminatedUnionSchema, 5 | inferParsedUnidiscriminatedUnionSchema 6 | >; 7 | 8 | export type inferRawUnidiscriminatedUnionSchema = inferRaw; 9 | 10 | export type inferParsedUnidiscriminatedUnionSchema = inferParsed; 11 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/types/Genre.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as serializers from "../../../index"; 6 | import * as FernApi from "../../../../api/index"; 7 | import * as core from "../../../../core"; 8 | 9 | export const Genre: core.serialization.Schema = core.serialization.enum_([ 10 | "HORROR", 11 | "COMEDY", 12 | "", 13 | ]); 14 | 15 | export declare namespace Genre { 16 | type Raw = "HORROR" | "COMEDY" | ""; 17 | } 18 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/errors/MovieDoesNotExistError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as errors from "../../../../errors/index"; 6 | import express from "express"; 7 | 8 | export class MovieDoesNotExistError extends errors.FernApiError { 9 | constructor() { 10 | super("MovieDoesNotExistError"); 11 | Object.setPrototypeOf(this, MovieDoesNotExistError.prototype); 12 | } 13 | 14 | public async send(res: express.Response): Promise { 15 | res.sendStatus(404); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/union/discriminant.ts: -------------------------------------------------------------------------------- 1 | export function discriminant( 2 | parsedDiscriminant: ParsedDiscriminant, 3 | rawDiscriminant: RawDiscriminant 4 | ): Discriminant { 5 | return { 6 | parsedDiscriminant, 7 | rawDiscriminant, 8 | }; 9 | } 10 | 11 | export interface Discriminant { 12 | parsedDiscriminant: ParsedDiscriminant; 13 | rawDiscriminant: RawDiscriminant; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/isPlainObject.ts: -------------------------------------------------------------------------------- 1 | // borrowed from https://github.com/lodash/lodash/blob/master/isPlainObject.js 2 | export function isPlainObject(value: unknown): value is Record { 3 | if (typeof value !== "object" || value === null) { 4 | return false; 5 | } 6 | 7 | if (Object.getPrototypeOf(value) === null) { 8 | return true; 9 | } 10 | 11 | let proto = value; 12 | while (Object.getPrototypeOf(proto) !== null) { 13 | proto = Object.getPrototypeOf(proto); 14 | } 15 | 16 | return Object.getPrototypeOf(value) === proto; 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:@typescript-eslint/strict", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | ], 12 | ignorePatterns: ["*.js", "*.cjs", "src/api/generated"], 13 | overrides: [], 14 | parser: "@typescript-eslint/parser", 15 | parserOptions: { 16 | ecmaVersion: "latest", 17 | sourceType: "module", 18 | project: "./tsconfig.json", 19 | }, 20 | plugins: ["@typescript-eslint"], 21 | rules: {}, 22 | }; 23 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/record/types.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema } from "../../Schema"; 2 | import { SchemaUtils } from "../schema-utils"; 3 | 4 | export type RecordSchema< 5 | RawKey extends string | number, 6 | RawValue, 7 | ParsedKey extends string | number, 8 | ParsedValue 9 | > = BaseRecordSchema & 10 | SchemaUtils, Record>; 11 | 12 | export type BaseRecordSchema< 13 | RawKey extends string | number, 14 | RawValue, 15 | ParsedKey extends string | number, 16 | ParsedValue 17 | > = BaseSchema, Record>; 18 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: "Check" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Install fern 15 | run: npm install -g fern-api 16 | - name: fern generate 17 | run: fern generate 18 | env: 19 | FERN_TOKEN: ${{ secrets.FERN_TOKEN }} 20 | - name: Install dependencies 21 | run: yarn install 22 | - name: Lint 23 | run: yarn lint 24 | - name: Check formatting 25 | run: yarn format:check 26 | - name: Compile 27 | run: yarn build 28 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/getErrorMessageForIncorrectType.ts: -------------------------------------------------------------------------------- 1 | export function getErrorMessageForIncorrectType(value: unknown, expectedType: string): string { 2 | return `Expected ${expectedType}. Received ${getTypeAsString(value)}.`; 3 | } 4 | 5 | function getTypeAsString(value: unknown): string { 6 | if (Array.isArray(value)) { 7 | return "list"; 8 | } 9 | if (value === null) { 10 | return "null"; 11 | } 12 | switch (typeof value) { 13 | case "string": 14 | return `"${value}"`; 15 | case "number": 16 | case "boolean": 17 | case "undefined": 18 | return `${value}`; 19 | } 20 | return typeof value; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/types/Movie.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as serializers from "../../../index"; 6 | import * as FernApi from "../../../../api/index"; 7 | import * as core from "../../../../core"; 8 | 9 | export const Movie: core.serialization.ObjectSchema = core.serialization.object({ 10 | id: core.serialization.lazy(() => serializers.MovieId), 11 | title: core.serialization.string(), 12 | rating: core.serialization.number(), 13 | }); 14 | 15 | export declare namespace Movie { 16 | interface Raw { 17 | id: serializers.MovieId.Raw; 18 | title: string; 19 | rating: number; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/services/imdb.ts: -------------------------------------------------------------------------------- 1 | // this `FernApi` export is generated from your organization name in fern.config.json: 2 | import { FernApi } from "../api"; 3 | import { ImdbService } from "../api/generated/api/resources/imdb/service/ImdbService"; 4 | 5 | export default new ImdbService({ 6 | createMovie: (req, res) => { 7 | const id = req.body.title.toLowerCase().replaceAll(" ", "-"); 8 | 9 | // TODO, add movie to database 10 | 11 | return res.send(id); 12 | }, 13 | getMovie: (req, res) => { 14 | if (req.params.movieId === "goodwill-hunting") { 15 | res.send({ 16 | id: req.params.movieId, 17 | title: "Goodwill Hunting", 18 | rating: 4.9, 19 | }); 20 | } else { 21 | throw new FernApi.MovieDoesNotExistError(); 22 | } 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/createIdentitySchemaCreator.ts: -------------------------------------------------------------------------------- 1 | import { getSchemaUtils } from "../builders/schema-utils"; 2 | import { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType } from "../Schema"; 3 | import { maybeSkipValidation } from "./maybeSkipValidation"; 4 | 5 | export function createIdentitySchemaCreator( 6 | schemaType: SchemaType, 7 | validate: (value: unknown, opts?: SchemaOptions) => MaybeValid 8 | ): () => Schema { 9 | return () => { 10 | const baseSchema: BaseSchema = { 11 | parse: validate, 12 | json: validate, 13 | getType: () => schemaType, 14 | }; 15 | 16 | return { 17 | ...maybeSkipValidation(baseSchema), 18 | ...getSchemaUtils(baseSchema), 19 | }; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fern-express-starter", 3 | "scripts": { 4 | "build": "tsc", 5 | "start": "node lib/server.js", 6 | "dev": "nodemon src/server.ts", 7 | "format": "prettier --write src/**", 8 | "format:check": "prettier --check src/**", 9 | "lint": "eslint", 10 | "lint:fix": "eslint --fix" 11 | }, 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "express": "^4.18.2" 15 | }, 16 | "devDependencies": { 17 | "@types/cors": "^2.8.13", 18 | "@types/express": "^4.17.16", 19 | "@types/node": "^22.5.0", 20 | "@typescript-eslint/eslint-plugin": "latest", 21 | "@typescript-eslint/parser": "latest", 22 | "prettier": "^3.1.0", 23 | "nodemon": "^3.0.2", 24 | "eslint": "^8.55.0", 25 | "ts-node": "^10.9.2", 26 | "typescript": "4.6.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object/property.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "../../Schema"; 2 | 3 | export function property( 4 | rawKey: RawKey, 5 | valueSchema: Schema 6 | ): Property { 7 | return { 8 | rawKey, 9 | valueSchema, 10 | isProperty: true, 11 | }; 12 | } 13 | 14 | export interface Property { 15 | rawKey: RawKey; 16 | valueSchema: Schema; 17 | isProperty: true; 18 | } 19 | 20 | export function isProperty>(maybeProperty: unknown): maybeProperty is O { 21 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 22 | return (maybeProperty as O).isProperty; 23 | } 24 | -------------------------------------------------------------------------------- /src/api/generated/serialization/resources/imdb/types/CreateMovieRequest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as serializers from "../../../index"; 6 | import * as FernApi from "../../../../api/index"; 7 | import * as core from "../../../../core"; 8 | 9 | export const CreateMovieRequest: core.serialization.ObjectSchema< 10 | serializers.CreateMovieRequest.Raw, 11 | FernApi.CreateMovieRequest 12 | > = core.serialization.object({ 13 | title: core.serialization.string(), 14 | rating: core.serialization.number(), 15 | genre: core.serialization.lazy(() => serializers.Genre), 16 | }); 17 | 18 | export declare namespace CreateMovieRequest { 19 | interface Raw { 20 | title: string; 21 | rating: number; 22 | genre: serializers.Genre.Raw; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request 3 | 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v1 16 | with: 17 | github-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: Enable auto-merge for Dependabot PRs 19 | if: ${{contains(steps.metadata.outputs.dependency-names, 'my-dependency') && steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 20 | run: gh pr merge --auto --merge "$PR_URL" 21 | env: 22 | PR_URL: ${{github.event.pull_request.html_url}} 23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 24 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object/index.ts: -------------------------------------------------------------------------------- 1 | export { getObjectUtils, object } from "./object"; 2 | export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties"; 3 | export type { 4 | inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, 5 | inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, 6 | } from "./objectWithoutOptionalProperties"; 7 | export { isProperty, property } from "./property"; 8 | export type { Property } from "./property"; 9 | export type { 10 | BaseObjectSchema, 11 | inferObjectSchemaFromPropertySchemas, 12 | inferParsedObject, 13 | inferParsedObjectFromPropertySchemas, 14 | inferParsedPropertySchema, 15 | inferRawKey, 16 | inferRawObject, 17 | inferRawObjectFromPropertySchemas, 18 | inferRawPropertySchema, 19 | ObjectSchema, 20 | ObjectUtils, 21 | PropertySchemas, 22 | } from "./types"; 23 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/number.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export const number = createIdentitySchemaCreator( 6 | SchemaType.NUMBER, 7 | (value, { breadcrumbsPrefix = [] } = {}) => { 8 | if (typeof value === "number") { 9 | return { 10 | ok: true, 11 | value, 12 | }; 13 | } else { 14 | return { 15 | ok: false, 16 | errors: [ 17 | { 18 | path: breadcrumbsPrefix, 19 | message: getErrorMessageForIncorrectType(value, "number"), 20 | }, 21 | ], 22 | }; 23 | } 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/string.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export const string = createIdentitySchemaCreator( 6 | SchemaType.STRING, 7 | (value, { breadcrumbsPrefix = [] } = {}) => { 8 | if (typeof value === "string") { 9 | return { 10 | ok: true, 11 | value, 12 | }; 13 | } else { 14 | return { 15 | ok: false, 16 | errors: [ 17 | { 18 | path: breadcrumbsPrefix, 19 | message: getErrorMessageForIncorrectType(value, "string"), 20 | }, 21 | ], 22 | }; 23 | } 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/primitives/boolean.ts: -------------------------------------------------------------------------------- 1 | import { SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export const boolean = createIdentitySchemaCreator( 6 | SchemaType.BOOLEAN, 7 | (value, { breadcrumbsPrefix = [] } = {}) => { 8 | if (typeof value === "boolean") { 9 | return { 10 | ok: true, 11 | value, 12 | }; 13 | } else { 14 | return { 15 | ok: false, 16 | errors: [ 17 | { 18 | path: breadcrumbsPrefix, 19 | message: getErrorMessageForIncorrectType(value, "boolean"), 20 | }, 21 | ], 22 | }; 23 | } 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/lazy/lazyObject.ts: -------------------------------------------------------------------------------- 1 | import { getObjectUtils } from "../object"; 2 | import { getObjectLikeUtils } from "../object-like"; 3 | import { BaseObjectSchema, ObjectSchema } from "../object/types"; 4 | import { getSchemaUtils } from "../schema-utils"; 5 | import { constructLazyBaseSchema, getMemoizedSchema, SchemaGetter } from "./lazy"; 6 | 7 | export function lazyObject(getter: SchemaGetter>): ObjectSchema { 8 | const baseSchema: BaseObjectSchema = { 9 | ...constructLazyBaseSchema(getter), 10 | _getRawProperties: () => getMemoizedSchema(getter)._getRawProperties(), 11 | _getParsedProperties: () => getMemoizedSchema(getter)._getParsedProperties(), 12 | }; 13 | 14 | return { 15 | ...baseSchema, 16 | ...getSchemaUtils(baseSchema), 17 | ...getObjectLikeUtils(baseSchema), 18 | ...getObjectUtils(baseSchema), 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object/objectWithoutOptionalProperties.ts: -------------------------------------------------------------------------------- 1 | import { object } from "./object"; 2 | import { inferParsedPropertySchema, inferRawObjectFromPropertySchemas, ObjectSchema, PropertySchemas } from "./types"; 3 | 4 | export function objectWithoutOptionalProperties>( 5 | schemas: T 6 | ): inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas { 7 | return object(schemas) as unknown as inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas; 8 | } 9 | 10 | export type inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas> = 11 | ObjectSchema< 12 | inferRawObjectFromPropertySchemas, 13 | inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas 14 | >; 15 | 16 | export type inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas> = { 17 | [K in keyof T]: inferParsedPropertySchema; 18 | }; 19 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/union/types.ts: -------------------------------------------------------------------------------- 1 | import { inferParsedObject, inferRawObject, ObjectSchema } from "../object"; 2 | import { Discriminant } from "./discriminant"; 3 | 4 | export type UnionSubtypes = { 5 | [K in DiscriminantValues]: ObjectSchema; 6 | }; 7 | 8 | export type inferRawUnion, U extends UnionSubtypes> = { 9 | [K in keyof U]: Record, K> & inferRawObject; 10 | }[keyof U]; 11 | 12 | export type inferParsedUnion, U extends UnionSubtypes> = { 13 | [K in keyof U]: Record, K> & inferParsedObject; 14 | }[keyof U]; 15 | 16 | export type inferRawDiscriminant> = D extends string 17 | ? D 18 | : D extends Discriminant 19 | ? Raw 20 | : never; 21 | 22 | export type inferParsedDiscriminant> = D extends string 23 | ? D 24 | : D extends Discriminant 25 | ? Parsed 26 | : never; 27 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/literals/stringLiteral.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export function stringLiteral(literal: V): Schema { 6 | const schemaCreator = createIdentitySchemaCreator( 7 | SchemaType.STRING_LITERAL, 8 | (value, { breadcrumbsPrefix = [] } = {}) => { 9 | if (value === literal) { 10 | return { 11 | ok: true, 12 | value: literal, 13 | }; 14 | } else { 15 | return { 16 | ok: false, 17 | errors: [ 18 | { 19 | path: breadcrumbsPrefix, 20 | message: getErrorMessageForIncorrectType(value, `"${literal}"`), 21 | }, 22 | ], 23 | }; 24 | } 25 | } 26 | ); 27 | 28 | return schemaCreator(); 29 | } 30 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/literals/booleanLiteral.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export function booleanLiteral(literal: V): Schema { 6 | const schemaCreator = createIdentitySchemaCreator( 7 | SchemaType.BOOLEAN_LITERAL, 8 | (value, { breadcrumbsPrefix = [] } = {}) => { 9 | if (value === literal) { 10 | return { 11 | ok: true, 12 | value: literal, 13 | }; 14 | } else { 15 | return { 16 | ok: false, 17 | errors: [ 18 | { 19 | path: breadcrumbsPrefix, 20 | message: getErrorMessageForIncorrectType(value, `${literal.toString()}`), 21 | }, 22 | ], 23 | }; 24 | } 25 | } 26 | ); 27 | 28 | return schemaCreator(); 29 | } 30 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/lazy/lazy.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, Schema } from "../../Schema"; 2 | import { getSchemaUtils } from "../schema-utils"; 3 | 4 | export type SchemaGetter> = () => SchemaType; 5 | 6 | export function lazy(getter: SchemaGetter>): Schema { 7 | const baseSchema = constructLazyBaseSchema(getter); 8 | return { 9 | ...baseSchema, 10 | ...getSchemaUtils(baseSchema), 11 | }; 12 | } 13 | 14 | export function constructLazyBaseSchema( 15 | getter: SchemaGetter> 16 | ): BaseSchema { 17 | return { 18 | parse: (raw, opts) => getMemoizedSchema(getter).parse(raw, opts), 19 | json: (parsed, opts) => getMemoizedSchema(getter).json(parsed, opts), 20 | getType: () => getMemoizedSchema(getter).getType(), 21 | }; 22 | } 23 | 24 | type MemoizedGetter> = SchemaGetter & { __zurg_memoized?: SchemaType }; 25 | 26 | export function getMemoizedSchema>(getter: SchemaGetter): SchemaType { 27 | const castedGetter = getter as MemoizedGetter; 28 | if (castedGetter.__zurg_memoized == null) { 29 | castedGetter.__zurg_memoized = getter(); 30 | } 31 | return castedGetter.__zurg_memoized; 32 | } 33 | -------------------------------------------------------------------------------- /fern/definition/imdb.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json 2 | 3 | types: 4 | MovieId: string 5 | 6 | Movie: 7 | properties: 8 | id: MovieId 9 | title: string 10 | rating: 11 | type: double 12 | docs: The rating scale is one to five stars 13 | 14 | CreateMovieRequest: 15 | properties: 16 | title: string 17 | rating: double 18 | genre: Genre 19 | 20 | Genre: 21 | enum: 22 | - value: HORROR 23 | docs: This is for a horror movie 24 | - COMEDY 25 | - name: GREATER_THAN 26 | value: > 27 | 28 | service: 29 | auth: false 30 | base-path: /movies 31 | endpoints: 32 | # Here's an HTTP endpoint 33 | 34 | createMovie: 35 | docs: Add a movie to the database 36 | method: POST 37 | path: /create-movie 38 | request: CreateMovieRequest 39 | response: MovieId 40 | examples: 41 | - request: 42 | title: Goodwill Hunting 43 | genre: COMEDY 44 | rating: 9.5 45 | response: 46 | body: movie_1234083334 47 | 48 | getMovie: 49 | method: GET 50 | path: /{movieId} 51 | path-parameters: 52 | movieId: MovieId 53 | response: Movie 54 | errors: 55 | - MovieDoesNotExistError 56 | 57 | errors: 58 | MovieDoesNotExistError: 59 | status-code: 404 60 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/utils/maybeSkipValidation.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, MaybeValid, SchemaOptions } from "../Schema"; 2 | 3 | export function maybeSkipValidation, Raw, Parsed>(schema: S): S { 4 | return { 5 | ...schema, 6 | json: transformAndMaybeSkipValidation(schema.json), 7 | parse: transformAndMaybeSkipValidation(schema.parse), 8 | }; 9 | } 10 | 11 | function transformAndMaybeSkipValidation( 12 | transform: (value: unknown, opts?: SchemaOptions) => MaybeValid 13 | ): (value: unknown, opts?: SchemaOptions) => MaybeValid { 14 | return (value, opts): MaybeValid => { 15 | const transformed = transform(value, opts); 16 | const { skipValidation = false } = opts ?? {}; 17 | if (!transformed.ok && skipValidation) { 18 | // eslint-disable-next-line no-console 19 | console.warn( 20 | [ 21 | "Failed to validate.", 22 | ...transformed.errors.map( 23 | (error) => 24 | " - " + 25 | (error.path.length > 0 ? `${error.path.join(".")}: ${error.message}` : error.message) 26 | ), 27 | ].join("\n") 28 | ); 29 | 30 | return { 31 | ok: true, 32 | value: value as T, 33 | }; 34 | } else { 35 | return transformed; 36 | } 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/enum/enum.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaType } from "../../Schema"; 2 | import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | 5 | export function enum_(values: E): Schema { 6 | const validValues = new Set(values); 7 | 8 | const schemaCreator = createIdentitySchemaCreator( 9 | SchemaType.ENUM, 10 | (value, { allowUnrecognizedEnumValues, breadcrumbsPrefix = [] } = {}) => { 11 | if (typeof value !== "string") { 12 | return { 13 | ok: false, 14 | errors: [ 15 | { 16 | path: breadcrumbsPrefix, 17 | message: getErrorMessageForIncorrectType(value, "string"), 18 | }, 19 | ], 20 | }; 21 | } 22 | 23 | if (!validValues.has(value) && !allowUnrecognizedEnumValues) { 24 | return { 25 | ok: false, 26 | errors: [ 27 | { 28 | path: breadcrumbsPrefix, 29 | message: getErrorMessageForIncorrectType(value, "enum"), 30 | }, 31 | ], 32 | }; 33 | } 34 | 35 | return { 36 | ok: true, 37 | value: value as U, 38 | }; 39 | } 40 | ); 41 | 42 | return schemaCreator(); 43 | } 44 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/set/set.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, Schema, SchemaType } from "../../Schema"; 2 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 3 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 4 | import { list } from "../list"; 5 | import { getSchemaUtils } from "../schema-utils"; 6 | 7 | export function set(schema: Schema): Schema> { 8 | const listSchema = list(schema); 9 | const baseSchema: BaseSchema> = { 10 | parse: (raw, opts) => { 11 | const parsedList = listSchema.parse(raw, opts); 12 | if (parsedList.ok) { 13 | return { 14 | ok: true, 15 | value: new Set(parsedList.value), 16 | }; 17 | } else { 18 | return parsedList; 19 | } 20 | }, 21 | json: (parsed, opts) => { 22 | if (!(parsed instanceof Set)) { 23 | return { 24 | ok: false, 25 | errors: [ 26 | { 27 | path: opts?.breadcrumbsPrefix ?? [], 28 | message: getErrorMessageForIncorrectType(parsed, "Set"), 29 | }, 30 | ], 31 | }; 32 | } 33 | const jsonList = listSchema.json([...parsed], opts); 34 | return jsonList; 35 | }, 36 | getType: () => SchemaType.SET, 37 | }; 38 | 39 | return { 40 | ...maybeSkipValidation(baseSchema), 41 | ...getSchemaUtils(baseSchema), 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType, ValidationError } from "../../Schema"; 2 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 3 | import { getSchemaUtils } from "../schema-utils"; 4 | import { inferParsedUnidiscriminatedUnionSchema, inferRawUnidiscriminatedUnionSchema } from "./types"; 5 | 6 | export function undiscriminatedUnion, ...Schema[]]>( 7 | schemas: Schemas 8 | ): Schema, inferParsedUnidiscriminatedUnionSchema> { 9 | const baseSchema: BaseSchema< 10 | inferRawUnidiscriminatedUnionSchema, 11 | inferParsedUnidiscriminatedUnionSchema 12 | > = { 13 | parse: (raw, opts) => { 14 | return validateAndTransformUndiscriminatedUnion>( 15 | (schema, opts) => schema.parse(raw, opts), 16 | schemas, 17 | opts 18 | ); 19 | }, 20 | json: (parsed, opts) => { 21 | return validateAndTransformUndiscriminatedUnion>( 22 | (schema, opts) => schema.json(parsed, opts), 23 | schemas, 24 | opts 25 | ); 26 | }, 27 | getType: () => SchemaType.UNDISCRIMINATED_UNION, 28 | }; 29 | 30 | return { 31 | ...maybeSkipValidation(baseSchema), 32 | ...getSchemaUtils(baseSchema), 33 | }; 34 | } 35 | 36 | function validateAndTransformUndiscriminatedUnion( 37 | transform: (schema: Schema, opts: SchemaOptions) => MaybeValid, 38 | schemas: Schema[], 39 | opts: SchemaOptions | undefined 40 | ): MaybeValid { 41 | const errors: ValidationError[] = []; 42 | for (const [index, schema] of schemas.entries()) { 43 | const transformed = transform(schema, { ...opts, skipValidation: false }); 44 | if (transformed.ok) { 45 | return transformed; 46 | } else { 47 | for (const error of transformed.errors) { 48 | errors.push({ 49 | path: error.path, 50 | message: `[Variant ${index}] ${error.message}`, 51 | }); 52 | } 53 | } 54 | } 55 | 56 | return { 57 | ok: false, 58 | errors, 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/list/list.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, MaybeValid, Schema, SchemaType, ValidationError } from "../../Schema"; 2 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 3 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 4 | import { getSchemaUtils } from "../schema-utils"; 5 | 6 | export function list(schema: Schema): Schema { 7 | const baseSchema: BaseSchema = { 8 | parse: (raw, opts) => 9 | validateAndTransformArray(raw, (item, index) => 10 | schema.parse(item, { 11 | ...opts, 12 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], 13 | }) 14 | ), 15 | json: (parsed, opts) => 16 | validateAndTransformArray(parsed, (item, index) => 17 | schema.json(item, { 18 | ...opts, 19 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], 20 | }) 21 | ), 22 | getType: () => SchemaType.LIST, 23 | }; 24 | 25 | return { 26 | ...maybeSkipValidation(baseSchema), 27 | ...getSchemaUtils(baseSchema), 28 | }; 29 | } 30 | 31 | function validateAndTransformArray( 32 | value: unknown, 33 | transformItem: (item: Raw, index: number) => MaybeValid 34 | ): MaybeValid { 35 | if (!Array.isArray(value)) { 36 | return { 37 | ok: false, 38 | errors: [ 39 | { 40 | message: getErrorMessageForIncorrectType(value, "list"), 41 | path: [], 42 | }, 43 | ], 44 | }; 45 | } 46 | 47 | const maybeValidItems = value.map((item, index) => transformItem(item, index)); 48 | 49 | return maybeValidItems.reduce>( 50 | (acc, item) => { 51 | if (acc.ok && item.ok) { 52 | return { 53 | ok: true, 54 | value: [...acc.value, item.value], 55 | }; 56 | } 57 | 58 | const errors: ValidationError[] = []; 59 | if (!acc.ok) { 60 | errors.push(...acc.errors); 61 | } 62 | if (!item.ok) { 63 | errors.push(...item.errors); 64 | } 65 | 66 | return { 67 | ok: false, 68 | errors, 69 | }; 70 | }, 71 | { ok: true, value: [] } 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/date/date.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, Schema, SchemaType } from "../../Schema"; 2 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 3 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 4 | import { getSchemaUtils } from "../schema-utils"; 5 | 6 | // https://stackoverflow.com/questions/12756159/regex-and-iso8601-formatted-datetime 7 | const ISO_8601_REGEX = 8 | /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; 9 | 10 | export function date(): Schema { 11 | const baseSchema: BaseSchema = { 12 | parse: (raw, { breadcrumbsPrefix = [] } = {}) => { 13 | if (typeof raw !== "string") { 14 | return { 15 | ok: false, 16 | errors: [ 17 | { 18 | path: breadcrumbsPrefix, 19 | message: getErrorMessageForIncorrectType(raw, "string"), 20 | }, 21 | ], 22 | }; 23 | } 24 | if (!ISO_8601_REGEX.test(raw)) { 25 | return { 26 | ok: false, 27 | errors: [ 28 | { 29 | path: breadcrumbsPrefix, 30 | message: getErrorMessageForIncorrectType(raw, "ISO 8601 date string"), 31 | }, 32 | ], 33 | }; 34 | } 35 | return { 36 | ok: true, 37 | value: new Date(raw), 38 | }; 39 | }, 40 | json: (date, { breadcrumbsPrefix = [] } = {}) => { 41 | if (date instanceof Date) { 42 | return { 43 | ok: true, 44 | value: date.toISOString(), 45 | }; 46 | } else { 47 | return { 48 | ok: false, 49 | errors: [ 50 | { 51 | path: breadcrumbsPrefix, 52 | message: getErrorMessageForIncorrectType(date, "Date object"), 53 | }, 54 | ], 55 | }; 56 | } 57 | }, 58 | getType: () => SchemaType.DATE, 59 | }; 60 | 61 | return { 62 | ...maybeSkipValidation(baseSchema), 63 | ...getSchemaUtils(baseSchema), 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Express + Fern starter repo 2 | 3 | This repo is a starter repo for building an Express server using [Fern](https://github.com/fern-api/fern). 4 | 5 | **Fern is an open source format for defining REST APIs.** Fern makes it easy to: 6 | 7 | 1. Define your API 8 | 1. Implement your API on the backend 9 | 1. Consume your API on the frontend 10 | 11 | ## Quick start 12 | 13 | ### Step 1: Fork this repo 14 | 15 | ### Step 2: Clone your fork 16 | 17 | ### Step 3: Generate code 18 | 19 | Most of the code for this server is auto-generated by Fern! 20 | 21 | In a terminal, run: 22 | 23 | ``` 24 | npm install -g fern-api 25 | fern generate 26 | ``` 27 | 28 | ### Step 4: Run the backend 29 | 30 | ``` 31 | yarn install 32 | yarn dev 33 | ``` 34 | 35 | Now you can cURL the server! 36 | 37 | ``` 38 | curl 'http://localhost:8080/movies/goodwill-hunting' 39 | ``` 40 | 41 | ## Development 42 | 43 | To get started, open a VSCode window for the root of this repo: 44 | 45 | ``` 46 | code . 47 | ``` 48 | 49 | Install the recommended extensions. These will give you a better experience in the IDE. 50 | 51 | Install dependencies: 52 | 53 | ``` 54 | yarn install 55 | ``` 56 | 57 | Run the server: 58 | 59 | ``` 60 | yarn start 61 | ``` 62 | 63 | You can edit the server code, and the server will automatically reload. 64 | 65 | ### Auto-generated server code 66 | 67 | Nearly all of the server code is generated by Fern when you run `fern generate`. 68 | The generated code lives in [src/api/generated](src/api/generated). 69 | 70 | In addition to saving you time, the auto-generated code gives you compile-time safety 71 | that your API is implemented correctly. For example, if you forget to define the `getMovie` 72 | endpoint, you'll get a compile error: 73 | 74 | ![backend mypy error](assets/missing-getMovie.png) 75 | 76 | ## Changing the API 77 | 78 | The API is defined using [Fern](https://www.buildwithfern.comhttps://www.buildwithfern.com/). The definition 79 | lives in the [fern/](fern/api/definition) directory. You can edit these YAML files 80 | to update the API. Check out the [docs](https://docs.buildwithfern.com/definition) to read more about defining APIs. 81 | 82 | Most of the server code in this repo is **automatically generated 83 | by Fern.** You can regenerate the code using the Fern CLI: 84 | 85 | ``` 86 | npm install -g fern 87 | fern generate 88 | ``` 89 | 90 | This will output newly generated code to [src/api/generated](src/api/generated). 91 | 92 | When you change your API, you'll get compile errors if you're not implementing 93 | the API correctly. 94 | 95 | ## Issues & contributing 96 | 97 | If you run into any problems while using this ticket, plesae [file an 98 | issue](https://github.com/fern-api/express-starter/issues). Of course, PRs are 99 | welcome and encouraged! 100 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object/types.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema"; 2 | import { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties"; 3 | import { ObjectLikeUtils } from "../object-like"; 4 | import { SchemaUtils } from "../schema-utils"; 5 | import { Property } from "./property"; 6 | 7 | export type ObjectSchema = BaseObjectSchema & 8 | ObjectLikeUtils & 9 | ObjectUtils & 10 | SchemaUtils; 11 | 12 | export interface BaseObjectSchema extends BaseSchema { 13 | _getRawProperties: () => (keyof Raw)[]; 14 | _getParsedProperties: () => (keyof Parsed)[]; 15 | } 16 | 17 | export interface ObjectUtils { 18 | extend: ( 19 | schemas: ObjectSchema 20 | ) => ObjectSchema; 21 | } 22 | 23 | export type inferRawObject> = O extends ObjectSchema ? Raw : never; 24 | 25 | export type inferParsedObject> = O extends ObjectSchema 26 | ? Parsed 27 | : never; 28 | 29 | export type inferObjectSchemaFromPropertySchemas> = ObjectSchema< 30 | inferRawObjectFromPropertySchemas, 31 | inferParsedObjectFromPropertySchemas 32 | >; 33 | 34 | export type inferRawObjectFromPropertySchemas> = 35 | addQuestionMarksToNullableProperties<{ 36 | [ParsedKey in keyof T as inferRawKey]: inferRawPropertySchema; 37 | }>; 38 | 39 | export type inferParsedObjectFromPropertySchemas> = 40 | addQuestionMarksToNullableProperties<{ 41 | [K in keyof T]: inferParsedPropertySchema; 42 | }>; 43 | 44 | export type PropertySchemas = Record< 45 | ParsedKeys, 46 | Property | Schema 47 | >; 48 | 49 | export type inferRawPropertySchema

| Schema> = P extends Property< 50 | any, 51 | infer Raw, 52 | any 53 | > 54 | ? Raw 55 | : P extends Schema 56 | ? inferRaw

57 | : never; 58 | 59 | export type inferParsedPropertySchema

| Schema> = P extends Property< 60 | any, 61 | any, 62 | infer Parsed 63 | > 64 | ? Parsed 65 | : P extends Schema 66 | ? inferParsed

67 | : never; 68 | 69 | export type inferRawKey< 70 | ParsedKey extends string | number | symbol, 71 | P extends Property | Schema 72 | > = P extends Property ? Raw : ParsedKey; 73 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/Schema.ts: -------------------------------------------------------------------------------- 1 | import { SchemaUtils } from "./builders"; 2 | 3 | export type Schema = BaseSchema & SchemaUtils; 4 | 5 | export type inferRaw = S extends Schema ? Raw : never; 6 | export type inferParsed = S extends Schema ? Parsed : never; 7 | 8 | export interface BaseSchema { 9 | parse: (raw: unknown, opts?: SchemaOptions) => MaybeValid; 10 | json: (parsed: unknown, opts?: SchemaOptions) => MaybeValid; 11 | getType: () => SchemaType | SchemaType; 12 | } 13 | 14 | export const SchemaType = { 15 | DATE: "date", 16 | ENUM: "enum", 17 | LIST: "list", 18 | STRING_LITERAL: "stringLiteral", 19 | BOOLEAN_LITERAL: "booleanLiteral", 20 | OBJECT: "object", 21 | ANY: "any", 22 | BOOLEAN: "boolean", 23 | NUMBER: "number", 24 | STRING: "string", 25 | UNKNOWN: "unknown", 26 | RECORD: "record", 27 | SET: "set", 28 | UNION: "union", 29 | UNDISCRIMINATED_UNION: "undiscriminatedUnion", 30 | OPTIONAL: "optional", 31 | } as const; 32 | export type SchemaType = typeof SchemaType[keyof typeof SchemaType]; 33 | 34 | export type MaybeValid = Valid | Invalid; 35 | 36 | export interface Valid { 37 | ok: true; 38 | value: T; 39 | } 40 | 41 | export interface Invalid { 42 | ok: false; 43 | errors: ValidationError[]; 44 | } 45 | 46 | export interface ValidationError { 47 | path: string[]; 48 | message: string; 49 | } 50 | 51 | export interface SchemaOptions { 52 | /** 53 | * how to handle unrecognized keys in objects 54 | * 55 | * @default "fail" 56 | */ 57 | unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; 58 | 59 | /** 60 | * whether to fail when an unrecognized discriminant value is 61 | * encountered in a union 62 | * 63 | * @default false 64 | */ 65 | allowUnrecognizedUnionMembers?: boolean; 66 | 67 | /** 68 | * whether to fail when an unrecognized enum value is encountered 69 | * 70 | * @default false 71 | */ 72 | allowUnrecognizedEnumValues?: boolean; 73 | 74 | /** 75 | * whether to allow data that doesn't conform to the schema. 76 | * invalid data is passed through without transformation. 77 | * 78 | * when this is enabled, .parse() and .json() will always 79 | * return `ok: true`. `.parseOrThrow()` and `.jsonOrThrow()` 80 | * will never fail. 81 | * 82 | * @default false 83 | */ 84 | skipValidation?: boolean; 85 | 86 | /** 87 | * each validation failure contains a "path" property, which is 88 | * the breadcrumbs to the offending node in the JSON. you can supply 89 | * a prefix that is prepended to all the errors' paths. this can be 90 | * helpful for zurg's internal debug logging. 91 | */ 92 | breadcrumbsPrefix?: string[]; 93 | 94 | /** 95 | * whether to send 'null' for optional properties explicitly set to 'undefined'. 96 | */ 97 | omitUndefined?: boolean; 98 | } 99 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object-like/getObjectLikeUtils.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema } from "../../Schema"; 2 | import { filterObject } from "../../utils/filterObject"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | import { isPlainObject } from "../../utils/isPlainObject"; 5 | import { getSchemaUtils } from "../schema-utils"; 6 | import { ObjectLikeSchema, ObjectLikeUtils } from "./types"; 7 | 8 | export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { 9 | return { 10 | withParsedProperties: (properties) => withParsedProperties(schema, properties), 11 | }; 12 | } 13 | 14 | /** 15 | * object-like utils are defined in one file to resolve issues with circular imports 16 | */ 17 | 18 | export function withParsedProperties( 19 | objectLike: BaseSchema, 20 | properties: { [K in keyof Properties]: Properties[K] | ((parsed: ParsedObjectShape) => Properties[K]) } 21 | ): ObjectLikeSchema { 22 | const objectSchema: BaseSchema = { 23 | parse: (raw, opts) => { 24 | const parsedObject = objectLike.parse(raw, opts); 25 | if (!parsedObject.ok) { 26 | return parsedObject; 27 | } 28 | 29 | const additionalProperties = Object.entries(properties).reduce>( 30 | (processed, [key, value]) => { 31 | return { 32 | ...processed, 33 | [key]: typeof value === "function" ? value(parsedObject.value) : value, 34 | }; 35 | }, 36 | {} 37 | ); 38 | 39 | return { 40 | ok: true, 41 | value: { 42 | ...parsedObject.value, 43 | ...(additionalProperties as Properties), 44 | }, 45 | }; 46 | }, 47 | 48 | json: (parsed, opts) => { 49 | if (!isPlainObject(parsed)) { 50 | return { 51 | ok: false, 52 | errors: [ 53 | { 54 | path: opts?.breadcrumbsPrefix ?? [], 55 | message: getErrorMessageForIncorrectType(parsed, "object"), 56 | }, 57 | ], 58 | }; 59 | } 60 | 61 | // strip out added properties 62 | const addedPropertyKeys = new Set(Object.keys(properties)); 63 | const parsedWithoutAddedProperties = filterObject( 64 | parsed, 65 | Object.keys(parsed).filter((key) => !addedPropertyKeys.has(key)) 66 | ); 67 | 68 | return objectLike.json(parsedWithoutAddedProperties as ParsedObjectShape, opts); 69 | }, 70 | 71 | getType: () => objectLike.getType(), 72 | }; 73 | 74 | return { 75 | ...objectSchema, 76 | ...getSchemaUtils(objectSchema), 77 | ...getObjectLikeUtils(objectSchema), 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/schema-utils/getSchemaUtils.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, Schema, SchemaOptions, SchemaType } from "../../Schema"; 2 | import { JsonError } from "./JsonError"; 3 | import { ParseError } from "./ParseError"; 4 | 5 | export interface SchemaUtils { 6 | optional: () => Schema; 7 | transform: (transformer: SchemaTransformer) => Schema; 8 | parseOrThrow: (raw: unknown, opts?: SchemaOptions) => Parsed; 9 | jsonOrThrow: (raw: unknown, opts?: SchemaOptions) => Raw; 10 | } 11 | 12 | export interface SchemaTransformer { 13 | transform: (parsed: Parsed) => Transformed; 14 | untransform: (transformed: any) => Parsed; 15 | } 16 | 17 | export function getSchemaUtils(schema: BaseSchema): SchemaUtils { 18 | return { 19 | optional: () => optional(schema), 20 | transform: (transformer) => transform(schema, transformer), 21 | parseOrThrow: (raw, opts) => { 22 | const parsed = schema.parse(raw, opts); 23 | if (parsed.ok) { 24 | return parsed.value; 25 | } 26 | throw new ParseError(parsed.errors); 27 | }, 28 | jsonOrThrow: (parsed, opts) => { 29 | const raw = schema.json(parsed, opts); 30 | if (raw.ok) { 31 | return raw.value; 32 | } 33 | throw new JsonError(raw.errors); 34 | }, 35 | }; 36 | } 37 | 38 | /** 39 | * schema utils are defined in one file to resolve issues with circular imports 40 | */ 41 | 42 | export function optional( 43 | schema: BaseSchema 44 | ): Schema { 45 | const baseSchema: BaseSchema = { 46 | parse: (raw, opts) => { 47 | if (raw == null) { 48 | return { 49 | ok: true, 50 | value: undefined, 51 | }; 52 | } 53 | return schema.parse(raw, opts); 54 | }, 55 | json: (parsed, opts) => { 56 | if (opts?.omitUndefined && parsed === undefined) { 57 | return { 58 | ok: true, 59 | value: undefined, 60 | }; 61 | } 62 | if (parsed == null) { 63 | return { 64 | ok: true, 65 | value: null, 66 | }; 67 | } 68 | return schema.json(parsed, opts); 69 | }, 70 | getType: () => SchemaType.OPTIONAL, 71 | }; 72 | 73 | return { 74 | ...baseSchema, 75 | ...getSchemaUtils(baseSchema), 76 | }; 77 | } 78 | 79 | export function transform( 80 | schema: BaseSchema, 81 | transformer: SchemaTransformer 82 | ): Schema { 83 | const baseSchema: BaseSchema = { 84 | parse: (raw, opts) => { 85 | const parsed = schema.parse(raw, opts); 86 | if (!parsed.ok) { 87 | return parsed; 88 | } 89 | return { 90 | ok: true, 91 | value: transformer.transform(parsed.value), 92 | }; 93 | }, 94 | json: (transformed, opts) => { 95 | const parsed = transformer.untransform(transformed); 96 | return schema.json(parsed, opts); 97 | }, 98 | getType: () => schema.getType(), 99 | }; 100 | 101 | return { 102 | ...baseSchema, 103 | ...getSchemaUtils(baseSchema), 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/record/record.ts: -------------------------------------------------------------------------------- 1 | import { MaybeValid, Schema, SchemaType, ValidationError } from "../../Schema"; 2 | import { entries } from "../../utils/entries"; 3 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 4 | import { isPlainObject } from "../../utils/isPlainObject"; 5 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 6 | import { getSchemaUtils } from "../schema-utils"; 7 | import { BaseRecordSchema, RecordSchema } from "./types"; 8 | 9 | export function record( 10 | keySchema: Schema, 11 | valueSchema: Schema 12 | ): RecordSchema { 13 | const baseSchema: BaseRecordSchema = { 14 | parse: (raw, opts) => { 15 | return validateAndTransformRecord({ 16 | value: raw, 17 | isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, 18 | transformKey: (key) => 19 | keySchema.parse(key, { 20 | ...opts, 21 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], 22 | }), 23 | transformValue: (value, key) => 24 | valueSchema.parse(value, { 25 | ...opts, 26 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], 27 | }), 28 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 29 | }); 30 | }, 31 | json: (parsed, opts) => { 32 | return validateAndTransformRecord({ 33 | value: parsed, 34 | isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, 35 | transformKey: (key) => 36 | keySchema.json(key, { 37 | ...opts, 38 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], 39 | }), 40 | transformValue: (value, key) => 41 | valueSchema.json(value, { 42 | ...opts, 43 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], 44 | }), 45 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 46 | }); 47 | }, 48 | getType: () => SchemaType.RECORD, 49 | }; 50 | 51 | return { 52 | ...maybeSkipValidation(baseSchema), 53 | ...getSchemaUtils(baseSchema), 54 | }; 55 | } 56 | 57 | function validateAndTransformRecord({ 58 | value, 59 | isKeyNumeric, 60 | transformKey, 61 | transformValue, 62 | breadcrumbsPrefix = [], 63 | }: { 64 | value: unknown; 65 | isKeyNumeric: boolean; 66 | transformKey: (key: string | number) => MaybeValid; 67 | transformValue: (value: unknown, key: string | number) => MaybeValid; 68 | breadcrumbsPrefix: string[] | undefined; 69 | }): MaybeValid> { 70 | if (!isPlainObject(value)) { 71 | return { 72 | ok: false, 73 | errors: [ 74 | { 75 | path: breadcrumbsPrefix, 76 | message: getErrorMessageForIncorrectType(value, "object"), 77 | }, 78 | ], 79 | }; 80 | } 81 | 82 | return entries(value).reduce>>( 83 | (accPromise, [stringKey, value]) => { 84 | // skip nullish keys 85 | if (value == null) { 86 | return accPromise; 87 | } 88 | 89 | const acc = accPromise; 90 | 91 | let key: string | number = stringKey; 92 | if (isKeyNumeric) { 93 | const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; 94 | if (!isNaN(numberKey)) { 95 | key = numberKey; 96 | } 97 | } 98 | const transformedKey = transformKey(key); 99 | 100 | const transformedValue = transformValue(value, key); 101 | 102 | if (acc.ok && transformedKey.ok && transformedValue.ok) { 103 | return { 104 | ok: true, 105 | value: { 106 | ...acc.value, 107 | [transformedKey.value]: transformedValue.value, 108 | }, 109 | }; 110 | } 111 | 112 | const errors: ValidationError[] = []; 113 | if (!acc.ok) { 114 | errors.push(...acc.errors); 115 | } 116 | if (!transformedKey.ok) { 117 | errors.push(...transformedKey.errors); 118 | } 119 | if (!transformedValue.ok) { 120 | errors.push(...transformedValue.errors); 121 | } 122 | 123 | return { 124 | ok: false, 125 | errors, 126 | }; 127 | }, 128 | { ok: true, value: {} as Record } 129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /src/api/generated/api/resources/imdb/service/ImdbService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by Fern from our API Definition. 3 | */ 4 | 5 | import * as FernApi from "../../../index"; 6 | import express from "express"; 7 | import * as serializers from "../../../../serialization/index"; 8 | import * as errors from "../../../../errors/index"; 9 | 10 | export interface ImdbServiceMethods { 11 | createMovie( 12 | req: express.Request, 13 | res: { 14 | send: (responseBody: FernApi.MovieId) => Promise; 15 | cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; 16 | locals: any; 17 | }, 18 | next: express.NextFunction 19 | ): void | Promise; 20 | getMovie( 21 | req: express.Request< 22 | { 23 | movieId: serializers.MovieId.Raw; 24 | }, 25 | FernApi.Movie, 26 | never, 27 | never 28 | >, 29 | res: { 30 | send: (responseBody: FernApi.Movie) => Promise; 31 | cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; 32 | locals: any; 33 | }, 34 | next: express.NextFunction 35 | ): void | Promise; 36 | } 37 | 38 | export class ImdbService { 39 | private router; 40 | 41 | constructor(private readonly methods: ImdbServiceMethods, middleware: express.RequestHandler[] = []) { 42 | this.router = express.Router({ mergeParams: true }).use( 43 | express.json({ 44 | strict: false, 45 | }), 46 | ...middleware 47 | ); 48 | } 49 | 50 | public addMiddleware(handler: express.RequestHandler): this { 51 | this.router.use(handler); 52 | return this; 53 | } 54 | 55 | public toRouter(): express.Router { 56 | this.router.post("/create-movie", async (req, res, next) => { 57 | const request = serializers.CreateMovieRequest.parse(req.body); 58 | if (request.ok) { 59 | req.body = request.value; 60 | try { 61 | await this.methods.createMovie( 62 | req as any, 63 | { 64 | send: async (responseBody) => { 65 | res.json( 66 | serializers.MovieId.jsonOrThrow(responseBody, { unrecognizedObjectKeys: "strip" }) 67 | ); 68 | }, 69 | cookie: res.cookie.bind(res), 70 | locals: res.locals, 71 | }, 72 | next 73 | ); 74 | next(); 75 | } catch (error) { 76 | if (error instanceof errors.FernApiError) { 77 | console.warn( 78 | `Endpoint 'createMovie' unexpectedly threw ${error.constructor.name}.` + 79 | ` If this was intentional, please add ${error.constructor.name} to` + 80 | " the endpoint's errors list in your Fern Definition." 81 | ); 82 | await error.send(res); 83 | } else { 84 | res.status(500).json("Internal Server Error"); 85 | } 86 | next(error); 87 | } 88 | } else { 89 | res.status(422).json({ 90 | errors: request.errors.map( 91 | (error) => ["request", ...error.path].join(" -> ") + ": " + error.message 92 | ), 93 | }); 94 | next(request.errors); 95 | } 96 | }); 97 | this.router.get("/:movieId", async (req, res, next) => { 98 | try { 99 | await this.methods.getMovie( 100 | req as any, 101 | { 102 | send: async (responseBody) => { 103 | res.json(serializers.Movie.jsonOrThrow(responseBody, { unrecognizedObjectKeys: "strip" })); 104 | }, 105 | cookie: res.cookie.bind(res), 106 | locals: res.locals, 107 | }, 108 | next 109 | ); 110 | next(); 111 | } catch (error) { 112 | if (error instanceof errors.FernApiError) { 113 | switch (error.errorName) { 114 | case "MovieDoesNotExistError": 115 | break; 116 | default: 117 | console.warn( 118 | `Endpoint 'getMovie' unexpectedly threw ${error.constructor.name}.` + 119 | ` If this was intentional, please add ${error.constructor.name} to` + 120 | " the endpoint's errors list in your Fern Definition." 121 | ); 122 | } 123 | await error.send(res); 124 | } else { 125 | res.status(500).json("Internal Server Error"); 126 | } 127 | next(error); 128 | } 129 | }); 130 | return this.router; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/union/union.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchema, MaybeValid, SchemaType } from "../../Schema"; 2 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 3 | import { isPlainObject } from "../../utils/isPlainObject"; 4 | import { keys } from "../../utils/keys"; 5 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 6 | import { enum_ } from "../enum"; 7 | import { ObjectSchema } from "../object"; 8 | import { getObjectLikeUtils, ObjectLikeSchema } from "../object-like"; 9 | import { getSchemaUtils } from "../schema-utils"; 10 | import { Discriminant } from "./discriminant"; 11 | import { inferParsedDiscriminant, inferParsedUnion, inferRawDiscriminant, inferRawUnion, UnionSubtypes } from "./types"; 12 | 13 | export function union, U extends UnionSubtypes>( 14 | discriminant: D, 15 | union: U 16 | ): ObjectLikeSchema, inferParsedUnion> { 17 | const rawDiscriminant = 18 | typeof discriminant === "string" ? discriminant : (discriminant.rawDiscriminant as inferRawDiscriminant); 19 | const parsedDiscriminant = 20 | typeof discriminant === "string" 21 | ? discriminant 22 | : (discriminant.parsedDiscriminant as inferParsedDiscriminant); 23 | 24 | const discriminantValueSchema = enum_(keys(union) as string[]); 25 | 26 | const baseSchema: BaseSchema, inferParsedUnion> = { 27 | parse: (raw, opts) => { 28 | return transformAndValidateUnion({ 29 | value: raw, 30 | discriminant: rawDiscriminant, 31 | transformedDiscriminant: parsedDiscriminant, 32 | transformDiscriminantValue: (discriminantValue) => 33 | discriminantValueSchema.parse(discriminantValue, { 34 | allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, 35 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawDiscriminant], 36 | }), 37 | getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], 38 | allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, 39 | transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => 40 | additionalPropertiesSchema.parse(additionalProperties, opts), 41 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 42 | }); 43 | }, 44 | json: (parsed, opts) => { 45 | return transformAndValidateUnion({ 46 | value: parsed, 47 | discriminant: parsedDiscriminant, 48 | transformedDiscriminant: rawDiscriminant, 49 | transformDiscriminantValue: (discriminantValue) => 50 | discriminantValueSchema.json(discriminantValue, { 51 | allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, 52 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedDiscriminant], 53 | }), 54 | getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], 55 | allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, 56 | transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => 57 | additionalPropertiesSchema.json(additionalProperties, opts), 58 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 59 | }); 60 | }, 61 | getType: () => SchemaType.UNION, 62 | }; 63 | 64 | return { 65 | ...maybeSkipValidation(baseSchema), 66 | ...getSchemaUtils(baseSchema), 67 | ...getObjectLikeUtils(baseSchema), 68 | }; 69 | } 70 | 71 | function transformAndValidateUnion< 72 | TransformedDiscriminant extends string, 73 | TransformedDiscriminantValue extends string, 74 | TransformedAdditionalProperties 75 | >({ 76 | value, 77 | discriminant, 78 | transformedDiscriminant, 79 | transformDiscriminantValue, 80 | getAdditionalPropertiesSchema, 81 | allowUnrecognizedUnionMembers = false, 82 | transformAdditionalProperties, 83 | breadcrumbsPrefix = [], 84 | }: { 85 | value: unknown; 86 | discriminant: string; 87 | transformedDiscriminant: TransformedDiscriminant; 88 | transformDiscriminantValue: (discriminantValue: unknown) => MaybeValid; 89 | getAdditionalPropertiesSchema: (discriminantValue: string) => ObjectSchema | undefined; 90 | allowUnrecognizedUnionMembers: boolean | undefined; 91 | transformAdditionalProperties: ( 92 | additionalProperties: unknown, 93 | additionalPropertiesSchema: ObjectSchema 94 | ) => MaybeValid; 95 | breadcrumbsPrefix: string[] | undefined; 96 | }): MaybeValid & TransformedAdditionalProperties> { 97 | if (!isPlainObject(value)) { 98 | return { 99 | ok: false, 100 | errors: [ 101 | { 102 | path: breadcrumbsPrefix, 103 | message: getErrorMessageForIncorrectType(value, "object"), 104 | }, 105 | ], 106 | }; 107 | } 108 | 109 | const { [discriminant]: discriminantValue, ...additionalProperties } = value; 110 | 111 | if (discriminantValue == null) { 112 | return { 113 | ok: false, 114 | errors: [ 115 | { 116 | path: breadcrumbsPrefix, 117 | message: `Missing discriminant ("${discriminant}")`, 118 | }, 119 | ], 120 | }; 121 | } 122 | 123 | const transformedDiscriminantValue = transformDiscriminantValue(discriminantValue); 124 | if (!transformedDiscriminantValue.ok) { 125 | return { 126 | ok: false, 127 | errors: transformedDiscriminantValue.errors, 128 | }; 129 | } 130 | 131 | const additionalPropertiesSchema = getAdditionalPropertiesSchema(transformedDiscriminantValue.value); 132 | 133 | if (additionalPropertiesSchema == null) { 134 | if (allowUnrecognizedUnionMembers) { 135 | return { 136 | ok: true, 137 | value: { 138 | [transformedDiscriminant]: transformedDiscriminantValue.value, 139 | ...additionalProperties, 140 | } as Record & TransformedAdditionalProperties, 141 | }; 142 | } else { 143 | return { 144 | ok: false, 145 | errors: [ 146 | { 147 | path: [...breadcrumbsPrefix, discriminant], 148 | message: "Unexpected discriminant value", 149 | }, 150 | ], 151 | }; 152 | } 153 | } 154 | 155 | const transformedAdditionalProperties = transformAdditionalProperties( 156 | additionalProperties, 157 | additionalPropertiesSchema 158 | ); 159 | if (!transformedAdditionalProperties.ok) { 160 | return transformedAdditionalProperties; 161 | } 162 | 163 | return { 164 | ok: true, 165 | value: { 166 | [transformedDiscriminant]: discriminantValue, 167 | ...transformedAdditionalProperties.value, 168 | } as Record & TransformedAdditionalProperties, 169 | }; 170 | } 171 | -------------------------------------------------------------------------------- /src/api/generated/core/schemas/builders/object/object.ts: -------------------------------------------------------------------------------- 1 | import { MaybeValid, Schema, SchemaType, ValidationError } from "../../Schema"; 2 | import { entries } from "../../utils/entries"; 3 | import { filterObject } from "../../utils/filterObject"; 4 | import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; 5 | import { isPlainObject } from "../../utils/isPlainObject"; 6 | import { keys } from "../../utils/keys"; 7 | import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; 8 | import { partition } from "../../utils/partition"; 9 | import { getObjectLikeUtils } from "../object-like"; 10 | import { getSchemaUtils } from "../schema-utils"; 11 | import { isProperty } from "./property"; 12 | import { 13 | BaseObjectSchema, 14 | inferObjectSchemaFromPropertySchemas, 15 | inferParsedObjectFromPropertySchemas, 16 | inferRawObjectFromPropertySchemas, 17 | ObjectSchema, 18 | ObjectUtils, 19 | PropertySchemas, 20 | } from "./types"; 21 | 22 | interface ObjectPropertyWithRawKey { 23 | rawKey: string; 24 | parsedKey: string; 25 | valueSchema: Schema; 26 | } 27 | 28 | export function object>( 29 | schemas: T 30 | ): inferObjectSchemaFromPropertySchemas { 31 | const baseSchema: BaseObjectSchema< 32 | inferRawObjectFromPropertySchemas, 33 | inferParsedObjectFromPropertySchemas 34 | > = { 35 | _getRawProperties: () => 36 | Object.entries(schemas).map(([parsedKey, propertySchema]) => 37 | isProperty(propertySchema) ? propertySchema.rawKey : parsedKey 38 | ) as unknown as (keyof inferRawObjectFromPropertySchemas)[], 39 | _getParsedProperties: () => keys(schemas) as unknown as (keyof inferParsedObjectFromPropertySchemas)[], 40 | 41 | parse: (raw, opts) => { 42 | const rawKeyToProperty: Record = {}; 43 | const requiredKeys: string[] = []; 44 | 45 | for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { 46 | const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; 47 | const valueSchema: Schema = isProperty(schemaOrObjectProperty) 48 | ? schemaOrObjectProperty.valueSchema 49 | : schemaOrObjectProperty; 50 | 51 | const property: ObjectPropertyWithRawKey = { 52 | rawKey, 53 | parsedKey: parsedKey as string, 54 | valueSchema, 55 | }; 56 | 57 | rawKeyToProperty[rawKey] = property; 58 | 59 | if (isSchemaRequired(valueSchema)) { 60 | requiredKeys.push(rawKey); 61 | } 62 | } 63 | 64 | return validateAndTransformObject({ 65 | value: raw, 66 | requiredKeys, 67 | getProperty: (rawKey) => { 68 | const property = rawKeyToProperty[rawKey]; 69 | if (property == null) { 70 | return undefined; 71 | } 72 | return { 73 | transformedKey: property.parsedKey, 74 | transform: (propertyValue) => 75 | property.valueSchema.parse(propertyValue, { 76 | ...opts, 77 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], 78 | }), 79 | }; 80 | }, 81 | unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, 82 | skipValidation: opts?.skipValidation, 83 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 84 | omitUndefined: opts?.omitUndefined, 85 | }); 86 | }, 87 | 88 | json: (parsed, opts) => { 89 | const requiredKeys: string[] = []; 90 | 91 | for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { 92 | const valueSchema: Schema = isProperty(schemaOrObjectProperty) 93 | ? schemaOrObjectProperty.valueSchema 94 | : schemaOrObjectProperty; 95 | 96 | if (isSchemaRequired(valueSchema)) { 97 | requiredKeys.push(parsedKey as string); 98 | } 99 | } 100 | 101 | return validateAndTransformObject({ 102 | value: parsed, 103 | requiredKeys, 104 | getProperty: ( 105 | parsedKey 106 | ): { transformedKey: string; transform: (propertyValue: unknown) => MaybeValid } | undefined => { 107 | const property = schemas[parsedKey as keyof T]; 108 | 109 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 110 | if (property == null) { 111 | return undefined; 112 | } 113 | 114 | if (isProperty(property)) { 115 | return { 116 | transformedKey: property.rawKey, 117 | transform: (propertyValue) => 118 | property.valueSchema.json(propertyValue, { 119 | ...opts, 120 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], 121 | }), 122 | }; 123 | } else { 124 | return { 125 | transformedKey: parsedKey, 126 | transform: (propertyValue) => 127 | property.json(propertyValue, { 128 | ...opts, 129 | breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], 130 | }), 131 | }; 132 | } 133 | }, 134 | unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, 135 | skipValidation: opts?.skipValidation, 136 | breadcrumbsPrefix: opts?.breadcrumbsPrefix, 137 | omitUndefined: opts?.omitUndefined, 138 | }); 139 | }, 140 | 141 | getType: () => SchemaType.OBJECT, 142 | }; 143 | 144 | return { 145 | ...maybeSkipValidation(baseSchema), 146 | ...getSchemaUtils(baseSchema), 147 | ...getObjectLikeUtils(baseSchema), 148 | ...getObjectUtils(baseSchema), 149 | }; 150 | } 151 | 152 | function validateAndTransformObject({ 153 | value, 154 | requiredKeys, 155 | getProperty, 156 | unrecognizedObjectKeys = "fail", 157 | skipValidation = false, 158 | breadcrumbsPrefix = [], 159 | }: { 160 | value: unknown; 161 | requiredKeys: string[]; 162 | getProperty: ( 163 | preTransformedKey: string 164 | ) => { transformedKey: string; transform: (propertyValue: unknown) => MaybeValid } | undefined; 165 | unrecognizedObjectKeys: "fail" | "passthrough" | "strip" | undefined; 166 | skipValidation: boolean | undefined; 167 | breadcrumbsPrefix: string[] | undefined; 168 | omitUndefined: boolean | undefined; 169 | }): MaybeValid { 170 | if (!isPlainObject(value)) { 171 | return { 172 | ok: false, 173 | errors: [ 174 | { 175 | path: breadcrumbsPrefix, 176 | message: getErrorMessageForIncorrectType(value, "object"), 177 | }, 178 | ], 179 | }; 180 | } 181 | 182 | const missingRequiredKeys = new Set(requiredKeys); 183 | const errors: ValidationError[] = []; 184 | const transformed: Record = {}; 185 | 186 | for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { 187 | const property = getProperty(preTransformedKey); 188 | 189 | if (property != null) { 190 | missingRequiredKeys.delete(preTransformedKey); 191 | 192 | const value = property.transform(preTransformedItemValue); 193 | if (value.ok) { 194 | transformed[property.transformedKey] = value.value; 195 | } else { 196 | transformed[preTransformedKey] = preTransformedItemValue; 197 | errors.push(...value.errors); 198 | } 199 | } else { 200 | switch (unrecognizedObjectKeys) { 201 | case "fail": 202 | errors.push({ 203 | path: [...breadcrumbsPrefix, preTransformedKey], 204 | message: `Unexpected key "${preTransformedKey}"`, 205 | }); 206 | break; 207 | case "strip": 208 | break; 209 | case "passthrough": 210 | transformed[preTransformedKey] = preTransformedItemValue; 211 | break; 212 | } 213 | } 214 | } 215 | 216 | errors.push( 217 | ...requiredKeys 218 | .filter((key) => missingRequiredKeys.has(key)) 219 | .map((key) => ({ 220 | path: breadcrumbsPrefix, 221 | message: `Missing required key "${key}"`, 222 | })) 223 | ); 224 | 225 | if (errors.length === 0 || skipValidation) { 226 | return { 227 | ok: true, 228 | value: transformed as Transformed, 229 | }; 230 | } else { 231 | return { 232 | ok: false, 233 | errors, 234 | }; 235 | } 236 | } 237 | 238 | export function getObjectUtils(schema: BaseObjectSchema): ObjectUtils { 239 | return { 240 | extend: (extension: ObjectSchema) => { 241 | const baseSchema: BaseObjectSchema = { 242 | _getParsedProperties: () => [...schema._getParsedProperties(), ...extension._getParsedProperties()], 243 | _getRawProperties: () => [...schema._getRawProperties(), ...extension._getRawProperties()], 244 | parse: (raw, opts) => { 245 | return validateAndTransformExtendedObject({ 246 | extensionKeys: extension._getRawProperties(), 247 | value: raw, 248 | transformBase: (rawBase) => schema.parse(rawBase, opts), 249 | transformExtension: (rawExtension) => extension.parse(rawExtension, opts), 250 | }); 251 | }, 252 | json: (parsed, opts) => { 253 | return validateAndTransformExtendedObject({ 254 | extensionKeys: extension._getParsedProperties(), 255 | value: parsed, 256 | transformBase: (parsedBase) => schema.json(parsedBase, opts), 257 | transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), 258 | }); 259 | }, 260 | getType: () => SchemaType.OBJECT, 261 | }; 262 | 263 | return { 264 | ...baseSchema, 265 | ...getSchemaUtils(baseSchema), 266 | ...getObjectLikeUtils(baseSchema), 267 | ...getObjectUtils(baseSchema), 268 | }; 269 | }, 270 | }; 271 | } 272 | 273 | function validateAndTransformExtendedObject({ 274 | extensionKeys, 275 | value, 276 | transformBase, 277 | transformExtension, 278 | }: { 279 | extensionKeys: (keyof PreTransformedExtension)[]; 280 | value: unknown; 281 | transformBase: (value: unknown) => MaybeValid; 282 | transformExtension: (value: unknown) => MaybeValid; 283 | }): MaybeValid { 284 | const extensionPropertiesSet = new Set(extensionKeys); 285 | const [extensionProperties, baseProperties] = partition(keys(value), (key) => 286 | extensionPropertiesSet.has(key as keyof PreTransformedExtension) 287 | ); 288 | 289 | const transformedBase = transformBase(filterObject(value, baseProperties)); 290 | const transformedExtension = transformExtension(filterObject(value, extensionProperties)); 291 | 292 | if (transformedBase.ok && transformedExtension.ok) { 293 | return { 294 | ok: true, 295 | value: { 296 | ...transformedBase.value, 297 | ...transformedExtension.value, 298 | }, 299 | }; 300 | } else { 301 | return { 302 | ok: false, 303 | errors: [ 304 | ...(transformedBase.ok ? [] : transformedBase.errors), 305 | ...(transformedExtension.ok ? [] : transformedExtension.errors), 306 | ], 307 | }; 308 | } 309 | } 310 | 311 | function isSchemaRequired(schema: Schema): boolean { 312 | return !isSchemaOptional(schema); 313 | } 314 | 315 | function isSchemaOptional(schema: Schema): boolean { 316 | switch (schema.getType()) { 317 | case SchemaType.ANY: 318 | case SchemaType.UNKNOWN: 319 | case SchemaType.OPTIONAL: 320 | return true; 321 | default: 322 | return false; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aashutoshrathi/word-wrap@^1.2.3": 6 | version "1.2.6" 7 | resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" 8 | integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== 9 | 10 | "@cspotcode/source-map-support@^0.8.0": 11 | version "0.8.1" 12 | resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" 13 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== 14 | dependencies: 15 | "@jridgewell/trace-mapping" "0.3.9" 16 | 17 | "@eslint-community/eslint-utils@^4.2.0": 18 | version "4.2.0" 19 | resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz" 20 | integrity sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ== 21 | dependencies: 22 | eslint-visitor-keys "^3.3.0" 23 | 24 | "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": 25 | version "4.10.0" 26 | resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" 27 | integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== 28 | 29 | "@eslint/eslintrc@^2.1.4": 30 | version "2.1.4" 31 | resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" 32 | integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== 33 | dependencies: 34 | ajv "^6.12.4" 35 | debug "^4.3.2" 36 | espree "^9.6.0" 37 | globals "^13.19.0" 38 | ignore "^5.2.0" 39 | import-fresh "^3.2.1" 40 | js-yaml "^4.1.0" 41 | minimatch "^3.1.2" 42 | strip-json-comments "^3.1.1" 43 | 44 | "@eslint/js@8.55.0": 45 | version "8.55.0" 46 | resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6" 47 | integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA== 48 | 49 | "@humanwhocodes/config-array@^0.11.13": 50 | version "0.11.13" 51 | resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" 52 | integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== 53 | dependencies: 54 | "@humanwhocodes/object-schema" "^2.0.1" 55 | debug "^4.1.1" 56 | minimatch "^3.0.5" 57 | 58 | "@humanwhocodes/module-importer@^1.0.1": 59 | version "1.0.1" 60 | resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" 61 | integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== 62 | 63 | "@humanwhocodes/object-schema@^2.0.1": 64 | version "2.0.1" 65 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" 66 | integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== 67 | 68 | "@jridgewell/resolve-uri@^3.0.3": 69 | version "3.1.0" 70 | resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" 71 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 72 | 73 | "@jridgewell/sourcemap-codec@^1.4.10": 74 | version "1.4.14" 75 | resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" 76 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 77 | 78 | "@jridgewell/trace-mapping@0.3.9": 79 | version "0.3.9" 80 | resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" 81 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== 82 | dependencies: 83 | "@jridgewell/resolve-uri" "^3.0.3" 84 | "@jridgewell/sourcemap-codec" "^1.4.10" 85 | 86 | "@nodelib/fs.scandir@2.1.5": 87 | version "2.1.5" 88 | resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" 89 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 90 | dependencies: 91 | "@nodelib/fs.stat" "2.0.5" 92 | run-parallel "^1.1.9" 93 | 94 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 95 | version "2.0.5" 96 | resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" 97 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 98 | 99 | "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": 100 | version "1.2.8" 101 | resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" 102 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 103 | dependencies: 104 | "@nodelib/fs.scandir" "2.1.5" 105 | fastq "^1.6.0" 106 | 107 | "@tsconfig/node10@^1.0.7": 108 | version "1.0.9" 109 | resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" 110 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== 111 | 112 | "@tsconfig/node12@^1.0.7": 113 | version "1.0.11" 114 | resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" 115 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== 116 | 117 | "@tsconfig/node14@^1.0.0": 118 | version "1.0.3" 119 | resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" 120 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== 121 | 122 | "@tsconfig/node16@^1.0.2": 123 | version "1.0.3" 124 | resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz" 125 | integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== 126 | 127 | "@types/body-parser@*": 128 | version "1.19.2" 129 | resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" 130 | integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== 131 | dependencies: 132 | "@types/connect" "*" 133 | "@types/node" "*" 134 | 135 | "@types/connect@*": 136 | version "3.4.35" 137 | resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" 138 | integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== 139 | dependencies: 140 | "@types/node" "*" 141 | 142 | "@types/cors@^2.8.13": 143 | version "2.8.13" 144 | resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz" 145 | integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== 146 | dependencies: 147 | "@types/node" "*" 148 | 149 | "@types/express-serve-static-core@^4.17.33": 150 | version "4.17.33" 151 | resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz" 152 | integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== 153 | dependencies: 154 | "@types/node" "*" 155 | "@types/qs" "*" 156 | "@types/range-parser" "*" 157 | 158 | "@types/express@^4.17.16": 159 | version "4.17.17" 160 | resolved "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz" 161 | integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== 162 | dependencies: 163 | "@types/body-parser" "*" 164 | "@types/express-serve-static-core" "^4.17.33" 165 | "@types/qs" "*" 166 | "@types/serve-static" "*" 167 | 168 | "@types/json-schema@^7.0.9": 169 | version "7.0.11" 170 | resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" 171 | integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== 172 | 173 | "@types/mime@*": 174 | version "3.0.1" 175 | resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" 176 | integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== 177 | 178 | "@types/node@*", "@types/node@^22.5.0": 179 | version "22.5.0" 180 | resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.0.tgz#10f01fe9465166b4cab72e75f60d8b99d019f958" 181 | integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== 182 | dependencies: 183 | undici-types "~6.19.2" 184 | 185 | "@types/qs@*": 186 | version "6.9.7" 187 | resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" 188 | integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== 189 | 190 | "@types/range-parser@*": 191 | version "1.2.4" 192 | resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" 193 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== 194 | 195 | "@types/semver@^7.3.12": 196 | version "7.3.13" 197 | resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz" 198 | integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== 199 | 200 | "@types/serve-static@*": 201 | version "1.15.1" 202 | resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz" 203 | integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== 204 | dependencies: 205 | "@types/mime" "*" 206 | "@types/node" "*" 207 | 208 | "@typescript-eslint/eslint-plugin@latest": 209 | version "5.62.0" 210 | resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" 211 | integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== 212 | dependencies: 213 | "@eslint-community/regexpp" "^4.4.0" 214 | "@typescript-eslint/scope-manager" "5.62.0" 215 | "@typescript-eslint/type-utils" "5.62.0" 216 | "@typescript-eslint/utils" "5.62.0" 217 | debug "^4.3.4" 218 | graphemer "^1.4.0" 219 | ignore "^5.2.0" 220 | natural-compare-lite "^1.4.0" 221 | semver "^7.3.7" 222 | tsutils "^3.21.0" 223 | 224 | "@typescript-eslint/parser@latest": 225 | version "5.62.0" 226 | resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" 227 | integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== 228 | dependencies: 229 | "@typescript-eslint/scope-manager" "5.62.0" 230 | "@typescript-eslint/types" "5.62.0" 231 | "@typescript-eslint/typescript-estree" "5.62.0" 232 | debug "^4.3.4" 233 | 234 | "@typescript-eslint/scope-manager@5.62.0": 235 | version "5.62.0" 236 | resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" 237 | integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== 238 | dependencies: 239 | "@typescript-eslint/types" "5.62.0" 240 | "@typescript-eslint/visitor-keys" "5.62.0" 241 | 242 | "@typescript-eslint/type-utils@5.62.0": 243 | version "5.62.0" 244 | resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" 245 | integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== 246 | dependencies: 247 | "@typescript-eslint/typescript-estree" "5.62.0" 248 | "@typescript-eslint/utils" "5.62.0" 249 | debug "^4.3.4" 250 | tsutils "^3.21.0" 251 | 252 | "@typescript-eslint/types@5.62.0": 253 | version "5.62.0" 254 | resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" 255 | integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== 256 | 257 | "@typescript-eslint/typescript-estree@5.62.0": 258 | version "5.62.0" 259 | resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" 260 | integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== 261 | dependencies: 262 | "@typescript-eslint/types" "5.62.0" 263 | "@typescript-eslint/visitor-keys" "5.62.0" 264 | debug "^4.3.4" 265 | globby "^11.1.0" 266 | is-glob "^4.0.3" 267 | semver "^7.3.7" 268 | tsutils "^3.21.0" 269 | 270 | "@typescript-eslint/utils@5.62.0": 271 | version "5.62.0" 272 | resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" 273 | integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== 274 | dependencies: 275 | "@eslint-community/eslint-utils" "^4.2.0" 276 | "@types/json-schema" "^7.0.9" 277 | "@types/semver" "^7.3.12" 278 | "@typescript-eslint/scope-manager" "5.62.0" 279 | "@typescript-eslint/types" "5.62.0" 280 | "@typescript-eslint/typescript-estree" "5.62.0" 281 | eslint-scope "^5.1.1" 282 | semver "^7.3.7" 283 | 284 | "@typescript-eslint/visitor-keys@5.62.0": 285 | version "5.62.0" 286 | resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" 287 | integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== 288 | dependencies: 289 | "@typescript-eslint/types" "5.62.0" 290 | eslint-visitor-keys "^3.3.0" 291 | 292 | "@ungap/structured-clone@^1.2.0": 293 | version "1.2.0" 294 | resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" 295 | integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== 296 | 297 | abbrev@1: 298 | version "1.1.1" 299 | resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" 300 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 301 | 302 | accepts@~1.3.8: 303 | version "1.3.8" 304 | resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" 305 | integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== 306 | dependencies: 307 | mime-types "~2.1.34" 308 | negotiator "0.6.3" 309 | 310 | acorn-jsx@^5.3.2: 311 | version "5.3.2" 312 | resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" 313 | integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 314 | 315 | acorn-walk@^8.1.1: 316 | version "8.2.0" 317 | resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" 318 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 319 | 320 | acorn@^8.4.1, acorn@^8.9.0: 321 | version "8.11.2" 322 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" 323 | integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== 324 | 325 | ajv@^6.12.4: 326 | version "6.12.6" 327 | resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" 328 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 329 | dependencies: 330 | fast-deep-equal "^3.1.1" 331 | fast-json-stable-stringify "^2.0.0" 332 | json-schema-traverse "^0.4.1" 333 | uri-js "^4.2.2" 334 | 335 | ansi-regex@^5.0.1: 336 | version "5.0.1" 337 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 338 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 339 | 340 | ansi-styles@^4.1.0: 341 | version "4.3.0" 342 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 343 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 344 | dependencies: 345 | color-convert "^2.0.1" 346 | 347 | anymatch@~3.1.2: 348 | version "3.1.3" 349 | resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" 350 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 351 | dependencies: 352 | normalize-path "^3.0.0" 353 | picomatch "^2.0.4" 354 | 355 | arg@^4.1.0: 356 | version "4.1.3" 357 | resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" 358 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 359 | 360 | argparse@^2.0.1: 361 | version "2.0.1" 362 | resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" 363 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 364 | 365 | array-flatten@1.1.1: 366 | version "1.1.1" 367 | resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" 368 | integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== 369 | 370 | array-union@^2.1.0: 371 | version "2.1.0" 372 | resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" 373 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 374 | 375 | balanced-match@^1.0.0: 376 | version "1.0.2" 377 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" 378 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 379 | 380 | binary-extensions@^2.0.0: 381 | version "2.2.0" 382 | resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" 383 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 384 | 385 | body-parser@1.20.1: 386 | version "1.20.1" 387 | resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" 388 | integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== 389 | dependencies: 390 | bytes "3.1.2" 391 | content-type "~1.0.4" 392 | debug "2.6.9" 393 | depd "2.0.0" 394 | destroy "1.2.0" 395 | http-errors "2.0.0" 396 | iconv-lite "0.4.24" 397 | on-finished "2.4.1" 398 | qs "6.11.0" 399 | raw-body "2.5.1" 400 | type-is "~1.6.18" 401 | unpipe "1.0.0" 402 | 403 | brace-expansion@^1.1.7: 404 | version "1.1.11" 405 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 406 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 407 | dependencies: 408 | balanced-match "^1.0.0" 409 | concat-map "0.0.1" 410 | 411 | braces@^3.0.2, braces@~3.0.2: 412 | version "3.0.2" 413 | resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" 414 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 415 | dependencies: 416 | fill-range "^7.0.1" 417 | 418 | bytes@3.1.2: 419 | version "3.1.2" 420 | resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" 421 | integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== 422 | 423 | call-bind@^1.0.0: 424 | version "1.0.2" 425 | resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" 426 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== 427 | dependencies: 428 | function-bind "^1.1.1" 429 | get-intrinsic "^1.0.2" 430 | 431 | callsites@^3.0.0: 432 | version "3.1.0" 433 | resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" 434 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 435 | 436 | chalk@^4.0.0: 437 | version "4.1.2" 438 | resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 439 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 440 | dependencies: 441 | ansi-styles "^4.1.0" 442 | supports-color "^7.1.0" 443 | 444 | chokidar@^3.5.2: 445 | version "3.5.3" 446 | resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" 447 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 448 | dependencies: 449 | anymatch "~3.1.2" 450 | braces "~3.0.2" 451 | glob-parent "~5.1.2" 452 | is-binary-path "~2.1.0" 453 | is-glob "~4.0.1" 454 | normalize-path "~3.0.0" 455 | readdirp "~3.6.0" 456 | optionalDependencies: 457 | fsevents "~2.3.2" 458 | 459 | color-convert@^2.0.1: 460 | version "2.0.1" 461 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 462 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 463 | dependencies: 464 | color-name "~1.1.4" 465 | 466 | color-name@~1.1.4: 467 | version "1.1.4" 468 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 469 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 470 | 471 | concat-map@0.0.1: 472 | version "0.0.1" 473 | resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 474 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 475 | 476 | content-disposition@0.5.4: 477 | version "0.5.4" 478 | resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" 479 | integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== 480 | dependencies: 481 | safe-buffer "5.2.1" 482 | 483 | content-type@~1.0.4: 484 | version "1.0.5" 485 | resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" 486 | integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== 487 | 488 | cookie-signature@1.0.6: 489 | version "1.0.6" 490 | resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" 491 | integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== 492 | 493 | cookie@0.5.0: 494 | version "0.5.0" 495 | resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" 496 | integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== 497 | 498 | cors@^2.8.5: 499 | version "2.8.5" 500 | resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" 501 | integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== 502 | dependencies: 503 | object-assign "^4" 504 | vary "^1" 505 | 506 | create-require@^1.1.0: 507 | version "1.1.1" 508 | resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" 509 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 510 | 511 | cross-spawn@^7.0.2: 512 | version "7.0.3" 513 | resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" 514 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 515 | dependencies: 516 | path-key "^3.1.0" 517 | shebang-command "^2.0.0" 518 | which "^2.0.1" 519 | 520 | debug@2.6.9: 521 | version "2.6.9" 522 | resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" 523 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 524 | dependencies: 525 | ms "2.0.0" 526 | 527 | debug@^4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: 528 | version "4.3.4" 529 | resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" 530 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 531 | dependencies: 532 | ms "2.1.2" 533 | 534 | deep-is@^0.1.3: 535 | version "0.1.4" 536 | resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" 537 | integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== 538 | 539 | depd@2.0.0: 540 | version "2.0.0" 541 | resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" 542 | integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== 543 | 544 | destroy@1.2.0: 545 | version "1.2.0" 546 | resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" 547 | integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== 548 | 549 | diff@^4.0.1: 550 | version "4.0.2" 551 | resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" 552 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 553 | 554 | dir-glob@^3.0.1: 555 | version "3.0.1" 556 | resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" 557 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 558 | dependencies: 559 | path-type "^4.0.0" 560 | 561 | doctrine@^3.0.0: 562 | version "3.0.0" 563 | resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" 564 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 565 | dependencies: 566 | esutils "^2.0.2" 567 | 568 | ee-first@1.1.1: 569 | version "1.1.1" 570 | resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" 571 | integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== 572 | 573 | encodeurl@~1.0.2: 574 | version "1.0.2" 575 | resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" 576 | integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== 577 | 578 | escape-html@~1.0.3: 579 | version "1.0.3" 580 | resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" 581 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== 582 | 583 | escape-string-regexp@^4.0.0: 584 | version "4.0.0" 585 | resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" 586 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 587 | 588 | eslint-scope@^5.1.1: 589 | version "5.1.1" 590 | resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" 591 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 592 | dependencies: 593 | esrecurse "^4.3.0" 594 | estraverse "^4.1.1" 595 | 596 | eslint-scope@^7.2.2: 597 | version "7.2.2" 598 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" 599 | integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== 600 | dependencies: 601 | esrecurse "^4.3.0" 602 | estraverse "^5.2.0" 603 | 604 | eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: 605 | version "3.4.3" 606 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" 607 | integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== 608 | 609 | eslint@^8.55.0: 610 | version "8.55.0" 611 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.55.0.tgz#078cb7b847d66f2c254ea1794fa395bf8e7e03f8" 612 | integrity sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA== 613 | dependencies: 614 | "@eslint-community/eslint-utils" "^4.2.0" 615 | "@eslint-community/regexpp" "^4.6.1" 616 | "@eslint/eslintrc" "^2.1.4" 617 | "@eslint/js" "8.55.0" 618 | "@humanwhocodes/config-array" "^0.11.13" 619 | "@humanwhocodes/module-importer" "^1.0.1" 620 | "@nodelib/fs.walk" "^1.2.8" 621 | "@ungap/structured-clone" "^1.2.0" 622 | ajv "^6.12.4" 623 | chalk "^4.0.0" 624 | cross-spawn "^7.0.2" 625 | debug "^4.3.2" 626 | doctrine "^3.0.0" 627 | escape-string-regexp "^4.0.0" 628 | eslint-scope "^7.2.2" 629 | eslint-visitor-keys "^3.4.3" 630 | espree "^9.6.1" 631 | esquery "^1.4.2" 632 | esutils "^2.0.2" 633 | fast-deep-equal "^3.1.3" 634 | file-entry-cache "^6.0.1" 635 | find-up "^5.0.0" 636 | glob-parent "^6.0.2" 637 | globals "^13.19.0" 638 | graphemer "^1.4.0" 639 | ignore "^5.2.0" 640 | imurmurhash "^0.1.4" 641 | is-glob "^4.0.0" 642 | is-path-inside "^3.0.3" 643 | js-yaml "^4.1.0" 644 | json-stable-stringify-without-jsonify "^1.0.1" 645 | levn "^0.4.1" 646 | lodash.merge "^4.6.2" 647 | minimatch "^3.1.2" 648 | natural-compare "^1.4.0" 649 | optionator "^0.9.3" 650 | strip-ansi "^6.0.1" 651 | text-table "^0.2.0" 652 | 653 | espree@^9.6.0, espree@^9.6.1: 654 | version "9.6.1" 655 | resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" 656 | integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== 657 | dependencies: 658 | acorn "^8.9.0" 659 | acorn-jsx "^5.3.2" 660 | eslint-visitor-keys "^3.4.1" 661 | 662 | esquery@^1.4.2: 663 | version "1.5.0" 664 | resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" 665 | integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== 666 | dependencies: 667 | estraverse "^5.1.0" 668 | 669 | esrecurse@^4.3.0: 670 | version "4.3.0" 671 | resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" 672 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 673 | dependencies: 674 | estraverse "^5.2.0" 675 | 676 | estraverse@^4.1.1: 677 | version "4.3.0" 678 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" 679 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 680 | 681 | estraverse@^5.1.0, estraverse@^5.2.0: 682 | version "5.3.0" 683 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" 684 | integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== 685 | 686 | esutils@^2.0.2: 687 | version "2.0.3" 688 | resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" 689 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 690 | 691 | etag@~1.8.1: 692 | version "1.8.1" 693 | resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" 694 | integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== 695 | 696 | express@^4.18.2: 697 | version "4.18.2" 698 | resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" 699 | integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== 700 | dependencies: 701 | accepts "~1.3.8" 702 | array-flatten "1.1.1" 703 | body-parser "1.20.1" 704 | content-disposition "0.5.4" 705 | content-type "~1.0.4" 706 | cookie "0.5.0" 707 | cookie-signature "1.0.6" 708 | debug "2.6.9" 709 | depd "2.0.0" 710 | encodeurl "~1.0.2" 711 | escape-html "~1.0.3" 712 | etag "~1.8.1" 713 | finalhandler "1.2.0" 714 | fresh "0.5.2" 715 | http-errors "2.0.0" 716 | merge-descriptors "1.0.1" 717 | methods "~1.1.2" 718 | on-finished "2.4.1" 719 | parseurl "~1.3.3" 720 | path-to-regexp "0.1.7" 721 | proxy-addr "~2.0.7" 722 | qs "6.11.0" 723 | range-parser "~1.2.1" 724 | safe-buffer "5.2.1" 725 | send "0.18.0" 726 | serve-static "1.15.0" 727 | setprototypeof "1.2.0" 728 | statuses "2.0.1" 729 | type-is "~1.6.18" 730 | utils-merge "1.0.1" 731 | vary "~1.1.2" 732 | 733 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 734 | version "3.1.3" 735 | resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" 736 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 737 | 738 | fast-glob@^3.2.9: 739 | version "3.2.12" 740 | resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" 741 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== 742 | dependencies: 743 | "@nodelib/fs.stat" "^2.0.2" 744 | "@nodelib/fs.walk" "^1.2.3" 745 | glob-parent "^5.1.2" 746 | merge2 "^1.3.0" 747 | micromatch "^4.0.4" 748 | 749 | fast-json-stable-stringify@^2.0.0: 750 | version "2.1.0" 751 | resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" 752 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 753 | 754 | fast-levenshtein@^2.0.6: 755 | version "2.0.6" 756 | resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" 757 | integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== 758 | 759 | fastq@^1.6.0: 760 | version "1.15.0" 761 | resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" 762 | integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== 763 | dependencies: 764 | reusify "^1.0.4" 765 | 766 | file-entry-cache@^6.0.1: 767 | version "6.0.1" 768 | resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" 769 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 770 | dependencies: 771 | flat-cache "^3.0.4" 772 | 773 | fill-range@^7.0.1: 774 | version "7.0.1" 775 | resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" 776 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 777 | dependencies: 778 | to-regex-range "^5.0.1" 779 | 780 | finalhandler@1.2.0: 781 | version "1.2.0" 782 | resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" 783 | integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== 784 | dependencies: 785 | debug "2.6.9" 786 | encodeurl "~1.0.2" 787 | escape-html "~1.0.3" 788 | on-finished "2.4.1" 789 | parseurl "~1.3.3" 790 | statuses "2.0.1" 791 | unpipe "~1.0.0" 792 | 793 | find-up@^5.0.0: 794 | version "5.0.0" 795 | resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" 796 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 797 | dependencies: 798 | locate-path "^6.0.0" 799 | path-exists "^4.0.0" 800 | 801 | flat-cache@^3.0.4: 802 | version "3.0.4" 803 | resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" 804 | integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== 805 | dependencies: 806 | flatted "^3.1.0" 807 | rimraf "^3.0.2" 808 | 809 | flatted@^3.1.0: 810 | version "3.2.7" 811 | resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" 812 | integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== 813 | 814 | forwarded@0.2.0: 815 | version "0.2.0" 816 | resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" 817 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 818 | 819 | fresh@0.5.2: 820 | version "0.5.2" 821 | resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" 822 | integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== 823 | 824 | fs.realpath@^1.0.0: 825 | version "1.0.0" 826 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 827 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 828 | 829 | fsevents@~2.3.2: 830 | version "2.3.2" 831 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 832 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 833 | 834 | function-bind@^1.1.1: 835 | version "1.1.1" 836 | resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" 837 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 838 | 839 | get-intrinsic@^1.0.2: 840 | version "1.2.0" 841 | resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz" 842 | integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== 843 | dependencies: 844 | function-bind "^1.1.1" 845 | has "^1.0.3" 846 | has-symbols "^1.0.3" 847 | 848 | glob-parent@^5.1.2, glob-parent@~5.1.2: 849 | version "5.1.2" 850 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 851 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 852 | dependencies: 853 | is-glob "^4.0.1" 854 | 855 | glob-parent@^6.0.2: 856 | version "6.0.2" 857 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" 858 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 859 | dependencies: 860 | is-glob "^4.0.3" 861 | 862 | glob@^7.1.3: 863 | version "7.2.3" 864 | resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" 865 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 866 | dependencies: 867 | fs.realpath "^1.0.0" 868 | inflight "^1.0.4" 869 | inherits "2" 870 | minimatch "^3.1.1" 871 | once "^1.3.0" 872 | path-is-absolute "^1.0.0" 873 | 874 | globals@^13.19.0: 875 | version "13.20.0" 876 | resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" 877 | integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== 878 | dependencies: 879 | type-fest "^0.20.2" 880 | 881 | globby@^11.1.0: 882 | version "11.1.0" 883 | resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" 884 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 885 | dependencies: 886 | array-union "^2.1.0" 887 | dir-glob "^3.0.1" 888 | fast-glob "^3.2.9" 889 | ignore "^5.2.0" 890 | merge2 "^1.4.1" 891 | slash "^3.0.0" 892 | 893 | graphemer@^1.4.0: 894 | version "1.4.0" 895 | resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" 896 | integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== 897 | 898 | has-flag@^3.0.0: 899 | version "3.0.0" 900 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" 901 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 902 | 903 | has-flag@^4.0.0: 904 | version "4.0.0" 905 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 906 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 907 | 908 | has-symbols@^1.0.3: 909 | version "1.0.3" 910 | resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" 911 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 912 | 913 | has@^1.0.3: 914 | version "1.0.3" 915 | resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" 916 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 917 | dependencies: 918 | function-bind "^1.1.1" 919 | 920 | http-errors@2.0.0: 921 | version "2.0.0" 922 | resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" 923 | integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== 924 | dependencies: 925 | depd "2.0.0" 926 | inherits "2.0.4" 927 | setprototypeof "1.2.0" 928 | statuses "2.0.1" 929 | toidentifier "1.0.1" 930 | 931 | iconv-lite@0.4.24: 932 | version "0.4.24" 933 | resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" 934 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 935 | dependencies: 936 | safer-buffer ">= 2.1.2 < 3" 937 | 938 | ignore-by-default@^1.0.1: 939 | version "1.0.1" 940 | resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" 941 | integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== 942 | 943 | ignore@^5.2.0: 944 | version "5.2.4" 945 | resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" 946 | integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== 947 | 948 | import-fresh@^3.2.1: 949 | version "3.3.0" 950 | resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" 951 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 952 | dependencies: 953 | parent-module "^1.0.0" 954 | resolve-from "^4.0.0" 955 | 956 | imurmurhash@^0.1.4: 957 | version "0.1.4" 958 | resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" 959 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 960 | 961 | inflight@^1.0.4: 962 | version "1.0.6" 963 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 964 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 965 | dependencies: 966 | once "^1.3.0" 967 | wrappy "1" 968 | 969 | inherits@2, inherits@2.0.4: 970 | version "2.0.4" 971 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 972 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 973 | 974 | ipaddr.js@1.9.1: 975 | version "1.9.1" 976 | resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" 977 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 978 | 979 | is-binary-path@~2.1.0: 980 | version "2.1.0" 981 | resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 982 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 983 | dependencies: 984 | binary-extensions "^2.0.0" 985 | 986 | is-extglob@^2.1.1: 987 | version "2.1.1" 988 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 989 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 990 | 991 | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: 992 | version "4.0.3" 993 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 994 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 995 | dependencies: 996 | is-extglob "^2.1.1" 997 | 998 | is-number@^7.0.0: 999 | version "7.0.0" 1000 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 1001 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1002 | 1003 | is-path-inside@^3.0.3: 1004 | version "3.0.3" 1005 | resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" 1006 | integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== 1007 | 1008 | isexe@^2.0.0: 1009 | version "2.0.0" 1010 | resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" 1011 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 1012 | 1013 | js-yaml@^4.1.0: 1014 | version "4.1.0" 1015 | resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" 1016 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 1017 | dependencies: 1018 | argparse "^2.0.1" 1019 | 1020 | json-schema-traverse@^0.4.1: 1021 | version "0.4.1" 1022 | resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" 1023 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 1024 | 1025 | json-stable-stringify-without-jsonify@^1.0.1: 1026 | version "1.0.1" 1027 | resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" 1028 | integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== 1029 | 1030 | levn@^0.4.1: 1031 | version "0.4.1" 1032 | resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" 1033 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 1034 | dependencies: 1035 | prelude-ls "^1.2.1" 1036 | type-check "~0.4.0" 1037 | 1038 | locate-path@^6.0.0: 1039 | version "6.0.0" 1040 | resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" 1041 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 1042 | dependencies: 1043 | p-locate "^5.0.0" 1044 | 1045 | lodash.merge@^4.6.2: 1046 | version "4.6.2" 1047 | resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" 1048 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1049 | 1050 | lru-cache@^6.0.0: 1051 | version "6.0.0" 1052 | resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" 1053 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1054 | dependencies: 1055 | yallist "^4.0.0" 1056 | 1057 | make-error@^1.1.1: 1058 | version "1.3.6" 1059 | resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" 1060 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 1061 | 1062 | media-typer@0.3.0: 1063 | version "0.3.0" 1064 | resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" 1065 | integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== 1066 | 1067 | merge-descriptors@1.0.1: 1068 | version "1.0.1" 1069 | resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" 1070 | integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== 1071 | 1072 | merge2@^1.3.0, merge2@^1.4.1: 1073 | version "1.4.1" 1074 | resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" 1075 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1076 | 1077 | methods@~1.1.2: 1078 | version "1.1.2" 1079 | resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" 1080 | integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== 1081 | 1082 | micromatch@^4.0.4: 1083 | version "4.0.5" 1084 | resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" 1085 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 1086 | dependencies: 1087 | braces "^3.0.2" 1088 | picomatch "^2.3.1" 1089 | 1090 | mime-db@1.52.0: 1091 | version "1.52.0" 1092 | resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" 1093 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 1094 | 1095 | mime-types@~2.1.24, mime-types@~2.1.34: 1096 | version "2.1.35" 1097 | resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" 1098 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 1099 | dependencies: 1100 | mime-db "1.52.0" 1101 | 1102 | mime@1.6.0: 1103 | version "1.6.0" 1104 | resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" 1105 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 1106 | 1107 | minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: 1108 | version "3.1.2" 1109 | resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" 1110 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 1111 | dependencies: 1112 | brace-expansion "^1.1.7" 1113 | 1114 | ms@2.0.0: 1115 | version "2.0.0" 1116 | resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" 1117 | integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== 1118 | 1119 | ms@2.1.2: 1120 | version "2.1.2" 1121 | resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 1122 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1123 | 1124 | ms@2.1.3: 1125 | version "2.1.3" 1126 | resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" 1127 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 1128 | 1129 | natural-compare-lite@^1.4.0: 1130 | version "1.4.0" 1131 | resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz" 1132 | integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== 1133 | 1134 | natural-compare@^1.4.0: 1135 | version "1.4.0" 1136 | resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" 1137 | integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== 1138 | 1139 | negotiator@0.6.3: 1140 | version "0.6.3" 1141 | resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" 1142 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== 1143 | 1144 | nodemon@^3.0.2: 1145 | version "3.0.2" 1146 | resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.2.tgz#222dd0de79fc7b7b3eedba422d2b9e5fc678621e" 1147 | integrity sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA== 1148 | dependencies: 1149 | chokidar "^3.5.2" 1150 | debug "^4" 1151 | ignore-by-default "^1.0.1" 1152 | minimatch "^3.1.2" 1153 | pstree.remy "^1.1.8" 1154 | semver "^7.5.3" 1155 | simple-update-notifier "^2.0.0" 1156 | supports-color "^5.5.0" 1157 | touch "^3.1.0" 1158 | undefsafe "^2.0.5" 1159 | 1160 | nopt@~1.0.10: 1161 | version "1.0.10" 1162 | resolved "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" 1163 | integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== 1164 | dependencies: 1165 | abbrev "1" 1166 | 1167 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1168 | version "3.0.0" 1169 | resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 1170 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1171 | 1172 | object-assign@^4: 1173 | version "4.1.1" 1174 | resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" 1175 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 1176 | 1177 | object-inspect@^1.9.0: 1178 | version "1.12.3" 1179 | resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" 1180 | integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== 1181 | 1182 | on-finished@2.4.1: 1183 | version "2.4.1" 1184 | resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" 1185 | integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== 1186 | dependencies: 1187 | ee-first "1.1.1" 1188 | 1189 | once@^1.3.0: 1190 | version "1.4.0" 1191 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 1192 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1193 | dependencies: 1194 | wrappy "1" 1195 | 1196 | optionator@^0.9.3: 1197 | version "0.9.3" 1198 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" 1199 | integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== 1200 | dependencies: 1201 | "@aashutoshrathi/word-wrap" "^1.2.3" 1202 | deep-is "^0.1.3" 1203 | fast-levenshtein "^2.0.6" 1204 | levn "^0.4.1" 1205 | prelude-ls "^1.2.1" 1206 | type-check "^0.4.0" 1207 | 1208 | p-limit@^3.0.2: 1209 | version "3.1.0" 1210 | resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" 1211 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1212 | dependencies: 1213 | yocto-queue "^0.1.0" 1214 | 1215 | p-locate@^5.0.0: 1216 | version "5.0.0" 1217 | resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" 1218 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 1219 | dependencies: 1220 | p-limit "^3.0.2" 1221 | 1222 | parent-module@^1.0.0: 1223 | version "1.0.1" 1224 | resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" 1225 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1226 | dependencies: 1227 | callsites "^3.0.0" 1228 | 1229 | parseurl@~1.3.3: 1230 | version "1.3.3" 1231 | resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" 1232 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 1233 | 1234 | path-exists@^4.0.0: 1235 | version "4.0.0" 1236 | resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" 1237 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1238 | 1239 | path-is-absolute@^1.0.0: 1240 | version "1.0.1" 1241 | resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 1242 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1243 | 1244 | path-key@^3.1.0: 1245 | version "3.1.1" 1246 | resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" 1247 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1248 | 1249 | path-to-regexp@0.1.7: 1250 | version "0.1.7" 1251 | resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" 1252 | integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== 1253 | 1254 | path-type@^4.0.0: 1255 | version "4.0.0" 1256 | resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" 1257 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1258 | 1259 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: 1260 | version "2.3.1" 1261 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 1262 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1263 | 1264 | prelude-ls@^1.2.1: 1265 | version "1.2.1" 1266 | resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" 1267 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 1268 | 1269 | prettier@^3.1.0: 1270 | version "3.1.0" 1271 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.0.tgz#c6d16474a5f764ea1a4a373c593b779697744d5e" 1272 | integrity sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw== 1273 | 1274 | proxy-addr@~2.0.7: 1275 | version "2.0.7" 1276 | resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" 1277 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 1278 | dependencies: 1279 | forwarded "0.2.0" 1280 | ipaddr.js "1.9.1" 1281 | 1282 | pstree.remy@^1.1.8: 1283 | version "1.1.8" 1284 | resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" 1285 | integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== 1286 | 1287 | punycode@^2.1.0: 1288 | version "2.3.0" 1289 | resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" 1290 | integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== 1291 | 1292 | qs@6.11.0: 1293 | version "6.11.0" 1294 | resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" 1295 | integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== 1296 | dependencies: 1297 | side-channel "^1.0.4" 1298 | 1299 | queue-microtask@^1.2.2: 1300 | version "1.2.3" 1301 | resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" 1302 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1303 | 1304 | range-parser@~1.2.1: 1305 | version "1.2.1" 1306 | resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" 1307 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 1308 | 1309 | raw-body@2.5.1: 1310 | version "2.5.1" 1311 | resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" 1312 | integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== 1313 | dependencies: 1314 | bytes "3.1.2" 1315 | http-errors "2.0.0" 1316 | iconv-lite "0.4.24" 1317 | unpipe "1.0.0" 1318 | 1319 | readdirp@~3.6.0: 1320 | version "3.6.0" 1321 | resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" 1322 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1323 | dependencies: 1324 | picomatch "^2.2.1" 1325 | 1326 | resolve-from@^4.0.0: 1327 | version "4.0.0" 1328 | resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" 1329 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1330 | 1331 | reusify@^1.0.4: 1332 | version "1.0.4" 1333 | resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" 1334 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1335 | 1336 | rimraf@^3.0.2: 1337 | version "3.0.2" 1338 | resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" 1339 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1340 | dependencies: 1341 | glob "^7.1.3" 1342 | 1343 | run-parallel@^1.1.9: 1344 | version "1.2.0" 1345 | resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" 1346 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1347 | dependencies: 1348 | queue-microtask "^1.2.2" 1349 | 1350 | safe-buffer@5.2.1: 1351 | version "5.2.1" 1352 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 1353 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1354 | 1355 | "safer-buffer@>= 2.1.2 < 3": 1356 | version "2.1.2" 1357 | resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" 1358 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1359 | 1360 | semver@^7.3.7, semver@^7.5.3: 1361 | version "7.5.4" 1362 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" 1363 | integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== 1364 | dependencies: 1365 | lru-cache "^6.0.0" 1366 | 1367 | send@0.18.0: 1368 | version "0.18.0" 1369 | resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" 1370 | integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== 1371 | dependencies: 1372 | debug "2.6.9" 1373 | depd "2.0.0" 1374 | destroy "1.2.0" 1375 | encodeurl "~1.0.2" 1376 | escape-html "~1.0.3" 1377 | etag "~1.8.1" 1378 | fresh "0.5.2" 1379 | http-errors "2.0.0" 1380 | mime "1.6.0" 1381 | ms "2.1.3" 1382 | on-finished "2.4.1" 1383 | range-parser "~1.2.1" 1384 | statuses "2.0.1" 1385 | 1386 | serve-static@1.15.0: 1387 | version "1.15.0" 1388 | resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" 1389 | integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== 1390 | dependencies: 1391 | encodeurl "~1.0.2" 1392 | escape-html "~1.0.3" 1393 | parseurl "~1.3.3" 1394 | send "0.18.0" 1395 | 1396 | setprototypeof@1.2.0: 1397 | version "1.2.0" 1398 | resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" 1399 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 1400 | 1401 | shebang-command@^2.0.0: 1402 | version "2.0.0" 1403 | resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" 1404 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1405 | dependencies: 1406 | shebang-regex "^3.0.0" 1407 | 1408 | shebang-regex@^3.0.0: 1409 | version "3.0.0" 1410 | resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" 1411 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1412 | 1413 | side-channel@^1.0.4: 1414 | version "1.0.4" 1415 | resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" 1416 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== 1417 | dependencies: 1418 | call-bind "^1.0.0" 1419 | get-intrinsic "^1.0.2" 1420 | object-inspect "^1.9.0" 1421 | 1422 | simple-update-notifier@^2.0.0: 1423 | version "2.0.0" 1424 | resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" 1425 | integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== 1426 | dependencies: 1427 | semver "^7.5.3" 1428 | 1429 | slash@^3.0.0: 1430 | version "3.0.0" 1431 | resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" 1432 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1433 | 1434 | statuses@2.0.1: 1435 | version "2.0.1" 1436 | resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" 1437 | integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== 1438 | 1439 | strip-ansi@^6.0.1: 1440 | version "6.0.1" 1441 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" 1442 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1443 | dependencies: 1444 | ansi-regex "^5.0.1" 1445 | 1446 | strip-json-comments@^3.1.1: 1447 | version "3.1.1" 1448 | resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" 1449 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1450 | 1451 | supports-color@^5.5.0: 1452 | version "5.5.0" 1453 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" 1454 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1455 | dependencies: 1456 | has-flag "^3.0.0" 1457 | 1458 | supports-color@^7.1.0: 1459 | version "7.2.0" 1460 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 1461 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1462 | dependencies: 1463 | has-flag "^4.0.0" 1464 | 1465 | text-table@^0.2.0: 1466 | version "0.2.0" 1467 | resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" 1468 | integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== 1469 | 1470 | to-regex-range@^5.0.1: 1471 | version "5.0.1" 1472 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1473 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1474 | dependencies: 1475 | is-number "^7.0.0" 1476 | 1477 | toidentifier@1.0.1: 1478 | version "1.0.1" 1479 | resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" 1480 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 1481 | 1482 | touch@^3.1.0: 1483 | version "3.1.0" 1484 | resolved "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz" 1485 | integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== 1486 | dependencies: 1487 | nopt "~1.0.10" 1488 | 1489 | ts-node@^10.9.2: 1490 | version "10.9.2" 1491 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" 1492 | integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== 1493 | dependencies: 1494 | "@cspotcode/source-map-support" "^0.8.0" 1495 | "@tsconfig/node10" "^1.0.7" 1496 | "@tsconfig/node12" "^1.0.7" 1497 | "@tsconfig/node14" "^1.0.0" 1498 | "@tsconfig/node16" "^1.0.2" 1499 | acorn "^8.4.1" 1500 | acorn-walk "^8.1.1" 1501 | arg "^4.1.0" 1502 | create-require "^1.1.0" 1503 | diff "^4.0.1" 1504 | make-error "^1.1.1" 1505 | v8-compile-cache-lib "^3.0.1" 1506 | yn "3.1.1" 1507 | 1508 | tslib@^1.8.1: 1509 | version "1.14.1" 1510 | resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" 1511 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 1512 | 1513 | tsutils@^3.21.0: 1514 | version "3.21.0" 1515 | resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" 1516 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== 1517 | dependencies: 1518 | tslib "^1.8.1" 1519 | 1520 | type-check@^0.4.0, type-check@~0.4.0: 1521 | version "0.4.0" 1522 | resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" 1523 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 1524 | dependencies: 1525 | prelude-ls "^1.2.1" 1526 | 1527 | type-fest@^0.20.2: 1528 | version "0.20.2" 1529 | resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" 1530 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 1531 | 1532 | type-is@~1.6.18: 1533 | version "1.6.18" 1534 | resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" 1535 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 1536 | dependencies: 1537 | media-typer "0.3.0" 1538 | mime-types "~2.1.24" 1539 | 1540 | typescript@4.6.4: 1541 | version "4.6.4" 1542 | resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz" 1543 | integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== 1544 | 1545 | undefsafe@^2.0.5: 1546 | version "2.0.5" 1547 | resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" 1548 | integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== 1549 | 1550 | undici-types@~6.19.2: 1551 | version "6.19.8" 1552 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" 1553 | integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== 1554 | 1555 | unpipe@1.0.0, unpipe@~1.0.0: 1556 | version "1.0.0" 1557 | resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" 1558 | integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== 1559 | 1560 | uri-js@^4.2.2: 1561 | version "4.4.1" 1562 | resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" 1563 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1564 | dependencies: 1565 | punycode "^2.1.0" 1566 | 1567 | utils-merge@1.0.1: 1568 | version "1.0.1" 1569 | resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" 1570 | integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== 1571 | 1572 | v8-compile-cache-lib@^3.0.1: 1573 | version "3.0.1" 1574 | resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" 1575 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== 1576 | 1577 | vary@^1, vary@~1.1.2: 1578 | version "1.1.2" 1579 | resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" 1580 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== 1581 | 1582 | which@^2.0.1: 1583 | version "2.0.2" 1584 | resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" 1585 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1586 | dependencies: 1587 | isexe "^2.0.0" 1588 | 1589 | wrappy@1: 1590 | version "1.0.2" 1591 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1592 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1593 | 1594 | yallist@^4.0.0: 1595 | version "4.0.0" 1596 | resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" 1597 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1598 | 1599 | yn@3.1.1: 1600 | version "3.1.1" 1601 | resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" 1602 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 1603 | 1604 | yocto-queue@^0.1.0: 1605 | version "0.1.0" 1606 | resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" 1607 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 1608 | --------------------------------------------------------------------------------