import { reference, z } from "astro:content"; import { isValidLocale } from "../../utils/lang.ts"; import { get } from "../../utils/anonymous.ts"; import type { CollectionEntry } from "astro:content"; export const KEYWORDS = [ "Portugal", "democracy", "test", "health", "gym", "diet", ] as const; export const KeywordsEnum = z.enum(KEYWORDS); export const ENTITY_TYPES = ["author", "co-author", "translator"] as const; export const EntityTypesEnum = z.enum(ENTITY_TYPES); export const CREATIVE_COMMONS_LICENSES = [ "CC0", "CC-BY", "CC-BY-SA", "CC-BY-ND", "CC-BY-NC", "CC-BY-NC-SA", "CC-BY-NC-ND", ] as const; export const LICENSES = [ ...CREATIVE_COMMONS_LICENSES, "WTFPL", "public domain", ] as const; export const LicensesEnum = z.enum(LICENSES); export const Original = z.object({ kind: z.literal("original"), title: z.string().trim(), subtitle: z.string().trim().optional(), description: z.string().trim().optional(), keywords: z.array(KeywordsEnum).refine( (keywords) => new Set(keywords).size === keywords.length, { message: "Keywords must be unique" }, ), 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, { message: "Related posts referenced multiple times" }, ), lang: z.string().trim().refine(isValidLocale), signers: z.array( z.object({ entity: reference("entity"), role: EntityTypesEnum }), ).default([]).refine( (signers) => 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" }).refine( (signers) => signers.every(({ role }) => role !== "translator"), { message: "There can't be translator signers on non translated work" }, ), license: LicensesEnum.default("public domain"), }); export const Translation = z.object({ kind: z.literal("translation"), title: z.string().trim(), subtitle: z.string().trim().optional(), description: z.string().trim().optional(), dateCreated: z.coerce.date(), dateUpdated: z.coerce.date().optional(), lang: z.string().trim().refine(isValidLocale), translationOf: reference("blog"), signers: z.array( z.object({ entity: reference("entity"), role: EntityTypesEnum }), ).default([]).refine( (signers) => 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" }), license: LicensesEnum.default("public domain"), }); export const Micro = z.object({ kind: z.literal("micro"), title: z.string().trim(), keywords: z.array(KeywordsEnum).refine( (keywords) => new Set(keywords).size === keywords.length, { message: "Keywords must be unique" }, ), dateCreated: z.coerce.date(), dateUpdated: z.coerce.date().optional(), locationCreated: z.string().trim().optional(), lang: z.string().trim().refine(isValidLocale), signers: z.array( z.object({ entity: reference("entity"), role: EntityTypesEnum }), ).default([]).refine( (signers) => 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" }).refine( (signers) => signers.every(({ role }) => role !== "translator"), { message: "There can't be translator signers on non translated work" }, ), license: LicensesEnum.default("public domain"), }); export const Blog = z.discriminatedUnion("kind", [ Original, Translation, Micro, ]).refine( ({ dateCreated, dateUpdated }) => dateUpdated === undefined || dateCreated.getTime() <= dateUpdated.getTime(), { message: "Update before creation" }, ); export type OriginalEntry = CollectionEntry<"blog"> & { data: z.infer; }; export type TranslationEntry = CollectionEntry<"blog"> & { data: z.infer; }; export type MicroEntry = CollectionEntry<"blog"> & { data: z.infer; }; export type Entry = OriginalEntry | TranslationEntry | MicroEntry; export const Entity = z.object({ websites: z.array(z.string().url().trim()).default([]).transform((websites) => websites.map((x) => new URL(x).href) ), publickey: z.object({ armor: z.string().trim() }), });