summaryrefslogtreecommitdiff
path: root/src/lib/collection/schemas.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/collection/schemas.ts')
-rw-r--r--src/lib/collection/schemas.ts133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/lib/collection/schemas.ts b/src/lib/collection/schemas.ts
new file mode 100644
index 0000000..eca996f
--- /dev/null
+++ b/src/lib/collection/schemas.ts
@@ -0,0 +1,133 @@
+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"] 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<typeof Original>;
+};
+export type TranslationEntry = CollectionEntry<"blog"> & {
+ data: z.infer<typeof Translation>;
+};
+export type MicroEntry = CollectionEntry<"blog"> & {
+ data: z.infer<typeof Micro>;
+};
+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() }),
+});