first commit

This commit is contained in:
2024-09-17 12:11:39 +08:00
commit 42fbcc5d89
236 changed files with 27582 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { ApiResult } from "./ApiResult"
export class ApiError extends Error {
public readonly url: string
public readonly status: number
public readonly statusText: string
public readonly body: unknown
public readonly request: ApiRequestOptions
constructor(
request: ApiRequestOptions,
response: ApiResult,
message: string,
) {
super(message)
this.name = "ApiError"
this.url = response.url
this.status = response.status
this.statusText = response.statusText
this.body = response.body
this.request = request
}
}

View File

@@ -0,0 +1,20 @@
export type ApiRequestOptions = {
readonly method:
| "GET"
| "PUT"
| "POST"
| "DELETE"
| "OPTIONS"
| "HEAD"
| "PATCH"
readonly url: string
readonly path?: Record<string, unknown>
readonly cookies?: Record<string, unknown>
readonly headers?: Record<string, unknown>
readonly query?: Record<string, unknown>
readonly formData?: Record<string, unknown>
readonly body?: any
readonly mediaType?: string
readonly responseHeader?: string
readonly errors?: Record<number, string>
}

View File

@@ -0,0 +1,7 @@
export type ApiResult<TData = any> = {
readonly body: TData
readonly ok: boolean
readonly status: number
readonly statusText: string
readonly url: string
}

View File

@@ -0,0 +1,126 @@
export class CancelError extends Error {
constructor(message: string) {
super(message)
this.name = "CancelError"
}
public get isCancelled(): boolean {
return true
}
}
export interface OnCancel {
readonly isResolved: boolean
readonly isRejected: boolean
readonly isCancelled: boolean
(cancelHandler: () => void): void
}
export class CancelablePromise<T> implements Promise<T> {
private _isResolved: boolean
private _isRejected: boolean
private _isCancelled: boolean
readonly cancelHandlers: (() => void)[]
readonly promise: Promise<T>
private _resolve?: (value: T | PromiseLike<T>) => void
private _reject?: (reason?: unknown) => void
constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: unknown) => void,
onCancel: OnCancel,
) => void,
) {
this._isResolved = false
this._isRejected = false
this._isCancelled = false
this.cancelHandlers = []
this.promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve
this._reject = reject
const onResolve = (value: T | PromiseLike<T>): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return
}
this._isResolved = true
if (this._resolve) this._resolve(value)
}
const onReject = (reason?: unknown): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return
}
this._isRejected = true
if (this._reject) this._reject(reason)
}
const onCancel = (cancelHandler: () => void): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return
}
this.cancelHandlers.push(cancelHandler)
}
Object.defineProperty(onCancel, "isResolved", {
get: (): boolean => this._isResolved,
})
Object.defineProperty(onCancel, "isRejected", {
get: (): boolean => this._isRejected,
})
Object.defineProperty(onCancel, "isCancelled", {
get: (): boolean => this._isCancelled,
})
return executor(onResolve, onReject, onCancel as OnCancel)
})
}
get [Symbol.toStringTag]() {
return "Cancellable Promise"
}
public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
): Promise<TResult1 | TResult2> {
return this.promise.then(onFulfilled, onRejected)
}
public catch<TResult = never>(
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
): Promise<T | TResult> {
return this.promise.catch(onRejected)
}
public finally(onFinally?: (() => void) | null): Promise<T> {
return this.promise.finally(onFinally)
}
public cancel(): void {
if (this._isResolved || this._isRejected || this._isCancelled) {
return
}
this._isCancelled = true
if (this.cancelHandlers.length) {
try {
for (const cancelHandler of this.cancelHandlers) {
cancelHandler()
}
} catch (error) {
console.warn("Cancellation threw an error", error)
return
}
}
this.cancelHandlers.length = 0
if (this._reject) this._reject(new CancelError("Request aborted"))
}
public get isCancelled(): boolean {
return this._isCancelled
}
}

View File

