You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
2.0 KiB
TypeScript

import { z } from "./deps/zod.ts";
import { zUnionLiterals } from "./utils.ts";
import { ValidationErrors } from "./validation.ts";
export const FieldKinds = ["string" as const, "textarea" as const] as const;
export const FieldKind = zUnionLiterals(FieldKinds);
export type FieldKind = z.infer<typeof FieldKind>;
export const FieldShape = z.object({
name: z.string(),
kind: FieldKind,
required: z.boolean(),
});
export type FieldShape = z.infer<typeof FieldShape>;
const SchemaBase = {
id: z.number(),
fields: z.array(FieldShape),
name: z.string(),
};
export const Schema = z.object(SchemaBase);
export type ID<Brand extends string, T = number> = T & {
readonly [P in Brand]: never;
};
export type RefineID<T, IDT> = Omit<T, "id"> & {
id: IDT;
};
export type SchemaID = ID<"__schema_id">;
export type Schema = RefineID<z.infer<typeof Schema>, SchemaID>;
export const MaybePersistedSchema = z.object({
...SchemaBase,
id: z.number().optional(),
});
export type MaybePersistedSchema = z.infer<typeof MaybePersistedSchema>;
export type ValidationErrors_<T> = ValidationErrors<Omit<T, "id">>;
export const validateSchema = (
schema: MaybePersistedSchema,
): ValidationErrors_<Schema> => {
const errors: ReturnType<typeof validateSchema> = {};
const fieldNameMap = new Map<string, { idx: number; count: number }>();
for (let i = 0; i < schema.fields.length; ++i) {
const f = schema.fields[i];
if (!fieldNameMap.has(f.name)) {
fieldNameMap.set(f.name, { idx: i, count: 0 });
}
const ent = fieldNameMap.get(f.name)!;
ent.count++;
ent.idx = i;
}
for (const [, v] of fieldNameMap) {
if (v.count > 1) {
errors[`fields.${v.idx}.name`] = ["Duplicate field name"];
}
}
for (let i = 0; i < schema.fields.length; ++i) {
const field = schema.fields[i];
if (field.name == "") {
errors[`fields.${i}.name`] = ["Field name cannot be empty"];
}
}
if (schema.name == "") {
errors.name = ["Name cannot be empty"];
}
// TODO: validate that schemas are named uniquely
return errors;
};