summaryrefslogtreecommitdiff
path: root/src/content.config.ts
blob: f652cc3a72edc60ee1add58a300c08adb1b51087 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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<typeof Blog>;

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<typeof Entity>;

const entity = defineCollection({
  loader: file("./src/content/entities.toml", {
    parser: (text) => parse(text).entities as Entity[],
  }),
  schema: Entity,
});

export const collections = { blog, entity };