mirror of
https://github.com/versia-pub/api.git
synced 2026-03-13 04:09:15 +01:00
refactor(federation): 🚚 Rename schemas to validators
Fixes issues with Bun bundling
This commit is contained in:
parent
ebf16a82cb
commit
f80cffd01a
19 changed files with 40 additions and 37 deletions
|
|
@ -1,259 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import {
|
||||
ContentFormatSchema,
|
||||
ImageOnlyContentFormatSchema,
|
||||
TextOnlyContentFormatSchema,
|
||||
} from "./content_format.ts";
|
||||
import { ExtensionPropertySchema } from "./extensions.ts";
|
||||
import { VanityExtensionSchema } from "./extensions/vanity.ts";
|
||||
import { extensionRegex, isISOString, semverRegex } from "./regex.ts";
|
||||
|
||||
export const EntitySchema = z
|
||||
.object({
|
||||
id: z.string().max(512),
|
||||
created_at: z
|
||||
.string()
|
||||
.refine((v) => isISOString(v), "must be a valid ISO8601 datetime"),
|
||||
uri: z.string().url(),
|
||||
type: z.string(),
|
||||
extensions: ExtensionPropertySchema.optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const NoteSchema = EntitySchema.extend({
|
||||
type: z.literal("Note"),
|
||||
attachments: z.array(ContentFormatSchema).optional().nullable(),
|
||||
author: z.string().url(),
|
||||
category: z
|
||||
.enum([
|
||||
"microblog",
|
||||
"forum",
|
||||
"blog",
|
||||
"image",
|
||||
"video",
|
||||
"audio",
|
||||
"messaging",
|
||||
])
|
||||
.optional()
|
||||
.nullable(),
|
||||
content: TextOnlyContentFormatSchema.optional().nullable(),
|
||||
device: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
version: z.string().optional().nullable(),
|
||||
url: z.string().url().optional().nullable(),
|
||||
})
|
||||
.strict()
|
||||
.optional()
|
||||
.nullable(),
|
||||
group: z
|
||||
.string()
|
||||
.url()
|
||||
.or(z.enum(["public", "followers"]))
|
||||
.optional()
|
||||
.nullable(),
|
||||
is_sensitive: z.boolean().optional().nullable(),
|
||||
mentions: z.array(z.string().url()).optional().nullable(),
|
||||
previews: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
link: z.string().url(),
|
||||
title: z.string(),
|
||||
description: z.string().optional().nullable(),
|
||||
image: z.string().url().optional().nullable(),
|
||||
icon: z.string().url().optional().nullable(),
|
||||
})
|
||||
.strict(),
|
||||
)
|
||||
.optional()
|
||||
.nullable(),
|
||||
quotes: z.string().url().optional().nullable(),
|
||||
replies_to: z.string().url().optional().nullable(),
|
||||
subject: z.string().optional().nullable(),
|
||||
extensions: ExtensionPropertySchema.extend({
|
||||
"pub.versia:reactions": z
|
||||
.object({
|
||||
reactions: z.string().url(),
|
||||
})
|
||||
.strict()
|
||||
.optional()
|
||||
.nullable(),
|
||||
"pub.versia:polls": z
|
||||
.object({
|
||||
options: z.array(TextOnlyContentFormatSchema),
|
||||
votes: z.array(
|
||||
z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1),
|
||||
),
|
||||
multiple_choice: z.boolean(),
|
||||
expires_at: z
|
||||
.string()
|
||||
.refine(
|
||||
(v) => isISOString(v),
|
||||
"must be a valid ISO8601 datetime",
|
||||
),
|
||||
})
|
||||
.strict()
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.optional()
|
||||
.nullable(),
|
||||
});
|
||||
|
||||
export const CollectionSchema = z.object({
|
||||
author: z.string().url().nullable(),
|
||||
first: z.string().url(),
|
||||
last: z.string().url(),
|
||||
total: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1),
|
||||
next: z.string().url().nullable(),
|
||||
previous: z.string().url().nullable(),
|
||||
items: z.array(z.any()),
|
||||
});
|
||||
|
||||
export const PublicKeyDataSchema = z
|
||||
.object({
|
||||
key: z.string().min(1),
|
||||
actor: z.string().url(),
|
||||
algorithm: z.literal("ed25519"),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const UserSchema = EntitySchema.extend({
|
||||
type: z.literal("User"),
|
||||
avatar: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
bio: TextOnlyContentFormatSchema.optional().nullable(),
|
||||
display_name: z.string().optional().nullable(),
|
||||
fields: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
key: TextOnlyContentFormatSchema,
|
||||
value: TextOnlyContentFormatSchema,
|
||||
})
|
||||
.strict(),
|
||||
)
|
||||
.optional()
|
||||
.nullable(),
|
||||
username: z
|
||||
.string()
|
||||
.min(1)
|
||||
.regex(
|
||||
/^[a-z0-9_-]+$/,
|
||||
"must be lowercase, alphanumeric, and may contain _ or -",
|
||||
),
|
||||
header: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
public_key: PublicKeyDataSchema,
|
||||
manually_approves_followers: z.boolean().optional().nullable(),
|
||||
indexable: z.boolean().optional().nullable(),
|
||||
inbox: z.string().url(),
|
||||
collections: z
|
||||
.object({
|
||||
featured: z.string().url(),
|
||||
followers: z.string().url(),
|
||||
following: z.string().url(),
|
||||
outbox: z.string().url(),
|
||||
"pub.versia:likes/Likes": z.string().url().optional().nullable(),
|
||||
"pub.versia:likes/Dislikes": z.string().url().optional().nullable(),
|
||||
})
|
||||
.catchall(z.string().url()),
|
||||
extensions: ExtensionPropertySchema.extend({
|
||||
"pub.versia:vanity": VanityExtensionSchema.optional().nullable(),
|
||||
})
|
||||
.optional()
|
||||
.nullable(),
|
||||
});
|
||||
|
||||
export const DeleteSchema = EntitySchema.extend({
|
||||
uri: z.null().optional(),
|
||||
type: z.literal("Delete"),
|
||||
author: z.string().url().nullable(),
|
||||
deleted_type: z.string(),
|
||||
target: z.string().url(),
|
||||
});
|
||||
|
||||
export const FollowSchema = EntitySchema.extend({
|
||||
type: z.literal("Follow"),
|
||||
uri: z.null().optional(),
|
||||
author: z.string().url(),
|
||||
followee: z.string().url(),
|
||||
});
|
||||
|
||||
export const FollowAcceptSchema = EntitySchema.extend({
|
||||
type: z.literal("FollowAccept"),
|
||||
uri: z.null().optional(),
|
||||
author: z.string().url(),
|
||||
follower: z.string().url(),
|
||||
});
|
||||
|
||||
export const FollowRejectSchema = EntitySchema.extend({
|
||||
type: z.literal("FollowReject"),
|
||||
uri: z.null().optional(),
|
||||
author: z.string().url(),
|
||||
follower: z.string().url(),
|
||||
});
|
||||
|
||||
export const UnfollowSchema = EntitySchema.extend({
|
||||
type: z.literal("Unfollow"),
|
||||
uri: z.null().optional(),
|
||||
author: z.string().url(),
|
||||
followee: z.string().url(),
|
||||
});
|
||||
|
||||
export const GroupSchema = EntitySchema.extend({
|
||||
type: z.literal("Group"),
|
||||
name: TextOnlyContentFormatSchema.optional().nullable(),
|
||||
description: TextOnlyContentFormatSchema.optional().nullable(),
|
||||
members: z.string().url(),
|
||||
notes: z.string().url().optional().nullable(),
|
||||
});
|
||||
|
||||
export const InstanceMetadataSchema = EntitySchema.extend({
|
||||
type: z.literal("InstanceMetadata"),
|
||||
id: z.null().optional(),
|
||||
uri: z.null().optional(),
|
||||
name: z.string().min(1),
|
||||
software: z
|
||||
.object({
|
||||
name: z.string().min(1),
|
||||
version: z.string().min(1),
|
||||
})
|
||||
.strict(),
|
||||
compatibility: z
|
||||
.object({
|
||||
versions: z.array(
|
||||
z.string().regex(semverRegex, "must be a valid SemVer version"),
|
||||
),
|
||||
extensions: z.array(
|
||||
z
|
||||
.string()
|
||||
.min(1)
|
||||
.regex(
|
||||
extensionRegex,
|
||||
"must be in the format 'namespaced_url:extension_name', e.g. 'pub.versia:reactions'",
|
||||
),
|
||||
),
|
||||
})
|
||||
.strict(),
|
||||
description: z.string().optional().nullable(),
|
||||
host: z.string(),
|
||||
shared_inbox: z.string().url().optional().nullable(),
|
||||
public_key: z
|
||||
.object({
|
||||
key: z.string().min(1),
|
||||
algorithm: z.literal("ed25519"),
|
||||
})
|
||||
.strict(),
|
||||
moderators: z.string().url().optional().nullable(),
|
||||
admins: z.string().url().optional().nullable(),
|
||||
logo: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
banner: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
});
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
import { types } from "mime-types";
|
||||
import { z } from "zod";
|
||||
|
||||
const hashes = {
|
||||
sha256: 64,
|
||||
sha512: 128,
|
||||
"sha3-256": 64,
|
||||
"sha3-512": 128,
|
||||
"blake2b-256": 64,
|
||||
"blake2b-512": 128,
|
||||
"blake3-256": 64,
|
||||
"blake3-512": 128,
|
||||
md5: 32,
|
||||
sha1: 40,
|
||||
sha224: 56,
|
||||
sha384: 96,
|
||||
"sha3-224": 56,
|
||||
"sha3-384": 96,
|
||||
"blake2s-256": 64,
|
||||
"blake2s-512": 128,
|
||||
"blake3-224": 56,
|
||||
"blake3-384": 96,
|
||||
};
|
||||
|
||||
const contentFormatFromAllowedMimes = (allowedMimes: [string, ...string[]]) =>
|
||||
z.record(
|
||||
z.enum(allowedMimes),
|
||||
z
|
||||
.object({
|
||||
content: z.string(),
|
||||
remote: z.boolean(),
|
||||
description: z.string().optional().nullable(),
|
||||
size: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1)
|
||||
.optional()
|
||||
.nullable(),
|
||||
hash: z
|
||||
.object(
|
||||
Object.fromEntries(
|
||||
Object.entries(hashes).map(([k, v]) => [
|
||||
k,
|
||||
z.string().length(v).optional().nullable(),
|
||||
]),
|
||||
),
|
||||
)
|
||||
.strict()
|
||||
.optional()
|
||||
.nullable(),
|
||||
thumbhash: z.string().optional().nullable(),
|
||||
fps: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1)
|
||||
.optional()
|
||||
.nullable(),
|
||||
width: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1)
|
||||
.optional()
|
||||
.nullable(),
|
||||
height: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1)
|
||||
.optional()
|
||||
.nullable(),
|
||||
duration: z
|
||||
.number()
|
||||
.nonnegative()
|
||||
.max(2 ** 16 - 1)
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(v) =>
|
||||
v.remote
|
||||
? z.string().url().safeParse(v.content).success
|
||||
: true,
|
||||
"if remote is true, content must be a valid URL",
|
||||
),
|
||||
);
|
||||
|
||||
export const ContentFormatSchema = contentFormatFromAllowedMimes(
|
||||
Object.values(types) as [string, ...string[]],
|
||||
);
|
||||
|
||||
export const ImageOnlyContentFormatSchema = contentFormatFromAllowedMimes(
|
||||
Object.values(types).filter((v) => v.startsWith("image/")) as [
|
||||
string,
|
||||
...string[],
|
||||
],
|
||||
);
|
||||
|
||||
export const TextOnlyContentFormatSchema = contentFormatFromAllowedMimes(
|
||||
Object.values(types).filter((v) => v.startsWith("text/")) as [
|
||||
string,
|
||||
...string[],
|
||||
],
|
||||
);
|
||||
|
||||
export const AudioOnlyContentFormatSchema = contentFormatFromAllowedMimes(
|
||||
Object.values(types).filter((v) => v.startsWith("audio/")) as [
|
||||
string,
|
||||
...string[],
|
||||
],
|
||||
);
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { CustomEmojiExtensionSchema } from "./extensions/custom_emojis.ts";
|
||||
|
||||
export const ExtensionPropertySchema = z
|
||||
.object({
|
||||
"pub.versia:custom_emojis":
|
||||
CustomEmojiExtensionSchema.optional().nullable(),
|
||||
})
|
||||
.catchall(z.any());
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* Custom emojis extension.
|
||||
* @module federation/schemas/extensions/custom_emojis
|
||||
* @see module:federation/schemas/base
|
||||
* @see https://versia.pub/extensions/custom-emojis
|
||||
*/
|
||||
import { z } from "zod";
|
||||
import { ImageOnlyContentFormatSchema } from "../content_format.ts";
|
||||
import { emojiRegex } from "../regex.ts";
|
||||
|
||||
/**
|
||||
* @description Used to validate the properties the extension's custom field
|
||||
* @see https://versia.pub/extensions/custom-emojis
|
||||
* @example
|
||||
* {
|
||||
* // ...
|
||||
* "extensions": {
|
||||
* "pub.versia:custom_emojis": {
|
||||
* "emojis": [
|
||||
* {
|
||||
* "name": ":happy_face:",
|
||||
* "url": {
|
||||
* "image/png": {
|
||||
* "content": "https://cdn.example.com/emojis/happy_face.png",
|
||||
* "remote": true
|
||||
* }
|
||||
* }
|
||||
* },
|
||||
* // ...
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
export const CustomEmojiExtensionSchema = z.object({
|
||||
emojis: z.array(
|
||||
z
|
||||
.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(256)
|
||||
.regex(
|
||||
emojiRegex,
|
||||
"Emoji name must be alphanumeric, underscores, or dashes, and surrounded by identifiers",
|
||||
),
|
||||
url: ImageOnlyContentFormatSchema,
|
||||
})
|
||||
.strict(),
|
||||
),
|
||||
});
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { EntitySchema } from "../base.ts";
|
||||
|
||||
/**
|
||||
* @description Like entity
|
||||
* @see https://versia.pub/extensions/likes
|
||||
* @example
|
||||
* {
|
||||
* "id": "3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "type": "pub.versia:likes/Like",
|
||||
* "created_at": "2021-01-01T00:00:00.000Z",
|
||||
* "author": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
* "uri": "https://example.com/likes/3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "liked": "https://otherexample.org/notes/fmKZ763jzIU8"
|
||||
* }
|
||||
*/
|
||||
export const LikeSchema = EntitySchema.extend({
|
||||
type: z.literal("pub.versia:likes/Like"),
|
||||
author: z.string().url(),
|
||||
liked: z.string().url(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Dislike entity
|
||||
* @see https://versia.pub/extensions/likes
|
||||
* @example
|
||||
* {
|
||||
* "id": "3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "type": "pub.versia:likes/Dislike",
|
||||
* "created_at": "2021-01-01T00:00:00.000Z",
|
||||
* "author": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
* "uri": "https://example.com/dislikes/3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "disliked": "https://otherexample.org/notes/fmKZ763jzIU8"
|
||||
* }
|
||||
*/
|
||||
export const DislikeSchema = EntitySchema.extend({
|
||||
type: z.literal("pub.versia:likes/Dislike"),
|
||||
author: z.string().url(),
|
||||
disliked: z.string().url(),
|
||||
});
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Polls extension
|
||||
* @module federation/schemas/extensions/polls
|
||||
* @see module:federation/schemas/base
|
||||
* @see https://versia.pub/extensions/polls
|
||||
*/
|
||||
import { z } from "zod";
|
||||
import { EntitySchema } from "../base.ts";
|
||||
|
||||
/**
|
||||
* @description Vote extension entity
|
||||
* @see https://versia.pub/extensions/polls
|
||||
* @example
|
||||
* {
|
||||
* "id": "6f27bc77-58ee-4c9b-b804-8cc1c1182fa9",
|
||||
* "type": "pub.versia:polls/Vote",
|
||||
* "uri": "https://example.com/actions/6f27bc77-58ee-4c9b-b804-8cc1c1182fa9",
|
||||
* "created_at": "2021-01-01T00:00:00.000Z",
|
||||
* "author": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
* "poll": "https://example.com/notes/f08a124e-fe90-439e-8be4-15a428a72a19",
|
||||
* "option": 1
|
||||
* }
|
||||
*/
|
||||
export const VoteSchema = EntitySchema.extend({
|
||||
type: z.literal("pub.versia:polls/Vote"),
|
||||
author: z.string().url(),
|
||||
poll: z.string().url(),
|
||||
option: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.max(2 ** 64 - 1),
|
||||
});
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* Reactions extension
|
||||
* @module federation/schemas/extensions/reactions
|
||||
* @see module:federation/schemas/base
|
||||
* @see https://versia.pub/extensions/reactions
|
||||
*/
|
||||
import { z } from "zod";
|
||||
import { EntitySchema } from "../base.ts";
|
||||
|
||||
/**
|
||||
* @description Reaction extension entity
|
||||
* @see https://versia.pub/extensions/reactions
|
||||
* @example
|
||||
* {
|
||||
* "id": "6f27bc77-58ee-4c9b-b804-8cc1c1182fa9",
|
||||
* "type": "pub.versia:reactions/Reaction",
|
||||
* "uri": "https://example.com/actions/6f27bc77-58ee-4c9b-b804-8cc1c1182fa9",
|
||||
* "created_at": "2021-01-01T00:00:00.000Z",
|
||||
* "author": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
* "object": "https://example.com/publications/f08a124e-fe90-439e-8be4-15a428a72a19",
|
||||
* "content": "😀",
|
||||
* }
|
||||
*/
|
||||
export const ReactionSchema = EntitySchema.extend({
|
||||
type: z.literal("pub.versia:reactions/Reaction"),
|
||||
author: z.string().url(),
|
||||
object: z.string().url(),
|
||||
content: z.string().min(1).max(256),
|
||||
});
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { EntitySchema } from "../base.ts";
|
||||
|
||||
/**
|
||||
* @description Share entity
|
||||
* @see https://versia.pub/extensions/share
|
||||
* @example
|
||||
* {
|
||||
* "id": "3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "type": "pub.versia:share/Share",
|
||||
* "created_at": "2021-01-01T00:00:00.000Z",
|
||||
* "author": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
* "uri": "https://example.com/shares/3e7e4750-afd4-4d99-a256-02f0710a0520",
|
||||
* "shared": "https://otherexample.org/notes/fmKZ763jzIU8"
|
||||
* }
|
||||
*/
|
||||
export const ShareSchema = EntitySchema.extend({
|
||||
type: z.literal("pub.versia:share/Share"),
|
||||
author: z.string().url(),
|
||||
shared: z.string().url(),
|
||||
});
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/**
|
||||
* Vanity extension schema.
|
||||
* @module federation/schemas/extensions/vanity
|
||||
* @see module:federation/schemas/base
|
||||
* @see https://versia.pub/extensions/vanity
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AudioOnlyContentFormatSchema,
|
||||
ImageOnlyContentFormatSchema,
|
||||
} from "../content_format.ts";
|
||||
import { isISOString } from "../regex.ts";
|
||||
|
||||
/**
|
||||
* @description Vanity extension entity
|
||||
* @see https://versia.pub/extensions/vanity
|
||||
* @example
|
||||
* {
|
||||
* // ...
|
||||
* "type": "User",
|
||||
* // ...
|
||||
* "extensions": {
|
||||
* "pub.versia:vanity": {
|
||||
* "avatar_overlays": [
|
||||
* {
|
||||
* "image/png": {
|
||||
* "content": "https://cdn.example.com/ab5081cf-b11f-408f-92c2-7c246f290593/cat_ears.png",
|
||||
* "remote": true,
|
||||
* }
|
||||
* }
|
||||
* ],
|
||||
* "avatar_mask": {
|
||||
* "image/png": {
|
||||
* "content": "https://cdn.example.com/d8c42be1-d0f7-43ef-b4ab-5f614e1beba4/rounded_square.jpeg",
|
||||
* "remote": true,
|
||||
* }
|
||||
* },
|
||||
* "background": {
|
||||
* "image/png": {
|
||||
* "content": "https://cdn.example.com/6492ddcd-311e-4921-9567-41b497762b09/untitled-file-0019822.png",
|
||||
* "remote": true,
|
||||
* }
|
||||
* },
|
||||
* "audio": {
|
||||
* "audio/mpeg": {
|
||||
* "content": "https://cdn.example.com/4da2f0d4-4728-4819-83e4-d614e4c5bebc/michael-jackson-thriller.mp3",
|
||||
* "remote": true,
|
||||
* }
|
||||
* },
|
||||
* "pronouns": {
|
||||
* "en-us": [
|
||||
* "he/him",
|
||||
* {
|
||||
* "subject": "they",
|
||||
* "object": "them",
|
||||
* "dependent_possessive": "their",
|
||||
* "independent_possessive": "theirs",
|
||||
* "reflexive": "themself"
|
||||
* },
|
||||
* ]
|
||||
* },
|
||||
* "birthday": "1998-04-12",
|
||||
* "location": "+40.6894-074.0447/",
|
||||
* "aliases": [
|
||||
* "https://burger.social/accounts/349ee237-c672-41c1-aadc-677e185f795a",
|
||||
* "https://versia.social/users/f565ef02-035d-4974-ba5e-f62a8558331d"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export const VanityExtensionSchema = z
|
||||
.object({
|
||||
avatar_overlays: z
|
||||
.array(ImageOnlyContentFormatSchema)
|
||||
.optional()
|
||||
.nullable(),
|
||||
avatar_mask: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
background: ImageOnlyContentFormatSchema.optional().nullable(),
|
||||
audio: AudioOnlyContentFormatSchema.optional().nullable(),
|
||||
pronouns: z.record(
|
||||
z.string(),
|
||||
z.array(
|
||||
z.union([
|
||||
z
|
||||
.object({
|
||||
subject: z.string(),
|
||||
object: z.string(),
|
||||
dependent_possessive: z.string(),
|
||||
independent_possessive: z.string(),
|
||||
reflexive: z.string(),
|
||||
})
|
||||
.strict(),
|
||||
z.string(),
|
||||
]),
|
||||
),
|
||||
),
|
||||
birthday: z
|
||||
.string()
|
||||
.refine((v) => isISOString(v), "must be a valid ISO8601 datetime")
|
||||
.optional()
|
||||
.nullable(),
|
||||
location: z.string().optional().nullable(),
|
||||
aliases: z.array(z.string().url()).optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* Regular expressions for matching various strings.
|
||||
* @module federation/schemas/regex
|
||||
* @see module:federation/schemas/base
|
||||
*/
|
||||
|
||||
import {
|
||||
charIn,
|
||||
charNotIn,
|
||||
createRegExp,
|
||||
digit,
|
||||
exactly,
|
||||
global,
|
||||
letter,
|
||||
not,
|
||||
oneOrMore,
|
||||
} from "magic-regexp";
|
||||
|
||||
/**
|
||||
* Regular expression for matching emojis.
|
||||
*/
|
||||
export const emojiRegex: RegExp = createRegExp(
|
||||
exactly(
|
||||
exactly(not.letter.or(not.digit).or(charNotIn("_-"))).times(1),
|
||||
oneOrMore(letter.or(digit).or(charIn("_-"))),
|
||||
exactly(not.letter.or(not.digit).or(charNotIn("_-"))).times(1),
|
||||
),
|
||||
[global],
|
||||
);
|
||||
|
||||
export const semverRegex: RegExp = new RegExp(
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm,
|
||||
);
|
||||
|
||||
/**
|
||||
* Regular expression for matching an extension_type
|
||||
* @example pub.versia:custom_emojis/Emoji
|
||||
*/
|
||||
export const extensionTypeRegex: RegExp = createRegExp(
|
||||
// org namespace, then colon, then alphanumeric/_/-, then extension name
|
||||
exactly(
|
||||
oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-.")))),
|
||||
exactly(":"),
|
||||
oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-")))),
|
||||
exactly("/"),
|
||||
oneOrMore(exactly(letter.or(digit).or(charIn("_-")))),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Regular expression for matching an extension
|
||||
* @example pub.versia:custom_emojis
|
||||
*/
|
||||
export const extensionRegex: RegExp = createRegExp(
|
||||
// org namespace, then colon, then alphanumeric/_/-, then extension name
|
||||
exactly(
|
||||
oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-.")))),
|
||||
exactly(":"),
|
||||
oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-")))),
|
||||
),
|
||||
);
|
||||
|
||||
export const isISOString = (val: string | Date) => {
|
||||
const d = new Date(val);
|
||||
return !Number.isNaN(d.valueOf()) && d.toISOString() === val;
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const WebFingerSchema = z.object({
|
||||
subject: z.string().url(),
|
||||
aliases: z.array(z.string().url()).optional(),
|
||||
properties: z.record(z.string().url(), z.string().or(z.null())).optional(),
|
||||
links: z
|
||||
.array(
|
||||
z.object({
|
||||
rel: z.string(),
|
||||
type: z.string().optional(),
|
||||
href: z.string().url().optional(),
|
||||
titles: z.record(z.string(), z.string()).optional(),
|
||||
properties: z
|
||||
.record(z.string().url(), z.string().or(z.null()))
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue