convex-helpers
Version:
A collection of useful code to complement the official convex package.
311 lines • 18.8 kB
TypeScript
import type { DataModelFromSchemaDefinition, Expand, GenericDatabaseReader, GenericDataModel, SchemaDefinition, TableNamesInDataModel } from "convex/server";
import type { GenericId, GenericValidator, Infer, ObjectType, OptionalProperty, PropertyValidators, Validator, VAny, VArray, VBoolean, VBytes, VFloat64, VId, VInt64, VLiteral, VNull, VObject, VOptional, VRecord, VString, VUnion } from "convex/values";
import { v } from "convex/values";
/**
* Helper for defining a union of literals more concisely.
*
* e.g. `literals("a", 1, false)` is equivalent to
* `v.union(v.literal("a"), v.literal(1), v.literal(false))`
* To use with an array:
* ```ts
* const myLiterals = ["a", 1, false] as const;
* const literalValidator = literals(...myLiterals)
* ```
* A similar result can be achieved with `v.union(...myLiterals.map(v.literal))`
* however the type of each union member will be the union of literal types,
* rather than each member being a specific literal type.
*
* @param args Values you want to use in a union of literals.
* @returns A validator for the union of the literals.
*/
export declare const literals: <T extends Array<string | number | boolean | bigint>>(...args: T) => VUnion<T[number], NoInfer<{ [K in keyof T]: VLiteral<T[K]>; }>>;
/**
* nullable define a validator that can be the value or null more consisely.
*
* @param x The validator to make nullable. As in, it can be the value or null.
* @returns A new validator that can be the value or null.
*/
export declare const nullable: <V extends Validator<any, "required", any>>(x: V) => VUnion<(VNull<null, "required"> | V)["type"], [VNull<null, "required">, V], "required", (VNull<null, "required"> | V)["fieldPaths"]>;
/**
* partial helps you define an object of optional validators more concisely.
*
* `partial({a: v.string(), b: v.number()})` is equivalent to
* `{a: v.optional(v.string()), b: v.optional(v.number())}`
*
* @param obj The object of validators to make optional. e.g. {a: v.string()}
* @returns A new object of validators that can be the value or undefined.
*/
export declare function partial<T extends PropertyValidators>(obj: T): {
[K in keyof T]: VOptional<T[K]>;
};
/**
* partial helps you define an object of optional validators more concisely.
*
* `partial(v.object({a: v.string(), b: v.number()}))` is equivalent to
* `v.object({a: v.optional(v.string()), b: v.optional(v.number())})`
*
* @param obj The object of validators to make optional. e.g. v.object({a: v.string()})
* @returns A new object of validators that can be the value or undefined.
*/
export declare function partial<T, V extends Record<string, GenericValidator>, O extends OptionalProperty>(obj: VObject<T, V, O>): PartialVObject<T, V, O>;
/**
* partial helps you define a union of optional validators more concisely.
*
* `partial(v.union(v.object({a: v.string()}), v.object({b: v.number()})))`
* is equivalent to
* `v.union(v.object({a: v.optional(v.string())}), v.object({b: v.optional(v.number())}))`
*
* @param obj The union of validators to make optional. e.g. v.union(v.object({a: v.string()}), v.object({b: v.number()}))
* @returns A new union of validators that can be the value or undefined.
*/
export declare function partial<T, V extends Validator<T, "required", any>[], O extends OptionalProperty>(obj: VUnion<T, V, O>): PartialVUnion<T, V, O>;
/**
* partial helps you define a union of optional validators more concisely.
*
* `partial(v.union(v.object({a: v.string()}), v.object({b: v.number()})))`
* is equivalent to
* `v.union(v.object({a: v.optional(v.string())}), v.object({b: v.optional(v.number())}))`
*
* @param obj The union of validators to make optional. e.g. v.union(v.object({a: v.string()}), v.object({b: v.number()}))
* @returns A new union of validators that can be the value or undefined.
*/
export declare function partial<Object extends VObject<any, any, any>, Union extends VUnion<any, any[], any>>(obj: Object | Union): PartialVUnion<Union["type"], Union["members"], Object["isOptional"]> | PartialVObject<Object["type"], Object["fields"], Object["isOptional"]>;
type PartialVObject<T, V extends Record<string, GenericValidator>, Optional extends OptionalProperty> = VObject<Partial<T>, {
[K in keyof V]: VOptional<V[K]>;
}, Optional>;
type PartialUnionMembers<Members extends readonly Validator<any, "required", any>[]> = {
[K in keyof Members]: Members[K] extends VObject<any, any, "required"> ? Members[K] extends VObject<infer MemberT, infer MemberV extends PropertyValidators, "required"> ? PartialVObject<MemberT, MemberV, "required"> : Members[K] : Members[K] extends VUnion<any, any, "required"> ? Members[K] extends VUnion<infer MemberT, infer MemberV, "required"> ? PartialVUnion<MemberT, MemberV, "required"> : Members[K] : Members[K];
};
type PartialVUnion<T, Members extends Validator<T, "required", any>[], Optional extends OptionalProperty> = VUnion<Partial<T>, PartialUnionMembers<Members>, Optional>;
/** @deprecated Use `v.string()` instead. Any string value. */
export declare const string: VString<string, "required">;
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
export declare const number: VFloat64<number, "required">;
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
export declare const float64: VFloat64<number, "required">;
/** @deprecated Use `v.boolean()` instead. boolean value. For typing it only as true, use `l(true)` */
export declare const boolean: VBoolean<boolean, "required">;
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
export declare const biging: VInt64<bigint, "required">;
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
export declare const int64: VInt64<bigint, "required">;
/** @deprecated Use `v.any()` instead. Any Convex value */
export declare const any: VAny<any, "required", string>;
/** @deprecated Use `v.null()` instead. Null value. Underscore is so it doesn't shadow the null builtin */
export declare const null_: VNull<null, "required">;
/** @deprecated Use `v.*()` instead. */
export declare const id: <TableName extends string>(tableName: TableName) => VId<GenericId<TableName>, "required">, object: <T_2 extends PropertyValidators>(fields: T_2) => VObject<Expand<{ [Property in { [Property_1 in keyof T_2]: T_2[Property_1]["isOptional"] extends "optional" ? Property_1 : never; }[keyof T_2]]?: Exclude<Infer<T_2[Property]>, undefined>; } & { [Property_1 in Exclude<keyof T_2, { [Property in keyof T_2]: T_2[Property]["isOptional"] extends "optional" ? Property : never; }[keyof T_2]>]: Infer<T_2[Property_1]>; }>, T_2, "required", { [Property_2 in keyof T_2]: Property_2 | `${Property_2 & string}.${T_2[Property_2]["fieldPaths"]}`; }[keyof T_2] & string>, array: <T_1 extends Validator<any, "required", any>>(element: T_1) => VArray<T_1["type"][], T_1, "required">, bytes: () => VBytes<ArrayBuffer, "required">, literal: <T extends string | number | bigint | boolean>(literal: T) => VLiteral<T, "required">, optional: <T_4 extends GenericValidator>(value: T_4) => VOptional<T_4>, union: <T_3 extends Validator<any, "required", any>[]>(...members: T_3) => VUnion<T_3[number]["type"], T_3, "required", T_3[number]["fieldPaths"]>;
/** @deprecated Use `v.bytes()` instead. ArrayBuffer validator. */
export declare const arrayBuffer: VBytes<ArrayBuffer, "required">;
/**
* Utility to get the validators for fields associated with a table.
* e.g. for systemFields("users") it would return:
* { _id: v.id("users"), _creationTime: v.number() }
*
* @param tableName The table name in the schema.
* @returns Validators for the system fields: _id and _creationTime
*/
export declare const systemFields: <TableName extends string>(tableName: TableName) => {
_id: VId<GenericId<TableName>, "required">;
_creationTime: VFloat64<number, "required">;
};
export type SystemFields<TableName extends string> = ReturnType<typeof systemFields<TableName>>;
/**
* Utility to add system fields to an object with fields mapping to validators.
* e.g. withSystemFields("users", { name: v.string() }) would return:
* { name: v.string(), _id: v.id("users"), _creationTime: v.number() }
*
* @param tableName Table name in the schema.
* @param fields The fields of the table mapped to their validators.
* @returns The fields plus system fields _id and _creationTime.
*/
export declare const withSystemFields: <TableName extends string, T extends Record<string, GenericValidator>>(tableName: TableName, fields: T) => Expand<T & {
_id: VId<GenericId<TableName>, "required">;
_creationTime: VFloat64<number, "required">;
}>;
export type AddFieldsToValidator<V extends Validator<any, any, any>, Fields extends PropertyValidators> = V extends VObject<infer T, infer F, infer O> ? VObject<Expand<T & ObjectType<Fields>>, Expand<F & Fields>, O> : Validator<Expand<V["type"] & ObjectType<Fields>>, V["isOptional"], V["fieldPaths"] & {
[Property in keyof Fields & string]: `${Property}.${Fields[Property]["fieldPaths"]}` | Property;
}[keyof Fields & string] & string>;
/**
* Equivalent to `v.object({ ...validator, ...fields })`.
*/
export declare function addFieldsToValidator<Props extends PropertyValidators, Fields extends PropertyValidators>(validator: Props, fields: Fields): VObject<ObjectType<Props & Fields>, Props & Fields, "required">;
/**
* Add fields to an object validator.
* Equivalent to `v.object({ ...validator.fields, ...fields })`.
*/
export declare function addFieldsToValidator<V extends VObject<any, any, any>, Fields extends PropertyValidators>(validator: V, fields: Fields): VObject<V["type"] & ObjectType<Fields>, V["fields"] & Fields, V["isOptional"]>;
/**
* Add fields to a union validator.
* Equivalent to `v.union(...validator.members.map((m) => addFieldsToValidator(m, fields)))`.
*/
export declare function addFieldsToValidator<V extends VUnion<any, any[], any>, Fields extends PropertyValidators>(validator: V, fields: Fields): AddFieldsToValidator<V, Fields>;
export declare function addFieldsToValidator<V extends VObject<any, any, any> | VUnion<any, any[], any>, Fields extends PropertyValidators>(validator: V, fields: Fields): AddFieldsToValidator<V, Fields>;
export declare const doc: <Schema extends SchemaDefinition<any, boolean>, TableName extends TableNamesInDataModel<DataModelFromSchemaDefinition<Schema>>>(schema: Schema, tableName: TableName) => AddFieldsToValidator<Schema["tables"][TableName]["validator"], SystemFields<TableName>>;
/**
* Creates a validator with a type-safe `.id(table)` and a new `.doc(table)`.
* Can be used instead of `v` for function arugments & return validators.
* However, it cannot be used as part of defining a schema, since it would be
* circular.
* ```ts
* import schema from "./schema";
* export const vv = typedV(schema);
*
* export const myQuery = query({
* args: { docId: vv.id("mytable") },
* returns: vv.doc("mytable"),
* handler: (ctx, args) => ctx.db.get(args.docId),
* })
*
* @param schema Typically from `import schema from "./schema"`.
* @returns A validator like `v` with type-safe `v.id` and a new `v.doc`
*/
export declare function typedV<Schema extends SchemaDefinition<any, boolean>>(schema: Schema): Omit<typeof v, "id"> & {
id: <TableName extends TableNamesInDataModel<DataModelFromSchemaDefinition<Schema>>>(tableName: TableName) => VId<GenericId<TableName>, "required">;
doc: <TableName extends TableNamesInDataModel<DataModelFromSchemaDefinition<Schema>>>(tableName: TableName) => AddFieldsToValidator<Schema["tables"][TableName]["validator"], SystemFields<TableName>>;
};
/**
* A string validator that is a branded string type.
*
* Read more at https://stack.convex.dev/using-branded-types-in-validators
*
* @param _brand - A unique string literal to brand the string with
*/
export declare const brandedString: <T extends string>(_brand: T) => VString<string & {
_: T;
}>;
/** Mark fields as deprecated with this permissive validator typed as null */
export declare const deprecated: Validator<null, "optional">;
/** A maximally permissive validator that type checks as a given validator.
*
* If you want to have types that match some validator but you have invalid data
* and you want to temporarily not validate schema for this field,
* you can use this function to cast the permissive validator.
*
* Example in a schema:
* ```ts
* export default defineSchema({
* myTable: defineTable({
* myString: pretend(v.array(v.string())),
* }),
* });
* //...in some mutation
* ctx.db.insert("myTable", { myString: 123 as any }); // no runtime error
* ```
* Example in function argument validation:
* ```ts
* const myQuery = defineQuery({
* args: { myNumber: pretend(v.number()) },
* handler: async (ctx, args) => {
* // args.myNumber is typed as number, but it's not validated.
* const num = typeof args.myNumber === "number" ?
* args.myNumber : Number(args.myNumber);
* },
* });
*/
export declare const pretend: <T extends GenericValidator>(_typeToImmitate: T) => T;
/** A validator that validates as optional but type checks as required.
*
* If you want to assume a field is set for type checking, but your data may not
* actually have it set for all documents (e.g. when adding a new field),
* you can use this function to allow the field to be unset at runtime.
* This is unsafe, but can be convenient in these situations:
*
* 1. You are developing locally and want to add a required field and write
* code assuming it is set. Once you push the code & schema, you can update
* the data to match before running your code.
* 2. You are going to run a migration right after pushing code, and are ok with
* and you don't want to edit your code to handle the field being unset,
* your app being in an inconsistent state until the migration completes.
*
* This differs from {@link pretend} in that it type checks the inner validator,
* if the value is provided.
*
* Example in a schema:
* ```ts
* export default defineSchema({
* myTable: defineTable({
* myString: pretendRequired(v.array(v.string())),
* }),
* });
* //...in some mutation
* ctx.db.insert("myTable", { myString: undefined }); // no runtime error
* ```
* Example in function argument validation:
* ```ts
* const myQuery = defineQuery({
* args: { myNumber: pretendRequired(v.number()) },
* handler: async (ctx, args) => {
* // args.myNumber is typed as number, but it might be undefined
* const num = args.myNumber || 0;
* },
* });
*/
export declare const pretendRequired: <T extends Validator<any, "required", any>>(optionalType: T) => T;
export declare class ValidationError extends Error {
expected: string;
got: string;
path?: string | undefined;
constructor(expected: string, got: string, path?: string | undefined);
}
/**
* Validate a value against a validator.
*
* WARNING: This does not validate that v.id is an ID for the given table.
* It only validates that the ID is a string. Function `args`, `returns` and
* schema definitions will validate that the ID is an ID for the given table.
*
* @param validator The validator to validate against.
* @param value The value to validate.
* @returns Whether the value is valid against the validator.
*/
export declare function validate<T extends Validator<any, any, any>>(validator: T, value: unknown, opts?: {
throw?: boolean;
db?: GenericDatabaseReader<GenericDataModel>;
allowUnknownFields?: boolean;
_pathPrefix?: string;
}): value is T["type"];
/**
* Parse a value, using a Convex validator. This differs from `validate` in that
* it strips unknown fields instead of throwing an error on them.
*
* @param validator - The Convex validator to parse the value against.
* @param value - The value to parse.
* @returns The parsed value, without fields not specified in the validator.
*/
export declare function parse<T extends Validator<any, any, any>>(validator: T, value: unknown): Infer<T>;
type NotUndefined<T> = Exclude<T, undefined>;
/**
* A type that converts an optional validator to a required validator.
*
* This is the inverse of `VOptional`. It takes a validator that may be optional
* and returns the equivalent required validator type.
*
* @example
* ```ts
* type OptionalString = VOptional<VString<string, "required">>;
* type RequiredString = VRequired<OptionalString>; // VString<string, "required">
* ```
*/
export type VRequired<T extends Validator<any, OptionalProperty, any>> = T extends VId<infer Type, OptionalProperty> ? VId<NotUndefined<Type>, "required"> : T extends VString<infer Type, OptionalProperty> ? VString<NotUndefined<Type>, "required"> : T extends VFloat64<infer Type, OptionalProperty> ? VFloat64<NotUndefined<Type>, "required"> : T extends VInt64<infer Type, OptionalProperty> ? VInt64<NotUndefined<Type>, "required"> : T extends VBoolean<infer Type, OptionalProperty> ? VBoolean<NotUndefined<Type>, "required"> : T extends VNull<infer Type, OptionalProperty> ? VNull<NotUndefined<Type>, "required"> : T extends VAny<infer Type, OptionalProperty> ? VAny<NotUndefined<Type>, "required"> : T extends VLiteral<infer Type, OptionalProperty> ? VLiteral<NotUndefined<Type>, "required"> : T extends VBytes<infer Type, OptionalProperty> ? VBytes<NotUndefined<Type>, "required"> : T extends VObject<infer Type, infer Fields, OptionalProperty, infer FieldPaths> ? VObject<NotUndefined<Type>, Fields, "required", FieldPaths> : T extends VArray<infer Type, infer Element, OptionalProperty> ? VArray<NotUndefined<Type>, Element, "required"> : T extends VRecord<infer Type, infer Key, infer Value, OptionalProperty, infer FieldPaths> ? VRecord<NotUndefined<Type>, Key, Value, "required", FieldPaths> : T extends VUnion<infer Type, infer Members, OptionalProperty, infer FieldPaths> ? VUnion<NotUndefined<Type>, Members, "required", FieldPaths> : never;
/**
* Converts an optional validator to a required validator.
*
* This is the inverse of `v.optional()`. It takes a validator that may be optional
* and returns the equivalent required validator.
*
* ```ts
* const optionalString = v.optional(v.string());
* const requiredString = vRequired(optionalString); // v.string()
*
* // Already required validators are returned as-is
* const alreadyRequired = v.string();
* const stillRequired = vRequired(alreadyRequired); // v.string()
* ```
*
* @param validator The validator to make required.
* @returns A required version of the validator.
*/
export declare function vRequired<T extends Validator<any, OptionalProperty, any>>(validator: T): VRequired<T>;
export {};
//# sourceMappingURL=validators.d.ts.map