From 74e9a7aa396f4136e0c25c532f47f7687993ddb1 Mon Sep 17 00:00:00 2001 From: Alexandre Stahmer Date: Sat, 23 Aug 2025 21:35:14 +0200 Subject: [PATCH 1/2] feat: Allow transforming schema & endpoint names; automatically prevents generating reserved TS/JS keyords names --- .changeset/true-pears-beam.md | 7 ++ .../src/generate-client-files.ts | 10 ++- packages/typed-openapi/src/generator.ts | 2 + packages/typed-openapi/src/index.ts | 1 + .../src/map-openapi-endpoints.ts | 21 +++-- packages/typed-openapi/src/ref-resolver.ts | 14 +++- packages/typed-openapi/src/sanitize-name.ts | 79 +++++++++++++++++++ packages/typed-openapi/src/types.ts | 15 +++- ...p-openapi-endpoints.name-transform.test.ts | 59 ++++++++++++++ .../tests/ref-resolver.name-transform.test.ts | 46 +++++++++++ .../typed-openapi/tests/ref-resolver.test.ts | 45 ++++++++++- 11 files changed, 287 insertions(+), 12 deletions(-) create mode 100644 .changeset/true-pears-beam.md create mode 100644 packages/typed-openapi/src/sanitize-name.ts create mode 100644 packages/typed-openapi/tests/map-openapi-endpoints.name-transform.test.ts create mode 100644 packages/typed-openapi/tests/ref-resolver.name-transform.test.ts diff --git a/.changeset/true-pears-beam.md b/.changeset/true-pears-beam.md new file mode 100644 index 0000000..3a5efe2 --- /dev/null +++ b/.changeset/true-pears-beam.md @@ -0,0 +1,7 @@ +--- +"typed-openapi": patch +--- + +Allow transforming schema & endpoint names; automatically prevents generating reserved TS/JS keyords names + +Fix https://github.com/astahmer/typed-openapi/issues/90 diff --git a/packages/typed-openapi/src/generate-client-files.ts b/packages/typed-openapi/src/generate-client-files.ts index e8c8275..e820317 100644 --- a/packages/typed-openapi/src/generate-client-files.ts +++ b/packages/typed-openapi/src/generate-client-files.ts @@ -7,6 +7,7 @@ import { allowedRuntimes, generateFile } from "./generator.ts"; import { mapOpenApiEndpoints } from "./map-openapi-endpoints.ts"; import { generateTanstackQueryFile } from "./tanstack-query.generator.ts"; import { prettify } from "./format.ts"; +import type { NameTransformOptions } from "./types.ts"; const cwd = process.cwd(); const now = new Date(); @@ -26,10 +27,14 @@ export const optionsSchema = type({ schemasOnly: "boolean", }); -export async function generateClientFiles(input: string, options: typeof optionsSchema.infer) { +type GenerateClientFilesOptions = typeof optionsSchema.infer & { + nameTransform?: NameTransformOptions; +}; + +export async function generateClientFiles(input: string, options: GenerateClientFilesOptions) { const openApiDoc = (await SwaggerParser.bundle(input)) as OpenAPIObject; - const ctx = mapOpenApiEndpoints(openApiDoc); + const ctx = mapOpenApiEndpoints(openApiDoc, options); console.log(`Found ${ctx.endpointList.length} endpoints`); const content = await prettify( @@ -37,6 +42,7 @@ export async function generateClientFiles(input: string, options: typeof options ...ctx, runtime: options.runtime, schemasOnly: options.schemasOnly, + nameTransform: options.nameTransform, }), ); const outputPath = join( diff --git a/packages/typed-openapi/src/generator.ts b/packages/typed-openapi/src/generator.ts index 7b1ede8..d6f248f 100644 --- a/packages/typed-openapi/src/generator.ts +++ b/packages/typed-openapi/src/generator.ts @@ -6,10 +6,12 @@ import * as Codegen from "@sinclair/typebox-codegen"; import { match } from "ts-pattern"; import { type } from "arktype"; import { wrapWithQuotesIfNeeded } from "./string-utils.ts"; +import type { NameTransformOptions } from "./types.ts"; type GeneratorOptions = ReturnType & { runtime?: "none" | keyof typeof runtimeValidationGenerator; schemasOnly?: boolean; + nameTransform?: NameTransformOptions | undefined; }; type GeneratorContext = Required; diff --git a/packages/typed-openapi/src/index.ts b/packages/typed-openapi/src/index.ts index 623d06b..ca76944 100644 --- a/packages/typed-openapi/src/index.ts +++ b/packages/typed-openapi/src/index.ts @@ -2,6 +2,7 @@ export * from "./box-factory.ts"; export { generateFile, type OutputRuntime } from "./generator.ts"; export * from "./tanstack-query.generator.ts"; export * from "./map-openapi-endpoints.ts"; +export * from "./generate-client-files.ts"; export * from "./openapi-schema-to-ts.ts"; export * from "./ref-resolver.ts"; export * from "./ts-factory.ts"; diff --git a/packages/typed-openapi/src/map-openapi-endpoints.ts b/packages/typed-openapi/src/map-openapi-endpoints.ts index d358b2f..25ba783 100644 --- a/packages/typed-openapi/src/map-openapi-endpoints.ts +++ b/packages/typed-openapi/src/map-openapi-endpoints.ts @@ -8,11 +8,13 @@ import { createRefResolver } from "./ref-resolver.ts"; import { tsFactory } from "./ts-factory.ts"; import { AnyBox, BoxRef, OpenapiSchemaConvertContext } from "./types.ts"; import { pathToVariableName } from "./string-utils.ts"; +import { NameTransformOptions } from "./types.ts"; import { match, P } from "ts-pattern"; +import { sanitizeName } from "./sanitize-name.ts"; const factory = tsFactory; -export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { +export const mapOpenApiEndpoints = (doc: OpenAPIObject, options?: { nameTransform?: NameTransformOptions }) => { const refs = createRefResolver(doc, factory); const ctx: OpenapiSchemaConvertContext = { refs, factory }; const endpointList = [] as Array; @@ -22,6 +24,10 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { Object.entries(pathItem).forEach(([method, operation]) => { if (operation.deprecated) return; + let alias = getAlias({ path, method, operation } as Endpoint); + if (options?.nameTransform?.transformEndpointName) { + alias = options.nameTransform.transformEndpointName({ alias, path, method: method as Method, operation }); + } const endpoint = { operation, method: method as Method, @@ -29,7 +35,7 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { requestFormat: "json", response: openApiSchemaToTs({ schema: {}, ctx }), meta: { - alias: getAlias({ path, method, operation } as Endpoint), + alias, areParametersRequired: false, hasParameters: false, }, @@ -84,7 +90,7 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { if (matchingMediaType && content[matchingMediaType]) { params.body = openApiSchemaToTs({ - schema: content[matchingMediaType]?.schema ?? {} ?? {}, + schema: content[matchingMediaType]?.schema ?? {}, ctx, }); } @@ -139,7 +145,7 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { const matchingMediaType = Object.keys(content).find(isResponseMediaType); if (matchingMediaType && content[matchingMediaType]) { endpoint.response = openApiSchemaToTs({ - schema: content[matchingMediaType]?.schema ?? {} ?? {}, + schema: content[matchingMediaType]?.schema ?? {}, ctx, }); } @@ -180,10 +186,13 @@ const isAllowedParamMediaTypes = ( const isResponseMediaType = (mediaType: string) => mediaType === "application/json"; const getAlias = ({ path, method, operation }: Endpoint) => - (method + "_" + capitalize(operation.operationId ?? pathToVariableName(path))).replace(/-/g, "__"); + sanitizeName( + (method + "_" + capitalize(operation.operationId ?? pathToVariableName(path))).replace(/-/g, "__"), + "endpoint", + ); type MutationMethod = "post" | "put" | "patch" | "delete"; -type Method = "get" | "head" | "options" | MutationMethod; +export type Method = "get" | "head" | "options" | MutationMethod; export type EndpointParameters = { body?: Box; diff --git a/packages/typed-openapi/src/ref-resolver.ts b/packages/typed-openapi/src/ref-resolver.ts index 26bf08e..a5330bb 100644 --- a/packages/typed-openapi/src/ref-resolver.ts +++ b/packages/typed-openapi/src/ref-resolver.ts @@ -5,8 +5,10 @@ import { Box } from "./box.ts"; import { isReferenceObject } from "./is-reference-object.ts"; import { openApiSchemaToTs } from "./openapi-schema-to-ts.ts"; import { normalizeString } from "./string-utils.ts"; +import { NameTransformOptions } from "./types.ts"; import { AnyBoxDef, GenericFactory, type LibSchemaObject } from "./types.ts"; import { topologicalSort } from "./topological-sort.ts"; +import { sanitizeName } from "./sanitize-name.ts"; const autocorrectRef = (ref: string) => (ref[1] === "/" ? ref : "#/" + ref.slice(1)); const componentsWithSchemas = ["schemas", "responses", "parameters", "requestBodies", "headers"]; @@ -26,7 +28,11 @@ export type RefInfo = { kind: "schemas" | "responses" | "parameters" | "requestBodies" | "headers"; }; -export const createRefResolver = (doc: OpenAPIObject, factory: GenericFactory) => { +export const createRefResolver = ( + doc: OpenAPIObject, + factory: GenericFactory, + nameTransform?: NameTransformOptions, +) => { // both used for debugging purpose const nameByRef = new Map(); const refByName = new Map(); @@ -48,7 +54,11 @@ export const createRefResolver = (doc: OpenAPIObject, factory: GenericFactory) = // "#/components/schemas/Something.jsonld" -> "Something.jsonld" const name = split[split.length - 1]!; - const normalized = normalizeString(name); + let normalized = normalizeString(name); + if (nameTransform?.transformSchemaName) { + normalized = nameTransform.transformSchemaName(normalized); + } + normalized = sanitizeName(normalized, "schema"); nameByRef.set(correctRef, normalized); refByName.set(normalized, correctRef); diff --git a/packages/typed-openapi/src/sanitize-name.ts b/packages/typed-openapi/src/sanitize-name.ts new file mode 100644 index 0000000..908c9c7 --- /dev/null +++ b/packages/typed-openapi/src/sanitize-name.ts @@ -0,0 +1,79 @@ +const reservedWords = new Set([ + // TS keywords and built-ins + "import", + "package", + "namespace", + "Record", + "Partial", + "Required", + "Readonly", + "Pick", + "Omit", + "String", + "Number", + "Boolean", + "Object", + "Array", + "Function", + "any", + "unknown", + "never", + "void", + "extends", + "super", + "class", + "interface", + "type", + "enum", + "const", + "let", + "var", + "if", + "else", + "for", + "while", + "do", + "switch", + "case", + "default", + "break", + "continue", + "return", + "try", + "catch", + "finally", + "throw", + "new", + "delete", + "in", + "instanceof", + "typeof", + "void", + "with", + "yield", + "await", + "static", + "public", + "private", + "protected", + "abstract", + "as", + "asserts", + "from", + "get", + "set", + "module", + "require", + "keyof", + "readonly", + "global", + "symbol", + "bigint", +]); + +export function sanitizeName(name: string, type: "schema" | "endpoint") { + let n = name.replace(/[\W/]+/g, "_"); + if (/^\d/.test(n)) n = "_" + n; + if (reservedWords.has(n)) n = (type === "schema" ? "Schema_" : "Endpoint_") + n; + return n; +} diff --git a/packages/typed-openapi/src/types.ts b/packages/typed-openapi/src/types.ts index dbacd46..abdabfc 100644 --- a/packages/typed-openapi/src/types.ts +++ b/packages/typed-openapi/src/types.ts @@ -1,8 +1,9 @@ -import type { ReferenceObject, SchemaObject } from "openapi3-ts/oas31"; +import type { OperationObject, ReferenceObject, SchemaObject } from "openapi3-ts/oas31"; import type { SchemaObject as SchemaObject3 } from "openapi3-ts/oas30"; import type { RefResolver } from "./ref-resolver.ts"; import { Box } from "./box.ts"; +import type { Method } from "./map-openapi-endpoints.ts"; export type LibSchemaObject = SchemaObject & SchemaObject3; @@ -94,10 +95,22 @@ export type FactoryCreator = ( schema: SchemaObject | ReferenceObject, ctx: OpenapiSchemaConvertContext, ) => GenericFactory; + +export type NameTransformOptions = { + transformSchemaName?: (name: string) => string; + transformEndpointName?: (endpoint: { + alias: string; + operation: OperationObject; + method: Method; + path: string; + }) => string; +}; + export type OpenapiSchemaConvertContext = { factory: FactoryCreator | GenericFactory; refs: RefResolver; onBox?: (box: Box) => Box; + nameTransform?: NameTransformOptions; }; export type StringOrBox = string | Box; diff --git a/packages/typed-openapi/tests/map-openapi-endpoints.name-transform.test.ts b/packages/typed-openapi/tests/map-openapi-endpoints.name-transform.test.ts new file mode 100644 index 0000000..b81d497 --- /dev/null +++ b/packages/typed-openapi/tests/map-openapi-endpoints.name-transform.test.ts @@ -0,0 +1,59 @@ +import { mapOpenApiEndpoints } from "../src/map-openapi-endpoints.ts"; +import { describe, it, expect } from "vitest"; + +const openApiDoc = { + openapi: "3.0.0", + info: { title: "Test", version: "1.0.0" }, + paths: { + "/foo/bar": { + get: { operationId: "import", responses: { 200: { description: "ok" } } }, + post: { operationId: "Record", responses: { 200: { description: "ok" } } }, + }, + "/user/profile": { + get: { operationId: "user/profile", responses: { 200: { description: "ok" } } }, + }, + "/import": { + get: { operationId: "import", responses: { 200: { description: "ok" } } }, + }, + "/set": { + get: { operationId: "set", responses: { 200: { description: "ok" } } }, + }, + "/normal": { + get: { operationId: "normal", responses: { 200: { description: "ok" } } }, + }, + }, +} as any; + +describe("mapOpenApiEndpoints with NameTransformOptions for endpoints", () => { + it("avoids reserved words and invalid chars", () => { + const ctx = mapOpenApiEndpoints(openApiDoc); + const aliases = ctx.endpointList.map((e) => e.meta.alias); + expect(aliases).toMatchInlineSnapshot(` + [ + "get_Import", + "post_Record", + "get_User_profile", + "get_Import", + "get_Set", + "get_Normal", + ] + `); + }); + + it("applies transformEndpointName and avoids reserved words", () => { + const ctx = mapOpenApiEndpoints(openApiDoc, { + nameTransform: { transformEndpointName: (endpoint) => `E_${endpoint.alias}_E` }, + }); + const aliases = ctx.endpointList.map((e) => e.meta.alias); + expect(aliases).toMatchInlineSnapshot(` + [ + "E_get_Import_E", + "E_post_Record_E", + "E_get_User_profile_E", + "E_get_Import_E", + "E_get_Set_E", + "E_get_Normal_E", + ] + `); + }); +}); diff --git a/packages/typed-openapi/tests/ref-resolver.name-transform.test.ts b/packages/typed-openapi/tests/ref-resolver.name-transform.test.ts new file mode 100644 index 0000000..b000032 --- /dev/null +++ b/packages/typed-openapi/tests/ref-resolver.name-transform.test.ts @@ -0,0 +1,46 @@ +import { createRefResolver } from "../src/ref-resolver.ts"; +import { describe, it, expect } from "vitest"; +import { tsFactory } from "../src/ts-factory.ts"; + +const openApiDoc = { + openapi: "3.0.0", + info: { title: "Test", version: "1.0.0" }, + components: { + schemas: { + "import": { type: "string" }, + "set": { type: "object", properties: { id: { type: "string" } } }, + "Record": { type: "number" }, + "normal": { type: "boolean" }, + }, + }, +} as any; + +describe("createRefResolver with NameTransformOptions", () => { + it("applies transformSchemaName and avoids reserved words", () => { + const resolver = createRefResolver(openApiDoc, tsFactory, { + transformSchemaName: (name) => `X_${name}_X`, + }); + const infos = Array.from(resolver.infos.values()).map(i => i.normalized); + expect(infos).toMatchInlineSnapshot(` + [ + "X_import_X", + "X_set_X", + "X_Record_X", + "X_normal_X", + ] + `); + }); + + it("applies no transform and still avoids reserved words and invalid chars", () => { + const resolver = createRefResolver(openApiDoc, tsFactory); + const infos = Array.from(resolver.infos.values()).map(i => i.normalized); + expect(infos).toMatchInlineSnapshot(` + [ + "Schema_import", + "Schema_set", + "Schema_Record", + "normal", + ] + `); + }); +}); diff --git a/packages/typed-openapi/tests/ref-resolver.test.ts b/packages/typed-openapi/tests/ref-resolver.test.ts index 7a33245..1baff1d 100644 --- a/packages/typed-openapi/tests/ref-resolver.test.ts +++ b/packages/typed-openapi/tests/ref-resolver.test.ts @@ -1,10 +1,53 @@ import SwaggerParser from "@apidevtools/swagger-parser"; import type { OpenAPIObject } from "openapi3-ts/oas31"; -import { describe, test } from "vitest"; +import { describe, expect, test } from "vitest"; import { createRefResolver } from "../src/ref-resolver.ts"; import { tsFactory } from "../src/ts-factory.ts"; describe("generator", () => { + const openApiDoc = { + openapi: "3.0.0", + info: { title: "Test", version: "1.0.0" }, + components: { + schemas: { + import: { type: "string" }, + set: { type: "object", properties: { id: { type: "string" } } }, + Record: { type: "number" }, + normal: { type: "boolean" }, + }, + }, + } as any; + + describe("createRefResolver with NameTransformOptions", () => { + test("avoids reserved words", () => { + const resolver = createRefResolver(openApiDoc, tsFactory); + const infos = Array.from(resolver.infos.values()).map((i) => i.normalized); + expect(infos).toMatchInlineSnapshot(` + [ + "Schema_import", + "Schema_set", + "Schema_Record", + "normal", + ] + `); + }); + + test("applies transformSchemaName and avoids reserved words", () => { + const resolver = createRefResolver(openApiDoc, tsFactory, { + transformSchemaName: (name) => `X_${name}_X`, + }); + const infos = Array.from(resolver.infos.values()).map((i) => i.normalized); + expect(infos).toMatchInlineSnapshot(` + [ + "X_import_X", + "X_set_X", + "X_Record_X", + "X_normal_X", + ] + `); + }); + }); + test("petstore", async ({ expect }) => { const openApiDoc = (await SwaggerParser.parse("./tests/samples/petstore.yaml")) as OpenAPIObject; const ref = createRefResolver(openApiDoc, tsFactory); From cb94f700187250ee28120126e14f17b14aca5768 Mon Sep 17 00:00:00 2001 From: Alexandre Stahmer Date: Sun, 24 Aug 2025 10:26:32 +0200 Subject: [PATCH 2/2] chore: split prettier export --- packages/typed-openapi/package.json | 3 +- packages/typed-openapi/src/index.ts | 1 - packages/typed-openapi/src/node.export.ts | 1 - packages/typed-openapi/src/pretty.export.ts | 1 + packages/typed-openapi/tsup.config.ts | 2 +- packages/web/declarations/yup.d.ts | 89 +++++---------------- 6 files changed, 26 insertions(+), 71 deletions(-) create mode 100644 packages/typed-openapi/src/pretty.export.ts diff --git a/packages/typed-openapi/package.json b/packages/typed-openapi/package.json index 6cfdb59..89d7260 100644 --- a/packages/typed-openapi/package.json +++ b/packages/typed-openapi/package.json @@ -6,7 +6,8 @@ "module": "dist/index.js", "exports": { ".": "./dist/index.js", - "./node": "./dist/node.export.js" + "./node": "./dist/node.export.js", + "./pretty": "./dist/pretty.export.js" }, "bin": { "typed-openapi": "bin.js" diff --git a/packages/typed-openapi/src/index.ts b/packages/typed-openapi/src/index.ts index ca76944..623d06b 100644 --- a/packages/typed-openapi/src/index.ts +++ b/packages/typed-openapi/src/index.ts @@ -2,7 +2,6 @@ export * from "./box-factory.ts"; export { generateFile, type OutputRuntime } from "./generator.ts"; export * from "./tanstack-query.generator.ts"; export * from "./map-openapi-endpoints.ts"; -export * from "./generate-client-files.ts"; export * from "./openapi-schema-to-ts.ts"; export * from "./ref-resolver.ts"; export * from "./ts-factory.ts"; diff --git a/packages/typed-openapi/src/node.export.ts b/packages/typed-openapi/src/node.export.ts index c9ab55d..7a12f91 100644 --- a/packages/typed-openapi/src/node.export.ts +++ b/packages/typed-openapi/src/node.export.ts @@ -1,2 +1 @@ -export { prettify } from "./format.ts"; export { generateClientFiles } from "./generate-client-files.ts"; diff --git a/packages/typed-openapi/src/pretty.export.ts b/packages/typed-openapi/src/pretty.export.ts new file mode 100644 index 0000000..6547ede --- /dev/null +++ b/packages/typed-openapi/src/pretty.export.ts @@ -0,0 +1 @@ +export { prettify } from "./format.ts"; diff --git a/packages/typed-openapi/tsup.config.ts b/packages/typed-openapi/tsup.config.ts index f65db39..0bacb7e 100644 --- a/packages/typed-openapi/tsup.config.ts +++ b/packages/typed-openapi/tsup.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ clean: true, - entryPoints: ["src/cli.ts", "src/index.ts", "src/node.export.ts"], + entryPoints: ["src/cli.ts", "src/index.ts", "src/node.export.ts", "src/pretty.export.ts"], outDir: "dist", dts: true, format: ["esm"], diff --git a/packages/web/declarations/yup.d.ts b/packages/web/declarations/yup.d.ts index 2b37c5f..8c7de77 100644 --- a/packages/web/declarations/yup.d.ts +++ b/packages/web/declarations/yup.d.ts @@ -20,14 +20,12 @@ declare class ValidationError extends Error { value: any; path?: string; type?: string; - params?: Params; errors: string[]; + params?: Params; inner: ValidationError[]; static formatError(message: string | ((params: Params) => string) | unknown, params: Params): any; static isError(err: any): err is ValidationError; - constructor(errorOrErrors: string | ValidationError | readonly ValidationError[], value?: any, field?: string, type?: string, disableStack?: boolean); - static [Symbol.hasInstance](inst: any): boolean; - [Symbol.toStringTag]: string; + constructor(errorOrErrors: string | ValidationError | readonly ValidationError[], value?: any, field?: string, type?: string); } type PanicCallback = (err: Error) => void; @@ -37,7 +35,6 @@ type CreateErrorOptions = { message?: Message; params?: ExtraParams; type?: string; - disableStackTrace?: boolean; }; type TestContext = { path: string; @@ -88,13 +85,9 @@ type SchemaSpec = { strip?: boolean; strict?: boolean; recursive?: boolean; - disableStackTrace?: boolean; label?: string | undefined; - meta?: SchemaMetadata; + meta?: any; }; -interface CustomSchemaMetadata { -} -type SchemaMetadata = keyof CustomSchemaMetadata extends never ? Record : CustomSchemaMetadata; type SchemaOptions = { type: string; spec?: Partial>; @@ -139,13 +132,13 @@ interface SchemaObjectDescription extends SchemaDescription { interface SchemaLazyDescription { type: string; label?: string; - meta?: SchemaMetadata; + meta: object | undefined; } type SchemaFieldDescription = SchemaDescription | SchemaRefDescription | SchemaObjectDescription | SchemaInnerTypeDescription | SchemaLazyDescription; interface SchemaDescription { type: string; label?: string; - meta?: SchemaMetadata; + meta: object | undefined; oneOf: unknown[]; notOneOf: unknown[]; default?: unknown; @@ -178,8 +171,8 @@ declare abstract class Schema>): this; label(label: string): this; - meta(): SchemaMetadata | undefined; - meta(obj: SchemaMetadata): this; + meta(): Record | undefined; + meta(obj: Record): this; withMutation(fn: (schema: this) => T): T; concat(schema: this): this; concat(schema: AnySchema): AnySchema; @@ -373,10 +366,6 @@ interface ValidateOptions { * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true */ recursive?: boolean; - /** - * When true ValidationError instance won't include stack trace information. Default - false - */ - disableStackTrace?: boolean; /** * Any context needed for validating schema conditions (see: when()) */ @@ -396,14 +385,12 @@ interface MessageParams { path: string; value: any; originalValue: any; - originalPath: string; label: string; type: string; spec: SchemaSpec & Record; } type Message = any> = string | ((params: Extra & MessageParams) => unknown) | Record; type ExtraParams = Record; -type AnyMessageParams = MessageParams & ExtraParams; interface NestedTestConfig { options: InternalOptions; parent: any; @@ -436,7 +423,7 @@ interface MixedSchema = AnyPresentValue | u required(msg?: Message): MixedSchema, TContext, TDefault, TFlags>; notRequired(): MixedSchema, TContext, TDefault, TFlags>; nullable(msg?: Message): MixedSchema; - nonNullable(msg?: Message): MixedSchema, TContext, TDefault, TFlags>; + nonNullable(): MixedSchema, TContext, TDefault, TFlags>; strip(enabled: false): MixedSchema>; strip(enabled?: true): MixedSchema>; } @@ -496,11 +483,6 @@ interface StringLocale { uuid?: Message<{ regex: RegExp; }>; - datetime?: Message; - datetime_offset?: Message; - datetime_precision?: Message<{ - precision: number; - }>; trim?: Message; lowercase?: Message; uppercase?: Message; @@ -535,12 +517,7 @@ interface DateLocale { }>; } interface ObjectLocale { - noUnknown?: Message<{ - unknown: string[]; - }>; - exact?: Message<{ - properties: string[]; - }>; + noUnknown?: Message; } interface ArrayLocale { length?: Message<{ @@ -553,9 +530,6 @@ interface ArrayLocale { max: number; }>; } -interface TupleLocale { - notType?: Message; -} interface BooleanLocale { isValue?: Message; } @@ -567,7 +541,6 @@ interface LocaleObject { boolean?: BooleanLocale; object?: ObjectLocale; array?: ArrayLocale; - tuple?: TupleLocale; } declare const _default: LocaleObject; @@ -578,16 +551,6 @@ type MatchOptions = { }>; name?: string; }; -type DateTimeOptions = { - message: Message<{ - allowOffset?: boolean; - precision?: number; - }>; - /** Allow a time zone offset. False requires UTC 'Z' timezone. (default: false) */ - allowOffset?: boolean; - /** Require a certain sub-second precision on the date. (default: undefined -- any or no sub-second precision) */ - precision?: number; -}; declare function create$6(): StringSchema; declare function create$6 = AnyObject>(): StringSchema; declare namespace create$6 { @@ -615,7 +578,6 @@ declare class StringSchema = string | undefined, TCo uuid(message?: Message<{ regex: RegExp; }>): this; - datetime(options?: DateTimeOptions | DateTimeOptions['message']): this; ensure(): StringSchema>; trim(message?: Message): this; lowercase(message?: Message): this; @@ -633,8 +595,8 @@ interface StringSchema = string | undefined, TContex optional(): StringSchema; required(msg?: Message): StringSchema, TContext, TDefault, TFlags>; notRequired(): StringSchema, TContext, TDefault, TFlags>; - nullable(msg?: Message): StringSchema; - nonNullable(msg?: Message): StringSchema, TContext, TDefault, TFlags>; + nullable(msg?: Message): StringSchema; + nonNullable(): StringSchema, TContext, TDefault, TFlags>; strip(enabled: false): StringSchema>; strip(enabled?: true): StringSchema>; } @@ -677,7 +639,7 @@ interface NumberSchema = number | undefined, TContex required(msg?: Message): NumberSchema, TContext, TDefault, TFlags>; notRequired(): NumberSchema, TContext, TDefault, TFlags>; nullable(msg?: Message): NumberSchema; - nonNullable(msg?: Message): NumberSchema, TContext, TDefault, TFlags>; + nonNullable(): NumberSchema, TContext, TDefault, TFlags>; strip(enabled: false): NumberSchema>; strip(enabled?: true): NumberSchema>; } @@ -707,7 +669,7 @@ interface DateSchema, TContext = AnyObject, TDefault = required(msg?: Message): DateSchema, TContext, TDefault, TFlags>; notRequired(): DateSchema, TContext, TDefault, TFlags>; nullable(msg?: Message): DateSchema; - nonNullable(msg?: Message): DateSchema, TContext, TDefault, TFlags>; + nonNullable(): DateSchema, TContext, TDefault, TFlags>; strip(enabled: false): DateSchema>; strip(enabled?: true): DateSchema>; } @@ -730,7 +692,7 @@ interface ObjectSchema, TContext = AnyObject, TDefa required(msg?: Message): ObjectSchema, TContext, TDefault, TFlags>; notRequired(): ObjectSchema, TContext, TDefault, TFlags>; nullable(msg?: Message): ObjectSchema; - nonNullable(msg?: Message): ObjectSchema, TContext, TDefault, TFlags>; + nonNullable(): ObjectSchema, TContext, TDefault, TFlags>; strip(enabled: false): ObjectSchema>; strip(enabled?: true): ObjectSchema>; } @@ -743,7 +705,7 @@ declare class ObjectSchema, TContext = AnyObject, T constructor(spec?: Shape); protected _cast(_value: any, options?: InternalOptions): any; protected _validate(_value: any, options: InternalOptions | undefined, panic: (err: Error, value: unknown) => void, next: (err: ValidationError[], value: unknown) => void): void; - clone(spec?: Partial): this; + clone(spec?: ObjectSchemaSpec): this; concat, IC, ID, IF extends Flags>(schema: ObjectSchema): ObjectSchema, TContext & IC, Extract extends never ? TDefault extends AnyObject ? ID extends AnyObject ? _> : ID : ID : ID, TFlags | IF>; concat(schema: this): this; protected _getDefault(options?: ResolveOptions): any; @@ -752,20 +714,13 @@ declare class ObjectSchema, TContext = AnyObject, T partial(): ObjectSchema, TContext, TDefault, TFlags>; deepPartial(): ObjectSchema, TContext, TDefault, TFlags>; pick(keys: readonly TKey[]): ObjectSchema<{ [K in TKey]: TIn[K]; }, TContext, TDefault, TFlags>; - omit(keys: readonly TKey[]): ObjectSchema<{ [K in Exclude]: TIn[K]; }, TContext, TDefault, TFlags>; + omit(keys: readonly TKey[]): ObjectSchema, TContext, TDefault, TFlags>; from(from: string, to: keyof TIn, alias?: boolean): this; /** Parse an input JSON string to an object */ json(): this; - /** - * Similar to `noUnknown` but only validates that an object is the right shape without stripping the unknown keys - */ - exact(message?: Message): this; - stripUnknown(): this; noUnknown(message?: Message): this; noUnknown(noAllow: boolean, message?: Message): this; - unknown(allow?: boolean, message?: Message<{ - unknown: string[]; - }>): this; + unknown(allow?: boolean, message?: Message): this; transformKeys(fn: (key: string) => string): this; camelCase(): this; snakeCase(): this; @@ -814,7 +769,7 @@ interface ArraySchema, TContext, TDefault, TFlags>; notRequired(): ArraySchema, TContext, TDefault, TFlags>; nullable(msg?: Message): ArraySchema; - nonNullable(msg?: Message): ArraySchema, TContext, TDefault, TFlags>; + nonNullable(): ArraySchema, TContext, TDefault, TFlags>; strip(enabled: false): ArraySchema>; strip(enabled?: true): ArraySchema>; } @@ -839,7 +794,7 @@ interface TupleSchema = AnyTuple | undefined, TCon required(msg?: Message): TupleSchema, TContext, TDefault, TFlags>; notRequired(): TupleSchema, TContext, TDefault, TFlags>; nullable(msg?: Message): TupleSchema; - nonNullable(msg?: Message): TupleSchema, TContext, TDefault, TFlags>; + nonNullable(): TupleSchema, TContext, TDefault, TFlags>; strip(enabled: false): TupleSchema>; strip(enabled?: true): TupleSchema>; } @@ -876,7 +831,7 @@ declare class Lazy implemen asNestedTest(config: NestedTestConfig): RunTest; validate(value: any, options?: ValidateOptions): Promise; validateSync(value: any, options?: ValidateOptions): T; - validateAt(path: string, value: any, options?: ValidateOptions): any; + validateAt(path: string, value: any, options?: ValidateOptions): Promise; validateSyncAt(path: string, value: any, options?: ValidateOptions): any; isValid(value: any, options?: ValidateOptions): Promise; isValidSync(value: any, options?: ValidateOptions): boolean; @@ -899,8 +854,8 @@ declare function printValue(value: any, quoteStrings?: boolean): any; declare function setLocale(custom: LocaleObject): void; declare function addMethod>(schemaType: (...arg: any[]) => T, name: string, fn: (this: T, ...args: any[]) => T): void; -declare function addMethod ISchema>(schemaType: T, name: string, fn: (this: InstanceType, ...args: any[]) => InstanceType): void; +declare function addMethod ISchema>(schemaType: T, name: string, fn: (this: InstanceType, ...args: any[]) => InstanceType): void; type AnyObjectSchema = ObjectSchema; type CastOptions = Omit; -export { AnyMessageParams, AnyObject, AnyObjectSchema, AnySchema, ArraySchema, InferType as Asserts, BooleanSchema, CastOptions, CreateErrorOptions, CustomSchemaMetadata, DateSchema, DefaultFromShape, DefaultThunk, Defined, Flags, ISchema, InferType, Lazy, Lazy as LazySchema, LocaleObject, MakePartial, Maybe, Message, MessageParams, MixedOptions, MixedSchema, TypeGuard as MixedTypeGuard, NotNull, NumberSchema, ObjectSchema, ObjectShape, Optionals, Reference, Schema, SchemaDescription, SchemaFieldDescription, SchemaInnerTypeDescription, SchemaLazyDescription, SchemaMetadata, SchemaObjectDescription, SchemaRefDescription, SetFlag, StringSchema, TestConfig, TestContext, TestFunction, TestOptions, ToggleDefault, TupleSchema, TypeFromShape, UnsetFlag, ValidateOptions, ValidationError, addMethod, create$2 as array, create$7 as bool, create$7 as boolean, create$4 as date, _default as defaultLocale, getIn, isSchema, create as lazy, create$8 as mixed, create$5 as number, create$3 as object, printValue, reach, create$9 as ref, setLocale, create$6 as string, create$1 as tuple }; +export { AnyObject, AnyObjectSchema, AnySchema, ArraySchema, InferType as Asserts, BooleanSchema, CastOptions, CreateErrorOptions, DateSchema, DefaultFromShape, DefaultThunk, Defined, Flags, ISchema, InferType, LocaleObject, MakePartial, Maybe, Message, MixedOptions, MixedSchema, TypeGuard as MixedTypeGuard, NotNull, NumberSchema, ObjectSchema, ObjectShape, Optionals, Schema, SchemaDescription, SchemaFieldDescription, SchemaInnerTypeDescription, SchemaLazyDescription, SchemaObjectDescription, SchemaRefDescription, SetFlag, StringSchema, TestConfig, TestContext, TestFunction, TestOptions, ToggleDefault, TupleSchema, TypeFromShape, UnsetFlag, ValidateOptions, ValidationError, addMethod, create$2 as array, create$7 as bool, create$7 as boolean, create$4 as date, _default as defaultLocale, getIn, isSchema, create as lazy, create$8 as mixed, create$5 as number, create$3 as object, printValue, reach, create$9 as ref, setLocale, create$6 as string, create$1 as tuple };