import { objectTransform } from "bknd/utils"; import { MediaField, mediaFieldConfigSchema } from "../media/MediaField"; import { FieldClassMap } from "data/fields"; import { RelationClassMap, RelationFieldClassMap } from "data/relations"; import { entityConfigSchema, entityTypes } from "data/entities"; import { primaryFieldTypes, baseFieldConfigSchema } from "./fields"; import { s } from "bknd/utils"; import { $object, $record } from "modules/mcp"; export const FIELDS = { ...FieldClassMap, ...RelationFieldClassMap, media: { schema: mediaFieldConfigSchema, field: MediaField }, }; export const FIELD_TYPES = Object.keys(FIELDS); export type FieldType = keyof typeof FIELDS; export const RELATIONS = RelationClassMap; export const fieldsSchemaObject = objectTransform(FIELDS, (field, name) => { return s.strictObject( { name: s.string().optional(), // @todo: verify, old schema wasn't strict (req in UI) type: s.literal(name), config: field.schema.optional(), }, { title: name, }, ); }); export const fieldsSchema = s.anyOf(Object.values(fieldsSchemaObject)); export const entityFields = s.record(fieldsSchema, { default: {} }); export type TAppDataField = s.Static; export type TAppDataEntityFields = s.Static; export const entitiesSchema = s.strictObject({ name: s.string().optional(), // @todo: verify, old schema wasn't strict (req in UI) type: s.string({ enum: entityTypes, default: "regular" }).optional(), config: entityConfigSchema.optional(), fields: entityFields.optional(), }); export type TAppDataEntity = s.Static; export const simpleEntitiesSchema = s.strictObject({ type: s.string({ enum: entityTypes, default: "regular" }).optional(), config: entityConfigSchema.optional(), fields: s .record( s.object({ type: s.anyOf([s.string({ enum: FIELD_TYPES }), s.string()]), config: baseFieldConfigSchema.optional(), }), { default: {} }, ) .optional(), }); export const relationsSchema = Object.entries(RelationClassMap).map(([name, relationClass]) => { return s.strictObject( { type: s.literal(name), source: s.string(), target: s.string(), config: relationClass.schema.optional(), }, { title: name, }, ); }); export type TAppDataRelation = s.Static<(typeof relationsSchema)[number]>; export const indicesSchema = s.strictObject({ entity: s.string(), fields: s.array(s.string(), { minItems: 1 }), unique: s.boolean({ default: false }).optional(), }); export const dataConfigSchema = $object("config_data", { basepath: s.string({ default: "/api/data" }).optional(), default_primary_format: s.string({ enum: primaryFieldTypes, default: "integer" }).optional(), entities: $record("config_data_entities", entitiesSchema, { default: {} }).optional(), relations: $record( "config_data_relations", s.anyOf(relationsSchema), { default: {}, }, s.strictObject({ type: s.string({ enum: Object.keys(RelationClassMap) }), source: s.string(), target: s.string(), config: s.object({}).optional(), }), ).optional(), indices: $record("config_data_indices", indicesSchema, { default: {}, mcp: { update: false }, }).optional(), }).strict(); export type AppDataConfig = s.Static;