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
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;
|
|
};
|