Skip to content

Commit

Permalink
Merge tag 'npm/0.1.71'
Browse files Browse the repository at this point in the history
  • Loading branch information
ianmacartney committed Feb 6, 2025
2 parents 3c6d4f1 + e79d3a9 commit e31e3cd
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion packages/convex-helpers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,16 @@ const balanceAndEmail = pick(vv.doc("accounts").fields, ["balance", "email"]);
const accountWithoutBalance = omit(vv.doc("accounts").fields, ["balance"]);

// Validate against a validator. Can optionally throw on error.
validate(balanceAndEmail, { balance: 123n, email: "[email protected]" });
const value = { balance: 123n, email: "[email protected]" };
validate(balanceAndEmail, value);

// This will throw a ValidationError if the value is not valid.
validate(balanceAndEmail, value, { throw: true });

// Warning: this only validates that `accountId` is a string.
validate(vv.id("accounts"), accountId);
// Whereas this validates that `accountId` is an id for the accounts table.
validate(vv.id("accounts"), accountId, { db: ctx.db });
```

## Filter
Expand Down
2 changes: 1 addition & 1 deletion packages/convex-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "convex-helpers",
"version": "0.1.70",
"version": "0.1.71",
"description": "A collection of useful code to complement the official convex package.",
"type": "module",
"bin": {
Expand Down
17 changes: 17 additions & 0 deletions packages/convex-helpers/server/validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,23 @@ describe("validate", () => {
expect(validate(optionalString, 123)).toBe(false);
});

test("validates id validator", async () => {
const idValidator = id("users");
expect(validate(idValidator, "123")).toBe(true);
expect(validate(idValidator, "any string")).toBe(true);
expect(
validate(object({ someArray: optional(array(idValidator)) }), {
someArray: ["string", "other string"],
}),
).toBe(true);
const t = convexTest(schema, modules);
await t.run(async (ctx) => {
const userId = await ctx.db.insert("users", { tokenIdentifier: "test" });
expect(validate(idValidator, userId, { db: ctx.db })).toBe(true);
expect(validate(idValidator, "not an id", { db: ctx.db })).toBe(false);
});
});

test("throws validation errors when configured", () => {
expect(() => validate(string, 123, { throw: true })).toThrow(
ValidationError,
Expand Down
33 changes: 24 additions & 9 deletions packages/convex-helpers/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
import { Expand } from "./index.js";
import {
DataModelFromSchemaDefinition,
GenericDatabaseReader,
GenericDataModel,
SchemaDefinition,
TableNamesInDataModel,
} from "convex/server";
Expand Down Expand Up @@ -357,8 +359,13 @@ export function validate<T extends Validator<any, any, any>>(
validator: T,
value: unknown,
opts?: {
/* If true, throw an error if the value is not valid. */
throw?: boolean;
pathPrefix?: string;
/* If provided, v.id validation will check that the id is for the table. */
db?: GenericDatabaseReader<GenericDataModel>;
/* A prefix for the path of the value being validated, for error reporting.
This is used for recursive calls, do not set it manually. */
_pathPrefix?: string;
},
): value is T["type"] {
let valid = true;
Expand Down Expand Up @@ -419,6 +426,11 @@ export function validate<T extends Validator<any, any, any>>(
case "id": {
if (typeof value !== "string") {
valid = false;
} else if (opts?.db) {
const id = opts.db.normalizeId(validator.tableName, value);
if (!id) {
valid = false;
}
}
break;
}
Expand All @@ -428,8 +440,11 @@ export function validate<T extends Validator<any, any, any>>(
break;
}
for (const [index, v] of value.entries()) {
const path = `${opts?.pathPrefix ?? ""}[${index}]`;
valid = validate(validator.element, v, { ...opts, pathPrefix: path });
const path = `${opts?._pathPrefix ?? ""}[${index}]`;
valid = validate(validator.element, v, {
...opts,
_pathPrefix: path,
});
if (!valid) {
expected = validator.element.kind;
break;
Expand Down Expand Up @@ -459,7 +474,7 @@ export function validate<T extends Validator<any, any, any>>(
for (const [k, fieldValidator] of Object.entries(validator.fields)) {
valid = validate(fieldValidator, (value as any)[k], {
...opts,
pathPrefix: appendPath(opts, k),
_pathPrefix: appendPath(opts, k),
});
if (!valid) {
break;
Expand Down Expand Up @@ -498,15 +513,15 @@ export function validate<T extends Validator<any, any, any>>(
for (const [k, fieldValue] of Object.entries(value)) {
valid = validate(validator.key, k, {
...opts,
pathPrefix: appendPath(opts, k),
_pathPrefix: appendPath(opts, k),
});
if (!valid) {
expected = validator.key.kind;
break;
}
valid = validate(validator.value, fieldValue, {
...opts,
pathPrefix: appendPath(opts, k),
_pathPrefix: appendPath(opts, k),
});
if (!valid) {
expected = validator.value.kind;
Expand All @@ -518,11 +533,11 @@ export function validate<T extends Validator<any, any, any>>(
}
}
if (!valid && opts?.throw) {
throw new ValidationError(expected, typeof value, opts?.pathPrefix);
throw new ValidationError(expected, typeof value, opts?._pathPrefix);
}
return valid;
}

function appendPath(opts: { pathPrefix?: string } | undefined, path: string) {
return opts?.pathPrefix ? `${opts.pathPrefix}.${path}` : path;
function appendPath(opts: { _pathPrefix?: string } | undefined, path: string) {
return opts?._pathPrefix ? `${opts._pathPrefix}.${path}` : path;
}

0 comments on commit e31e3cd

Please sign in to comment.