kitcn
Version:
kitcn - React Query integration and CLI tools for Convex
197 lines (195 loc) • 8.24 kB
JavaScript
import { t as assert } from "./upstream-BR6sBLg3.js";
import { asObjectValidator, v } from "convex/values";
//#region src/internal/upstream/validators.ts
function partial(fieldsOrObjOrUnion) {
if (fieldsOrObjOrUnion.isConvexValidator) {
if (fieldsOrObjOrUnion.kind === "object") return partialVObject(fieldsOrObjOrUnion);
if (fieldsOrObjOrUnion.kind === "union") return partialUnion(fieldsOrObjOrUnion);
throw new Error("partial only works with union or object Validators, or a Record<string, Validator> currently");
}
return partialFields(fieldsOrObjOrUnion);
}
/**
* partialFields helps you define an object of optional validators more concisely.
*
* e.g. `partialFields({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.
*/
function partialFields(obj) {
return Object.fromEntries(Object.entries(obj).map(([k, vv]) => [k, vv.isOptional === "optional" ? vv : v.optional(vv)]));
}
/**
* partialObject helps you define an object of optional validators more concisely.
*
* e.g. `partialObject({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.
*/
function partialVObject(obj) {
const o = v.object(partialFields(obj.fields));
if (obj.isOptional === "optional") return v.optional(o);
return o;
}
function partialUnion(union) {
const u = v.union(...union.members.map((m) => {
assert(m.isOptional === "required", "Union members cannot be optional");
if (m.kind === "object") return partialVObject(m);
if (m.kind === "union") return partialUnion(m);
throw new Error(`Invalid union member type: ${m.kind}`);
}));
if (union.isOptional === "optional") return v.optional(u);
return u;
}
/** @deprecated Use `v.string()` instead. Any string value. */
const string = v.string();
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
const number = v.float64();
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
const float64 = v.float64();
/** @deprecated Use `v.boolean()` instead. boolean value. For typing it only as true, use `l(true)` */
const boolean = v.boolean();
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
const biging = v.int64();
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
const int64 = v.int64();
/** @deprecated Use `v.any()` instead. Any Convex value */
const any = v.any();
/** @deprecated Use `v.null()` instead. Null value. Underscore is so it doesn't shadow the null builtin */
const null_ = v.null();
/** @deprecated Use `v.*()` instead. */
const { id, object, array, bytes, literal, optional, union } = v;
/** @deprecated Use `v.bytes()` instead. ArrayBuffer validator. */
const arrayBuffer = v.bytes();
function addFieldsToValidator(validatorOrFields, fields) {
const validator = asObjectValidator(validatorOrFields);
if (Object.keys(fields).length === 0) return validator;
switch (validator.kind) {
case "object": return v.object(intersectValidators(validator.fields, fields));
case "union": return v.union(...validator.members.map((m) => addFieldsToValidator(m, fields)));
default: throw new Error("Cannot add arguments to a validator that is not an object or union.");
}
}
function intersectValidators(fields, fields2) {
const specificFields = { ...fields };
for (const [k, v] of Object.entries(fields2)) {
const existing = specificFields[k];
if (existing) {
if (existing.kind !== v.kind) throw new Error(`Cannot intersect validators with different kinds: ${existing.kind} and ${v.kind}`);
if (existing.isOptional !== v.isOptional && existing.isOptional === "optional") specificFields[k] = v;
} else specificFields[k] = v;
}
return specificFields;
}
/** Mark fields as deprecated with this permissive validator typed as null */
const deprecated = v.optional(v.any());
/** 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);
* },
* });
*/
const pretend = (_typeToImmitate) => v.optional(v.any());
/** 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;
* },
* });
*/
const pretendRequired = (optionalType) => v.optional(optionalType);
/**
* 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.
*/
function vRequired(validator) {
const { kind, isOptional } = validator;
if (isOptional === "required") return validator;
switch (kind) {
case "id": return v.id(validator.tableName);
case "string": return v.string();
case "float64": return v.float64();
case "int64": return v.int64();
case "boolean": return v.boolean();
case "null": return v.null();
case "any": return v.any();
case "literal": return v.literal(validator.value);
case "bytes": return v.bytes();
case "object": return v.object(validator.fields);
case "array": return v.array(validator.element);
case "record": return v.record(validator.key, validator.value);
case "union": return v.union(...validator.members);
default: throw new Error("Unknown Convex validator type: " + kind);
}
}
//#endregion
export { pretendRequired as a, pretend as i, deprecated as n, vRequired as o, partial as r, addFieldsToValidator as t };