import type { ValidationFunction, ValidationFunctionResult } from '@naturalcycles/js-lib'; import type { Set2 } from '@naturalcycles/js-lib/object'; import type { AnyObject, BaseDBEntity, IANATimezone, Inclusiveness, IsoDate, IsoDateTime, IsoMonth, NumberEnum, StringEnum, StringMap, UnixTimestamp, UnixTimestampMillis } from '@naturalcycles/js-lib/types'; import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec'; import type { Ajv } from 'ajv'; import { AjvValidationError } from './ajvValidationError.js'; export declare const j: { /** * Matches literally any value - equivalent to TypeScript's `any` type. * Use sparingly, as it bypasses type validation entirely. */ any(): JBuilder; string(): JString; number(): JNumber; boolean(): JBoolean; object: typeof object & { dbEntity: typeof objectDbEntity; infer: typeof objectInfer; any(): JObject; stringMap>(schema: S): JObject>>; /** * @experimental Look around, maybe you find a rule that is better for your use-case. * * For Record type of validations. * ```ts * const schema = j.object * .record( * j * .string() * .regex(/^\d{3,4}$/) * .branded(), * j.number().nullable(), * ) * .isOfType>() * ``` * * When the keys of the Record are values from an Enum, prefer `j.object.withEnumKeys`! * * Non-matching keys will be stripped from the object, i.e. they will not cause an error. * * Caveat: This rule first validates values of every properties of the object, and only then validates the keys. * A consequence of that is that the validation will throw when there is an unexpected property with a value not matching the value schema. */ record: typeof record; /** * For Record type of validations. * * When the keys of the Record are values from an Enum, * this helper is more performant and behaves in a more conventional manner than `j.object.record` would. * * */ withEnumKeys: typeof withEnumKeys; withRegexKeys: typeof withRegexKeys; /** * Validates that the value is an instance of the given class/constructor. * * ```ts * j.object.instanceOf(Date) // typed as Date * j.object.instanceOf(Date).optional() // typed as Date | undefined * ``` */ instanceOf(ctor: new (...args: any[]) => T): JBuilder; }; array(itemSchema: JSchema): JArray; tuple[]>(items: S): JTuple; set(itemSchema: JSchema): JSet2Builder; buffer(): JBuilder; enum(input: T, opt?: JsonBuilderRuleOpt): JEnum; /** * Use only with primitive values, otherwise this function will throw to avoid bugs. * To validate objects, use `anyOfBy`. * * Our Ajv is configured to strip unexpected properties from objects, * and since Ajv is mutating the input, this means that it cannot * properly validate the same data over multiple schemas. * * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format). * Use `oneOf` when schemas are mutually exclusive. */ oneOf[]>(items: [...B]): JBuilder, false>; /** * Use only with primitive values, otherwise this function will throw to avoid bugs. * To validate objects, use `anyOfBy` or `anyOfThese`. * * Our Ajv is configured to strip unexpected properties from objects, * and since Ajv is mutating the input, this means that it cannot * properly validate the same data over multiple schemas. * * Use `anyOf` when schemas may overlap (e.g., AccountId | PartnerId with same format). * Use `oneOf` when schemas are mutually exclusive. */ anyOf[]>(items: [...B_1]): JBuilder, false>; /** * Pick validation schema for an object based on the value of a specific property. * * ``` * const schemaMap = { * true: successSchema, * false: errorSchema * } * * const schema = j.anyOfBy('success', schemaMap) * ``` */ anyOfBy>>(propertyName: string, schemaDictionary: D): JBuilder, false>; /** * Custom version of `anyOf` which - in contrast to the original function - does not mutate the input. * This comes with a performance penalty, so do not use it where performance matters. * * ``` * const schema = j.anyOfThese([successSchema, errorSchema]) * ``` */ anyOfThese[]>(items: [...B_2]): JBuilder, false>; and(): { silentBob: () => never; }; literal(v: V): JEnum; /** * Create a JSchema from a plain JsonSchema object. * Useful when the schema is loaded from a JSON file or generated externally. * * Optionally accepts a custom Ajv instance and/or inputName for error messages. */ fromSchema(schema: JsonSchema, cfg?: { ajv?: Ajv; inputName?: string; }): JSchema; }; export declare const HIDDEN_AJV_SCHEMA: unique symbol; export type WithCachedAjvSchema = Base & { [HIDDEN_AJV_SCHEMA]: AjvSchema; }; export declare class JSchema implements StandardSchemaV1, StandardJSONSchemaV1 { protected [HIDDEN_AJV_SCHEMA]: AjvSchema | undefined; protected schema: JsonSchema; private _cfg?; constructor(schema: JsonSchema, cfg?: { ajv?: Ajv; inputName?: string; }); private _builtSchema?; private _compiledFns?; private _getBuiltSchema; private _getCompiled; getSchema(): JsonSchema; /** * @deprecated * The usage of this function is discouraged as it defeats the purpose of having type-safe validation. */ castAs(): JSchema; /** * A helper function that takes a type parameter and compares it with the type inferred from the schema. * * When the type inferred from the schema differs from the passed-in type, * the schema becomes unusable, by turning its type into `never`. */ isOfType(): ExactMatch extends true ? this : never; /** * Produces a "clean schema object" without methods. * Same as if it would be JSON.stringified. */ build(): JsonSchema; clone(): this; cloneAndUpdateSchema(schema: Partial): this; get ['~standard'](): StandardSchemaV1.Props & StandardJSONSchemaV1.Props; validate(input: unknown, opt?: AjvValidationOptions): OUT; isValid(input: unknown, opt?: AjvValidationOptions): boolean; getValidationResult(input: unknown, opt?: AjvValidationOptions): ValidationFunctionResult; getValidationFunction(opt?: AjvValidationOptions): ValidationFunction; /** * Specify a function to be called after the normal validation is finished. * * This function will receive the validated, type-safe data, and you can use it * to do further validations, e.g. conditional validations based on certain property values, * or to do data modifications either by mutating the input or returning a new value. * * If you throw an error from this function, it will show up as an error in the validation. */ postValidation(fn: PostValidatonFn): JSchema; /** * @experimental */ out: OUT; opt: Opt; /** Forces OUT to be invariant (prevents covariant subtype matching in object property constraints). */ protected _invariantOut: (x: OUT) => void; } export declare class JBuilder extends JSchema { protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void; /** * @deprecated * The usage of this function is discouraged as it defeats the purpose of having type-safe validation. */ castAs(): JBuilder; $schema($schema: string): this; $schemaDraft7(): this; $id($id: string): this; title(title: string): this; description(description: string): this; deprecated(deprecated?: boolean): this; type(type: string): this; default(v: any): this; instanceof(of: string): this; /** * @param optionalValues List of values that should be considered/converted as `undefined`. * * This `optionalValues` feature only works when the current schema is nested in an object or array schema, * due to how mutability works in Ajv. * * Make sure this `optional()` call is at the end of your call chain. * * When `null` is included in optionalValues, the return type becomes `JSchema` * (no further chaining allowed) because the schema is wrapped in an anyOf structure. */ optional(optionalValues?: T): T extends readonly (string | number | boolean | null)[] ? JSchema : JBuilder; nullable(): JBuilder; /** * Locks the given schema chain and no other modification can be done to it. */ final(): JSchema; /** * * @param validator A validator function that returns an error message or undefined. * * You may add multiple custom validators and they will be executed in the order you added them. */ custom(validator: CustomValidatorFn): JBuilder; /** * * @param converter A converter function that returns a new value. * * You may add multiple converters and they will be executed in the order you added them, * each converter receiving the result from the previous one. * * This feature only works when the current schema is nested in an object or array schema, * due to how mutability works in Ajv. */ convert(converter: CustomConverterFn): JBuilder; } export declare class JString extends JBuilder { constructor(); regex(pattern: RegExp, opt?: JsonBuilderRuleOpt): this; pattern(pattern: string, opt?: JsonBuilderRuleOpt): this; minLength(minLength: number): this; maxLength(maxLength: number): this; length(exactLength: number): this; length(minLength: number, maxLength: number): this; email(opt?: Partial): this; trim(): this; toLowerCase(): this; toUpperCase(): this; truncate(toLength: number): this; branded(): JString; /** * Validates that the input is a fully-specified YYYY-MM-DD formatted valid IsoDate value. * * All previous expectations in the schema chain are dropped - including `.optional()` - * because this call effectively starts a new schema chain. */ isoDate(): JIsoDate; isoDateTime(): JString; isoMonth(): JBuilder; /** * Validates the string format to be JWT. * Expects the JWT to be signed! */ jwt(): this; url(): this; ipv4(): this; ipv6(): this; slug(): this; semVer(): this; languageTag(): this; countryCode(): this; currency(): this; /** * Validates that the input is a valid IANATimzone value. * * All previous expectations in the schema chain are dropped - including `.optional()` - * because this call effectively starts a new schema chain as an `enum` validation. */ ianaTimezone(): JEnum; base64Url(): this; uuid(): this; } export interface JsonSchemaStringEmailOptions { checkTLD: boolean; } export declare class JIsoDate extends JBuilder { constructor(); before(date: string): this; sameOrBefore(date: string): this; after(date: string): this; sameOrAfter(date: string): this; between(fromDate: string, toDate: string, incl: Inclusiveness): this; } export interface JsonSchemaIsoDateOptions { before?: string; sameOrBefore?: string; after?: string; sameOrAfter?: string; } export interface JsonSchemaIsoMonthOptions { } export declare class JNumber extends JBuilder { constructor(); integer(): this; branded(): JNumber; multipleOf(multipleOf: number): this; min(minimum: number): this; exclusiveMin(exclusiveMinimum: number): this; max(maximum: number): this; exclusiveMax(exclusiveMaximum: number): this; lessThan(value: number): this; lessThanOrEqual(value: number): this; moreThan(value: number): this; moreThanOrEqual(value: number): this; equal(value: number): this; range(minimum: number, maximum: number, incl: Inclusiveness): this; int32(): this; int64(): this; float(): this; double(): this; unixTimestamp(): JNumber; unixTimestamp2000(): JNumber; unixTimestampMillis(): JNumber; unixTimestamp2000Millis(): JNumber; utcOffset(): this; utcOffsetHour(): this; /** * Specify the precision of the floating point numbers by the number of digits after the ".". * Excess digits will be cut-off when the current schema is nested in an object or array schema, * due to how mutability works in Ajv. */ precision(numberOfDigits: number): this; } export declare class JBoolean extends JBuilder { constructor(); } export declare class JObject extends JBuilder { constructor(props?: AnyObject, opt?: JObjectOpts); /** * When set, the validation will not strip away properties that are not specified explicitly in the schema. */ allowAdditionalProperties(): this; extend

>>(props: P): JObject ? IsOpt extends true ? never : K : never]: P[K] extends JSchema ? OUT2 : never; } & { [K in keyof P as P[K] extends JSchema ? IsOpt extends true ? K : never : never]?: P[K] extends JSchema ? OUT2 : never; }>>, false>; /** * Concatenates another schema to the current schema. * * It expects you to use `isOfType()` in the chain, * otherwise the validation will throw. This is to ensure * that the schemas you concatenated match the intended final type. */ concat(other: JObject): JObject; /** * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions. */ dbEntity(): JObject; updated: JNumber; } & {}>>, false>; minProperties(minProperties: number): this; maxProperties(maxProperties: number): this; exclusiveProperties(propNames: readonly (keyof OUT & string)[]): this; } interface JObjectOpts { hasIsOfTypeCheck?: false; patternProperties?: StringMap>; keySchema?: JsonSchema; } export declare class JObjectInfer>, Opt extends boolean = false> extends JBuilder ? IsOpt extends true ? never : K : never]: PROPS[K] extends JSchema ? OUT : never; } & { [K in keyof PROPS as PROPS[K] extends JSchema ? IsOpt extends true ? K : never : never]?: PROPS[K] extends JSchema ? OUT : never; }>, Opt> { constructor(props?: PROPS); /** * When set, the validation will not strip away properties that are not specified explicitly in the schema. */ allowAdditionalProperties(): this; extend>>(props: NEW_PROPS): JObjectInfer<{ [K in keyof PROPS | keyof NEW_PROPS]: K extends keyof NEW_PROPS ? NEW_PROPS[K] : K extends keyof PROPS ? PROPS[K] : never; }, Opt>; /** * Extends the current schema with `id`, `created` and `updated` according to NC DB conventions. */ dbEntity(): JObjectInfer<{ [K in "created" | "id" | "updated" | keyof PROPS]: K extends "created" | "id" | "updated" ? { id: JString; created: JNumber; updated: JNumber; }[K] : K extends keyof PROPS ? PROPS[K] : never; }, Opt>; } export declare class JArray extends JBuilder { constructor(itemsSchema: JSchema); minLength(minItems: number): this; maxLength(maxItems: number): this; length(exactLength: number): this; length(minItems: number, maxItems: number): this; exactLength(length: number): this; unique(): this; } declare class JSet2Builder extends JBuilder, Opt> { constructor(itemsSchema: JSchema); min(minItems: number): this; max(maxItems: number): this; } export declare class JEnum extends JBuilder { constructor(enumValues: readonly OUT[], baseType: EnumBaseType, opt?: JsonBuilderRuleOpt); branded(): JEnum; } export declare class JTuple[]> extends JBuilder, false> { constructor(items: ITEMS); } declare function object(props: AnyObject): never; declare function object(props: [keyof OUT] extends [never] ? Record : { [K in keyof Required]-?: JSchema; }): [keyof OUT] extends [never] ? never : JObject; declare function objectInfer

