import { file, glob } from "astro/loaders"; import { defineCollection, reference, z } from "astro:content"; //import { parse } from "@std/toml"; import { parse } from "toml"; import { EntityTypesEnum, KeywordsEnum, LicensesEnum } from "./consts.ts"; import { get, instanciate } from "./utils/anonymous.ts"; import { isValidLocale } from "./utils/lang.ts"; const Blog = z.object({ title: z.string().trim(), subtitle: z.string().trim().optional(), description: z.string().trim().optional(), keywords: z.array(KeywordsEnum).optional().refine( (keywords) => new Set(keywords).size === (keywords?.length ?? 0), { message: "Keywords must be unique", }, ).transform((keywords) => keywords !== undefined ? new Set(keywords).values().toArray() : undefined ), dateCreated: z.coerce.date(), dateUpdated: z.coerce.date().optional(), locationCreated: z.string().trim().optional(), relatedPosts: z.array(reference("blog")).default([]).refine( (posts) => new Set(posts).size === (posts?.length ?? 0), { message: "Related posts referenced multiple times", }, ).transform((x) => new Set(x)).transform((set) => set.values().toArray()), lang: z.string().trim().refine(isValidLocale), translationOf: reference("blog").optional(), signers: z.array( z.object({ entity: reference("entity"), role: EntityTypesEnum }), ).optional().refine( (signers) => { if (signers === undefined) return true; return signers.filter((s) => s.role === "author").length <= 1; }, { message: "There can only be one author", }, ).refine( (signers) => { const ids = signers?.map(get("entity")) ?? []; return new Set(ids).size === ids.length; }, { message: "Reusing signers", }, //).transform((signers) => // Object.fromEntries(new Map(signers?.map(({ entity, ...rest }) => [entity, rest]) ?? [])) ), license: LicensesEnum, }).refine( ({ dateCreated, dateUpdated }) => dateUpdated === undefined || dateCreated.getTime() <= dateUpdated.getTime(), { message: "Update before creation" }, ).refine( ({ translationOf, keywords }) => translationOf !== undefined || (keywords?.length ?? 0) > 0, { message: "Originals must include at least one keyword", path: ["keywords"], }, ).refine( ({ translationOf, keywords }) => (translationOf === undefined) !== ((keywords?.length ?? 0) <= 0), { message: "we will use this information from the original, " + "so no need to specify it for translations", path: ["keywords"], }, ).refine( ({ translationOf, relatedPosts }) => (translationOf === undefined) || (relatedPosts.length <= 0), { message: "we will use this information from the original, " + "so no need to specify it for translations", path: ["relatedPosts"], }, ).refine( ({ translationOf, signers = [] }) => (translationOf === undefined) || (signers.values().every(({ role }) => role !== "translator")), { message: "There can't be translator signers on non translated work", path: ["signers"], }, ); const blog = defineCollection({ loader: glob({ base: "./public/blog", pattern: "+([0-9a-z-]).md" }), schema: Blog, }); export type Blog = z.infer; const Entity = z.object({ websites: z.array(z.string().url().trim()).default([]).transform((websites) => websites.map(instanciate(URL)) ), publickey: z.object({ armor: z.string().trim(), }), }); type Entity = z.infer; const entity = defineCollection({ loader: file("./src/content/entities.toml", { parser: (text) => parse(text).entities as Entity[], }), schema: Entity, }); export const collections = { blog, entity };