import * as fc from 'fast-check' import type { SeedMap } from './generator.js' import { TypeNames } from './typename.js' import { byTag } from './generator-seed.js' export type ArrayParams = { /* length?: number */ minLength?: number maxLength?: number } export type IntegerParams = { minimum?: number maximum?: number multipleOf?: number } export type NumberParams = { minimum?: number maximum?: number minExcluded?: boolean maxExcluded?: boolean multipleOf?: number } export type BigIntParams = { minimum?: bigint maximum?: bigint multipleOf?: bigint } export type StringParams = { /* prefix?: string, postfix?: string, pattern?: string, substring?: string, length?: number */ minLength?: number maxLength?: number } export type Params = { any?: {} array?: ArrayParams bigint?: BigIntParams boolean?: {} catch?: {} custom?: {} date?: {} default?: {} enum?: {} file?: {} int?: IntegerParams intersection?: {} lazy?: {} literal?: {} map?: {} nan?: {} never?: {} nonoptional?: {} null?: {} nullable?: {} number?: NumberParams object?: {} optional?: {} pipe?: {} readonly?: {} record?: {} set?: {} string?: StringParams success?: {} symbol?: {} template_literal?: {} transform?: {} tuple?: {} undefined?: {} union?: {} unknown?: {} void?: {} // interface?: {} promise?: {} } export interface Options extends Partial>, Constraints {} export type InferConfigType = S extends Options ? T : never export interface OptionsBase< T = never, K extends | string & keyof T = string & keyof T > { include: readonly K[] exclude: readonly K[] root: '*' | K sortBias: { [K in keyof SeedMap]+?: number } forceInvalid: boolean } export interface Config extends OptionsBase, byTypeName {} type StringConstraints = { unbounded?: boolean } & fc.StringConstraints export type Constraints = { any?: {} array?: { minLength?: number, maxLength?: number, unbounded?: boolean } bigint?: { min?: undefined | bigint, max?: undefined | bigint, multipleOf?: bigint | null, unbounded?: boolean } boolean?: {} catch?: {} custom?: {} date?: {} default?: {} enum?: {} file?: {} int?: { min?: undefined | number, max?: undefined | number, multipleOf?: number, unbounded?: boolean } & fc.IntegerConstraints intersection?: {} lazy?: {} literal?: {} map?: {} nan?: {} never?: {} nonoptional?: {} null?: {} nullable?: {} number?: { min?: undefined | number, max?: undefined | number, multipleOf?: number, unbounded?: boolean } & fc.DoubleConstraints object?: ObjectConstraints optional?: {} pipe?: {} prefault?: {} readonly?: {} record?: fc.DictionaryConstraints set?: {} string?: StringConstraints success?: {} symbol?: {} template_literal?: fc.ArrayConstraints transform?: {} tuple?: fc.ArrayConstraints undefined?: {} union?: fc.ArrayConstraints unknown?: {} void?: {} promise?: {} ['*']?: fc.OneOfConstraints } export interface byTypeName extends Required> { object: fc.UniqueArrayConstraintsRecommended<[k: string, v: unknown], string> array: fc.IntegerConstraints & { unbounded?: boolean } } export type ObjectConstraints = & Omit, 'minLength' | 'maxLength'> & { minKeys?: number maxKeys?: number size?: fc.SizeForArbitrary } const objectDefaults = { minKeys: 1, maxKeys: 3, size: 'xsmall', selector: ([k]) => k, comparator: 'SameValueZero', depthIdentifier: fc.createDepthIdentifier(), } satisfies ObjectConstraints export const defaultConstraints = { object: objectDefaults, any: {}, array: { unbounded: false, minLength: 0, maxLength: 0x10 }, bigint: { unbounded: false, min: undefined as never, max: undefined as never, multipleOf: null, }, boolean: {}, catch: {}, custom: {}, date: {}, default: {}, enum: {}, file: {}, int: { unbounded: false, min: undefined as never, max: undefined as never, multipleOf: Number.NaN, }, intersection: {}, lazy: {}, literal: {}, map: {}, nan: {}, never: {}, nonoptional: {}, null: {}, nullable: {}, number: { unbounded: false, min: -0x10000, max: 0x10000, multipleOf: Number.NaN, noNaN: true, noDefaultInfinity: true, minExcluded: false, maxExcluded: false, noInteger: false, }, optional: {}, pipe: {}, prefault: {}, readonly: {}, record: { depthIdentifier: fc.createDepthIdentifier(), maxKeys: 3, minKeys: 1, noNullPrototype: false, size: 'xsmall', } satisfies fc.DictionaryConstraints, set: {}, string: { unbounded: false, minLength: 0, maxLength: 0x100, size: 'xsmall', unit: 'grapheme-ascii', } satisfies StringConstraints, success: {}, symbol: {}, template_literal: { minLength: 0, maxLength: 10, depthIdentifier: fc.createDepthIdentifier(), size: 'xsmall', } satisfies fc.ArrayConstraints, transform: {}, tuple: { minLength: 1, maxLength: 3, size: 'xsmall', depthIdentifier: fc.createDepthIdentifier(), } satisfies fc.ArrayConstraints, undefined: {}, union: { depthIdentifier: fc.createDepthIdentifier(), minLength: 1, maxLength: 3, size: 'xsmall', } satisfies fc.ArrayConstraints, unknown: {}, void: {}, promise: {}, ['*']: { maxDepth: 3, depthIdentifier: fc.createDepthIdentifier(), depthSize: 'xsmall', withCrossShrink: true, } satisfies fc.OneOfConstraints, } as const satisfies { [K in keyof Constraints]-?: Required } export const unsupportedSchemas = ['promise'] satisfies (keyof SeedMap)[] export const defaults = { exclude: unsupportedSchemas, forceInvalid: false, include: TypeNames, root: '*', sortBias: byTag, } as const satisfies OptionsBase export function parseOptions(options?: Opts): Config> export function parseOptions(options?: Options): Config export function parseOptions(options: Options = defaults as never): Config { const { exclude = defaults.exclude, forceInvalid = defaults.forceInvalid, include = defaults.include, root = defaults.root, sortBias = defaults.sortBias, ['*']: { maxDepth: starMaxDepth = defaultConstraints['*'].maxDepth, depthSize: starDepthSize = defaultConstraints['*'].depthSize, ...STAR } = defaultConstraints['*'], any = defaultConstraints.any, array: { unbounded: arrayUnbounded, maxLength: arrayMax = defaultConstraints.array.maxLength, minLength: arrayMin = defaultConstraints.array.minLength, ...ARRAY } = defaultConstraints.array, bigint: { unbounded: bigIntUnbounded, max: bigIntMax, min: bigIntMin, ...BIGINT } = defaultConstraints.bigint, boolean = defaultConstraints.boolean, catch: catch_ = defaultConstraints.catch, custom = defaultConstraints.custom, date = defaultConstraints.date, default: default_ = defaultConstraints.default, enum: enum_ = defaultConstraints.enum, file = defaultConstraints.file, int: { unbounded: intUnbounded, max: intMax, min: intMin, // ...INT } = defaultConstraints.int, intersection = defaultConstraints.intersection, lazy = defaultConstraints.lazy, literal = defaultConstraints.literal, map = defaultConstraints.map, nan = defaultConstraints.nan, never = defaultConstraints.never, nonoptional = defaultConstraints.nonoptional, null: null_ = defaultConstraints.null, nullable = defaultConstraints.nullable, number: { unbounded: numberUnbounded, max: numberMax, maxExcluded: numberMaxExcluded, min: numberMin, minExcluded: numberMinExcluded, // ...NUMBER } = defaultConstraints.number, optional = defaultConstraints.optional, pipe = defaultConstraints.pipe, prefault = defaultConstraints.prefault, readonly = defaultConstraints.readonly, record: { maxKeys: recordMaxKeys = defaultConstraints.record.maxKeys, minKeys: recordMinKeys = defaultConstraints.record.minKeys, size: recordSize = defaultConstraints.record.size, ...RECORD } = defaultConstraints.record, set = defaultConstraints.set, string: { unbounded: stringUnbounded, minLength: stringMinLength, maxLength: stringMaxLength, size: stringSize = defaultConstraints.string.size, // ...STRING } = defaultConstraints.string, success = defaultConstraints.success, symbol = defaultConstraints.symbol, template_literal: { maxLength: templateLiteralMaxLength, minLength: templateLiteralMinLength, size: templateLiteralSize, ...TEMPLATE_LITERAL } = defaultConstraints.template_literal, transform = defaultConstraints.transform, tuple: { maxLength: tupleMaxLength = defaultConstraints.tuple.maxLength, minLength: tupleMinLength = defaultConstraints.tuple.minLength, ...TUPLE } = defaultConstraints.tuple, undefined: undefined_ = defaultConstraints.undefined, union: { minLength: unionMinLength = defaultConstraints.union.minLength, maxLength: unionMaxLength = defaultConstraints.union.maxLength, size: unionSize = defaultConstraints.union.size, ...UNION } = defaultConstraints.union, unknown = defaultConstraints.unknown, void: void_ = defaultConstraints.void, promise = defaultConstraints.promise, object: { maxKeys: objectMaxKeys = defaultConstraints.object.maxKeys, minKeys: objectMinKeys = defaultConstraints.object.minKeys, size: objectSize = defaultConstraints.object.size, ...OBJECT } = { maxKeys: defaultConstraints.object.maxKeys, minKeys: defaultConstraints.object.minKeys, size: defaultConstraints.object.size, }, } = options return { exclude, forceInvalid, include: include.length === 0 || include[0] === '*' ? defaults.include : include, root, sortBias: { ...defaults.sortBias, ...sortBias }, ['*']: { ...STAR, depthSize: starDepthSize, maxDepth: starMaxDepth, }, object: { ...OBJECT, minLength: objectMinKeys, maxLength: objectMaxKeys, size: objectSize, }, any, array: { ...ARRAY, unbounded: arrayUnbounded, min: arrayMin, max: arrayMax, }, bigint: { ...BIGINT, unbounded: bigIntUnbounded, max: bigIntMax, min: bigIntMin, }, boolean, catch: catch_, custom, date, default: default_, enum: enum_, file, int: { // ...INT, unbounded: intUnbounded, max: intMax, min: intMin, }, intersection, lazy, literal, map, nan, never, nonoptional, null: null_, nullable, number: { unbounded: numberUnbounded, max: numberMax, min: numberMin, maxExcluded: numberMaxExcluded, minExcluded: numberMinExcluded, }, optional, pipe, prefault, readonly, record: { ...RECORD, maxKeys: recordMaxKeys, minKeys: recordMinKeys, size: recordSize, }, set, string: { // ...STRING, unbounded: stringUnbounded, minLength: stringMinLength, maxLength: stringMaxLength, size: stringSize, }, success, symbol, template_literal: { ...TEMPLATE_LITERAL, maxLength: templateLiteralMaxLength, minLength: templateLiteralMinLength, size: templateLiteralSize, }, transform, tuple: { ...TUPLE, minLength: tupleMinLength, maxLength: tupleMaxLength, }, undefined: undefined_, union: { ...UNION, minLength: unionMinLength, maxLength: unionMaxLength, size: unionSize, }, unknown, void: void_, promise, } }