@@ -0,0 +1,57 @@
import type { AxiosRequestConfig, AxiosResponse } from "axios"
import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { TResult } from "./types"
type Headers = Record<string, string>
type Middleware<T> = (value: T) => T | Promise<T>
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
export class Interceptors<T> {
_fns: Middleware<T>[]
constructor() {
this._fns = []
}
eject(fn: Middleware<T>) {
const index = this._fns.indexOf(fn)
if (index !== -1) {
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]
}
}
use(fn: Middleware<T>) {
this._fns = [...this._fns, fn]
}
}
export type OpenAPIConfig = {
BASE: string
CREDENTIALS: "include" | "omit" | "same-origin"
ENCODE_PATH?: ((path: string) => string) | undefined
HEADERS?: Headers | Resolver<Headers> | undefined
PASSWORD?: string | Resolver<string> | undefined
RESULT?: TResult
TOKEN?: string | Resolver<string> | undefined
USERNAME?: string | Resolver<string> | undefined
VERSION: string
WITH_CREDENTIALS: boolean
interceptors: {
request: Interceptors<AxiosRequestConfig>
response: Interceptors<AxiosResponse>
}
}
export const OpenAPI: OpenAPIConfig = {
BASE: "",
CREDENTIALS: "include",
ENCODE_PATH: undefined,
HEADERS: undefined,
PASSWORD: undefined,
RESULT: "body",
TOKEN: undefined,
USERNAME: undefined,
VERSION: "0.1.0",
WITH_CREDENTIALS: false,
interceptors: { request: new Interceptors(), response: new Interceptors() },
}

View File