>>(props: P): JObjectInfer; declare function objectDbEntity(props: AnyObject): never; declare function objectDbEntity = Exclude>(props: { [K in EXTRA_KEYS]-?: BuilderFor; } & (ExactMatch extends true ? { id?: BuilderFor; } : { id: BuilderFor; }) & (ExactMatch extends true ? { created?: BuilderFor; } : { created: BuilderFor; }) & (ExactMatch extends true ? { updated?: BuilderFor; } : { updated: BuilderFor; })): JObject; declare function record, VS extends JSchema, Opt extends boolean = SchemaOpt>(keySchema: KS, valueSchema: VS): SchemaOut extends string ? JObject, SchemaOut>> : Record, SchemaOut>, false> : never; declare function withRegexKeys>(keyRegex: RegExp | string, schema: S): JObject>, false>; /** * Builds the object schema with the indicated `keys` and uses the `schema` for their validation. */ declare function withEnumKeys, K extends string | number = EnumKeyUnion, Opt extends boolean = SchemaOpt>(keys: T, schema: S): JObject; } : { [P in K]: SchemaOut; }, false>; /** * On creation - compiles ajv validation function. * Provides convenient methods, error reporting, etc. */ export declare class AjvSchema { schema: JsonSchema; private constructor(); /** * Shortcut for AjvSchema.create(schema, { lazy: true }) */ static createLazy(schema: SchemaHandledByAjv, cfg?: Partial): AjvSchema; /** * Conveniently allows to pass either JsonSchema or JSchema builder, or existing AjvSchema. * If it's already an AjvSchema - it'll just return it without any processing. * If it's a Builder - will call `build` before proceeding. * Otherwise - will construct AjvSchema instance ready to be used. */ static create(schema: SchemaHandledByAjv, cfg?: Partial): AjvSchema; /** * Creates a minimal AjvSchema wrapper from a pre-compiled validate function. * Used internally by JSchema to cache a compatible AjvSchema instance. */ static _wrap(schema: JsonSchema, compiledFn: any): AjvSchema; static isSchemaWithCachedAjvSchema(schema: Base): schema is WithCachedAjvSchema; static cacheAjvSchema(schema: Base, ajvSchema: AjvSchema): WithCachedAjvSchema; static requireCachedAjvSchema(schema: WithCachedAjvSchema): AjvSchema; readonly cfg: AjvSchemaCfg; private _compiledFn; private _getValidateFn; /** * It returns the original object just for convenience. */ validate(input: unknown, opt?: AjvValidationOptions): OUT; isValid(input: unknown, opt?: AjvValidationOptions): boolean; getValidationResult(input: unknown, opt?: AjvValidationOptions): ValidationFunctionResult; getValidationFunction(): ValidationFunction; private static requireValidJsonSchema; } type EnumBaseType = 'string' | 'number' | 'other'; export interface AjvValidationOptions { /** * Custom Ajv instance to use for this validation. * Overrides the default Ajv or any Ajv set at construction time. * Compiled functions are cached per Ajv instance. */ ajv?: Ajv; /** * Defaults to true, * because that's how AJV works by default, * and what gives it performance advantage. * (Because we have found that deep-clone is surprisingly slow, * nearly as slow as Joi validation). * * If set to true - AJV will mutate the input in case it needs to apply transformations * (strip unknown properties, convert types, etc). * * If false - it will deep-clone (using JSON.stringify+parse) the input to prevent its mutation. * Will return the cloned/mutated object. * Please note that JSON.stringify+parse has side-effects, * e.g it will transform Buffer into a weird object. */ mutateInput?: boolean; inputName?: string; inputId?: string; /** * Function that returns "original input". * What is original input? * It's an input in its original non-mutated form. * Why is it needed? * Because we mutates the Input here. And after its been mutated - we no longer * can include it "how it was" in an error message. So, for that reason we'll use * `getOriginalInput()`, if it's provided. */ getOriginalInput?: () => unknown; } export interface AjvSchemaCfg { /** * Pass Ajv instance, otherwise Ajv will be created with * AjvSchema default (not the same as Ajv defaults) parameters */ ajv: Ajv; inputName?: string; /** * If true - schema will be compiled on-demand (lazily). * Default: false. */ lazy?: boolean; } export type SchemaHandledByAjv = JSchema | JsonSchema | AjvSchema; export interface JsonSchema { readonly out?: OUT; $schema?: string; $id?: string; title?: string; description?: string; deprecated?: boolean; readOnly?: boolean; writeOnly?: boolean; type?: string | string[]; items?: JsonSchema; prefixItems?: JsonSchema[]; properties?: { [K in keyof OUT]: JsonSchema; }; patternProperties?: StringMap>; required?: string[]; additionalProperties?: boolean; minProperties?: number; maxProperties?: number; default?: OUT; if?: JsonSchema; then?: JsonSchema; else?: JsonSchema; anyOf?: JsonSchema[]; oneOf?: JsonSchema[]; /** * This is a temporary "intermediate AST" field that is used inside the parser. * In the final schema this field will NOT be present. */ optionalField?: true; pattern?: string; minLength?: number; maxLength?: number; format?: string; contentMediaType?: string; contentEncoding?: string; multipleOf?: number; minimum?: number; exclusiveMinimum?: number; maximum?: number; exclusiveMaximum?: number; minItems?: number; maxItems?: number; uniqueItems?: boolean; enum?: any; hasIsOfTypeCheck?: boolean; email?: JsonSchemaStringEmailOptions; Set2?: JsonSchema; Buffer?: true; IsoDate?: JsonSchemaIsoDateOptions; IsoDateTime?: true; IsoMonth?: JsonSchemaIsoMonthOptions; instanceof?: string | string[]; transform?: { trim?: true; toLowerCase?: true; toUpperCase?: true; truncate?: number; }; errorMessages?: StringMap; optionalValues?: (string | number | boolean | null)[]; keySchema?: JsonSchema; isUndefined?: true; minProperties2?: number; exclusiveProperties?: (readonly string[])[]; anyOfBy?: { propertyName: string; schemaDictionary: Record; }; anyOfThese?: JsonSchema[]; precision?: number; customValidations?: CustomValidatorFn[]; customConversions?: CustomConverterFn[]; postValidation?: PostValidatonFn; } export type PostValidatonFn = (v: OUT) => OUT2; export type CustomValidatorFn = (v: any) => string | undefined; export type CustomConverterFn = (v: any) => OUT; type Expand = { [K in keyof T]: T[K]; }; type StripIndexSignatureDeep = T extends readonly unknown[] ? T : T extends Record ? { [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: StripIndexSignatureDeep; } : T; type RelaxIndexSignature = T extends readonly unknown[] ? T : T extends AnyObject ? { [K in keyof T]: RelaxIndexSignature; } : T; type Override = Omit & U; declare const allowExtraKeysSymbol: unique symbol; type HasAllowExtraKeys = T extends { readonly [allowExtraKeysSymbol]?: true; } ? true : false; type IsAny = 0 extends 1 & T ? true : false; type IsAssignableRelaxed = IsAny> extends true ? true : [RelaxIndexSignature] extends [B] ? true : false; type ExactMatchBase = (() => T extends A ? 1 : 2) extends () => (T extends B ? 1 : 2) ? (() => T extends B ? 1 : 2) extends () => (T extends A ? 1 : 2) ? true : false : false; type ExactMatch = HasAllowExtraKeys extends true ? IsAssignableRelaxed : ExactMatchBase, Expand> extends true ? true : ExactMatchBase>, Expand>> extends true ? true : ExactMatchBase, keyof Expand> extends true ? [Expand] extends [Expand] ? [Expand] extends [Expand] ? true : false : false : false; type BuilderOutUnion[]> = { [K in keyof B]: B[K] extends JSchema ? O : never; }[number]; type AnyOfByOut>> = { [K in keyof D]: D[K] extends JSchema ? O : never; }[keyof D]; type BuilderFor = JSchema; export interface JsonBuilderRuleOpt { /** * Text of error message to return when the validation fails for the given rule: * * `{ msg: "is not a valid Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"` */ msg?: string; /** * A friendly name for what we are validating, that will be used in error messages: * * `{ name: "Oompa-loompa" } => "Object.property is not a valid Oompa-loompa"` */ name?: string; } type EnumKeyUnion = T extends readonly (infer U)[] ? `${U & (string | number)}` : T extends StringEnum | NumberEnum ? `${T[keyof T] & (string | number)}` : never; type SchemaOut = S extends JSchema ? OUT : never; type SchemaOpt = S extends JSchema ? (Opt extends true ? true : false) : false; type TupleOut[]> = { [K in keyof T]: T[K] extends JSchema ? O : never; }; export {};