Merge branch 'develop' into hooks-migration
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,5 @@
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { directoryCategorySchema, directoryLanguageSchema, directoryServerSchema, directoryStatisticsPeriodSchema } from './entities';
|
||||
import { filteredArray } from './entities/utils';
|
||||
import request from './request';
|
||||
@ -23,25 +25,25 @@ class PlApiDirectoryClient {
|
||||
async getStatistics() {
|
||||
const response = await this.request('/statistics');
|
||||
|
||||
return filteredArray(directoryStatisticsPeriodSchema).parse(response.json);
|
||||
return v.parse(filteredArray(directoryStatisticsPeriodSchema), response.json);
|
||||
}
|
||||
|
||||
async getCategories(params?: Params) {
|
||||
const response = await this.request('/categories', { params });
|
||||
|
||||
return filteredArray(directoryCategorySchema).parse(response.json);
|
||||
return v.parse(filteredArray(directoryCategorySchema), response.json);
|
||||
}
|
||||
|
||||
async getLanguages(params?: Params) {
|
||||
const response = await this.request('/categories', { params });
|
||||
|
||||
return filteredArray(directoryLanguageSchema).parse(response.json);
|
||||
return v.parse(filteredArray(directoryLanguageSchema), response.json);
|
||||
}
|
||||
|
||||
async getServers(params?: Params) {
|
||||
const response = await this.request('/servers', { params });
|
||||
|
||||
return filteredArray(directoryServerSchema).parse(response.json);
|
||||
return v.parse(filteredArray(directoryServerSchema), response.json);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Appeal/} */
|
||||
const appealSchema = z.object({
|
||||
text: z.string(),
|
||||
state: z.enum(['approved', 'rejected', 'pending']),
|
||||
const appealSchema = v.object({
|
||||
text: v.string(),
|
||||
state: v.picklist(['approved', 'rejected', 'pending']),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/AccountWarning/} */
|
||||
const accountWarningSchema = z.object({
|
||||
id: z.string(),
|
||||
action: z.enum(['none', 'disable', 'mark_statuses_as_sensitive', 'delete_statuses', 'sensitive', 'silence', 'suspend']),
|
||||
text: z.string().catch(''),
|
||||
status_ids: z.array(z.string()).catch([]),
|
||||
const accountWarningSchema = v.object({
|
||||
id: v.string(),
|
||||
action: v.picklist(['none', 'disable', 'mark_statuses_as_sensitive', 'delete_statuses', 'sensitive', 'silence', 'suspend']),
|
||||
text: v.fallback(v.string(), ''),
|
||||
status_ids: v.fallback(v.array(v.string()), []),
|
||||
target_account: accountSchema,
|
||||
appeal: appealSchema.nullable().catch(null),
|
||||
created_at: dateSchema,
|
||||
appeal: v.fallback(v.nullable(appealSchema), null),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
});
|
||||
|
||||
type AccountWarning = z.infer<typeof accountWarningSchema>;
|
||||
type AccountWarning = v.InferOutput<typeof accountWarningSchema>;
|
||||
|
||||
export { accountWarningSchema, type AccountWarning };
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import pick from 'lodash.pick';
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { relationshipSchema } from './relationship';
|
||||
import { roleSchema } from './role';
|
||||
import { coerceObject, dateSchema, filteredArray } from './utils';
|
||||
import { coerceObject, datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
const getDomainFromURL = (account: Pick<Account, 'url'>): string => {
|
||||
try {
|
||||
@ -27,9 +27,9 @@ const guessFqn = (account: Pick<Account, 'acct' | 'url'>): string => {
|
||||
};
|
||||
|
||||
const filterBadges = (tags?: string[]) =>
|
||||
tags?.filter(tag => tag.startsWith('badge:')).map(tag => roleSchema.parse({ id: tag, name: tag.replace(/^badge:/, '') }));
|
||||
tags?.filter(tag => tag.startsWith('badge:')).map(tag => v.parse(roleSchema, { id: tag, name: tag.replace(/^badge:/, '') }));
|
||||
|
||||
const preprocessAccount = (account: any) => {
|
||||
const preprocessAccount = v.transform((account: any) => {
|
||||
if (!account?.acct) return null;
|
||||
|
||||
const username = account.username || account.acct.split('@')[0];
|
||||
@ -82,129 +82,131 @@ const preprocessAccount = (account: any) => {
|
||||
])), ...account.source }
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const fieldSchema = z.object({
|
||||
name: z.string(),
|
||||
value: z.string(),
|
||||
verified_at: z.string().datetime({ offset: true }).nullable().catch(null),
|
||||
});
|
||||
|
||||
const baseAccountSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string().catch(''),
|
||||
acct: z.string().catch(''),
|
||||
url: z.string().url(),
|
||||
display_name: z.string().catch(''),
|
||||
note: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
avatar: z.string().catch(''),
|
||||
avatar_static: z.string().url().catch(''),
|
||||
header: z.string().url().catch(''),
|
||||
header_static: z.string().url().catch(''),
|
||||
locked: z.boolean().catch(false),
|
||||
const fieldSchema = v.object({
|
||||
name: v.string(),
|
||||
value: v.string(),
|
||||
verified_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
});
|
||||
|
||||
const baseAccountSchema = v.object({
|
||||
id: v.string(),
|
||||
username: v.fallback(v.string(), ''),
|
||||
acct: v.fallback(v.string(), ''),
|
||||
url: v.pipe(v.string(), v.url()),
|
||||
display_name: v.fallback(v.string(), ''),
|
||||
content: v.fallback(v.pipe(v.string(), v.transform((note => note === '<p></p>' ? '' : note))), ''),
|
||||
avatar: v.fallback(v.string(), ''),
|
||||
avatar_static: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
header: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
header_static: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
locked: v.fallback(v.boolean(), false),
|
||||
fields: filteredArray(fieldSchema),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
bot: z.boolean().catch(false),
|
||||
group: z.boolean().catch(false),
|
||||
discoverable: z.boolean().catch(false),
|
||||
noindex: z.boolean().nullable().catch(null),
|
||||
moved: z.null().catch(null),
|
||||
suspended: z.boolean().optional().catch(undefined),
|
||||
limited: z.boolean().optional().catch(undefined),
|
||||
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
||||
last_status_at: z.string().date().nullable().catch(null),
|
||||
statuses_count: z.number().catch(0),
|
||||
followers_count: z.number().catch(0),
|
||||
following_count: z.number().catch(0),
|
||||
bot: v.fallback(v.boolean(), false),
|
||||
group: v.fallback(v.boolean(), false),
|
||||
discoverable: v.fallback(v.boolean(), false),
|
||||
noindex: v.fallback(v.nullable(v.boolean()), null),
|
||||
suspended: v.fallback(v.optional(v.boolean()), undefined),
|
||||
limited: v.fallback(v.optional(v.boolean()), undefined),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toUTCString()),
|
||||
last_status_at: v.fallback(v.nullable(v.pipe(v.string(), v.isoDate())), null),
|
||||
statuses_count: v.fallback(v.number(), 0),
|
||||
followers_count: v.fallback(v.number(), 0),
|
||||
following_count: v.fallback(v.number(), 0),
|
||||
roles: filteredArray(roleSchema),
|
||||
|
||||
fqn: z.string().nullable().catch(null),
|
||||
ap_id: z.string().nullable().catch(null),
|
||||
background_image: z.string().nullable().catch(null),
|
||||
relationship: relationshipSchema.optional().catch(undefined),
|
||||
is_moderator: z.boolean().optional().catch(undefined),
|
||||
is_admin: z.boolean().optional().catch(undefined),
|
||||
is_suggested: z.boolean().optional().catch(undefined),
|
||||
hide_favorites: z.boolean().catch(true),
|
||||
hide_followers: z.boolean().optional().catch(undefined),
|
||||
hide_follows: z.boolean().optional().catch(undefined),
|
||||
hide_followers_count: z.boolean().optional().catch(undefined),
|
||||
hide_follows_count: z.boolean().optional().catch(undefined),
|
||||
accepts_chat_messages: z.boolean().nullable().catch(null),
|
||||
favicon: z.string().optional().catch(undefined),
|
||||
birthday: z.string().date().optional().catch(undefined),
|
||||
deactivated: z.boolean().optional().catch(undefined),
|
||||
fqn: v.fallback(v.nullable(v.string()), null),
|
||||
ap_id: v.fallback(v.nullable(v.string()), null),
|
||||
background_image: v.fallback(v.nullable(v.string()), null),
|
||||
relationship: v.fallback(v.optional(relationshipSchema), undefined),
|
||||
is_moderator: v.fallback(v.optional(v.boolean()), undefined),
|
||||
is_admin: v.fallback(v.optional(v.boolean()), undefined),
|
||||
is_suggested: v.fallback(v.optional(v.boolean()), undefined),
|
||||
hide_favorites: v.fallback(v.boolean(), true),
|
||||
hide_followers: v.fallback(v.optional(v.boolean()), undefined),
|
||||
hide_follows: v.fallback(v.optional(v.boolean()), undefined),
|
||||
hide_followers_count: v.fallback(v.optional(v.boolean()), undefined),
|
||||
hide_follows_count: v.fallback(v.optional(v.boolean()), undefined),
|
||||
accepts_chat_messages: v.fallback(v.nullable(v.boolean()), null),
|
||||
favicon: v.fallback(v.optional(v.string()), undefined),
|
||||
birthday: v.fallback(v.optional(v.pipe(v.string(), v.isoDate())), undefined),
|
||||
deactivated: v.fallback(v.optional(v.boolean()), undefined),
|
||||
|
||||
location: z.string().optional().catch(undefined),
|
||||
local: z.boolean().optional().catch(false),
|
||||
location: v.fallback(v.optional(v.string()), undefined),
|
||||
local: v.fallback(v.optional(v.boolean()), false),
|
||||
|
||||
avatar_description: z.string().catch(''),
|
||||
enable_rss: z.boolean().catch(false),
|
||||
header_description: z.string().catch(''),
|
||||
avatar_description: v.fallback(v.string(), ''),
|
||||
enable_rss: v.fallback(v.boolean(), false),
|
||||
header_description: v.fallback(v.string(), ''),
|
||||
|
||||
verified: z.boolean().optional().catch(undefined),
|
||||
domain: z.string().catch(''),
|
||||
verified: v.fallback(v.optional(v.boolean()), undefined),
|
||||
domain: v.fallback(v.string(), ''),
|
||||
|
||||
__meta: coerceObject({
|
||||
pleroma: z.any().optional().catch(undefined),
|
||||
source: z.any().optional().catch(undefined),
|
||||
pleroma: v.fallback(v.any(), undefined),
|
||||
source: v.fallback(v.any(), undefined),
|
||||
}),
|
||||
});
|
||||
|
||||
const accountWithMovedAccountSchema = baseAccountSchema.extend({
|
||||
moved: z.lazy((): typeof baseAccountSchema => accountWithMovedAccountSchema as any).nullable().catch(null),
|
||||
const accountWithMovedAccountSchema = v.object({
|
||||
...baseAccountSchema.entries,
|
||||
moved: v.fallback(v.nullable(v.lazy((): typeof baseAccountSchema => accountWithMovedAccountSchema as any)), null),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Account/} */
|
||||
const untypedAccountSchema = z.preprocess(preprocessAccount, accountWithMovedAccountSchema);
|
||||
const untypedAccountSchema = v.pipe(v.any(), preprocessAccount, accountWithMovedAccountSchema);
|
||||
|
||||
type WithMoved = {
|
||||
moved: Account | null;
|
||||
};
|
||||
|
||||
type Account = z.infer<typeof accountWithMovedAccountSchema> & WithMoved;
|
||||
type Account = v.InferOutput<typeof accountWithMovedAccountSchema> & WithMoved;
|
||||
|
||||
const accountSchema: z.ZodType<Account> = untypedAccountSchema as any;
|
||||
const accountSchema: v.BaseSchema<any, Account, v.BaseIssue<unknown>> = untypedAccountSchema as any;
|
||||
|
||||
const untypedCredentialAccountSchema = z.preprocess(preprocessAccount, accountWithMovedAccountSchema.extend({
|
||||
source: z.object({
|
||||
note: z.string().catch(''),
|
||||
const untypedCredentialAccountSchema = v.pipe(v.any(), preprocessAccount, v.object({
|
||||
...accountWithMovedAccountSchema.entries,
|
||||
source: v.fallback(v.nullable(v.object({
|
||||
note: v.fallback(v.string(), ''),
|
||||
fields: filteredArray(fieldSchema),
|
||||
privacy: z.enum(['public', 'unlisted', 'private', 'direct']),
|
||||
sensitive: z.boolean().catch(false),
|
||||
language: z.string().nullable().catch(null),
|
||||
follow_requests_count: z.number().int().nonnegative().catch(0),
|
||||
privacy: v.picklist(['public', 'unlisted', 'private', 'direct']),
|
||||
sensitive: v.fallback(v.boolean(), false),
|
||||
language: v.fallback(v.nullable(v.string()), null),
|
||||
follow_requests_count: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 0),
|
||||
|
||||
show_role: z.boolean().optional().nullable().catch(undefined),
|
||||
no_rich_text: z.boolean().optional().nullable().catch(undefined),
|
||||
discoverable: z.boolean().optional().catch(undefined),
|
||||
actor_type: z.string().optional().catch(undefined),
|
||||
show_birthday: z.boolean().optional().catch(undefined),
|
||||
}).nullable().catch(null),
|
||||
role: roleSchema.nullable().catch(null),
|
||||
show_role: v.fallback(v.nullable(v.optional(v.boolean())), undefined),
|
||||
no_rich_text: v.fallback(v.nullable(v.optional(v.boolean())), undefined),
|
||||
discoverable: v.fallback(v.optional(v.boolean()), undefined),
|
||||
actor_type: v.fallback(v.optional(v.string()), undefined),
|
||||
show_birthday: v.fallback(v.optional(v.boolean()), undefined),
|
||||
})), null),
|
||||
role: v.fallback(v.nullable(roleSchema), null),
|
||||
|
||||
settings_store: z.record(z.any()).optional().catch(undefined),
|
||||
chat_token: z.string().optional().catch(undefined),
|
||||
allow_following_move: z.boolean().optional().catch(undefined),
|
||||
unread_conversation_count: z.number().optional().catch(undefined),
|
||||
unread_notifications_count: z.number().optional().catch(undefined),
|
||||
notification_settings: z.object({
|
||||
block_from_strangers: z.boolean().catch(false),
|
||||
hide_notification_contents: z.boolean().catch(false),
|
||||
}).optional().catch(undefined),
|
||||
settings_store: v.fallback(v.optional(v.record(v.string(), v.any())), undefined),
|
||||
chat_token: v.fallback(v.optional(v.string()), undefined),
|
||||
allow_following_move: v.fallback(v.optional(v.boolean()), undefined),
|
||||
unread_conversation_count: v.fallback(v.optional(v.number()), undefined),
|
||||
unread_notifications_count: v.fallback(v.optional(v.number()), undefined),
|
||||
notification_settings: v.fallback(v.optional(v.object({
|
||||
block_from_strangers: v.fallback(v.boolean(), false),
|
||||
hide_notification_contents: v.fallback(v.boolean(), false),
|
||||
})), undefined),
|
||||
}));
|
||||
|
||||
type CredentialAccount = z.infer<typeof untypedCredentialAccountSchema> & WithMoved;
|
||||
type CredentialAccount = v.InferOutput<typeof untypedCredentialAccountSchema> & WithMoved;
|
||||
|
||||
const credentialAccountSchema: z.ZodType<CredentialAccount> = untypedCredentialAccountSchema as any;
|
||||
const credentialAccountSchema: v.BaseSchema<any, CredentialAccount, v.BaseIssue<unknown>> = untypedCredentialAccountSchema as any;
|
||||
|
||||
const untypedMutedAccountSchema = z.preprocess(preprocessAccount, accountWithMovedAccountSchema.extend({
|
||||
mute_expires_at: dateSchema.nullable().catch(null),
|
||||
const untypedMutedAccountSchema = v.pipe(v.any(), preprocessAccount, v.object({
|
||||
...accountWithMovedAccountSchema.entries,
|
||||
mute_expires_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
}));
|
||||
|
||||
type MutedAccount = z.infer<typeof untypedMutedAccountSchema> & WithMoved;
|
||||
type MutedAccount = v.InferOutput<typeof untypedMutedAccountSchema> & WithMoved;
|
||||
|
||||
const mutedAccountSchema: z.ZodType<MutedAccount> = untypedMutedAccountSchema as any;
|
||||
const mutedAccountSchema: v.BaseSchema<any, MutedAccount, v.BaseIssue<unknown>> = untypedMutedAccountSchema as any;
|
||||
|
||||
export {
|
||||
accountSchema,
|
||||
|
||||
@ -1,67 +1,71 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from '../account';
|
||||
import { roleSchema } from '../role';
|
||||
import { dateSchema, filteredArray } from '../utils';
|
||||
import { datetimeSchema, filteredArray } from '../utils';
|
||||
|
||||
import { adminIpSchema } from './ip';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Account/} */
|
||||
const adminAccountSchema = z.preprocess((account: any) => {
|
||||
if (!account.account) {
|
||||
const adminAccountSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((account: any) => {
|
||||
if (!account.account) {
|
||||
/**
|
||||
* Convert Pleroma account schema
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminusers}
|
||||
*/
|
||||
return {
|
||||
id: account.id,
|
||||
account: null,
|
||||
username: account.nickname,
|
||||
domain: account.nickname.split('@')[1] || null,
|
||||
created_at: account.created_at,
|
||||
email: account.email,
|
||||
invite_request: account.registration_reason,
|
||||
role: account.roles?.is_admin
|
||||
? roleSchema.parse({ name: 'Admin' })
|
||||
: account.roles?.moderator
|
||||
? roleSchema.parse({ name: 'Moderator ' }) :
|
||||
null,
|
||||
confirmed: account.is_confirmed,
|
||||
approved: account.is_approved,
|
||||
disabled: !account.is_active,
|
||||
return {
|
||||
id: account.id,
|
||||
account: null,
|
||||
username: account.nickname,
|
||||
domain: account.nickname.split('@')[1] || null,
|
||||
created_at: account.created_at,
|
||||
email: account.email,
|
||||
invite_request: account.registration_reason,
|
||||
role: account.roles?.is_admin
|
||||
? v.parse(roleSchema, { name: 'Admin' })
|
||||
: account.roles?.moderator
|
||||
? v.parse(roleSchema, { name: 'Moderator ' }) :
|
||||
null,
|
||||
confirmed: account.is_confirmed,
|
||||
approved: account.is_approved,
|
||||
disabled: !account.is_active,
|
||||
|
||||
actor_type: account.actor_type,
|
||||
display_name: account.display_name,
|
||||
suggested: account.is_suggested,
|
||||
};
|
||||
}
|
||||
return account;
|
||||
}, z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
domain: z.string().nullable().catch(null),
|
||||
created_at: dateSchema,
|
||||
email: z.string().nullable().catch(null),
|
||||
ip: z.string().ip().nullable().catch(null),
|
||||
ips: filteredArray(adminIpSchema),
|
||||
locale: z.string().nullable().catch(null),
|
||||
invite_request: z.string().nullable().catch(null),
|
||||
role: roleSchema.nullable().catch(null),
|
||||
confirmed: z.boolean().catch(false),
|
||||
approved: z.boolean().catch(false),
|
||||
disabled: z.boolean().catch(false),
|
||||
silenced: z.boolean().catch(false),
|
||||
suspended: z.boolean().catch(false),
|
||||
account: accountSchema.nullable().catch(null),
|
||||
created_by_application_id: z.string().optional().catch(undefined),
|
||||
invited_by_account_id: z.string().optional().catch(undefined),
|
||||
actor_type: account.actor_type,
|
||||
display_name: account.display_name,
|
||||
suggested: account.is_suggested,
|
||||
};
|
||||
}
|
||||
return account;
|
||||
}),
|
||||
v.object({
|
||||
id: v.string(),
|
||||
username: v.string(),
|
||||
domain: v.fallback(v.nullable(v.string()), null),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
email: v.fallback(v.nullable(v.string()), null),
|
||||
ip: v.fallback(v.nullable(v.pipe(v.string(), v.ip())), null),
|
||||
ips: filteredArray(adminIpSchema),
|
||||
locale: v.fallback(v.nullable(v.string()), null),
|
||||
invite_request: v.fallback(v.nullable(v.string()), null),
|
||||
role: v.fallback(v.nullable(roleSchema), null),
|
||||
confirmed: v.fallback(v.boolean(), false),
|
||||
approved: v.fallback(v.boolean(), false),
|
||||
disabled: v.fallback(v.boolean(), false),
|
||||
silenced: v.fallback(v.boolean(), false),
|
||||
suspended: v.fallback(v.boolean(), false),
|
||||
account: v.fallback(v.nullable(accountSchema), null),
|
||||
created_by_application_id: v.fallback(v.optional(v.string()), undefined),
|
||||
invited_by_account_id: v.fallback(v.optional(v.string()), undefined),
|
||||
|
||||
actor_type: z.string().nullable().catch(null),
|
||||
display_name: z.string().nullable().catch(null),
|
||||
suggested: z.boolean().nullable().catch(null),
|
||||
}));
|
||||
actor_type: v.fallback(v.nullable(v.string()), null),
|
||||
display_name: v.fallback(v.nullable(v.string()), null),
|
||||
suggested: v.fallback(v.nullable(v.boolean()), null),
|
||||
}),
|
||||
);
|
||||
|
||||
type AdminAccount = z.infer<typeof adminAccountSchema>;
|
||||
type AdminAccount = v.InferOutput<typeof adminAccountSchema>;
|
||||
|
||||
export {
|
||||
adminAccountSchema,
|
||||
|
||||
@ -1,16 +1,21 @@
|
||||
import pick from 'lodash.pick';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { announcementSchema } from '../announcement';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminannouncements} */
|
||||
const adminAnnouncementSchema = z.preprocess((announcement: any) => ({
|
||||
...announcement,
|
||||
...pick(announcement.pleroma, 'raw_content'),
|
||||
}), announcementSchema.extend({
|
||||
raw_content: z.string().catch(''),
|
||||
}));
|
||||
const adminAnnouncementSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((announcement: any) => ({
|
||||
...announcement,
|
||||
...pick(announcement.pleroma, 'raw_content'),
|
||||
})),
|
||||
v.object({
|
||||
...announcementSchema.entries,
|
||||
raw_content: v.fallback(v.string(), ''),
|
||||
}),
|
||||
);
|
||||
|
||||
type AdminAnnouncement = z.infer<typeof adminAnnouncementSchema>;
|
||||
type AdminAnnouncement = v.InferOutput<typeof adminAnnouncementSchema>;
|
||||
|
||||
export { adminAnnouncementSchema, type AdminAnnouncement };
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_CanonicalEmailBlock/} */
|
||||
const adminCanonicalEmailBlockSchema = z.object({
|
||||
id: z.string(),
|
||||
canonical_email_hash: z.string(),
|
||||
const adminCanonicalEmailBlockSchema = v.object({
|
||||
id: v.string(),
|
||||
canonical_email_hash: v.string(),
|
||||
});
|
||||
|
||||
type AdminCanonicalEmailBlock = z.infer<typeof adminCanonicalEmailBlockSchema>;
|
||||
type AdminCanonicalEmailBlock = v.InferOutput<typeof adminCanonicalEmailBlockSchema>;
|
||||
|
||||
export {
|
||||
adminCanonicalEmailBlockSchema,
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Cohort/} */
|
||||
const adminCohortSchema = z.object({
|
||||
period: z.string().datetime({ offset: true }),
|
||||
frequency: z.enum(['day', 'month']),
|
||||
data: z.array(z.object({
|
||||
date: z.string().datetime({ offset: true }),
|
||||
rate: z.number(),
|
||||
value: z.number().int(),
|
||||
const adminCohortSchema = v.object({
|
||||
period: datetimeSchema,
|
||||
frequency: v.picklist(['day', 'month']),
|
||||
data: v.array(v.object({
|
||||
date: datetimeSchema,
|
||||
rate: v.number(),
|
||||
value: v.pipe(v.number(), v.integer()),
|
||||
})),
|
||||
});
|
||||
|
||||
type AdminCohort = z.infer<typeof adminCohortSchema>;
|
||||
type AdminCohort = v.InferOutput<typeof adminCohortSchema>;
|
||||
|
||||
export {
|
||||
adminCohortSchema,
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Dimension/} */
|
||||
const adminDimensionSchema = z.object({
|
||||
key: z.string(),
|
||||
data: z.object({
|
||||
key: z.string(),
|
||||
human_key: z.string(),
|
||||
value: z.string(),
|
||||
unit: z.string().optional().catch(undefined),
|
||||
human_value: z.string().optional().catch(undefined),
|
||||
const adminDimensionSchema = v.object({
|
||||
key: v.string(),
|
||||
data: v.object({
|
||||
key: v.string(),
|
||||
human_key: v.string(),
|
||||
value: v.string(),
|
||||
unit: v.fallback(v.optional(v.string()), undefined),
|
||||
human_value: v.fallback(v.optional(v.string()), undefined),
|
||||
}),
|
||||
});
|
||||
|
||||
type AdminDimension = z.infer<typeof adminDimensionSchema>;
|
||||
type AdminDimension = v.InferOutput<typeof adminDimensionSchema>;
|
||||
|
||||
export {
|
||||
adminDimensionSchema,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from '../utils';
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_DomainAllow/} */
|
||||
const adminDomainAllowSchema = z.object({
|
||||
id: z.string(),
|
||||
domain: z.string(),
|
||||
created_at: dateSchema,
|
||||
const adminDomainAllowSchema = v.object({
|
||||
id: v.string(),
|
||||
domain: v.string(),
|
||||
created_at: datetimeSchema,
|
||||
});
|
||||
|
||||
type AdminDomainAllow = z.infer<typeof adminDomainAllowSchema>;
|
||||
type AdminDomainAllow = v.InferOutput<typeof adminDomainAllowSchema>;
|
||||
|
||||
export {
|
||||
adminDomainAllowSchema,
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from '../utils';
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_DomainBlock/} */
|
||||
const adminDomainBlockSchema = z.object({
|
||||
id: z.string(),
|
||||
domain: z.string(),
|
||||
digest: z.string(),
|
||||
created_at: dateSchema,
|
||||
severity: z.enum(['silence', 'suspend', 'noop']),
|
||||
reject_media: z.boolean(),
|
||||
reject_reports: z.boolean(),
|
||||
private_comment: z.string().nullable().catch(null),
|
||||
public_comment: z.string().nullable().catch(null),
|
||||
obfuscate: z.boolean(),
|
||||
const adminDomainBlockSchema = v.object({
|
||||
id: v.string(),
|
||||
domain: v.string(),
|
||||
digest: v.string(),
|
||||
created_at: datetimeSchema,
|
||||
severity: v.picklist(['silence', 'suspend', 'noop']),
|
||||
reject_media: v.boolean(),
|
||||
reject_reports: v.boolean(),
|
||||
private_comment: v.fallback(v.nullable(v.string()), null),
|
||||
public_comment: v.fallback(v.nullable(v.string()), null),
|
||||
obfuscate: v.boolean(),
|
||||
});
|
||||
|
||||
type AdminDomainBlock = z.infer<typeof adminDomainBlockSchema>;
|
||||
type AdminDomainBlock = v.InferOutput<typeof adminDomainBlockSchema>;
|
||||
|
||||
export {
|
||||
adminDomainBlockSchema,
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const adminDomainSchema = z.object({
|
||||
domain: z.string().catch(''),
|
||||
id: z.coerce.string(),
|
||||
public: z.boolean().catch(false),
|
||||
resolves: z.boolean().catch(false),
|
||||
last_checked_at: z.string().datetime().catch(''),
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
const adminDomainSchema = v.object({
|
||||
domain: v.fallback(v.string(), ''),
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
public: v.fallback(v.boolean(), false),
|
||||
resolves: v.fallback(v.boolean(), false),
|
||||
last_checked_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
});
|
||||
|
||||
type AdminDomain = z.infer<typeof adminDomainSchema>
|
||||
type AdminDomain = v.InferOutput<typeof adminDomainSchema>
|
||||
|
||||
export { adminDomainSchema, type AdminDomain };
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from '../utils';
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_EmailDomainBlock/} */
|
||||
const adminEmailDomainBlockSchema = z.object({
|
||||
id: z.string(),
|
||||
domain: z.string(),
|
||||
created_at: dateSchema,
|
||||
history: z.array(z.object({
|
||||
day: z.coerce.string(),
|
||||
accounts: z.coerce.string(),
|
||||
uses: z.coerce.string(),
|
||||
const adminEmailDomainBlockSchema = v.object({
|
||||
id: v.string(),
|
||||
domain: v.string(),
|
||||
created_at: datetimeSchema,
|
||||
history: v.array(v.object({
|
||||
day: v.pipe(v.unknown(), v.transform(String)),
|
||||
accounts: v.pipe(v.unknown(), v.transform(String)),
|
||||
uses: v.pipe(v.unknown(), v.transform(String)),
|
||||
})),
|
||||
});
|
||||
|
||||
type AdminEmailDomainBlock = z.infer<typeof adminEmailDomainBlockSchema>;
|
||||
type AdminEmailDomainBlock = v.InferOutput<typeof adminEmailDomainBlockSchema>;
|
||||
|
||||
export {
|
||||
adminEmailDomainBlockSchema,
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from '../utils';
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_IpBlock/} */
|
||||
const adminIpBlockSchema = z.object({
|
||||
id: z.string(),
|
||||
ip: z.string().ip(),
|
||||
severity: z.enum(['sign_up_requires_approval', 'sign_up_block', 'no_access']),
|
||||
comment: z.string().catch(''),
|
||||
created_at: dateSchema,
|
||||
expires_at: z.string().datetime({ offset: true }),
|
||||
const adminIpBlockSchema = v.object({
|
||||
id: v.string(),
|
||||
ip: v.pipe(v.string(), v.ip()),
|
||||
severity: v.picklist(['sign_up_requires_approval', 'sign_up_block', 'no_access']),
|
||||
comment: v.fallback(v.string(), ''),
|
||||
created_at: datetimeSchema,
|
||||
expires_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
});
|
||||
|
||||
type AdminIpBlock = z.infer<typeof adminIpBlockSchema>;
|
||||
type AdminIpBlock = v.InferOutput<typeof adminIpBlockSchema>;
|
||||
|
||||
export {
|
||||
adminIpBlockSchema,
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from '../utils';
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Ip/} */
|
||||
const adminIpSchema = z.object({
|
||||
ip: z.string().ip(),
|
||||
used_at: dateSchema,
|
||||
const adminIpSchema = v.object({
|
||||
ip: v.pipe(v.string(), v.ip()),
|
||||
used_at: datetimeSchema,
|
||||
});
|
||||
|
||||
type AdminIp = z.infer<typeof adminIpSchema>;
|
||||
type AdminIp = v.InferOutput<typeof adminIpSchema>;
|
||||
|
||||
export {
|
||||
adminIpSchema,
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { datetimeSchema } from '../utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Measure/} */
|
||||
const adminMeasureSchema = z.object({
|
||||
key: z.string(),
|
||||
unit: z.string().nullable().catch(null),
|
||||
total: z.coerce.number(),
|
||||
human_value: z.string().optional().catch(undefined),
|
||||
previous_total: z.coerce.string().optional().catch(undefined),
|
||||
data: z.array(z.object({
|
||||
date: z.string().datetime({ offset: true }),
|
||||
value: z.coerce.string(),
|
||||
const adminMeasureSchema = v.object({
|
||||
key: v.string(),
|
||||
unit: v.fallback(v.nullable(v.string()), null),
|
||||
total: v.pipe(v.unknown(), v.transform(Number)),
|
||||
human_value: v.fallback(v.optional(v.string()), undefined),
|
||||
previous_total: v.fallback(v.optional(v.pipe(v.unknown(), v.transform(String))), undefined),
|
||||
data: v.array(v.object({
|
||||
date: datetimeSchema,
|
||||
value: v.pipe(v.unknown(), v.transform(String)),
|
||||
})),
|
||||
});
|
||||
|
||||
type AdminMeasure = z.infer<typeof adminMeasureSchema>;
|
||||
type AdminMeasure = v.InferOutput<typeof adminMeasureSchema>;
|
||||
|
||||
export {
|
||||
adminMeasureSchema,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminmoderation_log} */
|
||||
const adminModerationLogEntrySchema = z.object({
|
||||
id: z.coerce.string(),
|
||||
data: z.record(z.string(), z.any()).catch({}),
|
||||
time: z.number().catch(0),
|
||||
message: z.string().catch(''),
|
||||
const adminModerationLogEntrySchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
data: v.fallback(v.record(v.string(), v.any()), {}),
|
||||
time: v.fallback(v.number(), 0),
|
||||
message: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
type AdminModerationLogEntry = z.infer<typeof adminModerationLogEntrySchema>
|
||||
type AdminModerationLogEntry = v.InferOutput<typeof adminModerationLogEntrySchema>
|
||||
|
||||
export { adminModerationLogEntrySchema, type AdminModerationLogEntry };
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const pleromaConfigSchema = z.object({
|
||||
configs: z.array(z.object({
|
||||
value: z.any(),
|
||||
group: z.string(),
|
||||
key: z.string(),
|
||||
const pleromaConfigSchema = v.object({
|
||||
configs: v.array(v.object({
|
||||
value: v.any(),
|
||||
group: v.string(),
|
||||
key: v.string(),
|
||||
})),
|
||||
need_reboot: z.boolean(),
|
||||
need_reboot: v.boolean(),
|
||||
});
|
||||
|
||||
type PleromaConfig = z.infer<typeof pleromaConfigSchema>
|
||||
type PleromaConfig = v.InferOutput<typeof pleromaConfigSchema>
|
||||
|
||||
export { pleromaConfigSchema, type PleromaConfig };
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const adminRelaySchema = z.preprocess((data: any) => ({ id: data.actor, ...data }), z.object({
|
||||
actor: z.string().catch(''),
|
||||
id: z.string(),
|
||||
followed_back: z.boolean().catch(false),
|
||||
}));
|
||||
const adminRelaySchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((data: any) => ({ id: data.actor, ...data })),
|
||||
v.object({
|
||||
actor: v.fallback(v.string(), ''),
|
||||
id: v.string(),
|
||||
followed_back: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
);
|
||||
|
||||
type AdminRelay = z.infer<typeof adminRelaySchema>
|
||||
type AdminRelay = v.InferOutput<typeof adminRelaySchema>
|
||||
|
||||
export { adminRelaySchema, type AdminRelay };
|
||||
|
||||
@ -1,46 +1,50 @@
|
||||
import pick from 'lodash.pick';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { ruleSchema } from '../rule';
|
||||
import { statusWithoutAccountSchema } from '../status';
|
||||
import { dateSchema, filteredArray } from '../utils';
|
||||
import { datetimeSchema, filteredArray } from '../utils';
|
||||
|
||||
import { adminAccountSchema } from './account';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Admin_Report/} */
|
||||
const adminReportSchema = z.preprocess((report: any) => {
|
||||
if (report.actor) {
|
||||
const adminReportSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((report: any) => {
|
||||
if (report.actor) {
|
||||
/**
|
||||
* Convert Pleroma report schema
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminreports}
|
||||
*/
|
||||
return {
|
||||
action_taken: report.state !== 'open',
|
||||
comment: report.content,
|
||||
updated_at: report.created_at,
|
||||
account: report.actor,
|
||||
target_account: report.account,
|
||||
...(pick(report, ['id', 'assigned_account', 'created_at', 'rules', 'statuses'])),
|
||||
};
|
||||
}
|
||||
return report;
|
||||
}, z.object({
|
||||
id: z.string(),
|
||||
action_taken: z.boolean().optional().catch(undefined),
|
||||
action_taken_at: dateSchema.nullable().catch(null),
|
||||
category: z.string().optional().catch(undefined),
|
||||
comment: z.string().optional().catch(undefined),
|
||||
forwarded: z.boolean().optional().catch(undefined),
|
||||
created_at: dateSchema.optional().catch(undefined),
|
||||
updated_at: dateSchema.optional().catch(undefined),
|
||||
account: adminAccountSchema,
|
||||
target_account: adminAccountSchema,
|
||||
assigned_account: adminAccountSchema.nullable().catch(null),
|
||||
action_taken_by_account: adminAccountSchema.nullable().catch(null),
|
||||
statuses: filteredArray(statusWithoutAccountSchema),
|
||||
rules: filteredArray(ruleSchema),
|
||||
}));
|
||||
return {
|
||||
action_taken: report.state !== 'open',
|
||||
comment: report.content,
|
||||
updated_at: report.created_at,
|
||||
account: report.actor,
|
||||
target_account: report.account,
|
||||
...(pick(report, ['id', 'assigned_account', 'created_at', 'rules', 'statuses'])),
|
||||
};
|
||||
}
|
||||
return report;
|
||||
}),
|
||||
v.object({
|
||||
id: v.string(),
|
||||
action_taken: v.fallback(v.optional(v.boolean()), undefined),
|
||||
action_taken_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
category: v.fallback(v.optional(v.string()), undefined),
|
||||
comment: v.fallback(v.optional(v.string()), undefined),
|
||||
forwarded: v.fallback(v.optional(v.boolean()), undefined),
|
||||
created_at: v.fallback(v.optional(datetimeSchema), undefined),
|
||||
updated_at: v.fallback(v.optional(datetimeSchema), undefined),
|
||||
account: adminAccountSchema,
|
||||
target_account: adminAccountSchema,
|
||||
assigned_account: v.fallback(v.nullable(adminAccountSchema), null),
|
||||
action_taken_by_account: v.fallback(v.nullable(adminAccountSchema), null),
|
||||
statuses: filteredArray(statusWithoutAccountSchema),
|
||||
rules: filteredArray(ruleSchema),
|
||||
}),
|
||||
);
|
||||
|
||||
type AdminReport = z.infer<typeof adminReportSchema>;
|
||||
type AdminReport = v.InferOutput<typeof adminReportSchema>;
|
||||
|
||||
export { adminReportSchema, type AdminReport };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminrules} */
|
||||
const adminRuleSchema = z.object({
|
||||
id: z.string(),
|
||||
text: z.string().catch(''),
|
||||
hint: z.string().catch(''),
|
||||
priority: z.number().nullable().catch(null),
|
||||
const adminRuleSchema = v.object({
|
||||
id: v.string(),
|
||||
text: v.fallback(v.string(), ''),
|
||||
hint: v.fallback(v.string(), ''),
|
||||
priority: v.fallback(v.nullable(v.number()), null),
|
||||
});
|
||||
|
||||
type AdminRule = z.infer<typeof adminRuleSchema>;
|
||||
type AdminRule = v.InferOutput<typeof adminRuleSchema>;
|
||||
|
||||
export { adminRuleSchema, type AdminRule };
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { tagSchema } from '../tag';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Tag/#admin} */
|
||||
const adminTagSchema = tagSchema.extend({
|
||||
id: z.string(),
|
||||
trendable: z.boolean(),
|
||||
usable: z.boolean(),
|
||||
requires_review: z.boolean(),
|
||||
const adminTagSchema = v.object({
|
||||
...tagSchema.entries,
|
||||
id: v.string(),
|
||||
trendable: v.boolean(),
|
||||
usable: v.boolean(),
|
||||
requires_review: v.boolean(),
|
||||
});
|
||||
|
||||
type AdminTag = z.infer<typeof adminTagSchema>;
|
||||
type AdminTag = v.InferOutput<typeof adminTagSchema>;
|
||||
|
||||
export {
|
||||
adminTagSchema,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/announcement/} */
|
||||
const announcementReactionSchema = z.object({
|
||||
name: z.string().catch(''),
|
||||
count: z.number().int().nonnegative().catch(0),
|
||||
me: z.boolean().catch(false),
|
||||
url: z.string().nullable().catch(null),
|
||||
static_url: z.string().nullable().catch(null),
|
||||
announcement_id: z.string().catch(''),
|
||||
const announcementReactionSchema = v.object({
|
||||
name: v.fallback(v.string(), ''),
|
||||
count: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 0),
|
||||
me: v.fallback(v.boolean(), false),
|
||||
url: v.fallback(v.nullable(v.string()), null),
|
||||
static_url: v.fallback(v.nullable(v.string()), null),
|
||||
announcement_id: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
type AnnouncementReaction = z.infer<typeof announcementReactionSchema>;
|
||||
type AnnouncementReaction = v.InferOutput<typeof announcementReactionSchema>;
|
||||
|
||||
export { announcementReactionSchema, type AnnouncementReaction };
|
||||
|
||||
@ -1,33 +1,34 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { announcementReactionSchema } from './announcement-reaction';
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { mentionSchema } from './mention';
|
||||
import { tagSchema } from './tag';
|
||||
import { dateSchema, filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/announcement/} */
|
||||
const announcementSchema = z.object({
|
||||
id: z.string(),
|
||||
content: z.string().catch(''),
|
||||
starts_at: z.string().datetime().nullable().catch(null),
|
||||
ends_at: z.string().datetime().nullable().catch(null),
|
||||
all_day: z.boolean().catch(false),
|
||||
read: z.boolean().catch(false),
|
||||
published_at: dateSchema,
|
||||
const announcementSchema = v.object({
|
||||
id: v.string(),
|
||||
content: v.fallback(v.string(), ''),
|
||||
starts_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
ends_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
all_day: v.fallback(v.boolean(), false),
|
||||
read: v.fallback(v.boolean(), false),
|
||||
published_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
reactions: filteredArray(announcementReactionSchema),
|
||||
statuses: z.preprocess(
|
||||
(statuses: any) => Array.isArray(statuses)
|
||||
statuses: v.pipe(
|
||||
v.any(),
|
||||
v.transform((statuses: any) => Array.isArray(statuses)
|
||||
? Object.fromEntries(statuses.map((status: any) => [status.url, status.account?.acct]) || [])
|
||||
: statuses,
|
||||
z.record(z.string(), z.string()),
|
||||
: statuses),
|
||||
v.record(v.string(), v.string()),
|
||||
),
|
||||
mentions: filteredArray(mentionSchema),
|
||||
tags: filteredArray(tagSchema),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
updated_at: dateSchema,
|
||||
updated_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
});
|
||||
|
||||
type Announcement = z.infer<typeof announcementSchema>;
|
||||
type Announcement = v.InferOutput<typeof announcementSchema>;
|
||||
|
||||
export { announcementSchema, type Announcement };
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Application/} */
|
||||
const applicationSchema = z.object({
|
||||
name: z.string().catch(''),
|
||||
website: z.string().optional().catch(undefined),
|
||||
client_id: z.string().optional().catch(undefined),
|
||||
client_secret: z.string().optional().catch(undefined),
|
||||
redirect_uri: z.string().optional().catch(undefined),
|
||||
const applicationSchema = v.object({
|
||||
name: v.fallback(v.string(), ''),
|
||||
website: v.fallback(v.optional(v.string()), undefined),
|
||||
client_id: v.fallback(v.optional(v.string()), undefined),
|
||||
client_secret: v.fallback(v.optional(v.string()), undefined),
|
||||
redirect_uri: v.fallback(v.optional(v.string()), undefined),
|
||||
|
||||
id: z.string().optional().catch(undefined),
|
||||
id: v.fallback(v.optional(v.string()), undefined),
|
||||
|
||||
/** @deprecated */
|
||||
vapid_key: z.string().optional().catch(undefined),
|
||||
vapid_key: v.fallback(v.optional(v.string()), undefined),
|
||||
});
|
||||
|
||||
type Application = z.infer<typeof applicationSchema>;
|
||||
type Application = v.InferOutput<typeof applicationSchema>;
|
||||
|
||||
export { applicationSchema, type Application };
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema, mimeSchema } from './utils';
|
||||
import { datetimeSchema, mimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#post-apiv1pleromabackups} */
|
||||
const backupSchema = z.object({
|
||||
id: z.coerce.string(),
|
||||
const backupSchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
contentType: mimeSchema,
|
||||
file_size: z.number().catch(0),
|
||||
inserted_at: dateSchema,
|
||||
processed: z.boolean().catch(false),
|
||||
url: z.string().catch(''),
|
||||
file_size: v.fallback(v.number(), 0),
|
||||
inserted_at: datetimeSchema,
|
||||
processed: v.fallback(v.boolean(), false),
|
||||
url: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
type Backup = z.infer<typeof backupSchema>;
|
||||
type Backup = v.InferOutput<typeof backupSchema>;
|
||||
|
||||
export { backupSchema, type Backup };
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const bookmarkFolderSchema = z.object({
|
||||
id: z.coerce.string(),
|
||||
name: z.string().catch(''),
|
||||
emoji: z.string().nullable().catch(null),
|
||||
emoji_url: z.string().nullable().catch(null),
|
||||
const bookmarkFolderSchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
name: v.fallback(v.string(), ''),
|
||||
emoji: v.fallback(v.nullable(v.string()), null),
|
||||
emoji_url: v.fallback(v.nullable(v.string()), null),
|
||||
});
|
||||
|
||||
type BookmarkFolder = z.infer<typeof bookmarkFolderSchema>;
|
||||
type BookmarkFolder = v.InferOutput<typeof bookmarkFolderSchema>;
|
||||
|
||||
export { bookmarkFolderSchema, type BookmarkFolder };
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { mediaAttachmentSchema } from './media-attachment';
|
||||
import { previewCardSchema } from './preview-card';
|
||||
import { dateSchema, filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/chats/#getting-the-messages-for-a-chat} */
|
||||
const chatMessageSchema = z.object({
|
||||
id: z.string(),
|
||||
content: z.string().catch(''),
|
||||
chat_id: z.string(),
|
||||
account_id: z.string(),
|
||||
created_at: dateSchema,
|
||||
const chatMessageSchema = v.object({
|
||||
id: v.string(),
|
||||
content: v.fallback(v.string(), ''),
|
||||
chat_id: v.string(),
|
||||
account_id: v.string(),
|
||||
created_at: datetimeSchema,
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
attachment: mediaAttachmentSchema.nullable().catch(null),
|
||||
unread: z.boolean(),
|
||||
card: previewCardSchema.nullable().catch(null),
|
||||
attachment: v.fallback(v.nullable(mediaAttachmentSchema), null),
|
||||
unread: v.boolean(),
|
||||
card: v.fallback(v.nullable(previewCardSchema), null),
|
||||
});
|
||||
|
||||
type ChatMessage = z.infer<typeof chatMessageSchema>;
|
||||
type ChatMessage = v.InferOutput<typeof chatMessageSchema>;
|
||||
|
||||
export { chatMessageSchema, type ChatMessage };
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { chatMessageSchema } from './chat-message';
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/chats/#getting-a-list-of-chats} */
|
||||
const chatSchema = z.object({
|
||||
id: z.string(),
|
||||
const chatSchema = v.object({
|
||||
id: v.string(),
|
||||
account: accountSchema,
|
||||
unread: z.number().int(),
|
||||
last_message: chatMessageSchema.nullable().catch(null),
|
||||
created_at: dateSchema,
|
||||
unread: v.pipe(v.number(), v.integer()),
|
||||
last_message: v.fallback(v.nullable(chatMessageSchema), null),
|
||||
updated_at: datetimeSchema,
|
||||
});
|
||||
|
||||
type Chat = z.infer<typeof chatSchema>;
|
||||
type Chat = v.InferOutput<typeof chatSchema>;
|
||||
|
||||
export { chatSchema, type Chat };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { statusSchema } from './status';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Context/} */
|
||||
const contextSchema = z.object({
|
||||
ancestors: z.array(statusSchema),
|
||||
descendants: z.array(statusSchema),
|
||||
const contextSchema = v.object({
|
||||
ancestors: v.array(statusSchema),
|
||||
descendants: v.array(statusSchema),
|
||||
});
|
||||
|
||||
type Context = z.infer<typeof contextSchema>;
|
||||
type Context = v.InferOutput<typeof contextSchema>;
|
||||
|
||||
export { contextSchema, type Context };
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { filteredArray } from './utils';
|
||||
|
||||
import { accountSchema, statusSchema } from '.';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Conversation} */
|
||||
const conversationSchema = z.object({
|
||||
id: z.string(),
|
||||
unread: z.boolean().catch(false),
|
||||
const conversationSchema = v.object({
|
||||
id: v.string(),
|
||||
unread: v.fallback(v.boolean(), false),
|
||||
accounts: filteredArray(accountSchema),
|
||||
last_status: statusSchema.nullable().catch(null),
|
||||
last_status: v.fallback(v.nullable(statusSchema), null),
|
||||
});
|
||||
|
||||
type Conversation = z.infer<typeof conversationSchema>;
|
||||
type Conversation = v.InferOutput<typeof conversationSchema>;
|
||||
|
||||
export { conversationSchema, type Conversation };
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/**
|
||||
* Represents a custom emoji.
|
||||
* @see {@link https://docs.joinmastodon.org/entities/CustomEmoji/}
|
||||
*/
|
||||
const customEmojiSchema = z.object({
|
||||
shortcode: z.string(),
|
||||
url: z.string(),
|
||||
static_url: z.string().catch(''),
|
||||
visible_in_picker: z.boolean().catch(true),
|
||||
category: z.string().nullable().catch(null),
|
||||
const customEmojiSchema = v.object({
|
||||
shortcode: v.string(),
|
||||
url: v.string(),
|
||||
static_url: v.fallback(v.string(), ''),
|
||||
visible_in_picker: v.fallback(v.boolean(), true),
|
||||
category: v.fallback(v.nullable(v.string()), null),
|
||||
});
|
||||
|
||||
type CustomEmoji = z.infer<typeof customEmojiSchema>;
|
||||
type CustomEmoji = v.InferOutput<typeof customEmojiSchema>;
|
||||
|
||||
export { customEmojiSchema, type CustomEmoji };
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const directoryCategorySchema = z.object({
|
||||
category: z.string(),
|
||||
servers_count: z.coerce.number().nullable().catch(null),
|
||||
const directoryCategorySchema = v.object({
|
||||
category: v.string(),
|
||||
servers_count: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null),
|
||||
});
|
||||
|
||||
type DirectoryCategory = z.infer<typeof directoryCategorySchema>;
|
||||
type DirectoryCategory = v.InferOutput<typeof directoryCategorySchema>;
|
||||
|
||||
export { directoryCategorySchema, type DirectoryCategory };
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const directoryLanguageSchema = z.object({
|
||||
locale: z.string(),
|
||||
language: z.string(),
|
||||
servers_count: z.coerce.number().nullable().catch(null),
|
||||
const directoryLanguageSchema = v.object({
|
||||
locale: v.string(),
|
||||
language: v.string(),
|
||||
servers_count: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null),
|
||||
});
|
||||
|
||||
type DirectoryLanguage = z.infer<typeof directoryLanguageSchema>;
|
||||
type DirectoryLanguage = v.InferOutput<typeof directoryLanguageSchema>;
|
||||
|
||||
export { directoryLanguageSchema, type DirectoryLanguage };
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const directoryServerSchema = z.object({
|
||||
domain: z.string(),
|
||||
version: z.string(),
|
||||
description: z.string(),
|
||||
languages: z.array(z.string()),
|
||||
region: z.string(),
|
||||
categories: z.array(z.string()),
|
||||
proxied_thumbnail: z.string().url().nullable().catch(null),
|
||||
blurhash: z.string().nullable().catch(null),
|
||||
total_users: z.coerce.number(),
|
||||
last_week_users: z.coerce.number(),
|
||||
approval_required: z.boolean(),
|
||||
language: z.string(),
|
||||
category: z.string(),
|
||||
const directoryServerSchema = v.object({
|
||||
domain: v.string(),
|
||||
version: v.string(),
|
||||
description: v.string(),
|
||||
languages: v.array(v.string()),
|
||||
region: v.string(),
|
||||
categories: v.array(v.string()),
|
||||
proxied_thumbnail: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null),
|
||||
blurhash: v.fallback(v.nullable(v.string()), null),
|
||||
total_users: v.pipe(v.unknown(), v.transform(Number)),
|
||||
last_week_users: v.pipe(v.unknown(), v.transform(Number)),
|
||||
approval_required: v.boolean(),
|
||||
language: v.string(),
|
||||
category: v.string(),
|
||||
});
|
||||
|
||||
type DirectoryServer = z.infer<typeof directoryServerSchema>;
|
||||
type DirectoryServer = v.InferOutput<typeof directoryServerSchema>;
|
||||
|
||||
export { directoryServerSchema, type DirectoryServer };
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const directoryStatisticsPeriodSchema = z.object({
|
||||
period: z.string().date(),
|
||||
server_count: z.coerce.number().nullable().catch(null),
|
||||
user_count: z.coerce.number().nullable().catch(null),
|
||||
active_user_count: z.coerce.number().nullable().catch(null),
|
||||
const directoryStatisticsPeriodSchema = v.object({
|
||||
period: v.pipe(v.string(), v.isoDate()),
|
||||
server_count: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null),
|
||||
user_count: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null),
|
||||
active_user_count: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null),
|
||||
});
|
||||
|
||||
type DirectoryStatisticsPeriod = z.infer<typeof directoryStatisticsPeriodSchema>;
|
||||
type DirectoryStatisticsPeriod = v.InferOutput<typeof directoryStatisticsPeriodSchema>;
|
||||
|
||||
export { directoryStatisticsPeriodSchema, type DirectoryStatisticsPeriod };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/DomainBlock} */
|
||||
const domainBlockSchema = z.object({
|
||||
domain: z.string(),
|
||||
digest: z.string(),
|
||||
severity: z.enum(['silence', 'suspend']),
|
||||
comment: z.string().optional().catch(undefined),
|
||||
const domainBlockSchema = v.object({
|
||||
domain: v.string(),
|
||||
digest: v.string(),
|
||||
severity: v.picklist(['silence', 'suspend']),
|
||||
comment: v.fallback(v.optional(v.string()), undefined),
|
||||
});
|
||||
|
||||
type DomainBlock = z.infer<typeof domainBlockSchema>;
|
||||
type DomainBlock = v.InferOutput<typeof domainBlockSchema>;
|
||||
|
||||
export { domainBlockSchema, type DomainBlock };
|
||||
|
||||
@ -1,34 +1,39 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { emojiSchema, filteredArray } from './utils';
|
||||
|
||||
const baseEmojiReactionSchema = z.object({
|
||||
count: z.number().nullable().catch(null),
|
||||
me: z.boolean().catch(false),
|
||||
const baseEmojiReactionSchema = v.object({
|
||||
count: v.fallback(v.nullable(v.number()), null),
|
||||
me: v.fallback(v.boolean(), false),
|
||||
name: emojiSchema,
|
||||
url: z.literal(undefined).catch(undefined),
|
||||
static_url: z.literal(undefined).catch(undefined),
|
||||
url: v.fallback(v.undefined(), undefined),
|
||||
static_url: v.fallback(v.undefined(), undefined),
|
||||
accounts: filteredArray(accountSchema),
|
||||
account_ids: filteredArray(z.string()).catch([]),
|
||||
account_ids: v.fallback(filteredArray(v.string()), []),
|
||||
});
|
||||
|
||||
const customEmojiReactionSchema = baseEmojiReactionSchema.extend({
|
||||
name: z.string(),
|
||||
url: z.string().url(),
|
||||
static_url: z.string().url(),
|
||||
const customEmojiReactionSchema = v.object({
|
||||
...baseEmojiReactionSchema.entries,
|
||||
name: v.string(),
|
||||
url: v.pipe(v.string(), v.url()),
|
||||
static_url: v.pipe(v.string(), v.url()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Pleroma emoji reaction.
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/differences_in_mastoapi_responses/#statuses}
|
||||
*/
|
||||
const emojiReactionSchema = z.preprocess((reaction: any) => reaction ? {
|
||||
static_url: reaction.url,
|
||||
account_ids: reaction.accounts?.map((account: any) => account?.id),
|
||||
...reaction,
|
||||
} : null, baseEmojiReactionSchema.or(customEmojiReactionSchema));
|
||||
const emojiReactionSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((reaction: any) => reaction ? {
|
||||
static_url: reaction.url,
|
||||
account_ids: reaction.accounts?.map((account: any) => account?.id),
|
||||
...reaction,
|
||||
} : null),
|
||||
v.union([baseEmojiReactionSchema, customEmojiReactionSchema]),
|
||||
);
|
||||
|
||||
type EmojiReaction = z.infer<typeof emojiReactionSchema>;
|
||||
type EmojiReaction = v.InferOutput<typeof emojiReactionSchema>;
|
||||
|
||||
export { emojiReactionSchema, type EmojiReaction };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/ExtendedDescription} */
|
||||
const extendedDescriptionSchema = z.object({
|
||||
updated_at: dateSchema,
|
||||
content: z.string(),
|
||||
const extendedDescriptionSchema = v.object({
|
||||
updated_at: datetimeSchema,
|
||||
content: v.string(),
|
||||
});
|
||||
|
||||
type ExtendedDescription = z.infer<typeof extendedDescriptionSchema>;
|
||||
type ExtendedDescription = v.InferOutput<typeof extendedDescriptionSchema>;
|
||||
|
||||
export { extendedDescriptionSchema, type ExtendedDescription };
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/FamiliarFollowers/} */
|
||||
const familiarFollowersSchema = z.object({
|
||||
id: z.string(),
|
||||
const familiarFollowersSchema = v.object({
|
||||
id: v.string(),
|
||||
accounts: filteredArray(accountSchema),
|
||||
});
|
||||
|
||||
type FamiliarFollowers = z.infer<typeof familiarFollowersSchema>
|
||||
type FamiliarFollowers = v.InferOutput<typeof familiarFollowersSchema>
|
||||
|
||||
export { familiarFollowersSchema, type FamiliarFollowers };
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/FeaturedTag/} */
|
||||
const featuredTagSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
url: z.string().optional().catch(undefined),
|
||||
statuses_count: z.number(),
|
||||
last_status_at: z.number(),
|
||||
const featuredTagSchema = v.object({
|
||||
id: v.string(),
|
||||
name: v.string(),
|
||||
url: v.fallback(v.optional(v.string()), undefined),
|
||||
statuses_count: v.number(),
|
||||
last_status_at: v.number(),
|
||||
});
|
||||
|
||||
type FeaturedTag = z.infer<typeof featuredTagSchema>;
|
||||
type FeaturedTag = v.InferOutput<typeof featuredTagSchema>;
|
||||
|
||||
export { featuredTagSchema, type FeaturedTag };
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { filterSchema } from './filter';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/FilterResult/} */
|
||||
const filterResultSchema = z.object({
|
||||
const filterResultSchema = v.object({
|
||||
filter: filterSchema,
|
||||
keyword_matches: z.array(z.string()).nullable().catch(null),
|
||||
status_matches: z.array(z.string()).nullable().catch(null),
|
||||
keyword_matches: v.fallback(v.nullable(v.string()), null),
|
||||
status_matches: v.fallback(v.nullable(v.string()), null),
|
||||
});
|
||||
|
||||
type FilterResult = z.infer<typeof filterResultSchema>;
|
||||
type FilterResult = v.InferOutput<typeof filterResultSchema>;
|
||||
|
||||
export { filterResultSchema, type FilterResult };
|
||||
|
||||
@ -1,45 +1,49 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/FilterKeyword/} */
|
||||
const filterKeywordSchema = z.object({
|
||||
id: z.string(),
|
||||
keyword: z.string(),
|
||||
whole_word: z.boolean(),
|
||||
const filterKeywordSchema = v.object({
|
||||
id: v.string(),
|
||||
keyword: v.string(),
|
||||
whole_word: v.boolean(),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/FilterStatus/} */
|
||||
const filterStatusSchema = z.object({
|
||||
id: z.string(),
|
||||
status_id: z.string(),
|
||||
const filterStatusSchema = v.object({
|
||||
id: v.string(),
|
||||
status_id: v.string(),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Filter/} */
|
||||
const filterSchema = z.preprocess((filter: any) => {
|
||||
if (filter.phrase) {
|
||||
return {
|
||||
...filter,
|
||||
title: filter.phrase,
|
||||
keywords: [{
|
||||
id: '1',
|
||||
keyword: filter.phrase,
|
||||
whole_word: filter.whole_word,
|
||||
}],
|
||||
filter_action: filter.irreversible ? 'hide' : 'warn',
|
||||
};
|
||||
}
|
||||
return filter;
|
||||
}, z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
context: z.array(z.enum(['home', 'notifications', 'public', 'thread', 'account'])),
|
||||
expires_at: z.string().datetime({ offset: true }).nullable().catch(null),
|
||||
filter_action: z.enum(['warn', 'hide']).catch('warn'),
|
||||
keywords: filteredArray(filterKeywordSchema),
|
||||
statuses: filteredArray(filterStatusSchema),
|
||||
}));
|
||||
const filterSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((filter: any) => {
|
||||
if (filter.phrase) {
|
||||
return {
|
||||
...filter,
|
||||
title: filter.phrase,
|
||||
keywords: [{
|
||||
id: '1',
|
||||
keyword: filter.phrase,
|
||||
whole_word: filter.whole_word,
|
||||
}],
|
||||
filter_action: filter.irreversible ? 'hide' : 'warn',
|
||||
};
|
||||
}
|
||||
return filter;
|
||||
}),
|
||||
v.object({
|
||||
id: v.string(),
|
||||
title: v.string(),
|
||||
context: v.array(v.picklist(['home', 'notifications', 'public', 'thread', 'account'])),
|
||||
expires_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
filter_action: v.fallback(v.picklist(['warn', 'hide']), 'warn'),
|
||||
keywords: filteredArray(filterKeywordSchema),
|
||||
statuses: filteredArray(filterStatusSchema),
|
||||
}),
|
||||
);
|
||||
|
||||
type Filter = z.infer<typeof filterSchema>;
|
||||
type Filter = v.InferOutput<typeof filterSchema>;
|
||||
|
||||
export { filterKeywordSchema, filterStatusSchema, filterSchema, type Filter };
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
|
||||
@ -10,12 +10,12 @@ enum GroupRoles {
|
||||
|
||||
type GroupRole =`${GroupRoles}`;
|
||||
|
||||
const groupMemberSchema = z.object({
|
||||
id: z.string(),
|
||||
const groupMemberSchema = v.object({
|
||||
id: v.string(),
|
||||
account: accountSchema,
|
||||
role: z.nativeEnum(GroupRoles),
|
||||
role: v.enum(GroupRoles),
|
||||
});
|
||||
|
||||
type GroupMember = z.infer<typeof groupMemberSchema>;
|
||||
type GroupMember = v.InferOutput<typeof groupMemberSchema>;
|
||||
|
||||
export { groupMemberSchema, type GroupMember, GroupRoles, type GroupRole };
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { GroupRoles } from './group-member';
|
||||
|
||||
const groupRelationshipSchema = z.object({
|
||||
id: z.string(),
|
||||
member: z.boolean().catch(false),
|
||||
role: z.nativeEnum(GroupRoles).catch(GroupRoles.USER),
|
||||
requested: z.boolean().catch(false),
|
||||
const groupRelationshipSchema = v.object({
|
||||
id: v.string(),
|
||||
member: v.fallback(v.boolean(), false),
|
||||
role: v.fallback(v.enum(GroupRoles), GroupRoles.USER),
|
||||
requested: v.fallback(v.boolean(), false),
|
||||
});
|
||||
|
||||
type GroupRelationship = z.infer<typeof groupRelationshipSchema>;
|
||||
type GroupRelationship = v.InferOutput<typeof groupRelationshipSchema>;
|
||||
|
||||
export { groupRelationshipSchema, type GroupRelationship };
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { groupRelationshipSchema } from './group-relationship';
|
||||
import { filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
const groupSchema = z.object({
|
||||
avatar: z.string().catch(''),
|
||||
avatar_static: z.string().catch(''),
|
||||
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
||||
display_name: z.string().catch(''),
|
||||
domain: z.string().catch(''),
|
||||
const groupSchema = v.object({
|
||||
avatar: v.fallback(v.string(), ''),
|
||||
avatar_static: v.fallback(v.string(), ''),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
display_name: v.fallback(v.string(), ''),
|
||||
domain: v.fallback(v.string(), ''),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
header: z.string().catch(''),
|
||||
header_static: z.string().catch(''),
|
||||
id: z.coerce.string(),
|
||||
locked: z.boolean().catch(false),
|
||||
membership_required: z.boolean().catch(false),
|
||||
members_count: z.number().catch(0),
|
||||
owner: z.object({ id: z.string() }).nullable().catch(null),
|
||||
note: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
relationship: groupRelationshipSchema.nullable().catch(null), // Dummy field to be overwritten later
|
||||
statuses_visibility: z.string().catch('public'),
|
||||
uri: z.string().catch(''),
|
||||
url: z.string().catch(''),
|
||||
header: v.fallback(v.string(), ''),
|
||||
header_static: v.fallback(v.string(), ''),
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
locked: v.fallback(v.boolean(), false),
|
||||
membership_required: v.fallback(v.boolean(), false),
|
||||
members_count: v.fallback(v.number(), 0),
|
||||
owner: v.fallback(v.nullable(v.object({ id: v.string() })), null),
|
||||
note: v.fallback(v.pipe(v.string(), v.transform(note => note === '<p></p>' ? '' : note)), ''),
|
||||
relationship: v.fallback(v.nullable(groupRelationshipSchema), null), // Dummy field to be overwritten later
|
||||
statuses_visibility: v.fallback(v.string(), 'public'),
|
||||
uri: v.fallback(v.string(), ''),
|
||||
url: v.fallback(v.string(), ''),
|
||||
|
||||
avatar_description: z.string().catch(''),
|
||||
header_description: z.string().catch(''),
|
||||
avatar_description: v.fallback(v.string(), ''),
|
||||
header_description: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
type Group = z.infer<typeof groupSchema>;
|
||||
type Group = v.InferOutput<typeof groupSchema>;
|
||||
|
||||
export { groupSchema, type Group };
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint sort-keys: "error" */
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { ruleSchema } from './rule';
|
||||
@ -36,7 +36,7 @@ const instanceV1ToV2 = (data: any) => {
|
||||
uri,
|
||||
urls,
|
||||
...instance
|
||||
} = instanceV1Schema.parse(data);
|
||||
} = v.parse(instanceV1Schema, data);
|
||||
|
||||
return {
|
||||
...instance,
|
||||
@ -108,231 +108,226 @@ const fixVersion = (version: string) => {
|
||||
};
|
||||
|
||||
const configurationSchema = coerceObject({
|
||||
accounts: z.object({
|
||||
allow_custom_css: z.boolean(),
|
||||
max_featured_tags: z.number().int(),
|
||||
max_profile_fields: z.number().int(),
|
||||
}).nullable().catch(null),
|
||||
accounts: v.fallback(v.nullable(v.object({
|
||||
allow_custom_css: v.boolean(),
|
||||
max_featured_tags: v.pipe(v.number(), v.integer()),
|
||||
max_profile_fields: v.pipe(v.number(), v.integer()),
|
||||
})), null),
|
||||
chats: coerceObject({
|
||||
max_characters: z.number().catch(5000),
|
||||
max_characters: v.fallback(v.number(), 5000),
|
||||
}),
|
||||
groups: coerceObject({
|
||||
max_characters_description: z.number().catch(160),
|
||||
max_characters_name: z.number().catch(50),
|
||||
max_characters_description: v.fallback(v.number(), 160),
|
||||
max_characters_name: v.fallback(v.number(), 50),
|
||||
}),
|
||||
media_attachments: coerceObject({
|
||||
image_matrix_limit: z.number().optional().catch(undefined),
|
||||
image_size_limit: z.number().optional().catch(undefined),
|
||||
supported_mime_types: mimeSchema.array().optional().catch(undefined),
|
||||
video_duration_limit: z.number().optional().catch(undefined),
|
||||
video_frame_rate_limit: z.number().optional().catch(undefined),
|
||||
video_matrix_limit: z.number().optional().catch(undefined),
|
||||
video_size_limit: z.number().optional().catch(undefined),
|
||||
image_matrix_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
image_size_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
supported_mime_types: v.fallback(v.optional(v.array(mimeSchema)), undefined),
|
||||
video_duration_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
video_frame_rate_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
video_matrix_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
video_size_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
}),
|
||||
polls: coerceObject({
|
||||
max_characters_per_option: z.number().catch(25),
|
||||
max_expiration: z.number().catch(2629746),
|
||||
max_options: z.number().catch(4),
|
||||
min_expiration: z.number().catch(300),
|
||||
max_characters_per_option: v.fallback(v.number(), 25),
|
||||
max_expiration: v.fallback(v.number(), 2629746),
|
||||
max_options: v.fallback(v.number(), 4),
|
||||
min_expiration: v.fallback(v.number(), 300),
|
||||
}),
|
||||
reactions: coerceObject({
|
||||
max_reactions: z.number().catch(0),
|
||||
max_reactions: v.fallback(v.number(), 0),
|
||||
}),
|
||||
statuses: coerceObject({
|
||||
characters_reserved_per_url: z.number().optional().catch(undefined),
|
||||
max_characters: z.number().catch(500),
|
||||
max_media_attachments: z.number().catch(4),
|
||||
characters_reserved_per_url: v.fallback(v.optional(v.number()), undefined),
|
||||
max_characters: v.fallback(v.number(), 500),
|
||||
max_media_attachments: v.fallback(v.number(), 4),
|
||||
|
||||
}),
|
||||
translation: coerceObject({
|
||||
enabled: z.boolean().catch(false),
|
||||
enabled: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
urls: coerceObject({
|
||||
streaming: z.string().url().optional().catch(undefined),
|
||||
streaming: v.fallback(v.optional(v.pipe(v.string(), v.url())), undefined),
|
||||
}),
|
||||
vapid: coerceObject({
|
||||
public_key: z.string().catch(''),
|
||||
public_key: v.fallback(v.string(), ''),
|
||||
}),
|
||||
});
|
||||
|
||||
const contactSchema = coerceObject({
|
||||
contact_account: accountSchema.optional().catch(undefined),
|
||||
email: z.string().email().catch(''),
|
||||
contact_account: v.fallback(v.optional(accountSchema), undefined),
|
||||
email: v.fallback(v.pipe(v.string(), v.email()), ''),
|
||||
});
|
||||
|
||||
const pleromaSchema = coerceObject({
|
||||
metadata: coerceObject({
|
||||
account_activation_required: z.boolean().catch(false),
|
||||
birthday_min_age: z.number().catch(0),
|
||||
birthday_required: z.boolean().catch(false),
|
||||
description_limit: z.number().catch(1500),
|
||||
features: z.string().array().catch([]),
|
||||
account_activation_required: v.fallback(v.boolean(), false),
|
||||
birthday_min_age: v.fallback(v.number(), 0),
|
||||
birthday_required: v.fallback(v.boolean(), false),
|
||||
description_limit: v.fallback(v.number(), 1500),
|
||||
features: v.fallback(v.array(v.string()), []),
|
||||
federation: coerceObject({
|
||||
enabled: z.boolean().catch(true), // Assume true unless explicitly false
|
||||
mrf_policies: z.string().array().optional().catch(undefined),
|
||||
enabled: v.fallback(v.boolean(), true), // Assume true unless explicitly false
|
||||
mrf_policies: v.fallback(v.optional(v.array(v.string())), undefined),
|
||||
mrf_simple: coerceObject({
|
||||
accept: z.string().array().catch([]),
|
||||
avatar_removal: z.string().array().catch([]),
|
||||
banner_removal: z.string().array().catch([]),
|
||||
federated_timeline_removal: z.string().array().catch([]),
|
||||
followers_only: z.string().array().catch([]),
|
||||
media_nsfw: z.string().array().catch([]),
|
||||
media_removal: z.string().array().catch([]),
|
||||
reject: z.string().array().catch([]),
|
||||
reject_deletes: z.string().array().catch([]),
|
||||
report_removal: z.string().array().catch([]),
|
||||
accept: v.fallback(v.array(v.string()), []),
|
||||
avatar_removal: v.fallback(v.array(v.string()), []),
|
||||
banner_removal: v.fallback(v.array(v.string()), []),
|
||||
federated_timeline_removal: v.fallback(v.array(v.string()), []),
|
||||
followers_only: v.fallback(v.array(v.string()), []),
|
||||
media_nsfw: v.fallback(v.array(v.string()), []),
|
||||
media_removal: v.fallback(v.array(v.string()), []),
|
||||
reject: v.fallback(v.array(v.string()), []),
|
||||
reject_deletes: v.fallback(v.array(v.string()), []),
|
||||
report_removal: v.fallback(v.array(v.string()), []),
|
||||
}),
|
||||
}),
|
||||
fields_limits: coerceObject({
|
||||
max_fields: z.number().nonnegative().catch(4),
|
||||
name_length: z.number().nonnegative().catch(255),
|
||||
value_length: z.number().nonnegative().catch(2047),
|
||||
max_fields: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 4),
|
||||
name_length: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 255),
|
||||
value_length: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 2047),
|
||||
}),
|
||||
markup: coerceObject({
|
||||
allow_headings: z.boolean().catch(false),
|
||||
allow_inline_images: z.boolean().catch(false),
|
||||
allow_headings: v.fallback(v.boolean(), false),
|
||||
allow_inline_images: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
migration_cooldown_period: z.number().optional().catch(undefined),
|
||||
migration_cooldown_period: v.fallback(v.optional(v.number()), undefined),
|
||||
multitenancy: coerceObject({
|
||||
domains: z
|
||||
.array(
|
||||
z.object({
|
||||
domain: z.coerce.string(),
|
||||
id: z.string(),
|
||||
public: z.boolean().catch(false),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
enabled: z.boolean().catch(false),
|
||||
domains: v.optional(v.array(
|
||||
v.object({
|
||||
domain: v.pipe(v.unknown(), v.transform(String)),
|
||||
id: v.string(),
|
||||
public: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
)),
|
||||
enabled: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
post_formats: z.string().array().optional().catch(undefined),
|
||||
post_formats: v.fallback(v.optional(v.array(v.string())), undefined),
|
||||
restrict_unauthenticated: coerceObject({
|
||||
activities: coerceObject({
|
||||
local: z.boolean().catch(false),
|
||||
remote: z.boolean().catch(false),
|
||||
local: v.fallback(v.boolean(), false),
|
||||
remote: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
profiles: coerceObject({
|
||||
local: z.boolean().catch(false),
|
||||
remote: z.boolean().catch(false),
|
||||
local: v.fallback(v.boolean(), false),
|
||||
remote: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
timelines: coerceObject({
|
||||
bubble: z.boolean().catch(false),
|
||||
federated: z.boolean().catch(false),
|
||||
local: z.boolean().catch(false),
|
||||
bubble: v.fallback(v.boolean(), false),
|
||||
federated: v.fallback(v.boolean(), false),
|
||||
local: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
}),
|
||||
translation: coerceObject({
|
||||
allow_remote: z.boolean().catch(true),
|
||||
allow_unauthenticated: z.boolean().catch(false),
|
||||
source_languages: z.string().array().optional().catch(undefined),
|
||||
target_languages: z.string().array().optional().catch(undefined),
|
||||
allow_remote: v.fallback(v.boolean(), true),
|
||||
allow_unauthenticated: v.fallback(v.boolean(), false),
|
||||
source_languages: v.fallback(v.optional(v.array(v.string())), undefined),
|
||||
target_languages: v.fallback(v.optional(v.array(v.string())), undefined),
|
||||
}),
|
||||
}),
|
||||
oauth_consumer_strategies: z.string().array().catch([]),
|
||||
oauth_consumer_strategies: v.fallback(v.array(v.string()), []),
|
||||
stats: coerceObject({
|
||||
mau: z.number().optional().catch(undefined),
|
||||
mau: v.fallback(v.optional(v.number()), undefined),
|
||||
}),
|
||||
vapid_public_key: z.string().catch(''),
|
||||
vapid_public_key: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
const pleromaPollLimitsSchema = coerceObject({
|
||||
max_expiration: z.number().optional().catch(undefined),
|
||||
max_option_chars: z.number().optional().catch(undefined),
|
||||
max_options: z.number().optional().catch(undefined),
|
||||
min_expiration: z.number().optional().catch(undefined),
|
||||
max_expiration: v.fallback(v.optional(v.number()), undefined),
|
||||
max_option_chars: v.fallback(v.optional(v.number()), undefined),
|
||||
max_options: v.fallback(v.optional(v.number()), undefined),
|
||||
min_expiration: v.fallback(v.optional(v.number()), undefined),
|
||||
});
|
||||
|
||||
const registrations = coerceObject({
|
||||
approval_required: z.boolean().catch(false),
|
||||
enabled: z.boolean().catch(false),
|
||||
message: z.string().optional().catch(undefined),
|
||||
approval_required: v.fallback(v.boolean(), false),
|
||||
enabled: v.fallback(v.boolean(), false),
|
||||
message: v.fallback(v.optional(v.string()), undefined),
|
||||
});
|
||||
|
||||
const statsSchema = coerceObject({
|
||||
domain_count: z.number().optional().catch(undefined),
|
||||
status_count: z.number().optional().catch(undefined),
|
||||
user_count: z.number().optional().catch(undefined),
|
||||
domain_count: v.fallback(v.optional(v.number()), undefined),
|
||||
status_count: v.fallback(v.optional(v.number()), undefined),
|
||||
user_count: v.fallback(v.optional(v.number()), undefined),
|
||||
});
|
||||
|
||||
const thumbnailSchema = coerceObject({
|
||||
url: z.string().catch(''),
|
||||
url: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
const usageSchema = coerceObject({
|
||||
users: coerceObject({
|
||||
active_month: z.number().catch(0),
|
||||
active_month: v.fallback(v.number(), 0),
|
||||
}),
|
||||
});
|
||||
|
||||
const instanceV1Schema = coerceObject({
|
||||
account_domain: z.string().catch(''),
|
||||
approval_required: z.boolean().catch(false),
|
||||
account_domain: v.fallback(v.string(), ''),
|
||||
approval_required: v.fallback(v.boolean(), false),
|
||||
configuration: configurationSchema,
|
||||
contact_account: accountSchema.optional().catch(undefined),
|
||||
description: z.string().catch(''),
|
||||
description_limit: z.number().catch(1500),
|
||||
email: z.string().email().catch(''),
|
||||
feature_quote: z.boolean().catch(false),
|
||||
fedibird_capabilities: z.array(z.string()).catch([]),
|
||||
languages: z.string().array().catch([]),
|
||||
max_media_attachments: z.number().optional().catch(undefined),
|
||||
max_toot_chars: z.number().optional().catch(undefined),
|
||||
contact_account: v.fallback(v.optional(accountSchema), undefined),
|
||||
description: v.fallback(v.string(), ''),
|
||||
description_limit: v.fallback(v.number(), 1500),
|
||||
email: v.fallback(v.pipe(v.string(), v.email()), ''),
|
||||
feature_quote: v.fallback(v.boolean(), false),
|
||||
fedibird_capabilities: v.fallback(v.array(v.string()), []),
|
||||
languages: v.fallback(v.array(v.string()), []),
|
||||
max_media_attachments: v.fallback(v.optional(v.number()), undefined),
|
||||
max_toot_chars: v.fallback(v.optional(v.number()), undefined),
|
||||
pleroma: pleromaSchema,
|
||||
poll_limits: pleromaPollLimitsSchema,
|
||||
registrations: z.boolean().catch(false),
|
||||
registrations: v.fallback(v.boolean(), false),
|
||||
rules: filteredArray(ruleSchema),
|
||||
short_description: z.string().catch(''),
|
||||
short_description: v.fallback(v.string(), ''),
|
||||
stats: statsSchema,
|
||||
thumbnail: z.string().catch(''),
|
||||
title: z.string().catch(''),
|
||||
upload_limit: z.number().optional().catch(undefined),
|
||||
uri: z.string().catch(''),
|
||||
thumbnail: v.fallback(v.string(), ''),
|
||||
title: v.fallback(v.string(), ''),
|
||||
upload_limit: v.fallback(v.optional(v.number()), undefined),
|
||||
uri: v.fallback(v.string(), ''),
|
||||
urls: coerceObject({
|
||||
streaming_api: z.string().url().optional().catch(undefined),
|
||||
streaming_api: v.fallback(v.optional(v.pipe(v.string(), v.url())), undefined),
|
||||
}),
|
||||
usage: usageSchema,
|
||||
version: z.string().catch('0.0.0'),
|
||||
version: v.fallback(v.string(), '0.0.0'),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Instance/} */
|
||||
const instanceSchema = z.preprocess((data: any) => {
|
||||
const instanceSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((data: any) => {
|
||||
// Detect GoToSocial
|
||||
if (typeof data.configuration?.accounts?.allow_custom_css === 'boolean') {
|
||||
data.version = `0.0.0 (compatible; GoToSocial ${data.version})`;
|
||||
}
|
||||
if (typeof data.configuration?.accounts?.allow_custom_css === 'boolean') {
|
||||
data.version = `0.0.0 (compatible; GoToSocial ${data.version})`;
|
||||
}
|
||||
|
||||
const apiVersions = getApiVersions(data);
|
||||
const apiVersions = getApiVersions(data);
|
||||
|
||||
if (data.domain) return { account_domain: data.domain, ...data, api_versions: apiVersions };
|
||||
if (data.domain) return { account_domain: data.domain, ...data, api_versions: apiVersions };
|
||||
|
||||
return instanceV1ToV2({ ...data, api_versions: apiVersions });
|
||||
}, coerceObject({
|
||||
account_domain: z.string().catch(''),
|
||||
api_versions: z.record(z.number()).catch({}),
|
||||
configuration: configurationSchema,
|
||||
contact: contactSchema,
|
||||
description: z.string().catch(''),
|
||||
domain: z.string().catch(''),
|
||||
feature_quote: z.boolean().catch(false),
|
||||
fedibird_capabilities: z.array(z.string()).catch([]),
|
||||
languages: z.string().array().catch([]),
|
||||
pleroma: pleromaSchema,
|
||||
registrations: registrations,
|
||||
rules: filteredArray(ruleSchema),
|
||||
stats: statsSchema,
|
||||
thumbnail: thumbnailSchema,
|
||||
title: z.string().catch(''),
|
||||
usage: usageSchema,
|
||||
version: z.string().catch('0.0.0'),
|
||||
}).transform((instance) => {
|
||||
const version = fixVersion(instance.version);
|
||||
return instanceV1ToV2({ ...data, api_versions: apiVersions });
|
||||
}),
|
||||
coerceObject({
|
||||
account_domain: v.fallback(v.string(), ''),
|
||||
api_versions: v.fallback(v.record(v.string(), v.number()), {}),
|
||||
configuration: configurationSchema,
|
||||
contact: contactSchema,
|
||||
description: v.fallback(v.string(), ''),
|
||||
domain: v.fallback(v.string(), ''),
|
||||
feature_quote: v.fallback(v.boolean(), false),
|
||||
fedibird_capabilities: v.fallback(v.array(v.string()), []),
|
||||
languages: v.fallback(v.array(v.string()), []),
|
||||
pleroma: pleromaSchema,
|
||||
registrations: registrations,
|
||||
rules: filteredArray(ruleSchema),
|
||||
stats: statsSchema,
|
||||
thumbnail: thumbnailSchema,
|
||||
title: v.fallback(v.string(), ''),
|
||||
usage: usageSchema,
|
||||
version: v.pipe(v.fallback(v.string(), '0.0.0'), v.transform(fixVersion)),
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
...instance,
|
||||
version,
|
||||
};
|
||||
}));
|
||||
|
||||
type Instance = z.infer<typeof instanceSchema>;
|
||||
type Instance = v.InferOutput<typeof instanceSchema>;
|
||||
|
||||
export { instanceSchema, type Instance };
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { coerceObject } from './utils';
|
||||
|
||||
const interactionPolicyEntrySchema = z.enum(['public', 'followers', 'following', 'mutuals', 'mentioned', 'author', 'me']);
|
||||
const interactionPolicyEntrySchema = v.picklist(['public', 'followers', 'following', 'mutuals', 'mentioned', 'author', 'me']);
|
||||
|
||||
const interactionPolicyRuleSchema = coerceObject({
|
||||
always: z.array(interactionPolicyEntrySchema).default(['public']),
|
||||
with_approval: z.array(interactionPolicyEntrySchema).default([]),
|
||||
always: v.fallback(v.array(interactionPolicyEntrySchema), ['public']),
|
||||
with_approval: v.fallback(v.array(interactionPolicyEntrySchema), []),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.gotosocial.org/en/latest/api/swagger/} */
|
||||
@ -16,7 +16,7 @@ const interactionPolicySchema = coerceObject({
|
||||
can_reply: interactionPolicyRuleSchema,
|
||||
});
|
||||
|
||||
type InteractionPolicy = z.infer<typeof interactionPolicySchema>;
|
||||
type InteractionPolicy = v.InferOutput<typeof interactionPolicySchema>;
|
||||
|
||||
const interactionPoliciesSchema = coerceObject({
|
||||
public: interactionPolicySchema,
|
||||
@ -25,7 +25,7 @@ const interactionPoliciesSchema = coerceObject({
|
||||
direct: interactionPolicySchema,
|
||||
});
|
||||
|
||||
type InteractionPolicies = z.infer<typeof interactionPoliciesSchema>;
|
||||
type InteractionPolicies = v.InferOutput<typeof interactionPoliciesSchema>;
|
||||
|
||||
export { interactionPolicySchema, interactionPoliciesSchema, type InteractionPolicy, type InteractionPolicies };
|
||||
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { statusSchema } from './status';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.gotosocial.org/en/latest/api/swagger.yaml#/definitions/interactionRequest} */
|
||||
const interactionRequestSchema = z.object({
|
||||
accepted_at: z.string().datetime().nullable().catch(null),
|
||||
const interactionRequestSchema = v.object({
|
||||
accepted_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
account: accountSchema,
|
||||
created_at: z.string().datetime(),
|
||||
id: z.string(),
|
||||
rejected_at: z.string().datetime().nullable().catch(null),
|
||||
reply: statusSchema.nullable().catch(null),
|
||||
status: statusSchema.nullable().catch(null),
|
||||
type: z.enum(['favourite', 'reply', 'reblog']),
|
||||
uri: z.string().nullable().catch(null),
|
||||
created_at: datetimeSchema,
|
||||
id: v.string(),
|
||||
rejected_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
reply: v.fallback(v.nullable(statusSchema), null),
|
||||
status: v.fallback(v.nullable(statusSchema), null),
|
||||
type: v.picklist(['favourite', 'reply', 'reblog']),
|
||||
uri: v.fallback(v.nullable(v.string()), null),
|
||||
});
|
||||
|
||||
type InteractionRequest = z.infer<typeof interactionRequestSchema>;
|
||||
type InteractionRequest = v.InferOutput<typeof interactionRequestSchema>;
|
||||
|
||||
export { interactionRequestSchema, type InteractionRequest };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/List/} */
|
||||
const listSchema = z.object({
|
||||
id: z.coerce.string(),
|
||||
title: z.string(),
|
||||
replies_policy: z.string().optional().catch(undefined),
|
||||
exclusive: z.boolean().optional().catch(undefined),
|
||||
const listSchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
title: v.string(),
|
||||
replies_policy: v.fallback(v.optional(v.string()), undefined),
|
||||
exclusive: v.fallback(v.optional(v.boolean()), undefined),
|
||||
});
|
||||
|
||||
type List = z.infer<typeof listSchema>;
|
||||
type List = v.InferOutput<typeof listSchema>;
|
||||
|
||||
export { listSchema, type List };
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const locationSchema = z.object({
|
||||
url: z.string().url().catch(''),
|
||||
description: z.string().catch(''),
|
||||
country: z.string().catch(''),
|
||||
locality: z.string().catch(''),
|
||||
region: z.string().catch(''),
|
||||
postal_code: z.string().catch(''),
|
||||
street: z.string().catch(''),
|
||||
origin_id: z.string().catch(''),
|
||||
origin_provider: z.string().catch(''),
|
||||
type: z.string().catch(''),
|
||||
timezone: z.string().catch(''),
|
||||
geom: z.object({
|
||||
coordinates: z.tuple([z.number(), z.number()]).nullable().catch(null),
|
||||
srid: z.string().catch(''),
|
||||
}).nullable().catch(null),
|
||||
const locationSchema = v.object({
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
description: v.fallback(v.string(), ''),
|
||||
country: v.fallback(v.string(), ''),
|
||||
locality: v.fallback(v.string(), ''),
|
||||
region: v.fallback(v.string(), ''),
|
||||
postal_code: v.fallback(v.string(), ''),
|
||||
street: v.fallback(v.string(), ''),
|
||||
origin_id: v.fallback(v.string(), ''),
|
||||
origin_provider: v.fallback(v.string(), ''),
|
||||
type: v.fallback(v.string(), ''),
|
||||
timezone: v.fallback(v.string(), ''),
|
||||
geom: v.fallback(v.nullable(v.object({
|
||||
coordinates: v.fallback(v.nullable(v.tuple([v.number(), v.number()])), null),
|
||||
srid: v.fallback(v.string(), ''),
|
||||
})), null),
|
||||
});
|
||||
|
||||
type Location = z.infer<typeof locationSchema>;
|
||||
type Location = v.InferOutput<typeof locationSchema>;
|
||||
|
||||
export { locationSchema, type Location };
|
||||
|
||||
@ -1,23 +1,27 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
const markerSchema = z.preprocess((marker: any) => marker ? ({
|
||||
unread_count: marker.pleroma?.unread_count,
|
||||
...marker,
|
||||
}) : null, z.object({
|
||||
last_read_id: z.string(),
|
||||
version: z.number().int(),
|
||||
updated_at: dateSchema,
|
||||
unread_count: z.number().int().optional().catch(undefined),
|
||||
}));
|
||||
const markerSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((marker: any) => marker ? ({
|
||||
unread_count: marker.pleroma?.unread_count,
|
||||
...marker,
|
||||
}) : null),
|
||||
v.object({
|
||||
last_read_id: v.string(),
|
||||
version: v.pipe(v.number(), v.integer()),
|
||||
updated_at: datetimeSchema,
|
||||
unread_count: v.fallback(v.optional(v.pipe(v.number(), v.integer())), undefined),
|
||||
}),
|
||||
);
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Marker/} */
|
||||
type Marker = z.infer<typeof markerSchema>;
|
||||
type Marker = v.InferOutput<typeof markerSchema>;
|
||||
|
||||
const markersSchema = z.record(markerSchema);
|
||||
const markersSchema = v.record(v.string(), markerSchema);
|
||||
|
||||
type Markers = z.infer<typeof markersSchema>;
|
||||
type Markers = v.InferOutput<typeof markersSchema>;
|
||||
|
||||
export {
|
||||
markerSchema,
|
||||
|
||||
@ -1,109 +1,113 @@
|
||||
import { isBlurhashValid } from 'blurhash';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { mimeSchema } from './utils';
|
||||
|
||||
const blurhashSchema = z.string().superRefine((value, ctx) => {
|
||||
const r = isBlurhashValid(value);
|
||||
const blurhashSchema = v.pipe(v.string(), v.check(
|
||||
(value) => isBlurhashValid(value).result,
|
||||
'invalid blurhash', // .errorReason
|
||||
));
|
||||
|
||||
if (!r.result) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: r.errorReason,
|
||||
});
|
||||
}
|
||||
const baseAttachmentSchema = v.object({
|
||||
id: v.string(),
|
||||
type: v.string(),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
preview_url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
remote_url: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null),
|
||||
description: v.fallback(v.string(), ''),
|
||||
blurhash: v.fallback(v.nullable(blurhashSchema), null),
|
||||
|
||||
mime_type: v.fallback(v.nullable(mimeSchema), null),
|
||||
});
|
||||
|
||||
const baseAttachmentSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.string(),
|
||||
url: z.string().url().catch(''),
|
||||
preview_url: z.string().url().catch(''),
|
||||
remote_url: z.string().url().nullable().catch(null),
|
||||
description: z.string().catch(''),
|
||||
blurhash: blurhashSchema.nullable().catch(null),
|
||||
|
||||
mime_type: mimeSchema.nullable().catch(null),
|
||||
const imageMetaSchema = v.object({
|
||||
width: v.number(),
|
||||
height: v.number(),
|
||||
size: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+x\d+$/))), null),
|
||||
aspect: v.fallback(v.nullable(v.number()), null),
|
||||
});
|
||||
|
||||
const imageMetaSchema = z.object({
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
size: z.string().regex(/\d+x\d+$/).nullable().catch(null),
|
||||
aspect: z.number().nullable().catch(null),
|
||||
const imageAttachmentSchema = v.object({
|
||||
...baseAttachmentSchema.entries,
|
||||
type: v.literal('image'),
|
||||
meta: v.fallback(v.object({
|
||||
original: v.fallback(v.optional(imageMetaSchema), undefined),
|
||||
small: v.fallback(v.optional(imageMetaSchema), undefined),
|
||||
focus: v.fallback(v.optional(v.object({
|
||||
x: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)),
|
||||
y: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)),
|
||||
})), undefined),
|
||||
}), {}),
|
||||
});
|
||||
|
||||
const imageAttachmentSchema = baseAttachmentSchema.extend({
|
||||
type: z.literal('image'),
|
||||
meta: z.object({
|
||||
original: imageMetaSchema.optional().catch(undefined),
|
||||
small: imageMetaSchema.optional().catch(undefined),
|
||||
focus: z.object({
|
||||
x: z.number().min(-1).max(1),
|
||||
y: z.number().min(-1).max(1),
|
||||
}).optional().catch(undefined),
|
||||
}).catch({}),
|
||||
});
|
||||
|
||||
const videoAttachmentSchema = baseAttachmentSchema.extend({
|
||||
type: z.literal('video'),
|
||||
meta: z.object({
|
||||
duration: z.number().optional().catch(undefined),
|
||||
original: imageMetaSchema.extend({
|
||||
frame_rate: z.string().regex(/\d+\/\d+$/).nullable().catch(null),
|
||||
duration: z.number().nonnegative().nullable().catch(null),
|
||||
}).optional().catch(undefined),
|
||||
small: imageMetaSchema.optional().catch(undefined),
|
||||
const videoAttachmentSchema = v.object({
|
||||
...baseAttachmentSchema.entries,
|
||||
type: v.literal('video'),
|
||||
meta: v.fallback(v.object({
|
||||
duration: v.fallback(v.optional(v.number()), undefined),
|
||||
original: v.fallback(v.optional(v.object({
|
||||
...imageMetaSchema.entries,
|
||||
frame_rate: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+\/\d+$/))), null),
|
||||
duration: v.fallback(v.nullable(v.pipe(v.number(), v.minValue(0))), null),
|
||||
})), undefined),
|
||||
small: v.fallback(v.optional(imageMetaSchema), undefined),
|
||||
// WIP: add rest
|
||||
}).catch({}),
|
||||
}), {}),
|
||||
});
|
||||
|
||||
const gifvAttachmentSchema = baseAttachmentSchema.extend({
|
||||
type: z.literal('gifv'),
|
||||
meta: z.object({
|
||||
duration: z.number().optional().catch(undefined),
|
||||
original: imageMetaSchema.optional().catch(undefined),
|
||||
}).catch({}),
|
||||
const gifvAttachmentSchema = v.object({
|
||||
...baseAttachmentSchema.entries,
|
||||
type: v.literal('gifv'),
|
||||
meta: v.fallback(v.object({
|
||||
duration: v.fallback(v.optional(v.number()), undefined),
|
||||
original: v.fallback(v.optional(imageMetaSchema), undefined),
|
||||
}), {}),
|
||||
});
|
||||
|
||||
const audioAttachmentSchema = baseAttachmentSchema.extend({
|
||||
type: z.literal('audio'),
|
||||
meta: z.object({
|
||||
duration: z.number().optional().catch(undefined),
|
||||
colors: z.object({
|
||||
background: z.string().optional().catch(undefined),
|
||||
foreground: z.string().optional().catch(undefined),
|
||||
accent: z.string().optional().catch(undefined),
|
||||
duration: z.number().optional().catch(undefined),
|
||||
}).optional().catch(undefined),
|
||||
original: z.object({
|
||||
duration: z.number().optional().catch(undefined),
|
||||
bitrate: z.number().nonnegative().optional().catch(undefined),
|
||||
}).optional().catch(undefined),
|
||||
}).catch({}),
|
||||
const audioAttachmentSchema = v.object({
|
||||
...baseAttachmentSchema.entries,
|
||||
type: v.literal('audio'),
|
||||
meta: v.fallback(v.object({
|
||||
duration: v.fallback(v.optional(v.number()), undefined),
|
||||
colors: v.fallback(v.optional(v.object({
|
||||
background: v.fallback(v.optional(v.string()), undefined),
|
||||
foreground: v.fallback(v.optional(v.string()), undefined),
|
||||
accent: v.fallback(v.optional(v.string()), undefined),
|
||||
duration: v.fallback(v.optional(v.number()), undefined),
|
||||
})), undefined),
|
||||
original: v.fallback(v.optional(v.object({
|
||||
duration: v.fallback(v.optional(v.number()), undefined),
|
||||
bitrate: v.fallback(v.optional(v.pipe(v.number(), v.minValue(0))), undefined),
|
||||
})), undefined),
|
||||
}), {}),
|
||||
});
|
||||
|
||||
const unknownAttachmentSchema = baseAttachmentSchema.extend({
|
||||
type: z.literal('unknown'),
|
||||
const unknownAttachmentSchema = v.object({
|
||||
...baseAttachmentSchema.entries,
|
||||
type: v.literal('unknown'),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/MediaAttachment} */
|
||||
const mediaAttachmentSchema = z.preprocess((data: any) => {
|
||||
if (!data) return null;
|
||||
const mediaAttachmentSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((data: any) => {
|
||||
if (!data) return null;
|
||||
|
||||
return {
|
||||
mime_type: data.pleroma?.mime_type,
|
||||
preview_url: data.url,
|
||||
...data,
|
||||
};
|
||||
}, z.discriminatedUnion('type', [
|
||||
imageAttachmentSchema,
|
||||
videoAttachmentSchema,
|
||||
gifvAttachmentSchema,
|
||||
audioAttachmentSchema,
|
||||
unknownAttachmentSchema,
|
||||
]));
|
||||
return {
|
||||
mime_type: data.pleroma?.mime_type,
|
||||
preview_url: data.url,
|
||||
...data,
|
||||
};
|
||||
}),
|
||||
v.variant('type', [
|
||||
imageAttachmentSchema,
|
||||
videoAttachmentSchema,
|
||||
gifvAttachmentSchema,
|
||||
audioAttachmentSchema,
|
||||
unknownAttachmentSchema,
|
||||
]),
|
||||
);
|
||||
|
||||
type MediaAttachment = z.infer<typeof mediaAttachmentSchema>;
|
||||
type MediaAttachment = v.InferOutput<typeof mediaAttachmentSchema>;
|
||||
|
||||
export { blurhashSchema, mediaAttachmentSchema, type MediaAttachment };
|
||||
|
||||
@ -1,19 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Status/#Mention} */
|
||||
const mentionSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string().catch(''),
|
||||
url: z.string().url().catch(''),
|
||||
acct: z.string(),
|
||||
}).transform((mention) => {
|
||||
if (!mention.username) {
|
||||
mention.username = mention.acct.split('@')[0];
|
||||
}
|
||||
const mentionSchema = v.pipe(
|
||||
v.object({
|
||||
id: v.string(),
|
||||
username: v.fallback(v.string(), ''),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
acct: v.string(),
|
||||
}),
|
||||
v.transform((mention) => {
|
||||
if (!mention.username) {
|
||||
mention.username = mention.acct.split('@')[0];
|
||||
}
|
||||
|
||||
return mention;
|
||||
});
|
||||
return mention;
|
||||
}),
|
||||
);
|
||||
|
||||
type Mention = z.infer<typeof mentionSchema>;
|
||||
type Mention = v.InferOutput<typeof mentionSchema>;
|
||||
|
||||
export { mentionSchema, type Mention };
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/NotificationPolicy} */
|
||||
const notificationPolicySchema = z.object({
|
||||
filter_not_following: z.boolean(),
|
||||
filter_not_followers: z.boolean(),
|
||||
filter_new_accounts: z.boolean(),
|
||||
filter_private_mentions: z.boolean(),
|
||||
summary: z.object({
|
||||
pending_requests_count: z.number().int(),
|
||||
pending_notifications_count: z.number().int(),
|
||||
const notificationPolicySchema = v.object({
|
||||
filter_not_following: v.boolean(),
|
||||
filter_not_followers: v.boolean(),
|
||||
filter_new_accounts: v.boolean(),
|
||||
filter_private_mentions: v.boolean(),
|
||||
summary: v.object({
|
||||
pending_requests_count: v.pipe(v.number(), v.integer()),
|
||||
pending_notifications_count: v.pipe(v.number(), v.integer()),
|
||||
}),
|
||||
});
|
||||
|
||||
type NotificationPolicy = z.infer<typeof notificationPolicySchema>;
|
||||
type NotificationPolicy = v.InferOutput<typeof notificationPolicySchema>;
|
||||
|
||||
export { notificationPolicySchema, type NotificationPolicy };
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
import { accountSchema, statusSchema } from '.';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/NotificationRequest} */
|
||||
const notificationRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
created_at: dateSchema,
|
||||
updated_at: dateSchema,
|
||||
const notificationRequestSchema = v.object({
|
||||
id: v.string(),
|
||||
created_at: datetimeSchema,
|
||||
updated_at: datetimeSchema,
|
||||
account: accountSchema,
|
||||
notifications_count: z.coerce.string(),
|
||||
last_status: statusSchema.optional().catch(undefined),
|
||||
notifications_count: v.pipe(v.unknown(), v.transform(String)),
|
||||
last_status: v.fallback(v.optional(statusSchema), undefined),
|
||||
});
|
||||
|
||||
type NotificationRequest = z.infer<typeof notificationRequestSchema>;
|
||||
type NotificationRequest = v.InferOutput<typeof notificationRequestSchema>;
|
||||
|
||||
export { notificationRequestSchema, type NotificationRequest };
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import pick from 'lodash.pick';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { accountWarningSchema } from './account-warning';
|
||||
@ -7,94 +7,107 @@ import { chatMessageSchema } from './chat-message';
|
||||
import { relationshipSeveranceEventSchema } from './relationship-severance-event';
|
||||
import { reportSchema } from './report';
|
||||
import { statusSchema } from './status';
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
const baseNotificationSchema = z.object({
|
||||
const baseNotificationSchema = v.object({
|
||||
account: accountSchema,
|
||||
created_at: dateSchema,
|
||||
id: z.string(),
|
||||
group_key: z.string(),
|
||||
type: z.string(),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
id: v.string(),
|
||||
group_key: v.string(),
|
||||
type: v.string(),
|
||||
|
||||
is_muted: z.boolean().optional().catch(undefined),
|
||||
is_seen: z.boolean().optional().catch(undefined),
|
||||
is_muted: v.fallback(v.optional(v.boolean()), undefined),
|
||||
is_seen: v.fallback(v.optional(v.boolean()), undefined),
|
||||
});
|
||||
|
||||
const accountNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.enum(['follow', 'follow_request', 'admin.sign_up', 'bite']),
|
||||
const accountNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.picklist(['follow', 'follow_request', 'admin.sign_up', 'bite']),
|
||||
});
|
||||
|
||||
const mentionNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('mention'),
|
||||
subtype: z.enum(['reply']).nullable().catch(null),
|
||||
const mentionNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('mention'),
|
||||
subtype: v.fallback(v.nullable(v.picklist(['reply'])), null),
|
||||
status: statusSchema,
|
||||
});
|
||||
|
||||
const statusNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.enum(['status', 'reblog', 'favourite', 'poll', 'update', 'event_reminder']),
|
||||
const statusNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.picklist(['status', 'reblog', 'favourite', 'poll', 'update', 'event_reminder']),
|
||||
status: statusSchema,
|
||||
});
|
||||
|
||||
const reportNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('admin.report'),
|
||||
const reportNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('admin.report'),
|
||||
report: reportSchema,
|
||||
});
|
||||
|
||||
const severedRelationshipNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('severed_relationships'),
|
||||
const severedRelationshipNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('severed_relationships'),
|
||||
relationship_severance_event: relationshipSeveranceEventSchema,
|
||||
});
|
||||
|
||||
const moderationWarningNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('moderation_warning'),
|
||||
const moderationWarningNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('moderation_warning'),
|
||||
moderation_warning: accountWarningSchema,
|
||||
});
|
||||
|
||||
const moveNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('move'),
|
||||
const moveNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('move'),
|
||||
target: accountSchema,
|
||||
});
|
||||
|
||||
const emojiReactionNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('emoji_reaction'),
|
||||
emoji: z.string(),
|
||||
emoji_url: z.string().nullable().catch(null),
|
||||
const emojiReactionNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('emoji_reaction'),
|
||||
emoji: v.string(),
|
||||
emoji_url: v.fallback(v.nullable(v.string()), null),
|
||||
status: statusSchema,
|
||||
});
|
||||
|
||||
const chatMentionNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.literal('chat_mention'),
|
||||
const chatMentionNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.literal('chat_mention'),
|
||||
chat_message: chatMessageSchema,
|
||||
});
|
||||
|
||||
const eventParticipationRequestNotificationSchema = baseNotificationSchema.extend({
|
||||
type: z.enum(['participation_accepted', 'participation_request']),
|
||||
const eventParticipationRequestNotificationSchema = v.object({
|
||||
...baseNotificationSchema.entries,
|
||||
type: v.picklist(['participation_accepted', 'participation_request']),
|
||||
status: statusSchema,
|
||||
participation_message: z.string().nullable().catch(null),
|
||||
participation_message: v.fallback(v.nullable(v.string()), null),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Notification/} */
|
||||
const notificationSchema: z.ZodType<Notification> = z.preprocess((notification: any) => ({
|
||||
group_key: `ungrouped-${notification.id}`,
|
||||
...pick(notification.pleroma || {}, ['is_muted', 'is_seen']),
|
||||
...notification,
|
||||
type: notification.type === 'pleroma:report'
|
||||
? 'admin.report'
|
||||
: notification.type?.replace(/^pleroma:/, ''),
|
||||
}), z.discriminatedUnion('type', [
|
||||
accountNotificationSchema,
|
||||
mentionNotificationSchema,
|
||||
statusNotificationSchema,
|
||||
reportNotificationSchema,
|
||||
severedRelationshipNotificationSchema,
|
||||
moderationWarningNotificationSchema,
|
||||
moveNotificationSchema,
|
||||
emojiReactionNotificationSchema,
|
||||
chatMentionNotificationSchema,
|
||||
eventParticipationRequestNotificationSchema,
|
||||
])) as any;
|
||||
const notificationSchema: v.BaseSchema<any, Notification, v.BaseIssue<unknown>> = v.pipe(
|
||||
v.any(),
|
||||
v.transform((notification: any) => ({
|
||||
group_key: `ungrouped-${notification.id}`,
|
||||
...pick(notification.pleroma || {}, ['is_muted', 'is_seen']),
|
||||
...notification,
|
||||
type: notification.type === 'pleroma:report'
|
||||
? 'admin.report'
|
||||
: notification.type?.replace(/^pleroma:/, ''),
|
||||
})),
|
||||
v.variant('type', [
|
||||
accountNotificationSchema,
|
||||
mentionNotificationSchema,
|
||||
statusNotificationSchema,
|
||||
reportNotificationSchema,
|
||||
severedRelationshipNotificationSchema,
|
||||
moderationWarningNotificationSchema,
|
||||
moveNotificationSchema,
|
||||
emojiReactionNotificationSchema,
|
||||
chatMentionNotificationSchema,
|
||||
eventParticipationRequestNotificationSchema,
|
||||
])) as any;
|
||||
|
||||
type Notification = z.infer<
|
||||
type Notification = v.InferOutput<
|
||||
| typeof accountNotificationSchema
|
||||
| typeof mentionNotificationSchema
|
||||
| typeof statusNotificationSchema
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apioauth_tokens} */
|
||||
const oauthTokenSchema = z.preprocess((token: any) => ({
|
||||
...token,
|
||||
valid_until: token?.valid_until?.padEnd(27, 'Z'),
|
||||
}), z.object({
|
||||
app_name: z.string(),
|
||||
id: z.number(),
|
||||
valid_until: z.string().datetime({ offset: true }),
|
||||
}));
|
||||
const oauthTokenSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((token: any) => ({
|
||||
...token,
|
||||
valid_until: token?.valid_until?.padEnd(27, 'Z'),
|
||||
})),
|
||||
v.object({
|
||||
app_name: v.string(),
|
||||
id: v.number(),
|
||||
valid_until: datetimeSchema,
|
||||
}),
|
||||
);
|
||||
|
||||
type OauthToken = z.infer<typeof oauthTokenSchema>;
|
||||
type OauthToken = v.InferOutput<typeof oauthTokenSchema>;
|
||||
|
||||
export { oauthTokenSchema, type OauthToken };
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
const pollOptionSchema = z.object({
|
||||
title: z.string().catch(''),
|
||||
votes_count: z.number().catch(0),
|
||||
const pollOptionSchema = v.object({
|
||||
title: v.fallback(v.string(), ''),
|
||||
votes_count: v.fallback(v.number(), 0),
|
||||
|
||||
title_map: z.record(z.string()).nullable().catch(null),
|
||||
title_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Poll/} */
|
||||
const pollSchema = z.object({
|
||||
const pollSchema = v.object({
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
expired: z.boolean().catch(false),
|
||||
expires_at: z.string().datetime().nullable().catch(null),
|
||||
id: z.string(),
|
||||
multiple: z.boolean().catch(false),
|
||||
options: z.array(pollOptionSchema).min(2),
|
||||
voters_count: z.number().catch(0),
|
||||
votes_count: z.number().catch(0),
|
||||
own_votes: z.array(z.number()).nonempty().nullable().catch(null),
|
||||
voted: z.boolean().catch(false),
|
||||
expired: v.fallback(v.boolean(), false),
|
||||
expires_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
id: v.string(),
|
||||
multiple: v.fallback(v.boolean(), false),
|
||||
options: v.pipe(v.array(pollOptionSchema), v.minLength(2)),
|
||||
voters_count: v.fallback(v.number(), 0),
|
||||
votes_count: v.fallback(v.number(), 0),
|
||||
own_votes: v.fallback(v.nullable(v.pipe(v.array(v.number()), v.minLength(1))), null),
|
||||
voted: v.fallback(v.boolean(), false),
|
||||
|
||||
non_anonymous: z.boolean().catch(false),
|
||||
non_anonymous: v.fallback(v.boolean(), false),
|
||||
});
|
||||
|
||||
type Poll = z.infer<typeof pollSchema>;
|
||||
type Poll = v.InferOutput<typeof pollSchema>;
|
||||
type PollOption = Poll['options'][number];
|
||||
|
||||
export { pollSchema, type Poll, type PollOption };
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/PreviewCard/} */
|
||||
const previewCardSchema = z.object({
|
||||
author_name: z.string().catch(''),
|
||||
author_url: z.string().url().catch(''),
|
||||
blurhash: z.string().nullable().catch(null),
|
||||
description: z.string().catch(''),
|
||||
embed_url: z.string().url().catch(''),
|
||||
height: z.number().catch(0),
|
||||
html: z.string().catch(''),
|
||||
image: z.string().nullable().catch(null),
|
||||
image_description: z.string().catch(''),
|
||||
provider_name: z.string().catch(''),
|
||||
provider_url: z.string().url().catch(''),
|
||||
title: z.string().catch(''),
|
||||
type: z.enum(['link', 'photo', 'video', 'rich']).catch('link'),
|
||||
url: z.string().url(),
|
||||
width: z.number().catch(0),
|
||||
const previewCardSchema = v.object({
|
||||
author_name: v.fallback(v.string(), ''),
|
||||
author_url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
blurhash: v.fallback(v.nullable(v.string()), null),
|
||||
description: v.fallback(v.string(), ''),
|
||||
embed_url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
height: v.fallback(v.number(), 0),
|
||||
html: v.fallback(v.string(), ''),
|
||||
image: v.fallback(v.nullable(v.string()), null),
|
||||
image_description: v.fallback(v.string(), ''),
|
||||
provider_name: v.fallback(v.string(), ''),
|
||||
provider_url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
title: v.fallback(v.string(), ''),
|
||||
type: v.fallback(v.picklist(['link', 'photo', 'video', 'rich']), 'link'),
|
||||
url: v.pipe(v.string(), v.url()),
|
||||
width: v.fallback(v.number(), 0),
|
||||
});
|
||||
|
||||
type PreviewCard = z.infer<typeof previewCardSchema>;
|
||||
type PreviewCard = v.InferOutput<typeof previewCardSchema>;
|
||||
|
||||
export { previewCardSchema, type PreviewCard };
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/RelationshipSeveranceEvent/} */
|
||||
const relationshipSeveranceEventSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(['domain_block', 'user_domain_block', 'account_suspension']),
|
||||
purged: z.string(),
|
||||
relationships_count: z.number().optional().catch(undefined),
|
||||
created_at: dateSchema,
|
||||
const relationshipSeveranceEventSchema = v.object({
|
||||
id: v.string(),
|
||||
type: v.picklist(['domain_block', 'user_domain_block', 'account_suspension']),
|
||||
purged: v.string(),
|
||||
relationships_count: v.fallback(v.optional(v.number()), undefined),
|
||||
created_at: datetimeSchema,
|
||||
});
|
||||
|
||||
type RelationshipSeveranceEvent = z.infer<typeof relationshipSeveranceEventSchema>;
|
||||
type RelationshipSeveranceEvent = v.InferOutput<typeof relationshipSeveranceEventSchema>;
|
||||
|
||||
export { relationshipSeveranceEventSchema, type RelationshipSeveranceEvent };
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Relationship/} */
|
||||
const relationshipSchema = z.object({
|
||||
blocked_by: z.boolean().catch(false),
|
||||
blocking: z.boolean().catch(false),
|
||||
domain_blocking: z.boolean().catch(false),
|
||||
endorsed: z.boolean().catch(false),
|
||||
followed_by: z.boolean().catch(false),
|
||||
following: z.boolean().catch(false),
|
||||
id: z.string(),
|
||||
muting: z.boolean().catch(false),
|
||||
muting_notifications: z.boolean().catch(false),
|
||||
note: z.string().catch(''),
|
||||
notifying: z.boolean().catch(false),
|
||||
requested: z.boolean().catch(false),
|
||||
showing_reblogs: z.boolean().catch(false),
|
||||
const relationshipSchema = v.object({
|
||||
blocked_by: v.fallback(v.boolean(), false),
|
||||
blocking: v.fallback(v.boolean(), false),
|
||||
domain_blocking: v.fallback(v.boolean(), false),
|
||||
endorsed: v.fallback(v.boolean(), false),
|
||||
followed_by: v.fallback(v.boolean(), false),
|
||||
following: v.fallback(v.boolean(), false),
|
||||
id: v.string(),
|
||||
muting: v.fallback(v.boolean(), false),
|
||||
muting_notifications: v.fallback(v.boolean(), false),
|
||||
note: v.fallback(v.string(), ''),
|
||||
notifying: v.fallback(v.boolean(), false),
|
||||
requested: v.fallback(v.boolean(), false),
|
||||
showing_reblogs: v.fallback(v.boolean(), false),
|
||||
});
|
||||
|
||||
type Relationship = z.infer<typeof relationshipSchema>;
|
||||
type Relationship = v.InferOutput<typeof relationshipSchema>;
|
||||
|
||||
export { relationshipSchema, type Relationship };
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { dateSchema } from './utils';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Report/} */
|
||||
const reportSchema = z.object({
|
||||
id: z.string(),
|
||||
action_taken: z.boolean().optional().catch(undefined),
|
||||
action_taken_at: dateSchema.nullable().catch(null),
|
||||
category: z.string().optional().catch(undefined),
|
||||
comment: z.string().optional().catch(undefined),
|
||||
forwarded: z.boolean().optional().catch(undefined),
|
||||
created_at: dateSchema.optional().catch(undefined),
|
||||
status_ids: z.array(z.string()).nullable().catch(null),
|
||||
rule_ids: z.array(z.string()).nullable().catch(null),
|
||||
target_account: accountSchema.nullable().catch(null),
|
||||
const reportSchema = v.object({
|
||||
id: v.string(),
|
||||
action_taken: v.fallback(v.optional(v.boolean()), undefined),
|
||||
action_taken_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
category: v.fallback(v.optional(v.string()), undefined),
|
||||
comment: v.fallback(v.optional(v.string()), undefined),
|
||||
forwarded: v.fallback(v.optional(v.boolean()), undefined),
|
||||
created_at: v.fallback(v.optional(datetimeSchema), undefined),
|
||||
status_ids: v.fallback(v.nullable(v.string()), null),
|
||||
rule_ids: v.fallback(v.nullable(v.string()), null),
|
||||
target_account: v.fallback(v.nullable(accountSchema), null),
|
||||
});
|
||||
|
||||
type Report = z.infer<typeof reportSchema>;
|
||||
type Report = v.InferOutput<typeof reportSchema>;
|
||||
|
||||
export { reportSchema, type Report };
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const hexSchema = z.string().regex(/^#[a-f0-9]{6}$/i);
|
||||
const hexSchema = v.pipe(v.string(), v.regex(/^#[a-f0-9]{6}$/i));
|
||||
|
||||
const roleSchema = z.object({
|
||||
id: z.string().catch(''),
|
||||
name: z.string().catch(''),
|
||||
color: hexSchema.catch(''),
|
||||
permissions: z.string().catch(''),
|
||||
highlighted: z.boolean().catch(true),
|
||||
const roleSchema = v.object({
|
||||
id: v.fallback(v.string(), ''),
|
||||
name: v.fallback(v.string(), ''),
|
||||
color: v.fallback(hexSchema, ''),
|
||||
permissions: v.fallback(v.string(), ''),
|
||||
highlighted: v.fallback(v.boolean(), true),
|
||||
});
|
||||
|
||||
type Role = z.infer<typeof roleSchema>;
|
||||
type Role = v.InferOutput<typeof roleSchema>;
|
||||
|
||||
export {
|
||||
roleSchema,
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const baseRuleSchema = z.object({
|
||||
id: z.string(),
|
||||
text: z.string().catch(''),
|
||||
hint: z.string().catch(''),
|
||||
const baseRuleSchema = v.object({
|
||||
id: v.string(),
|
||||
text: v.fallback(v.string(), ''),
|
||||
hint: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Rule/} */
|
||||
const ruleSchema = z.preprocess((data: any) => ({
|
||||
...data,
|
||||
hint: data.hint || data.subtext,
|
||||
}), baseRuleSchema);
|
||||
const ruleSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((data: any) => ({
|
||||
...data,
|
||||
hint: data.hint || data.subtext,
|
||||
})),
|
||||
baseRuleSchema,
|
||||
);
|
||||
|
||||
type Rule = z.infer<typeof ruleSchema>;
|
||||
type Rule = v.InferOutput<typeof ruleSchema>;
|
||||
|
||||
export { ruleSchema, type Rule };
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { mediaAttachmentSchema } from './media-attachment';
|
||||
import { filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/ScheduledStatus/} */
|
||||
const scheduledStatusSchema = z.object({
|
||||
id: z.string(),
|
||||
scheduled_at: z.string().datetime({ offset: true }),
|
||||
params: z.object({
|
||||
text: z.string().nullable().catch(null),
|
||||
poll: z.object({
|
||||
options: z.array(z.string()),
|
||||
expires_in: z.coerce.string(),
|
||||
multiple: z.boolean().optional().catch(undefined),
|
||||
hide_totals: z.boolean().optional().catch(undefined),
|
||||
}).nullable().catch(null),
|
||||
media_ids: z.array(z.string()).nullable().catch(null),
|
||||
sensitive: z.coerce.boolean().nullable().catch(null),
|
||||
spoiler_text: z.string().nullable().catch(null),
|
||||
visibility: z.string().catch('public'),
|
||||
in_reply_to_id: z.string().nullable().catch(null),
|
||||
language: z.string().nullable().catch(null),
|
||||
application_id: z.number().int().nullable().catch(null),
|
||||
scheduled_at: z.string().datetime({ offset: true }).nullable().catch(null),
|
||||
idempotency: z.string().nullable().catch(null),
|
||||
with_rate_limit: z.boolean().catch(false),
|
||||
const scheduledStatusSchema = v.object({
|
||||
id: v.string(),
|
||||
scheduled_at: datetimeSchema,
|
||||
params: v.object({
|
||||
text: v.fallback(v.nullable(v.string()), null),
|
||||
poll: v.fallback(v.nullable(v.object({
|
||||
options: v.array(v.string()),
|
||||
expires_in: v.pipe(v.unknown(), v.transform(String)),
|
||||
multiple: v.fallback(v.optional(v.boolean()), undefined),
|
||||
hide_totals: v.fallback(v.optional(v.boolean()), undefined),
|
||||
})), null),
|
||||
media_ids: v.fallback(v.nullable(v.string()), null),
|
||||
sensitive: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Boolean))), null),
|
||||
spoiler_text: v.fallback(v.nullable(v.string()), null),
|
||||
visibility: v.fallback(v.string(), 'public'),
|
||||
in_reply_to_id: v.fallback(v.nullable(v.string()), null),
|
||||
language: v.fallback(v.nullable(v.string()), null),
|
||||
application_id: v.fallback(v.nullable(v.pipe(v.number(), v.integer())), null),
|
||||
scheduled_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
idempotency: v.fallback(v.nullable(v.string()), null),
|
||||
with_rate_limit: v.fallback(v.boolean(), false),
|
||||
|
||||
expires_in: z.number().nullable().catch(null),
|
||||
expires_in: v.fallback(v.nullable(v.number()), null),
|
||||
}),
|
||||
media_attachments: filteredArray(mediaAttachmentSchema),
|
||||
});
|
||||
|
||||
type ScheduledStatus = z.infer<typeof scheduledStatusSchema>;
|
||||
type ScheduledStatus = v.InferOutput<typeof scheduledStatusSchema>;
|
||||
|
||||
export { scheduledStatusSchema, type ScheduledStatus };
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { datetimeSchema } from './utils';
|
||||
|
||||
const scrobbleSchema = z.preprocess((scrobble: any) => scrobble ? {
|
||||
external_link: scrobble.externalLink,
|
||||
...scrobble,
|
||||
} : null, z.object({
|
||||
id: z.coerce.string(),
|
||||
account: accountSchema,
|
||||
created_at: z.string().datetime({ offset: true }),
|
||||
title: z.string(),
|
||||
artist: z.string().catch(''),
|
||||
album: z.string().catch(''),
|
||||
external_link: z.string().nullable().catch(null),
|
||||
length: z.number().nullable().catch(null),
|
||||
}));
|
||||
const scrobbleSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((scrobble: any) => scrobble ? {
|
||||
external_link: scrobble.externalLink,
|
||||
...scrobble,
|
||||
} : null),
|
||||
v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
account: accountSchema,
|
||||
created_at: datetimeSchema,
|
||||
title: v.string(),
|
||||
artist: v.fallback(v.string(), ''),
|
||||
album: v.fallback(v.string(), ''),
|
||||
external_link: v.fallback(v.nullable(v.string()), null),
|
||||
length: v.fallback(v.nullable(v.number()), null),
|
||||
}),
|
||||
);
|
||||
|
||||
type Scrobble = z.infer<typeof scrobbleSchema>;
|
||||
type Scrobble = v.InferOutput<typeof scrobbleSchema>;
|
||||
|
||||
export { scrobbleSchema, type Scrobble };
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { filteredArray } from './utils';
|
||||
|
||||
import { accountSchema, groupSchema, statusSchema, tagSchema } from '.';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Search} */
|
||||
const searchSchema = z.object({
|
||||
const searchSchema = v.object({
|
||||
accounts: filteredArray(accountSchema),
|
||||
statuses: filteredArray(statusSchema),
|
||||
hashtags: filteredArray(tagSchema),
|
||||
groups: filteredArray(groupSchema),
|
||||
});
|
||||
|
||||
type Search = z.infer<typeof searchSchema>;
|
||||
type Search = v.InferOutput<typeof searchSchema>;
|
||||
|
||||
export { searchSchema, type Search };
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { mediaAttachmentSchema } from './media-attachment';
|
||||
import { dateSchema, filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/StatusEdit/} */
|
||||
const statusEditSchema = z.object({
|
||||
content: z.string().catch(''),
|
||||
spoiler_text: z.string().catch(''),
|
||||
sensitive: z.coerce.boolean(),
|
||||
created_at: dateSchema,
|
||||
const statusEditSchema = v.object({
|
||||
content: v.fallback(v.string(), ''),
|
||||
spoiler_text: v.fallback(v.string(), ''),
|
||||
sensitive: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
account: accountSchema,
|
||||
poll: z.object({
|
||||
options: z.array(z.object({
|
||||
title: z.string(),
|
||||
poll: v.fallback(v.nullable(v.object({
|
||||
options: v.array(v.object({
|
||||
title: v.string(),
|
||||
})),
|
||||
}).nullable().catch(null),
|
||||
})), null),
|
||||
media_attachments: filteredArray(mediaAttachmentSchema),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
});
|
||||
|
||||
type StatusEdit = z.infer<typeof statusEditSchema>;
|
||||
type StatusEdit = v.InferOutput<typeof statusEditSchema>;
|
||||
|
||||
export { statusEditSchema, type StatusEdit };
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { locationSchema } from './location';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/StatusSource/} */
|
||||
const statusSourceSchema = z.object({
|
||||
id: z.string(),
|
||||
text: z.string().catch(''),
|
||||
spoiler_text: z.string().catch(''),
|
||||
const statusSourceSchema = v.object({
|
||||
id: v.string(),
|
||||
text: v.fallback(v.string(), ''),
|
||||
spoiler_text: v.fallback(v.string(), ''),
|
||||
|
||||
content_type: z.string().catch('text/plain'),
|
||||
location: locationSchema.nullable().catch(null),
|
||||
content_type: v.fallback(v.string(), 'text/plain'),
|
||||
location: v.fallback(v.nullable(locationSchema), null),
|
||||
|
||||
text_map: z.record(z.string()).nullable().catch(null),
|
||||
spoiler_text_map: z.record(z.string()).nullable().catch(null),
|
||||
text_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
spoiler_text_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
});
|
||||
|
||||
type StatusSource = z.infer<typeof statusSourceSchema>;
|
||||
type StatusSource = v.InferOutput<typeof statusSourceSchema>;
|
||||
|
||||
export { statusSourceSchema, type StatusSource };
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import pick from 'lodash.pick';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { type Account, accountSchema } from './account';
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
@ -13,91 +13,91 @@ import { pollSchema } from './poll';
|
||||
import { previewCardSchema } from './preview-card';
|
||||
import { tagSchema } from './tag';
|
||||
import { translationSchema } from './translation';
|
||||
import { dateSchema, filteredArray } from './utils';
|
||||
import { datetimeSchema, filteredArray } from './utils';
|
||||
|
||||
const statusEventSchema = z.object({
|
||||
name: z.string().catch(''),
|
||||
start_time: z.string().datetime().nullable().catch(null),
|
||||
end_time: z.string().datetime().nullable().catch(null),
|
||||
join_mode: z.enum(['free', 'restricted', 'invite']).nullable().catch(null),
|
||||
participants_count: z.number().catch(0),
|
||||
location: z.object({
|
||||
name: z.string().catch(''),
|
||||
url: z.string().url().catch(''),
|
||||
latitude: z.number().catch(0),
|
||||
longitude: z.number().catch(0),
|
||||
street: z.string().catch(''),
|
||||
postal_code: z.string().catch(''),
|
||||
locality: z.string().catch(''),
|
||||
region: z.string().catch(''),
|
||||
country: z.string().catch(''),
|
||||
}).nullable().catch(null),
|
||||
join_state: z.enum(['pending', 'reject', 'accept']).nullable().catch(null),
|
||||
const statusEventSchema = v.object({
|
||||
name: v.fallback(v.string(), ''),
|
||||
start_time: v.fallback(v.nullable(datetimeSchema), null),
|
||||
end_time: v.fallback(v.nullable(datetimeSchema), null),
|
||||
join_mode: v.fallback(v.nullable(v.picklist(['free', 'restricted', 'invite'])), null),
|
||||
participants_count: v.fallback(v.number(), 0),
|
||||
location: v.fallback(v.nullable(v.object({
|
||||
name: v.fallback(v.string(), ''),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
latitude: v.fallback(v.number(), 0),
|
||||
longitude: v.fallback(v.number(), 0),
|
||||
street: v.fallback(v.string(), ''),
|
||||
postal_code: v.fallback(v.string(), ''),
|
||||
locality: v.fallback(v.string(), ''),
|
||||
region: v.fallback(v.string(), ''),
|
||||
country: v.fallback(v.string(), ''),
|
||||
})), null),
|
||||
join_state: v.fallback(v.nullable(v.picklist(['pending', 'reject', 'accept'])), null),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Status/} */
|
||||
const baseStatusSchema = z.object({
|
||||
id: z.string(),
|
||||
uri: z.string().url().catch(''),
|
||||
created_at: dateSchema,
|
||||
const baseStatusSchema = v.object({
|
||||
id: v.string(),
|
||||
uri: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
created_at: v.fallback(datetimeSchema, new Date().toISOString()),
|
||||
account: accountSchema,
|
||||
content: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
visibility: z.string().catch('public'),
|
||||
sensitive: z.coerce.boolean(),
|
||||
spoiler_text: z.string().catch(''),
|
||||
content: v.fallback(v.pipe(v.string(), v.transform((note => note === '<p></p>' ? '' : note))), ''),
|
||||
visibility: v.fallback(v.string(), 'public'),
|
||||
sensitive: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
spoiler_text: v.fallback(v.string(), ''),
|
||||
media_attachments: filteredArray(mediaAttachmentSchema),
|
||||
application: z.object({
|
||||
name: z.string(),
|
||||
website: z.string().url().nullable().catch(null),
|
||||
}).nullable().catch(null),
|
||||
application: v.fallback(v.nullable(v.object({
|
||||
name: v.string(),
|
||||
website: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null),
|
||||
})), null),
|
||||
mentions: filteredArray(mentionSchema),
|
||||
tags: filteredArray(tagSchema),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
reblogs_count: z.number().catch(0),
|
||||
favourites_count: z.number().catch(0),
|
||||
replies_count: z.number().catch(0),
|
||||
url: z.string().url().catch(''),
|
||||
in_reply_to_id: z.string().nullable().catch(null),
|
||||
in_reply_to_account_id: z.string().nullable().catch(null),
|
||||
poll: pollSchema.nullable().catch(null),
|
||||
card: previewCardSchema.nullable().catch(null),
|
||||
language: z.string().nullable().catch(null),
|
||||
text: z.string().nullable().catch(null),
|
||||
edited_at: z.string().datetime().nullable().catch(null),
|
||||
favourited: z.coerce.boolean(),
|
||||
reblogged: z.coerce.boolean(),
|
||||
muted: z.coerce.boolean(),
|
||||
bookmarked: z.coerce.boolean(),
|
||||
pinned: z.coerce.boolean(),
|
||||
reblogs_count: v.fallback(v.number(), 0),
|
||||
favourites_count: v.fallback(v.number(), 0),
|
||||
replies_count: v.fallback(v.number(), 0),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
in_reply_to_id: v.fallback(v.nullable(v.string()), null),
|
||||
in_reply_to_account_id: v.fallback(v.nullable(v.string()), null),
|
||||
poll: v.fallback(v.nullable(pollSchema), null),
|
||||
card: v.fallback(v.nullable(previewCardSchema), null),
|
||||
language: v.fallback(v.nullable(v.string()), null),
|
||||
text: v.fallback(v.nullable(v.string()), null),
|
||||
edited_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
favourited: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
reblogged: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
muted: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
bookmarked: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
pinned: v.pipe(v.unknown(), v.transform(Boolean)),
|
||||
filtered: filteredArray(filterResultSchema),
|
||||
approval_status: z.enum(['pending', 'approval', 'rejected']).nullable().catch(null),
|
||||
group: groupSchema.nullable().catch(null),
|
||||
scheduled_at: z.null().catch(null),
|
||||
approval_status: v.fallback(v.nullable(v.picklist(['pending', 'approval', 'rejected'])), null),
|
||||
group: v.fallback(v.nullable(groupSchema), null),
|
||||
scheduled_at: v.fallback(v.null(), null),
|
||||
|
||||
quote_id: z.string().nullable().catch(null),
|
||||
local: z.boolean().optional().catch(undefined),
|
||||
conversation_id: z.string().optional().catch(undefined),
|
||||
direct_conversation_id: z.string().optional().catch(undefined),
|
||||
in_reply_to_account_acct: z.string().optional().catch(undefined),
|
||||
expires_at: z.string().datetime({ offset: true }).optional().catch(undefined),
|
||||
thread_muted: z.boolean().optional().catch(undefined),
|
||||
quote_id: v.fallback(v.nullable(v.string()), null),
|
||||
local: v.fallback(v.optional(v.boolean()), undefined),
|
||||
conversation_id: v.fallback(v.optional(v.string()), undefined),
|
||||
direct_conversation_id: v.fallback(v.optional(v.string()), undefined),
|
||||
in_reply_to_account_acct: v.fallback(v.optional(v.string()), undefined),
|
||||
expires_at: v.fallback(v.optional(datetimeSchema), undefined),
|
||||
thread_muted: v.fallback(v.optional(v.boolean()), undefined),
|
||||
emoji_reactions: filteredArray(emojiReactionSchema),
|
||||
parent_visible: z.boolean().optional().catch(undefined),
|
||||
pinned_at: z.string().datetime({ offset: true }).nullable().catch(null),
|
||||
quote_visible: z.boolean().optional().catch(undefined),
|
||||
quote_url: z.string().optional().catch(undefined),
|
||||
quotes_count: z.number().catch(0),
|
||||
bookmark_folder: z.string().nullable().catch(null),
|
||||
parent_visible: v.fallback(v.optional(v.boolean()), undefined),
|
||||
pinned_at: v.fallback(v.nullable(datetimeSchema), null),
|
||||
quote_visible: v.fallback(v.optional(v.boolean()), undefined),
|
||||
quote_url: v.fallback(v.optional(v.string()), undefined),
|
||||
quotes_count: v.fallback(v.number(), 0),
|
||||
bookmark_folder: v.fallback(v.nullable(v.string()), null),
|
||||
|
||||
event: statusEventSchema.nullable().catch(null),
|
||||
translation: translationSchema.nullable().or(z.literal(false)).catch(null),
|
||||
event: v.fallback(v.nullable(statusEventSchema), null),
|
||||
translation: v.fallback(v.union([v.nullable(translationSchema), v.literal(false)]), null),
|
||||
|
||||
content_map: z.record(z.string()).nullable().catch(null),
|
||||
text_map: z.record(z.string()).nullable().catch(null),
|
||||
spoiler_text_map: z.record(z.string()).nullable().catch(null),
|
||||
content_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
text_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
spoiler_text_map: v.fallback(v.nullable(v.record(v.string(), v.string())), null),
|
||||
|
||||
dislikes_count: z.number().catch(0),
|
||||
disliked: z.coerce.boolean().catch(false),
|
||||
dislikes_count: v.fallback(v.number(), 0),
|
||||
disliked: v.fallback(v.pipe(v.unknown(), v.transform(Boolean)), false),
|
||||
|
||||
interaction_policy: interactionPolicySchema,
|
||||
});
|
||||
@ -134,26 +134,28 @@ const preprocess = (status: any) => {
|
||||
return status;
|
||||
};
|
||||
|
||||
const statusSchema: z.ZodType<Status> = z.preprocess(preprocess, baseStatusSchema.extend({
|
||||
reblog: z.lazy(() => statusSchema).nullable().catch(null),
|
||||
const statusSchema: v.BaseSchema<any, Status, v.BaseIssue<unknown>> = v.pipe(v.any(), v.transform(preprocess), v.object({
|
||||
...baseStatusSchema.entries,
|
||||
reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
|
||||
|
||||
quote: z.lazy(() => statusSchema).nullable().catch(null),
|
||||
quote: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
|
||||
})) as any;
|
||||
|
||||
const statusWithoutAccountSchema = z.preprocess(preprocess, baseStatusSchema.omit({ account: true }).extend({
|
||||
account: accountSchema.nullable().catch(null),
|
||||
reblog: z.lazy(() => statusSchema).nullable().catch(null),
|
||||
const statusWithoutAccountSchema = v.pipe(v.any(), v.transform(preprocess), v.object({
|
||||
...(v.omit(baseStatusSchema, ['account']).entries),
|
||||
account: v.fallback(v.nullable(accountSchema), null),
|
||||
reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
|
||||
|
||||
quote: z.lazy(() => statusSchema).nullable().catch(null),
|
||||
quote: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
|
||||
}));
|
||||
|
||||
type StatusWithoutAccount = Omit<z.infer<typeof baseStatusSchema>, 'account'> & {
|
||||
type StatusWithoutAccount = Omit<v.InferOutput<typeof baseStatusSchema>, 'account'> & {
|
||||
account: Account | null;
|
||||
reblog: Status | null;
|
||||
quote: Status | null;
|
||||
}
|
||||
|
||||
type Status = z.infer<typeof baseStatusSchema> & {
|
||||
type Status = v.InferOutput<typeof baseStatusSchema> & {
|
||||
reblog: Status | null;
|
||||
quote: Status | null;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { announcementSchema } from './announcement';
|
||||
import { announcementReactionSchema } from './announcement-reaction';
|
||||
@ -8,102 +8,117 @@ import { markersSchema } from './marker';
|
||||
import { notificationSchema } from './notification';
|
||||
import { statusSchema } from './status';
|
||||
|
||||
const followRelationshipUpdateSchema = z.object({
|
||||
state: z.enum(['follow_pending', 'follow_accept', 'follow_reject']),
|
||||
follower: z.object({
|
||||
id: z.string(),
|
||||
follower_count: z.number().nullable().catch(null),
|
||||
following_count: z.number().nullable().catch(null),
|
||||
const followRelationshipUpdateSchema = v.object({
|
||||
state: v.picklist(['follow_pending', 'follow_accept', 'follow_reject']),
|
||||
follower: v.object({
|
||||
id: v.string(),
|
||||
follower_count: v.fallback(v.nullable(v.number()), null),
|
||||
following_count: v.fallback(v.nullable(v.number()), null),
|
||||
}),
|
||||
following: z.object({
|
||||
id: z.string(),
|
||||
follower_count: z.number().nullable().catch(null),
|
||||
following_count: z.number().nullable().catch(null),
|
||||
following: v.object({
|
||||
id: v.string(),
|
||||
follower_count: v.fallback(v.nullable(v.number()), null),
|
||||
following_count: v.fallback(v.nullable(v.number()), null),
|
||||
}),
|
||||
});
|
||||
|
||||
type FollowRelationshipUpdate = z.infer<typeof followRelationshipUpdateSchema>;
|
||||
type FollowRelationshipUpdate = v.InferOutput<typeof followRelationshipUpdateSchema>;
|
||||
|
||||
const baseStreamingEventSchema = z.object({
|
||||
stream: z.array(z.string()).catch([]),
|
||||
const baseStreamingEventSchema = v.object({
|
||||
stream: v.fallback(v.array(v.string()), []),
|
||||
});
|
||||
|
||||
const statusStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.enum(['update', 'status.update']),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), statusSchema),
|
||||
const statusStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.picklist(['update', 'status.update']),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), statusSchema),
|
||||
});
|
||||
|
||||
const stringStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.enum(['delete', 'announcement.delete']),
|
||||
payload: z.string(),
|
||||
const stringStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.picklist(['delete', 'announcement.delete']),
|
||||
payload: v.string(),
|
||||
});
|
||||
|
||||
const notificationStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('notification'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), notificationSchema),
|
||||
const notificationStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('notification'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), notificationSchema),
|
||||
});
|
||||
|
||||
const emptyStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('filters_changed'),
|
||||
const emptyStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('filters_changed'),
|
||||
});
|
||||
|
||||
const conversationStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('conversation'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), conversationSchema),
|
||||
const conversationStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('conversation'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), conversationSchema),
|
||||
});
|
||||
|
||||
const announcementStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('announcement'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), announcementSchema),
|
||||
const announcementStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('announcement'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), announcementSchema),
|
||||
});
|
||||
|
||||
const announcementReactionStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('announcement.reaction'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), announcementReactionSchema),
|
||||
const announcementReactionStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('announcement.reaction'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), announcementReactionSchema),
|
||||
});
|
||||
|
||||
const chatUpdateStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('chat_update'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), chatSchema),
|
||||
const chatUpdateStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('chat_update'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), chatSchema),
|
||||
});
|
||||
|
||||
const followRelationshipsUpdateStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('follow_relationships_update'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), followRelationshipUpdateSchema),
|
||||
const followRelationshipsUpdateStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('follow_relationships_update'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), followRelationshipUpdateSchema),
|
||||
});
|
||||
|
||||
const respondStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('respond'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), z.object({
|
||||
type: z.string(),
|
||||
result: z.enum(['success', 'ignored', 'error']),
|
||||
const respondStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('respond'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), v.object({
|
||||
type: v.string(),
|
||||
result: v.picklist(['success', 'ignored', 'error']),
|
||||
})),
|
||||
});
|
||||
|
||||
const markerStreamingEventSchema = baseStreamingEventSchema.extend({
|
||||
event: z.literal('marker'),
|
||||
payload: z.preprocess((payload: any) => JSON.parse(payload), markersSchema),
|
||||
const markerStreamingEventSchema = v.object({
|
||||
...baseStreamingEventSchema.entries,
|
||||
event: v.literal('marker'),
|
||||
payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), markersSchema),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/methods/streaming/#events} */
|
||||
const streamingEventSchema: z.ZodType<StreamingEvent> = z.preprocess((event: any) => ({
|
||||
...event,
|
||||
event: event.event?.replace(/^pleroma:/, ''),
|
||||
}), z.discriminatedUnion('event', [
|
||||
statusStreamingEventSchema,
|
||||
stringStreamingEventSchema,
|
||||
notificationStreamingEventSchema,
|
||||
emptyStreamingEventSchema,
|
||||
conversationStreamingEventSchema,
|
||||
announcementStreamingEventSchema,
|
||||
announcementReactionStreamingEventSchema,
|
||||
chatUpdateStreamingEventSchema,
|
||||
followRelationshipsUpdateStreamingEventSchema,
|
||||
respondStreamingEventSchema,
|
||||
markerStreamingEventSchema,
|
||||
])) as any;
|
||||
const streamingEventSchema: v.BaseSchema<any, StreamingEvent, v.BaseIssue<unknown>> = v.pipe(
|
||||
v.any(),
|
||||
v.transform((event: any) => ({
|
||||
...event,
|
||||
event: event.event?.replace(/^pleroma:/, ''),
|
||||
})),
|
||||
v.variant('event', [
|
||||
statusStreamingEventSchema,
|
||||
stringStreamingEventSchema,
|
||||
notificationStreamingEventSchema,
|
||||
emptyStreamingEventSchema,
|
||||
conversationStreamingEventSchema,
|
||||
announcementStreamingEventSchema,
|
||||
announcementReactionStreamingEventSchema,
|
||||
chatUpdateStreamingEventSchema,
|
||||
followRelationshipsUpdateStreamingEventSchema,
|
||||
respondStreamingEventSchema,
|
||||
markerStreamingEventSchema,
|
||||
]),
|
||||
) as any;
|
||||
|
||||
type StreamingEvent = z.infer<
|
||||
type StreamingEvent = v.InferOutput<
|
||||
| typeof statusStreamingEventSchema
|
||||
| typeof stringStreamingEventSchema
|
||||
| typeof notificationStreamingEventSchema
|
||||
|
||||
@ -1,40 +1,44 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { accountSchema } from './account';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Suggestion} */
|
||||
const suggestionSchema = z.preprocess((suggestion: any) => {
|
||||
const suggestionSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((suggestion: any) => {
|
||||
/**
|
||||
* Support `/api/v1/suggestions`
|
||||
* @see {@link https://docs.joinmastodon.org/methods/suggestions/#v1}
|
||||
*/
|
||||
if (!suggestion) return null;
|
||||
if (!suggestion) return null;
|
||||
|
||||
if (suggestion?.acct) return {
|
||||
source: 'staff',
|
||||
sources: ['featured'],
|
||||
account: suggestion,
|
||||
};
|
||||
if (suggestion?.acct) return {
|
||||
source: 'staff',
|
||||
sources: ['featured'],
|
||||
account: suggestion,
|
||||
};
|
||||
|
||||
if (!suggestion.sources) {
|
||||
suggestion.sources = [];
|
||||
switch (suggestion.source) {
|
||||
case 'staff':
|
||||
suggestion.sources.push('staff');
|
||||
break;
|
||||
case 'global':
|
||||
suggestion.sources.push('most_interactions');
|
||||
break;
|
||||
if (!suggestion.sources) {
|
||||
suggestion.sources = [];
|
||||
switch (suggestion.source) {
|
||||
case 'staff':
|
||||
suggestion.sources.push('staff');
|
||||
break;
|
||||
case 'global':
|
||||
suggestion.sources.push('most_interactions');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestion;
|
||||
}, z.object({
|
||||
source: z.string().nullable().catch(null),
|
||||
sources: z.array(z.string()).catch([]),
|
||||
account: accountSchema,
|
||||
}));
|
||||
return suggestion;
|
||||
}),
|
||||
v.object({
|
||||
source: v.fallback(v.nullable(v.string()), null),
|
||||
sources: v.fallback(v.array(v.string()), []),
|
||||
account: accountSchema,
|
||||
}),
|
||||
);
|
||||
|
||||
type Suggestion = z.infer<typeof suggestionSchema>;
|
||||
type Suggestion = v.InferOutput<typeof suggestionSchema>;
|
||||
|
||||
export { suggestionSchema, type Suggestion };
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const historySchema = z.object({
|
||||
day: z.coerce.number(),
|
||||
accounts: z.coerce.number(),
|
||||
uses: z.coerce.number(),
|
||||
});
|
||||
const historySchema = v.array(v.object({
|
||||
day: v.pipe(v.unknown(), v.transform(Number)),
|
||||
accounts: v.pipe(v.unknown(), v.transform(Number)),
|
||||
uses: v.pipe(v.unknown(), v.transform(Number)),
|
||||
}));
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/tag} */
|
||||
const tagSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
url: z.string().url().catch(''),
|
||||
history: z.array(historySchema).nullable().catch(null),
|
||||
following: z.boolean().optional().catch(undefined),
|
||||
const tagSchema = v.object({
|
||||
name: v.pipe(v.string(), v.minLength(1)),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
history: v.fallback(v.nullable(historySchema), null),
|
||||
following: v.fallback(v.optional(v.boolean()), undefined),
|
||||
});
|
||||
|
||||
type Tag = z.infer<typeof tagSchema>;
|
||||
type Tag = v.InferOutput<typeof tagSchema>;
|
||||
|
||||
export { historySchema, tagSchema, type Tag };
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Token/} */
|
||||
const tokenSchema = z.object({
|
||||
access_token: z.string(),
|
||||
token_type: z.string(),
|
||||
scope: z.string(),
|
||||
created_at: z.number().optional().catch(undefined),
|
||||
const tokenSchema = v.object({
|
||||
access_token: v.string(),
|
||||
token_type: v.string(),
|
||||
scope: v.string(),
|
||||
created_at: v.fallback(v.optional(v.number()), undefined),
|
||||
|
||||
id: z.number().optional().catch(undefined),
|
||||
refresh_token: z.string().optional().catch(undefined),
|
||||
expires_in: z.number().optional().catch(undefined),
|
||||
me: z.string().optional().catch(undefined),
|
||||
id: v.fallback(v.optional(v.number()), undefined),
|
||||
refresh_token: v.fallback(v.optional(v.string()), undefined),
|
||||
expires_in: v.fallback(v.optional(v.number()), undefined),
|
||||
me: v.fallback(v.optional(v.string()), undefined),
|
||||
});
|
||||
|
||||
type Token = z.infer<typeof tokenSchema>;
|
||||
type Token = v.InferOutput<typeof tokenSchema>;
|
||||
|
||||
export { tokenSchema, type Token };
|
||||
|
||||
@ -1,42 +1,46 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { filteredArray } from './utils';
|
||||
|
||||
const translationPollSchema = z.object({
|
||||
id: z.string(),
|
||||
options: z.array(z.object({
|
||||
title: z.string(),
|
||||
const translationPollSchema = v.object({
|
||||
id: v.string(),
|
||||
options: v.array(v.object({
|
||||
title: v.string(),
|
||||
})),
|
||||
});
|
||||
|
||||
const translationMediaAttachment = z.object({
|
||||
id: z.string(),
|
||||
description: z.string().catch(''),
|
||||
const translationMediaAttachment = v.object({
|
||||
id: v.string(),
|
||||
description: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Translation/} */
|
||||
const translationSchema = z.preprocess((translation: any) => {
|
||||
const translationSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((translation: any) => {
|
||||
/**
|
||||
* handle Akkoma
|
||||
* @see {@link https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/web/mastodon_api/controllers/status_controller.ex#L504}
|
||||
*/
|
||||
if (translation?.text) return {
|
||||
content: translation.text,
|
||||
detected_source_language: translation.detected_language,
|
||||
provider: '',
|
||||
};
|
||||
if (translation?.text) return {
|
||||
content: translation.text,
|
||||
detected_source_language: translation.detected_language,
|
||||
provider: '',
|
||||
};
|
||||
|
||||
return translation;
|
||||
}, z.object({
|
||||
id: z.string().nullable().catch(null),
|
||||
content: z.string().catch(''),
|
||||
spoiler_text: z.string().catch(''),
|
||||
poll: translationPollSchema.optional().catch(undefined),
|
||||
media_attachments: filteredArray(translationMediaAttachment),
|
||||
detected_source_language: z.string(),
|
||||
provider: z.string(),
|
||||
}));
|
||||
return translation;
|
||||
}),
|
||||
v.object({
|
||||
id: v.fallback(v.nullable(v.string()), null),
|
||||
content: v.fallback(v.string(), ''),
|
||||
spoiler_text: v.fallback(v.string(), ''),
|
||||
poll: v.fallback(v.optional(translationPollSchema), undefined),
|
||||
media_attachments: filteredArray(translationMediaAttachment),
|
||||
detected_source_language: v.string(),
|
||||
provider: v.string(),
|
||||
}),
|
||||
);
|
||||
|
||||
type Translation = z.infer<typeof translationSchema>;
|
||||
type Translation = v.InferOutput<typeof translationSchema>;
|
||||
|
||||
export { translationSchema, type Translation };
|
||||
|
||||
@ -1,29 +1,33 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { blurhashSchema } from './media-attachment';
|
||||
import { historySchema } from './tag';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/PreviewCard/#trends-link} */
|
||||
const trendsLinkSchema = z.preprocess((link: any) => ({ ...link, id: link.url }), z.object({
|
||||
id: z.string().catch(''),
|
||||
url: z.string().url().catch(''),
|
||||
title: z.string().catch(''),
|
||||
description: z.string().catch(''),
|
||||
type: z.enum(['link', 'photo', 'video', 'rich']).catch('link'),
|
||||
author_name: z.string().catch(''),
|
||||
author_url: z.string().catch(''),
|
||||
provider_name: z.string().catch(''),
|
||||
provider_url: z.string().catch(''),
|
||||
html: z.string().catch(''),
|
||||
width: z.number().nullable().catch(null),
|
||||
height: z.number().nullable().catch(null),
|
||||
image: z.string().nullable().catch(null),
|
||||
image_description: z.string().nullable().catch(null),
|
||||
embed_url: z.string().catch(''),
|
||||
blurhash: blurhashSchema.nullable().catch(null),
|
||||
history: z.array(historySchema).nullable().catch(null),
|
||||
}));
|
||||
const trendsLinkSchema = v.pipe(
|
||||
v.any(),
|
||||
v.transform((link: any) => ({ ...link, id: link.url })),
|
||||
v.object({
|
||||
id: v.fallback(v.string(), ''),
|
||||
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
||||
title: v.fallback(v.string(), ''),
|
||||
description: v.fallback(v.string(), ''),
|
||||
type: v.fallback(v.picklist(['link', 'photo', 'video', 'rich']), 'link'),
|
||||
author_name: v.fallback(v.string(), ''),
|
||||
author_url: v.fallback(v.string(), ''),
|
||||
provider_name: v.fallback(v.string(), ''),
|
||||
provider_url: v.fallback(v.string(), ''),
|
||||
html: v.fallback(v.string(), ''),
|
||||
width: v.fallback(v.nullable(v.number()), null),
|
||||
height: v.fallback(v.nullable(v.number()), null),
|
||||
image: v.fallback(v.nullable(v.string()), null),
|
||||
image_description: v.fallback(v.nullable(v.string()), null),
|
||||
embed_url: v.fallback(v.string(), ''),
|
||||
blurhash: v.fallback(v.nullable(blurhashSchema), null),
|
||||
history: v.fallback(v.nullable(historySchema), null),
|
||||
}),
|
||||
);
|
||||
|
||||
type TrendsLink = z.infer<typeof trendsLinkSchema>;
|
||||
type TrendsLink = v.InferOutput<typeof trendsLinkSchema>;
|
||||
|
||||
export { trendsLinkSchema, type TrendsLink };
|
||||
|
||||
@ -1,26 +1,38 @@
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** Validate to Mastodon's date format, or use the current date. */
|
||||
const dateSchema = z.string().datetime({ offset: true }).catch(new Date().toUTCString());
|
||||
const datetimeSchema = v.pipe(
|
||||
v.string(),
|
||||
// Adapted from Zod
|
||||
// https://github.com/colinhacks/zod/blob/main/src/types.ts#L619
|
||||
// at least it's not chatgpt
|
||||
v.regex(/^\d{4}-\d{2}-\d{2}T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(([+-]\d{2}:?\d{2})|(Z)?)$/),
|
||||
);
|
||||
|
||||
/** Validates individual items in an array, dropping any that aren't valid. */
|
||||
const filteredArray = <T extends z.ZodTypeAny>(schema: T) =>
|
||||
z.any().array().catch([])
|
||||
.transform((arr) => (
|
||||
const filteredArray = <T>(schema: v.BaseSchema<any, T, v.BaseIssue<unknown>>) =>
|
||||
v.pipe(
|
||||
v.fallback(v.array(v.any()), []),
|
||||
v.transform((arr) => (
|
||||
arr.map((item) => {
|
||||
const parsed = schema.safeParse(item);
|
||||
return parsed.success ? parsed.data : undefined;
|
||||
}).filter((item): item is z.infer<T> => Boolean(item))
|
||||
));
|
||||
const parsed = v.safeParse(schema, item);
|
||||
return parsed.success ? parsed.output : undefined;
|
||||
}).filter((item): item is T => Boolean(item))
|
||||
)),
|
||||
);
|
||||
|
||||
/** Validates the string as an emoji. */
|
||||
const emojiSchema = z.string().refine((v) => /\p{Extended_Pictographic}|[\u{1F1E6}-\u{1F1FF}]{2}/u.test(v));
|
||||
const emojiSchema = v.pipe(v.string(), v.emoji());
|
||||
|
||||
/** MIME schema, eg `image/png`. */
|
||||
const mimeSchema = z.string().regex(/^\w+\/[-+.\w]+$/);
|
||||
const mimeSchema = v.pipe(v.string(), v.regex(/^\w+\/[-+.\w]+$/));
|
||||
|
||||
/** zod schema to force the value into an object, if it isn't already. */
|
||||
const coerceObject = <T extends z.ZodRawShape>(shape: T) =>
|
||||
z.object({}).passthrough().catch({}).pipe(z.object(shape));
|
||||
/** valibot schema to force the value into an object, if it isn't already. */
|
||||
const coerceObject = <T extends v.ObjectEntries>(shape: T) =>
|
||||
v.pipe(
|
||||
v.any(),
|
||||
v.transform((input) => typeof input === 'object' ? input : {}),
|
||||
v.object(shape),
|
||||
);
|
||||
|
||||
export { filteredArray, emojiSchema, dateSchema, mimeSchema, coerceObject };
|
||||
export { filteredArray, emojiSchema, datetimeSchema, mimeSchema, coerceObject };
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/WebPushSubscription/} */
|
||||
const webPushSubscriptionSchema = z.object({
|
||||
id: z.coerce.string(),
|
||||
endpoint: z.string(),
|
||||
alerts: z.record(z.boolean()),
|
||||
server_key: z.string(),
|
||||
const webPushSubscriptionSchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
endpoint: v.string(),
|
||||
alerts: v.record(v.string(), v.boolean()),
|
||||
server_key: v.string(),
|
||||
});
|
||||
|
||||
type WebPushSubscription = z.infer<typeof webPushSubscriptionSchema>;
|
||||
type WebPushSubscription = v.InferOutput<typeof webPushSubscriptionSchema>;
|
||||
|
||||
export { webPushSubscriptionSchema, type WebPushSubscription };
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pl-api",
|
||||
"version": "0.0.42",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"homepage": "https://github.com/mkljczk/pl-fe/tree/fork/packages/pl-api",
|
||||
"repository": {
|
||||
@ -42,7 +42,7 @@
|
||||
"object-to-formdata": "^4.5.1",
|
||||
"query-string": "^9.1.0",
|
||||
"semver": "^7.6.3",
|
||||
"zod": "^3.23.8"
|
||||
"valibot": "^0.42.1"
|
||||
},
|
||||
"module": "./dist/main.es.js",
|
||||
"types": "dist/main.d.ts",
|
||||
|
||||
@ -3,6 +3,8 @@ import { resolve } from 'path';
|
||||
import { defineConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
import pkg from './package.json';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [dts({ include: ['lib'], insertTypesEntry: true })],
|
||||
build: {
|
||||
@ -15,5 +17,8 @@ export default defineConfig({
|
||||
},
|
||||
target: 'esnext',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
external: Object.keys(pkg.dependencies),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -2551,6 +2551,11 @@ uri-js@^4.2.2, uri-js@^4.4.1:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
valibot@^0.42.1:
|
||||
version "0.42.1"
|
||||
resolved "https://registry.yarnpkg.com/valibot/-/valibot-0.42.1.tgz#a31183d8e9d7552f98e22ca0977172cab8815188"
|
||||
integrity sha512-3keXV29Ar5b//Hqi4MbSdV7lfVp6zuYLZuA9V1PvQUsXqogr+u5lvLPLk3A4f74VUXDnf/JfWMN6sB+koJ/FFw==
|
||||
|
||||
vite-plugin-dts@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-4.2.3.tgz#e0d9616eb574700111dbd19ae98e166541433263"
|
||||
@ -2630,8 +2635,3 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod@^3.23.8:
|
||||
version "3.23.8"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
|
||||
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
|
||||
|
||||
@ -26,7 +26,7 @@ Changes made since the project forked from Soapbox in April 2024.
|
||||
- You can write posts with multiple language versions, when supported by backend.
|
||||
- Language detection is done client-side for composed posts, utilizing `fasttext.wasm.js`.
|
||||
- Draft posts. They are stored locally only and work with any backend.
|
||||
|
||||
- New visibility scopes are supported – local-only and list-only for Pleroma. Local-only is a separate switch on GoToSocial.
|
||||
|
||||
**Features:**
|
||||
- The most recent scrobble is displayed on user profile/card.
|
||||
@ -34,7 +34,7 @@ Changes made since the project forked from Soapbox in April 2024.
|
||||
- You can bite users, if supported by backend.
|
||||
- You can browse Bubble timeline, if supported by backend.
|
||||
- Mastodon displays trending articles on Search page.
|
||||
- Postsa can be addressed to lists of users, on Pleroma.
|
||||
- Posts can be addressed to lists of users, on Pleroma.
|
||||
|
||||
### Changed
|
||||
|
||||
@ -62,13 +62,14 @@ Changes made since the project forked from Soapbox in April 2024.
|
||||
- Updated Lists UI, to match the overall style.
|
||||
- RSS button is displayed in account header for local users, when unauthenticated.
|
||||
- Conversations page is always displayed, even when Chats are supported.
|
||||
- Made it woke.
|
||||
- Emojis are zoomed on hover.
|
||||
|
||||
**Internal:**
|
||||
- Migrated some local stores from Redux to Zustand.
|
||||
|
||||
**Dependencies:**
|
||||
- `@tanstack/react-virtual` is used for list virtualization, instead of `react-virtuoso`.
|
||||
- `@tanstack/react-virtual` is used for list virtualization, instead of `react-virtuoso`. This improves compatibility with Ladybird browser.
|
||||
- Replaced `react-popper` and `react-overlays` with `@floating-ui/react`.
|
||||
- `uuid` package is replaced by the `randomUUID()` method.
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pl-api": "^0.0.42",
|
||||
"pl-api": "^0.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"process": "^0.11.10",
|
||||
"punycode": "^2.1.1",
|
||||
@ -132,7 +132,6 @@
|
||||
"reselect": "^5.1.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sass": "^1.79.4",
|
||||
"semver": "^7.6.3",
|
||||
"stringz": "^2.1.0",
|
||||
"tiny-queue": "^0.2.1",
|
||||
"tslib": "^2.7.0",
|
||||
@ -140,13 +139,13 @@
|
||||
"type-fest": "^4.26.1",
|
||||
"typescript": "^5.6.2",
|
||||
"util": "^0.12.5",
|
||||
"valibot": "^0.42.1",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-compile-time": "^0.2.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-require": "^1.2.14",
|
||||
"vite-plugin-static-copy": "^1.0.6",
|
||||
"wicg-inert": "^3.1.3",
|
||||
"zod": "^3.23.8",
|
||||
"zustand": "^5.0.0-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import { credentialAccountSchema, PlApiClient, type CreateAccountParams, type Token } from 'pl-api';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { createAccount } from 'pl-fe/actions/accounts';
|
||||
import { createApp } from 'pl-fe/actions/apps';
|
||||
@ -155,7 +156,7 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
|
||||
if (error?.response?.status === 403 && error?.response?.json?.id) {
|
||||
// The user is waitlisted
|
||||
const account = error.response.json;
|
||||
const parsedAccount = credentialAccountSchema.parse(error.response.json);
|
||||
const parsedAccount = v.parse(credentialAccountSchema, error.response.json);
|
||||
importEntities({ accounts: [parsedAccount] });
|
||||
dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount });
|
||||
if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount));
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { instanceSchema, PlApiClient, type Instance } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { createApp } from 'pl-fe/actions/apps';
|
||||
import { authLoggedIn, verifyCredentials, switchAccount } from 'pl-fe/actions/auth';
|
||||
@ -24,7 +25,7 @@ const fetchExternalInstance = (baseURL: string) =>
|
||||
if (error.response?.status === 401) {
|
||||
// Authenticated fetch is enabled.
|
||||
// Continue with a limited featureset.
|
||||
return instanceSchema.parse({});
|
||||
return v.parse(instanceSchema, {});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntity } from 'pl-fe/entity-store/hooks';
|
||||
@ -19,7 +19,7 @@ const useRelationship = (accountId: string | undefined, opts: UseRelationshipOpt
|
||||
() => client.accounts.getRelationships([accountId!]),
|
||||
{
|
||||
enabled: enabled && !!accountId,
|
||||
schema: z.any().transform(arr => arr[0]),
|
||||
schema: v.pipe(v.any(), v.transform(arr => arr[0])),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
type AdminCreateAnnouncementParams,
|
||||
type AdminUpdateAnnouncementParams,
|
||||
} from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeAnnouncement, AdminAnnouncement } from 'pl-fe/normalizers';
|
||||
@ -36,7 +37,7 @@ const useAnnouncements = () => {
|
||||
retry: false,
|
||||
onSuccess: (data) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
[...prevResult, adminAnnouncementSchema.parse(data)],
|
||||
[...prevResult, v.parse(adminAnnouncementSchema, data)],
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
@ -50,7 +51,7 @@ const useAnnouncements = () => {
|
||||
retry: false,
|
||||
onSuccess: (data) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
prevResult.map((announcement) => announcement.id === data.id ? adminAnnouncementSchema.parse(data) : announcement),
|
||||
prevResult.map((announcement) => announcement.id === data.id ? v.parse(adminAnnouncementSchema, data) : announcement),
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { announcementReactionSchema, type AnnouncementReaction } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { type Announcement, normalizeAnnouncement } from 'pl-fe/normalizers';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
const updateReaction = (reaction: AnnouncementReaction, count: number, me?: boolean, overwrite?: boolean) => announcementReactionSchema.parse({
|
||||
const updateReaction = (reaction: AnnouncementReaction, count: number, me?: boolean, overwrite?: boolean) => v.parse(announcementReactionSchema, {
|
||||
...reaction,
|
||||
me: typeof me === 'boolean' ? me : reaction.me,
|
||||
count: overwrite ? count : (reaction.count + count),
|
||||
@ -18,7 +19,7 @@ const updateReactions = (reactions: AnnouncementReaction[], name: string, count:
|
||||
reactions = reactions.map(reaction => reaction.name === name ? updateReaction(reaction, count, me, overwrite) : reaction);
|
||||
}
|
||||
|
||||
return [...reactions, updateReaction(announcementReactionSchema.parse({ name }), count, me, overwrite)];
|
||||
return [...reactions, updateReaction(v.parse(announcementReactionSchema, { name }), count, me, overwrite)];
|
||||
};
|
||||
|
||||
const useAnnouncements = () => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
@ -13,7 +13,7 @@ const useDemoteGroupMember = (group: Pick<Group, 'id'>, groupMember: Pick<GroupM
|
||||
const { createEntity } = useCreateEntity(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => client.experimental.groups.demoteGroupUsers(group.id, account_ids, role),
|
||||
{ schema: z.any().transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
{ schema: v.pipe(v.any(), v.transform(arr => arr[0])), transform: normalizeGroupMember },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntity } from 'pl-fe/entity-store/hooks';
|
||||
@ -14,7 +14,7 @@ const useGroupRelationship = (groupId: string | undefined) => {
|
||||
() => client.experimental.groups.getGroupRelationships([groupId!]),
|
||||
{
|
||||
enabled: !!groupId,
|
||||
schema: z.any().transform(arr => arr[0]),
|
||||
schema: v.pipe(v.any(), v.transform(arr => arr[0])),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { instanceSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { __stub } from 'pl-fe/api';
|
||||
import { buildGroup } from 'pl-fe/jest/factory';
|
||||
@ -8,7 +9,7 @@ import { useGroups } from './useGroups';
|
||||
|
||||
const group = buildGroup({ id: '1', display_name: 'soapbox' });
|
||||
const store = {
|
||||
instance: instanceSchema.parse({
|
||||
instance: v.parse(instanceSchema, {
|
||||
version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
@ -13,7 +13,7 @@ const usePromoteGroupMember = (group: Pick<Group, 'id'>, groupMember: Pick<Group
|
||||
const { createEntity } = useCreateEntity(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => client.experimental.groups.promoteGroupUsers(group.id, account_ids, role),
|
||||
{ schema: z.any().transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
{ schema: v.pipe(v.any(), v.transform(arr => arr[0])), transform: normalizeGroupMember },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { type InteractionPolicies, interactionPoliciesSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient, useFeatures, useLoggedIn } from 'pl-fe/hooks';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
const emptySchema = interactionPoliciesSchema.parse({});
|
||||
const emptySchema = v.parse(interactionPoliciesSchema, {});
|
||||
|
||||
const useInteractionPolicies = () => {
|
||||
const client = useClient();
|
||||
|
||||
@ -113,7 +113,7 @@ const BirthdayInput: React.FC<IBirthdayInput> = ({ value, onChange, required })
|
||||
<div className='relative mt-1 rounded-md shadow-sm'>
|
||||
<DatePicker
|
||||
selected={selected}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
wrapperClassName='w-fit'
|
||||
onChange={handleChange}
|
||||
placeholderText={intl.formatMessage(messages.birthdayPlaceholder)}
|
||||
minDate={new Date('1900-01-01')}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import { type MediaAttachment, type PreviewCard as CardEntity, mediaAttachmentSchema } from 'pl-api';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import Blurhash from 'pl-fe/components/blurhash';
|
||||
import { HStack, Stack, Text, Icon } from 'pl-fe/components/ui';
|
||||
@ -43,7 +44,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
||||
const trimmedDescription = trim(card.description, maxDescription);
|
||||
|
||||
const handlePhotoClick = () => {
|
||||
const attachment = mediaAttachmentSchema.parse({
|
||||
const attachment = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: card.embed_url,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Entity } from '../types';
|
||||
import type z from 'zod';
|
||||
import type { BaseSchema, BaseIssue } from 'valibot';
|
||||
|
||||
type EntitySchema<TEntity extends Entity = Entity> = z.ZodType<TEntity, z.ZodTypeDef, any>;
|
||||
type EntitySchema<TEntity extends Entity = Entity> = BaseSchema<any, TEntity, BaseIssue<unknown>>;
|
||||
|
||||
/**
|
||||
* Tells us where to find/store the entity in the cache.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/useAppSelector';
|
||||
@ -29,7 +29,7 @@ const useBatchedEntities = <TEntity extends Entity>(
|
||||
const getState = useGetState();
|
||||
const dispatch = useAppDispatch();
|
||||
const { entityType, listKey, path } = parseEntitiesPath(expandedPath);
|
||||
const schema = opts.schema || z.custom<TEntity>();
|
||||
const schema = opts.schema || v.custom<TEntity>(() => true);
|
||||
|
||||
const isEnabled = opts.enabled ?? true;
|
||||
const isFetching = useListState(path, 'fetching');
|
||||
@ -54,7 +54,7 @@ const useBatchedEntities = <TEntity extends Entity>(
|
||||
dispatch(entitiesFetchRequest(entityType, listKey));
|
||||
try {
|
||||
const response = await entityFn(filteredIds);
|
||||
const entities = filteredArray(schema).parse(response);
|
||||
const entities = v.parse(filteredArray(schema), response);
|
||||
dispatch(entitiesFetchSuccess(entities, entityType, listKey, 'end', {
|
||||
next: null,
|
||||
prev: null,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
|
||||
import { useLoading } from 'pl-fe/hooks/useLoading';
|
||||
@ -31,8 +31,8 @@ const useCreateEntity = <TEntity extends Entity = Entity, TTransformedEntity ext
|
||||
callbacks: EntityCallbacks<TTransformedEntity, { response?: PlfeResponse }> = {},
|
||||
): Promise<void> => {
|
||||
const result = await setPromise(entityFn(data));
|
||||
const schema = opts.schema || z.custom<TEntity>();
|
||||
let entity: TEntity | TTransformedEntity = schema.parse(result);
|
||||
const schema = opts.schema || v.custom<TEntity>(() => true);
|
||||
let entity: TEntity | TTransformedEntity = v.parse(schema, result);
|
||||
if (opts.transform) entity = opts.transform(entity);
|
||||
|
||||
// TODO: optimistic updating
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import z from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/useAppSelector';
|
||||
@ -17,7 +17,7 @@ import type { PaginatedResponse } from 'pl-api';
|
||||
|
||||
/** Additional options for the hook. */
|
||||
interface UseEntitiesOpts<TEntity extends Entity, TTransformedEntity extends Entity> {
|
||||
/** A zod schema to parse the API entities. */
|
||||
/** A valibot schema to parse the API entities. */
|
||||
schema?: EntitySchema<TEntity>;
|
||||
/**
|
||||
* Time (milliseconds) until this query becomes stale and should be refetched.
|
||||
@ -43,7 +43,7 @@ const useEntities = <TEntity extends Entity, TTransformedEntity extends Entity =
|
||||
|
||||
const { entityType, listKey, path } = parseEntitiesPath(expandedPath);
|
||||
const entities = useAppSelector(state => selectEntities<TTransformedEntity>(state, path));
|
||||
const schema = opts.schema || z.custom<TEntity>();
|
||||
const schema = opts.schema || v.custom<TEntity>(() => true);
|
||||
|
||||
const isEnabled = opts.enabled ?? true;
|
||||
const isFetching = useListState(path, 'fetching');
|
||||
@ -64,7 +64,7 @@ const useEntities = <TEntity extends Entity, TTransformedEntity extends Entity =
|
||||
dispatch(entitiesFetchRequest(entityType, listKey));
|
||||
try {
|
||||
const response = await req();
|
||||
const entities = filteredArray(schema).parse(response);
|
||||
const entities = v.parse(filteredArray(schema), response);
|
||||
const transformedEntities = opts.transform && entities.map(opts.transform);
|
||||
|
||||
dispatch(entitiesFetchSuccess(transformedEntities || entities, entityType, listKey, pos, {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user