@@ -0,0 +1,376 @@
import axios from "axios"
import type {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
} from "axios"
import { ApiError } from "./ApiError"
import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { ApiResult } from "./ApiResult"
import { CancelablePromise } from "./CancelablePromise"
import type { OnCancel } from "./CancelablePromise"
import type { OpenAPIConfig } from "./OpenAPI"
export const isString = (value: unknown): value is string => {
return typeof value === "string"
}
export const isStringWithValue = (value: unknown): value is string => {
return isString(value) && value !== ""
}
export const isBlob = (value: any): value is Blob => {
return value instanceof Blob
}
export const isFormData = (value: unknown): value is FormData => {
return value instanceof FormData
}
export const isSuccess = (status: number): boolean => {
return status >= 200 && status < 300
}
export const base64 = (str: string): string => {
try {
return btoa(str)
} catch (err) {
// @ts-ignore
return Buffer.from(str).toString("base64")
}
}
export const getQueryString = (params: Record<string, unknown>): string => {
const qs: string[] = []
const append = (key: string, value: unknown) => {
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
}
const encodePair = (key: string, value: unknown) => {
if (value === undefined || value === null) {
return
}
if (Array.isArray(value)) {
value.forEach((v) => encodePair(key, v))
} else if (typeof value === "object") {
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v))
} else {
append(key, value)
}
}
Object.entries(params).forEach(([key, value]) => encodePair(key, value))
return qs.length ? `?${qs.join("&")}` : ""
}
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
const encoder = config.ENCODE_PATH || encodeURI
const path = options.url
.replace("{api-version}", config.VERSION)
.replace(/{(.*?)}/g, (substring: string, group: string) => {
if (options.path?.hasOwnProperty(group)) {
return encoder(String(options.path[group]))
}
return substring
})
const url = config.BASE + path
return options.query ? url + getQueryString(options.query) : url
}
export const getFormData = (
options: ApiRequestOptions,
): FormData | undefined => {
if (options.formData) {
const formData = new FormData()
const process = (key: string, value: unknown) => {
if (isString(value) || isBlob(value)) {
formData.append(key, value)
} else {
formData.append(key, JSON.stringify(value))
}
}
Object.entries(options.formData)
.filter(([, value]) => value !== undefined && value !== null)
.forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v) => process(key, v))
} else {
process(key, value)
}
})
return formData
}
return undefined
}
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
export const resolve = async <T>(
options: ApiRequestOptions,
resolver?: T | Resolver<T>,
): Promise<T | undefined> => {
if (typeof resolver === "function") {
return (resolver as Resolver<T>)(options)
}
return resolver
}
export const getHeaders = async (
config: OpenAPIConfig,
options: ApiRequestOptions,
): Promise<Record<string, string>> => {
const [token, username, password, additionalHeaders] = await Promise.all([
resolve(options, config.TOKEN),
resolve(options, config.USERNAME),
resolve(options, config.PASSWORD),
resolve(options, config.HEADERS),
])
const headers = Object.entries({
Accept: "application/json",
...additionalHeaders,
...options.headers,
})
.filter(([, value]) => value !== undefined && value !== null)
.reduce(
(headers, [key, value]) => ({
...headers,
[key]: String(value),
}),
{} as Record<string, string>,
)
if (isStringWithValue(token)) {
headers.Authorization = `Bearer ${token}`
}
if (isStringWithValue(username) && isStringWithValue(password)) {
const credentials = base64(`${username}:${password}`)
headers.Authorization = `Basic ${credentials}`
}
if (options.body !== undefined) {
if (options.mediaType) {
headers["Content-Type"] = options.mediaType
} else if (isBlob(options.body)) {
headers["Content-Type"] = options.body.type || "application/octet-stream"
} else if (isString(options.body)) {
headers["Content-Type"] = "text/plain"
} else if (!isFormData(options.body)) {
headers["Content-Type"] = "application/json"
}
} else if (options.formData !== undefined) {
if (options.mediaType) {
headers["Content-Type"] = options.mediaType
}
}
return headers
}
export const getRequestBody = (options: ApiRequestOptions): unknown => {
if (options.body) {
return options.body
}
return undefined
}
export const sendRequest = async <T>(
config: OpenAPIConfig,
options: ApiRequestOptions,
url: string,
body: unknown,
formData: FormData | undefined,
headers: Record<string, string>,
onCancel: OnCancel,
axiosClient: AxiosInstance,
): Promise<AxiosResponse<T>> => {
const controller = new AbortController()
let requestConfig: AxiosRequestConfig = {
data: body ?? formData,
headers,
method: options.method,
signal: controller.signal,
url,
withCredentials: config.WITH_CREDENTIALS,
}
onCancel(() => controller.abort())
for (const fn of config.interceptors.request._fns) {
requestConfig = await fn(requestConfig)
}
try {
return await axiosClient.request(requestConfig)
} catch (error) {
const axiosError = error as AxiosError<T>
if (axiosError.response) {
return axiosError.response
}
throw error
}
}
export const getResponseHeader = (
response: AxiosResponse<unknown>,
responseHeader?: string,
): string | undefined => {
if (responseHeader) {
const content = response.headers[responseHeader]
if (isString(content)) {
return content
}
}
return undefined
}
export const getResponseBody = (response: AxiosResponse<unknown>): unknown => {
if (response.status !== 204) {
return response.data
}
return undefined
}
export const catchErrorCodes = (
options: ApiRequestOptions,
result: ApiResult,
): void => {
const errors: Record<number, string> = {
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Payload Too Large",
414: "URI Too Long",
415: "Unsupported Media Type",
416: "Range Not Satisfiable",
417: "Expectation Failed",
418: "Im a teapot",
421: "Misdirected Request",
422: "Unprocessable Content",
423: "Locked",
424: "Failed Dependency",
425: "Too Early",
426: "Upgrade Required",
428: "Precondition Required",
429: "Too Many Requests",
431: "Request Header Fields Too Large",
451: "Unavailable For Legal Reasons",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates",
507: "Insufficient Storage",
508: "Loop Detected",
510: "Not Extended",
511: "Network Authentication Required",
...options.errors,
}
const error = errors[result.status]
if (error) {
throw new ApiError(options, result, error)
}
if (!result.ok) {
const errorStatus = result.status ?? "unknown"
const errorStatusText = result.statusText ?? "unknown"
const errorBody = (() => {
try {
return JSON.stringify(result.body, null, 2)
} catch (e) {
return undefined
}
})()
throw new ApiError(
options,
result,
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
)
}
}
/**
* Request method
* @param config The OpenAPI configuration object
* @param options The request options from the service
* @param axiosClient The axios client instance to use
* @returns CancelablePromise<T>
* @throws ApiError
*/
export const request = <T>(
config: OpenAPIConfig,
options: ApiRequestOptions,
axiosClient: AxiosInstance = axios,
): CancelablePromise<T> => {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(config, options)
const formData = getFormData(options)
const body = getRequestBody(options)
const headers = await getHeaders(config, options)
if (!onCancel.isCancelled) {
let response = await sendRequest<T>(
config,
options,
url,
body,
formData,
headers,
onCancel,
axiosClient,
)
for (const fn of config.interceptors.response._fns) {
response = await fn(response)
}
const responseBody = getResponseBody(response)
const responseHeader = getResponseHeader(
response,
options.responseHeader,
)
const result: ApiResult = {
url,
ok: isSuccess(response.status),
status: response.status,
statusText: response.statusText,
body: responseHeader ?? responseBody,
}
catchErrorCodes(options, result)
resolve(result.body)
}
} catch (error) {
reject(error)
}
})
}

View File

@@ -0,0 +1,14 @@
import type { ApiResult } from "./ApiResult"
export type TResult = "body" | "raw"
export type TApiResponse<T extends TResult, TData> = Exclude<
T,
"raw"
> extends never
? ApiResult<TData>
: ApiResult<TData>["body"]
export type TConfig<T extends TResult> = {
_result?: T
}

View File

