Improve schema and update tags
- Make constants in schema explicit - Enumerate tag-categories - More i18n utilities - Better anonymous user support without special field - Remove most tuples and unchecked type-casting
This commit is contained in:
parent
579e5879e1
commit
17ef8c652c
34 changed files with 223 additions and 221 deletions
|
|
@ -16,6 +16,8 @@ export const WEBSITE_LIST = [
|
|||
] as const;
|
||||
export const GAME_PLATFORMS = ["web", "windows", "linux", "macos", "android", "ios"] as const;
|
||||
export const DEFAULT_LANG = "eng";
|
||||
export const DEFAULT_AUTHOR = "bad-manners";
|
||||
export const ANONYMOUS_USER = "anonymous";
|
||||
|
||||
// Validators
|
||||
|
||||
|
|
@ -27,13 +29,34 @@ const inkbunnyPostUrlRegex = /^(?:https:\/\/)(?:www\.)?inkbunny\.net\/s\/([1-9]\
|
|||
const sofurryPostUrlRegex = /^(?:https:\/\/)www\.sofurry\.com\/view\/([1-9]\d*)\/?$/;
|
||||
const mastodonPostUrlRegex = /^(?:https:\/\/)((?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/@([a-zA-Z][a-zA-Z0-9_-]+)\/([1-9]\d*)\/?$/;
|
||||
|
||||
const refineAuthors = (value: { id: any } | any[]) => "id" in value || value.length > 0;
|
||||
const refineCopyrightedCharacters = (value: Record<string, any>) => !("" in value) || Object.keys(value).length == 1;
|
||||
const refineAuthors = [
|
||||
(value: { id: any } | any[]) => "id" in value || value.length > 0,
|
||||
`"authors" cannot be empty`,
|
||||
] as const;
|
||||
const refineCopyrightedCharacters = [
|
||||
(value: Record<string, any>) => !("" in value) || Object.keys(value).length == 1,
|
||||
`"copyrightedCharacters" cannot mix empty catch-all key with other keys`,
|
||||
] as const;
|
||||
|
||||
// Transformers
|
||||
|
||||
export const adjustDateForUTCOffset = (date: Date) =>
|
||||
new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
|
||||
export const parseMastodonPostUrl = (url: string, ctx: z.RefinementCtx) => {
|
||||
const match = mastodonPostUrlRegex.exec(url);
|
||||
if (!match) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `"mastodon" post contains an invalid URL`,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return {
|
||||
instance: match[1]!,
|
||||
user: match[2]!,
|
||||
postId: match[3]!,
|
||||
};
|
||||
};
|
||||
|
||||
// Types
|
||||
|
||||
|
|
@ -46,27 +69,15 @@ const mastodonPost = z
|
|||
user: z.string(),
|
||||
postId: z.string(),
|
||||
})
|
||||
.or(
|
||||
z.string().transform((mastodonPost, ctx) => {
|
||||
const match = mastodonPostUrlRegex.exec(mastodonPost);
|
||||
if (!match) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `"mastodon" post contains an invalid URL`,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return {
|
||||
instance: match[1],
|
||||
user: match[2],
|
||||
postId: match[3],
|
||||
};
|
||||
}),
|
||||
);
|
||||
.or(z.string().transform(parseMastodonPostUrl));
|
||||
const authors = z
|
||||
.union([reference("users"), z.array(reference("users"))])
|
||||
.default(DEFAULT_AUTHOR)
|
||||
.refine(...refineAuthors);
|
||||
const copyrightedCharacters = z
|
||||
.record(z.string(), reference("users"))
|
||||
.default({})
|
||||
.refine(refineCopyrightedCharacters, `"copyrightedCharacters" cannot mix empty catch-all key with other keys`);
|
||||
.refine(...refineCopyrightedCharacters);
|
||||
|
||||
export type Lang = z.output<typeof lang>;
|
||||
export type Website = z.infer<typeof website>;
|
||||
|
|
@ -89,10 +100,7 @@ const storiesCollection = defineCollection({
|
|||
pubDate: z.date().transform(adjustDateForUTCOffset).optional(),
|
||||
isDraft: z.boolean().default(false),
|
||||
shortTitle: z.string().optional(),
|
||||
authors: z
|
||||
.union([reference("users"), z.array(reference("users"))])
|
||||
.default("bad-manners")
|
||||
.refine(refineAuthors, `"authors" cannot be empty`),
|
||||
authors,
|
||||
summary: z.string().optional(),
|
||||
thumbnail: image().optional(),
|
||||
thumbnailWidth: z.number().int().optional(),
|
||||
|
|
@ -108,13 +116,14 @@ const storiesCollection = defineCollection({
|
|||
relatedGames: z.array(reference("games")).default([]),
|
||||
posts: z
|
||||
.object({
|
||||
eka: z.string().regex(ekaPostUrlRegex).optional(),
|
||||
furaffinity: z.string().regex(furaffinityPostUrlRegex).optional(),
|
||||
weasyl: z.string().regex(weasylPostUrlRegex).optional(),
|
||||
inkbunny: z.string().regex(inkbunnyPostUrlRegex).optional(),
|
||||
sofurry: z.string().regex(sofurryPostUrlRegex).optional(),
|
||||
mastodon: mastodonPost.optional(),
|
||||
eka: z.string().regex(ekaPostUrlRegex),
|
||||
furaffinity: z.string().regex(furaffinityPostUrlRegex),
|
||||
weasyl: z.string().regex(weasylPostUrlRegex),
|
||||
inkbunny: z.string().regex(inkbunnyPostUrlRegex),
|
||||
sofurry: z.string().regex(sofurryPostUrlRegex),
|
||||
mastodon: mastodonPost,
|
||||
})
|
||||
.partial()
|
||||
.default({}),
|
||||
}),
|
||||
});
|
||||
|
|
@ -131,10 +140,7 @@ const gamesCollection = defineCollection({
|
|||
// Optional
|
||||
pubDate: z.date().transform(adjustDateForUTCOffset).optional(),
|
||||
isDraft: z.boolean().default(false),
|
||||
authors: z
|
||||
.union([reference("users"), z.array(reference("users"))])
|
||||
.default("bad-manners")
|
||||
.refine(refineAuthors, `"authors" cannot be empty`),
|
||||
authors,
|
||||
thumbnail: image().optional(),
|
||||
thumbnailWidth: z.number().int().optional(),
|
||||
thumbnailHeight: z.number().int().optional(),
|
||||
|
|
@ -146,13 +152,14 @@ const gamesCollection = defineCollection({
|
|||
relatedGames: z.array(reference("games")).default([]),
|
||||
posts: z
|
||||
.object({
|
||||
eka: z.string().regex(ekaPostUrlRegex).optional(),
|
||||
furaffinity: z.string().regex(furaffinityPostUrlRegex).optional(),
|
||||
weasyl: z.string().regex(weasylPostUrlRegex).optional(),
|
||||
inkbunny: z.string().regex(inkbunnyPostUrlRegex).optional(),
|
||||
sofurry: z.string().regex(sofurryPostUrlRegex).optional(),
|
||||
mastodon: mastodonPost.optional(),
|
||||
eka: z.string().regex(ekaPostUrlRegex),
|
||||
furaffinity: z.string().regex(furaffinityPostUrlRegex),
|
||||
weasyl: z.string().regex(weasylPostUrlRegex),
|
||||
inkbunny: z.string().regex(inkbunnyPostUrlRegex),
|
||||
sofurry: z.string().regex(sofurryPostUrlRegex),
|
||||
mastodon: mastodonPost,
|
||||
})
|
||||
.partial()
|
||||
.default({}),
|
||||
}),
|
||||
});
|
||||
|
|
@ -169,9 +176,8 @@ const usersCollection = defineCollection({
|
|||
links: z.record(website, z.union([z.string().url(), z.tuple([z.string().url(), z.string()])])),
|
||||
// Optional
|
||||
preferredLink: website.nullish(),
|
||||
nameLang: z.record(lang, z.string()).default({}),
|
||||
lang: z.record(lang, z.string()).default({}),
|
||||
avatar: image().optional(),
|
||||
isAnonymous: z.boolean().default(false),
|
||||
})
|
||||
.refine(
|
||||
({ links, preferredLink }) => !preferredLink || preferredLink in links,
|
||||
|
|
@ -187,7 +193,7 @@ const seriesCollection = defineCollection({
|
|||
schema: z.object({
|
||||
// Required
|
||||
name: z.string(),
|
||||
url: z.string().regex(/^(\/[a-z0-9_-]+)*\/?$/, `"url" must be a local URL`),
|
||||
url: z.string().regex(/^(\/[a-z0-9_-]+)+\/?$/, `"url" must be a local URL`),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -199,10 +205,7 @@ const tagCategoriesCollection = defineCollection({
|
|||
index: z.number().int(),
|
||||
tags: z.array(
|
||||
z.object({
|
||||
name: z.union([
|
||||
z.string(),
|
||||
z.intersection(z.object({ [DEFAULT_LANG]: z.string() }), z.record(lang, z.string())),
|
||||
]),
|
||||
name: z.union([z.string(), z.object({ [DEFAULT_LANG]: z.string() }).and(z.record(lang, z.string()))]),
|
||||
description: z.string().optional(),
|
||||
related: z.array(z.string()).optional(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ tags:
|
|||
- non-binary prey
|
||||
- micro prey
|
||||
- soul vore
|
||||
- implied perma endo
|
||||
- long-term endo
|
||||
---
|
||||
|
||||
<iframe
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ tags:
|
|||
- unwilling prey
|
||||
- willing prey
|
||||
- size difference
|
||||
- long-term endo
|
||||
- implied perma endo
|
||||
- straight sex
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ tags:
|
|||
- willing prey
|
||||
- semi-willing prey
|
||||
- similar size
|
||||
- perma endo
|
||||
- implied perma endo
|
||||
- straight sex
|
||||
- gay sex
|
||||
- hyper
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ tags:
|
|||
- willing predator
|
||||
- unwilling prey
|
||||
- similar size
|
||||
- perma endo
|
||||
- implied perma endo
|
||||
- straight sex
|
||||
- gay sex
|
||||
- orgy
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ tags:
|
|||
- willing prey
|
||||
- size difference
|
||||
- masturbation
|
||||
- perma endo
|
||||
- long-term endo
|
||||
- implied perma endo
|
||||
- flash fiction
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ tags:
|
|||
- willing predator
|
||||
- unwilling prey
|
||||
- size difference
|
||||
- perma endo
|
||||
- implied perma endo
|
||||
- request
|
||||
requester: dee-lumeni
|
||||
copyrightedCharacters:
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ tags:
|
|||
- semi-willing predator
|
||||
- willing predator
|
||||
- willing prey
|
||||
- long-term endo
|
||||
- same size
|
||||
- hyper
|
||||
- inflation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: Types of vore
|
||||
index: 0
|
||||
index: 1
|
||||
tags:
|
||||
- name: { eng: oral vore, tok: moku musi kepeken uta }
|
||||
description: Scenarios where prey are consumed by the predator through their mouth.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Recurring characters
|
||||
index: 9
|
||||
index: 10
|
||||
tags:
|
||||
- name: Sam Brendan
|
||||
description: Content that includes my character and fursona [Sam, the mimic x maned wolf hybrid](https://badmanners.xyz/sam_brendan).
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Body types
|
||||
index: 1
|
||||
index: 2
|
||||
tags:
|
||||
- name: anthro predator
|
||||
description: Scenarios where at least one of the predators is an anthropomorphic animal, i.e. generally regarded as a "furry".
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Genders
|
||||
index: 2
|
||||
index: 3
|
||||
tags:
|
||||
- name: male predator
|
||||
description: Scenarios where at least one of the predators is a man and/or male-presenting.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Relative size
|
||||
index: 3
|
||||
index: 4
|
||||
tags:
|
||||
- name: macro predator
|
||||
description: Scenarios where at least one of the predators has a size/height one or more orders of magnitude larger than average.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Willingness
|
||||
index: 4
|
||||
index: 5
|
||||
tags:
|
||||
- name: { eng: willing predator, tok: jan pi wawa mute li wile e moku musi }
|
||||
description: Scenarios where at least one of the predators participates in vore willingly.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Vore-related scenarios
|
||||
index: 5
|
||||
index: 6
|
||||
tags:
|
||||
- name: point of view
|
||||
description: Scenarios where the narration takes the perspective of one of the characters, generally in first person.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Sexual content
|
||||
index: 6
|
||||
index: 7
|
||||
tags:
|
||||
- name: nudity
|
||||
description: Scenarios where a character is nude and displaying their sexual features, without necessarily participating in a sexual situation.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Other kinks
|
||||
index: 7
|
||||
index: 8
|
||||
tags:
|
||||
- name: hyper
|
||||
description: Scenarios where a character has abnormally large features compared to their body, usually genitalia.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Type of content
|
||||
index: 8
|
||||
index: 9
|
||||
tags:
|
||||
- name: request
|
||||
description: Stories made by someone else's request, as a gift.
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
name: Anonymous
|
||||
nameLang:
|
||||
lang:
|
||||
eng: anonymous
|
||||
tok: jan pi nimi ala
|
||||
isAnonymous: true
|
||||
links: {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: Bad Manners
|
||||
nameLang:
|
||||
lang:
|
||||
eng: Bad Manners
|
||||
tok: nasin ike Pemene
|
||||
avatar: /src/assets/images/logo_bm.png
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue