UNPKG

papr

Version:

MongoDB TypeScript-aware Models

466 lines (465 loc) 16.7 kB
import { Binary, ObjectId, Decimal128 } from 'mongodb'; import type { TupleItems } from './TupleItems.ts'; import type { ObjectType } from './utils.ts'; interface RequiredOptions { required: boolean; } type GenericOptions = Partial<RequiredOptions>; interface ArrayOptions extends GenericOptions { maxItems?: number; minItems?: number; uniqueItems?: boolean; } interface NumberOptions extends GenericOptions { enum?: number[]; exclusiveMaximum?: boolean; exclusiveMinimum?: boolean; maximum?: number; minimum?: number; multipleOf?: number; } interface ObjectOptions extends GenericOptions { additionalProperties?: boolean; dependencies?: Record<string, unknown>; maxProperties?: number; minProperties?: number; patternProperties?: Record<string, unknown>; } interface StringOptions extends GenericOptions { enum?: string[]; maxLength?: number; minLength?: number; pattern?: string; } type GetType<Type, Options> = Options extends RequiredOptions ? Options['required'] extends true ? Type : Type | undefined : Type | undefined; /** * @module intro * @description * * Types are the building blocks of `papr` [schemas](schema.md), which provide TypeScript type definitions, * as well as the ability to generate [JSON schema](https://docs.mongodb.com/manual/core/schema-validation/#json-schema) * for validators in MongoDB collections. * * Some types have additional options, based on the available options from JSON schema for that data type. * * The following data types are available to define the schemas of your `papr` models: */ /** * The following type creator functions return valid JSON schema definitions at runtime, * however for TypeScript they return actual TypeScript types. * * To workaround this difference between runtime and type-checking, we use the pattern `return X as unknown as Y`; * * All type creator functions below return a `$required` attribute, which is used only internally * to compute the `required` keys array in the containing parent object. * * This `$required` value is removed in the `createSchemaType` function. */ export declare function any<Options extends GenericOptions>(options?: Options): any; declare function array<Item, Options extends ArrayOptions>(items: Item, options?: Options): GetType<NonNullable<Item>[], Options>; declare function constant<Value, Options extends GenericOptions>(value: Value, options?: Options): GetType<Value, Options>; declare function enumType<Enum, Options extends GenericOptions>(values: readonly Enum[], options?: Options): GetType<Enum, Options>; declare function number<Options extends NumberOptions>(options?: Options): GetType<number, Options>; export declare function object<Properties extends Record<string, any>, Options extends ObjectOptions>(properties: Properties, options?: Options): GetType<ObjectType<Properties>, Options>; export declare function objectGeneric<Property, Options extends ObjectOptions>(property: Property, pattern?: string, options?: Options): GetType<Record<string, Property>, Options>; export declare function oneOf<Types extends any[], Options extends GenericOptions>(types: Types, options?: Options): GetType<Exclude<Types[number], undefined>, Options>; declare function string<Options extends StringOptions>(options?: Options): GetType<string, Options>; declare function tuple<Items extends readonly unknown[], Options extends GenericOptions>(items: Items, options?: Options): GetType<TupleItems<Items>, Options>; declare function unknown<Options extends GenericOptions>(options?: Options): unknown; declare const _default: { /** * Creates an array consisting of items of a single type. * * @param item {TItem} * @param [options] {ArrayOptions} * @param [options.maxItems] {number} * @param [options.minItems] {number} * @param [options.required] {boolean} * @param [options.uniqueItems] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredList: types.array(types.number(), { required: true }), * optionalList: types.array(types.number()), * // All inner types are `required` by default, so optionalList and anotherOptionalList * // are equivalent types * anotherOptionalList: types.array(types.number({ required: true })) * listWithAllOptions: types.array(types.number(), { * maxItems: 10, * minItems: 1, * required: true, * uniqueItems: true, * }), * }); * */ array: typeof array; /** * Creates a binary type. Useful for storing `Buffer` or any other binary data. * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredBinary: types.binary({ required: true }), * optionalBinary: types.binary(), * }); */ binary: <Options extends GenericOptions>(options?: Options | undefined) => GetType<Binary, Options>; /** * Creates a boolean type. * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredBoolean: types.boolean({ required: true }), * optionalBoolean: types.boolean(), * }); */ boolean: <Options extends GenericOptions>(options?: Options | undefined) => GetType<boolean, Options>; /** * Creates a constant value. Useful for creating discriminated unions with the `oneOf` type. * * @param value {TValue} * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * shape: types.oneOf([ * types.object({ * type: types.constant('circle' as const, { required: true }), * radius: types.number({ required: true }), * }), * types.object({ * type: types.constant('rectangle' as const, { required: true }), * width: types.number({ required: true }), * length: types.number({ required: true }), * }), * ]), * }); */ constant: typeof constant; /** * Creates a date type. * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredDate: types.date({ required: true }), * optionalDate: types.date(), * }); */ date: <Options extends GenericOptions>(options?: Options | undefined) => GetType<Date, Options>; /** * Creates a IEEE 754 decimal-based 128 bit floating-point number type. * Useful for storing monetary values, scientific computations or any other number that requires high precision. * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredDecimal: types.decimal({ required: true }), * optionalDecimal: types.decimal(), * }); */ decimal: <Options extends GenericOptions>(options?: Options | undefined) => GetType<Decimal128, Options>; /** * With `enum` you can create an enum type based on either: * * - a TypeScript `enum` structure * - a readonly/const array (`as const`) * * Enum types may contain `null` as well. * * Const enums require a full type cast when used in the schema `defaults`. * * @param values {Array<TValue>} * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * enum SampleEnum { * foo = 'foo', * bar = 'bar' * } * * const SampleConstArray = ['foo', 'bar'] as const; * * schema({ * // type: SampleEnum * requiredEnum: types.enum(Object.values(SampleEnum), { required: true }), * // type: SampleEnum | undefined * optionalEnum: types.enum(Object.values(SampleEnum)), * // type: SampleEnum | null | undefined * optionalEnumWithNull: types.enum([...Object.values(SampleEnum), null]), * // type: 'foo' | 'bar' * requiredEnumAsConstArray: types.enum(SampleConstArray, { required: true }), * // type: 'foo' | 'bar' | undefined * optionalEnumAsConstArray: types.enum(SampleConstArray), * }); */ enum: typeof enumType; /** * Creates a `null` type. Use discouraged. Typically used in conjunction with * another type when applying a schema to a collection that already contains * `null` values in a field. * * Usage of `null` as a value in Mongo is discouraged, as it makes some * common query patterns ambiguous: `find({ myField: null })` will match * documents that have the `myField` value set to the literal `null` _or_ * that match `{ myField: { $exists: false } }`. * * To match documents with a literal `null` value you must query with * `{ myField: { $type: 10 } }` (where `10` is the [BSON null type * constant](https://www.mongodb.com/docs/manual/reference/bson-types/)) * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * nullableNumber: types.oneOf([ types.number(), types.null() ]), * }); */ null: <Options extends GenericOptions>(options?: Options | undefined) => GetType<null, Options>; /** * Creates a number type. * * @param [options] {NumberOptions} * @param [options.enum] {Array<number>} * @param [options.exclusiveMaximum] {boolean} * @param [options.exclusiveMinimum] {boolean} * @param [options.maximum] {number} * @param [options.minimum] {number} * @param [options.mulitpleOf] {number} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * optionalNumber: types.number(), * requiredNumber: types.number({ required: true }), * numberWithAllOptions: types.number({ * enum: [1, 2, 3, 5, 8, 13], * exclusiveMaximum: true, * exclusiveMinimum: true, * maximum: 0, * minimum: 14, * multipleOf: 1, * required: true, * }), * }); */ number: typeof number; /** * Creates an object type specifying all the known properties upfront. * * @param properties {TProperties} * @param [options] {ObjectOptions} * @param [options.additionalProperties] {boolean} * @param [options.dependencies] {Record<string, Array<string>>} * @param [options.maxProperties] {number} * @param [options.minProperties] {number} * @param [options.patternProperties] {Record<string, unknown>} * @param [options.required] {boolean} * * The advanced JSON schema options for this type (e.g. `patternProperties`) are also available, but these will not get type checked. * * @example * import { schema, types } from 'papr'; * * schema({ * optionalObject: types.object({ * foo: types.number(), * bar: types.string(), * }), * requiredObject: types.object( * { * foo: types.number(), * bar: types.string(), * }, * { required: true } * ), * objectWithAllOptions: types.object( * { * foo: types.number(), * bar: types.string(), * }, * { * additionalProperties: true, * dependencies: { * foo: ['bar'], * }, * maxProperties: 10, * minProperties: 2, * patternProperties: { * '^f.+': { type: 'string' }, * }, * required: true, * } * ), * }); */ object: typeof object; /** * Creates an object type without any upfront properties defined, instead you define only a pattern for the properties names. All properties will expect the same type as value (`TValue`). * * Note: It's recommended to avoid using such a type. It might throw a TypeScript error (TS2589) in the projection logic due to the looseness of the type definition. * * @param value {TValue} * @param [pattern=.+] {string} * @param [options] {ObjectOptions} * @param [options.additionalProperties] {boolean} * @param [options.dependencies] {Record<string, Array<string>>} * @param [options.maxProperties] {number} * @param [options.minProperties] {number} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * // This accepts any property name with the value as a number * optionalObjectGeneric: types.objectGeneric(types.number()), * // This accepts only objects with properties starting with `foo` * requiredObjectGeneric: types.objectGeneric( * types.number(), * '^foo.+', * { required: true } * ), * }); */ objectGeneric: typeof objectGeneric; /** * Creates an `ObjectId` type. * * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * optionalObjectId: types.objectId(), * requiredObjectId: types.objectId({ required: true }), * }); */ objectId: <Options extends GenericOptions>(options?: Options | undefined) => GetType<ObjectId, Options>; /** * Creates a union type of multiple other types. * * This is useful when combined with `objectGeneric`. * * @param types {Type[]} * @param [options] {StringOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * optionalStringOrNumber: types.oneOf([types.string(), types.number()]), * requiredStringOrNumber: types.oneOf([types.string(), types.number()], { required: true }), * }); */ oneOf: typeof oneOf; /** * Creates a string type. * * @param [options] {StringOptions} * @param [options.enum] {Array<string>} * @param [options.maxLength] {number} * @param [options.minLength] {number} * @param [options.pattern] {string} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * optionalString: types.string(), * requiredString: types.string({ required: true }), * stringWithAllOptions: types.number({ * enum: ['foo', 'bar'], * maxLength: 3, * minLength: 1, * pattern: '^\\w+$', * required: true, * }), * }); */ string: typeof string; /** * Creates a tuple type for the items in the supplied items array. * * Items passed to tuple must be readonly to preserve their order and any optional properties preceding a required property are implicitly required as well. * * @param types {Type[]} * @param [options] {GenericOptions} * @param [options.required] {boolean} * * @example * import { schema, types } from 'papr'; * * schema({ * requiredTuple: types.tuple([ * types.number(), * types.string() * ] as const, { required: true }), * optionalTuple: types.tuple([ * types.number(), * types.string() * ] as const), * }); */ tuple: typeof tuple; /** * We recommend avoiding this type. It only exists as an escape hatch for unknown data. * * @example * import { schema, types } from 'papr'; * * schema({ * unknownData: types.any(), * }); */ any: typeof any; /** * This allows any value to be assigned, but is typed as unknown to force assertions * before relying on the data. Like with `any`, we recommend avoiding this type. * It only exists as an escape hatch for unknown data. * * @example * import { schema, types } from 'papr'; * * schema({ * unknownData: types.unknown(), * }); */ unknown: typeof unknown; }; export default _default;