@@ -0,0 +1,8 @@
export { ApiError } from "./core/ApiError"
export { CancelablePromise, CancelError } from "./core/CancelablePromise"
export { OpenAPI } from "./core/OpenAPI"
export type { OpenAPIConfig } from "./core/OpenAPI"
export * from "./models"
export * from "./schemas"
export * from "./services"

View File

@@ -0,0 +1,284 @@
export type Body_login_login_access_token = {
grant_type?: string | null
username: string
password: string
scope?: string
client_id?: string | null
client_secret?: string | null
}
export type HTTPValidationError = {
detail?: Array<ValidationError>
}
export type ItemCreate = {
title: string
description?: string | null
}
export type ItemPublic = {
title: string
description?: string | null
id: string
owner_id: string
}
export type ItemUpdate = {
title?: string | null
description?: string | null
}
export type ItemsPublic = {
data: Array<ItemPublic>
count: number
}
export type ClientMessagePublic = {
name: string,
phone: string,
email: string,
message: string,
id: string,
created_at: string
}
export type ClientMessagesPublic = {
data: Array<ClientMessagePublic>,
count: number
}
export type Message = {
message: string
}
export type NewPassword = {
token: string
new_password: string
}
export type Token = {
access_token: string
token_type?: string
}
export type UpdatePassword = {
current_password: string
new_password: string
}
export type UserCreate = {
email: string
is_active?: boolean
is_superuser?: boolean
full_name?: string | null
password: string
}
export type UserPublic = {
email: string
is_active?: boolean
is_superuser?: boolean
full_name?: string | null
id: string
}
export type UserRegister = {
email: string
password: string
full_name?: string | null
}
export type UserUpdate = {
email?: string | null
is_active?: boolean
is_superuser?: boolean
full_name?: string | null
password?: string | null
}
export type UserUpdateMe = {
full_name?: string | null
email?: string | null
}
export type UsersPublic = {
data: Array<UserPublic>
count: number
}
export type ValidationError = {
loc: Array<string | number>
msg: string
type: string
}
export type WebSettingPublic = {
address: string,
google_map_api_key: string,
latitude: Number,
longitude: Number,
phone: string,
email: string,
facebook: string,
instagram: string,
youtube: string,
youtube_link: string,
whatsapp: string,
id: string
}
export type WebSettingUpdate = {
address: string,
google_map_api_key: string,
latitude: Number,
longitude: Number,
phone: string,
email: string,
facebook: string,
instagram: string,
youtube: string,
youtube_link: string,
whatsapp: string,
}
export type AboutUssPublic = {
data: Array<AboutUsPublic>
count: number
}
export type AboutUsPublic = {
index: number,
description: string,
image: string,
id: string
}
export type AboutUsCreate = {
index: number,
description: string,
image: File,
}
export type AboutUsUpdate = {
index: number,
description: string,
image?: File | undefined | null,
}
export type CoursesPublic = {
data: Array<CoursePublic>
count: number
}
export type CoursePublic = {
title: string,
sort_description: string,
long_description: string,
information: string,
contant: string,
remark: string,
id: string,
created_at: string
}
export type CourseCreate = {
title: string,
sort_description: string,
long_description: string,
information: string,
contant: string,
remark: string,
}
export type CourseDetailsPublic = {
title: string,
sort_description: string,
long_description: string,
information: string,
contant: string,
remark: string,
id: string,
created_at: string
images: Array<ImagePublic>,
info_images: Array<Info_imagePublic>,
schedule: Array<SchedulePublic>
}
export type CourseUpdate = {
title: string,
sort_description: string,
long_description: string,
information: string,
contant: string,
remark: string,
}
export type ImagesPublic = {
data: Array<ImagePublic>,
}
export type ImagePublic = {
image: string,
course_id: string,
index: number,
id:string
}
export type ImageUpdate = {
index: number,
}
export type ImageCreate = {
image: File,
index: number,
course_id: string
}
export type Info_imagesPublic = {
data: Array<Info_imagePublic>,
}
export type Info_imagePublic = {
image: string,
course_id: string,
index: number
id: string
}
export type Info_imageUpdate = {
index: number,
}
export type Info_imagesCreate = {
image: File,
index: number,
course_id: string
}
export type SchedulesPublic = {
data: Array<SchedulePublic>,
}
export type SchedulePublic = {
title: string,
info1: string,
info2: string,
date: string,
course_id: string,
id:string
}
export type ScheduleCreate = {
title: string,
info1: string,
info2: string,
date: string,
course_id: string,
}
export type ScheduleUpdate = {
title: string,
info1: string,
info2: string,
date: string,
}

View File

@@ -0,0 +1,444 @@
export const $Body_login_login_access_token = {
properties: {
grant_type: {
type: "any-of",
contains: [
{
type: "string",
pattern: "password",
},
{
type: "null",
},
],
},
username: {
type: "string",
isRequired: true,
},
password: {
type: "string",
isRequired: true,
},
scope: {
type: "string",
default: "",
},
client_id: {
type: "any-of",
contains: [
{
type: "string",
},
{
type: "null",
},
],
},
client_secret: {
type: "any-of",
contains: [
{
type: "string",
},
{
type: "null",
},
],
},
},
} as const
export const $HTTPValidationError = {
properties: {
detail: {
type: "array",
contains: {
type: "ValidationError",
},
},
},
} as const
export const $ItemCreate = {
properties: {
title: {
type: "string",
isRequired: true,
maxLength: 255,
minLength: 1,
},
description: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
},
} as const
export const $ItemPublic = {
properties: {
title: {
type: "string",
isRequired: true,
maxLength: 255,
minLength: 1,
},
description: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
id: {
type: "string",
isRequired: true,
format: "uuid",
},
owner_id: {
type: "string",
isRequired: true,
format: "uuid",
},
},
} as const
export const $ItemUpdate = {
properties: {
title: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
minLength: 1,
},
{
type: "null",
},
],
},
description: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
},
} as const
export const $ItemsPublic = {
properties: {
data: {
type: "array",
contains: {
type: "ItemPublic",
},
isRequired: true,
},
count: {
type: "number",
isRequired: true,
},
},
} as const
export const $Message = {
properties: {
message: {
type: "string",
isRequired: true,
},
},
} as const
export const $NewPassword = {
properties: {
token: {
type: "string",
isRequired: true,
},
new_password: {
type: "string",
isRequired: true,
maxLength: 40,
minLength: 8,
},
},
} as const
export const $Token = {
properties: {
access_token: {
type: "string",
isRequired: true,
},
token_type: {
type: "string",
default: "bearer",
},
},
} as const
export const $UpdatePassword = {
properties: {
current_password: {
type: "string",
isRequired: true,
maxLength: 40,
minLength: 8,
},
new_password: {
type: "string",
isRequired: true,
maxLength: 40,
minLength: 8,
},
},
} as const
export const $UserCreate = {
properties: {
email: {
type: "string",
isRequired: true,
format: "email",
maxLength: 255,
},
is_active: {
type: "boolean",
default: true,
},
is_superuser: {
type: "boolean",
default: false,
},
full_name: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
password: {
type: "string",
isRequired: true,
maxLength: 40,
minLength: 8,
},
},
} as const
export const $UserPublic = {
properties: {
email: {
type: "string",
isRequired: true,
format: "email",
maxLength: 255,
},
is_active: {
type: "boolean",
default: true,
},
is_superuser: {
type: "boolean",
default: false,
},
full_name: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
id: {
type: "string",
isRequired: true,
format: "uuid",
},
},
} as const
export const $UserRegister = {
properties: {
email: {
type: "string",
isRequired: true,
format: "email",
maxLength: 255,
},
password: {
type: "string",
isRequired: true,
maxLength: 40,
minLength: 8,
},
full_name: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
},
} as const
export const $UserUpdate = {
properties: {
email: {
type: "any-of",
contains: [
{
type: "string",
format: "email",
maxLength: 255,
},
{
type: "null",
},
],
},
is_active: {
type: "boolean",
default: true,
},
is_superuser: {
type: "boolean",
default: false,
},
full_name: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
password: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 40,
minLength: 8,
},
{
type: "null",
},
],
},
},
} as const
export const $UserUpdateMe = {
properties: {
full_name: {
type: "any-of",
contains: [
{
type: "string",
maxLength: 255,
},
{
type: "null",
},
],
},
email: {
type: "any-of",
contains: [
{
type: "string",
format: "email",
maxLength: 255,
},
{
type: "null",
},
],
},
},
} as const
export const $UsersPublic = {
properties: {
data: {
type: "array",
contains: {
type: "UserPublic",
},
isRequired: true,
},
count: {
type: "number",
isRequired: true,
},
},
} as const
export const $ValidationError = {
properties: {
loc: {
type: "array",
contains: {
type: "any-of",
contains: [
{
type: "string",
},
{
type: "number",
},
],
},
isRequired: true,
},
msg: {
type: "string",
isRequired: true,
},
type: {
type: "string",
isRequired: true,
},
},
} as const

File diff suppressed because it is too large Load Diff