From 7abeee3d1ca9c7f44ca58648a795a4a45fba6cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 13 Oct 2024 22:24:06 +0200 Subject: [PATCH] Migrate pl-fe schemas to Valibot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-fe/src/schemas/pl-fe/settings.ts | 102 ++++++++++--------- packages/pl-fe/src/schemas/pleroma.ts | 34 ++++--- packages/pl-fe/src/schemas/utils.ts | 10 +- packages/pl-fe/src/stores/settings.ts | 11 +- packages/pl-fe/src/utils/config-db.ts | 9 +- 5 files changed, 92 insertions(+), 74 deletions(-) diff --git a/packages/pl-fe/src/schemas/pl-fe/settings.ts b/packages/pl-fe/src/schemas/pl-fe/settings.ts index 6d2ecd9e5..c0601614c 100644 --- a/packages/pl-fe/src/schemas/pl-fe/settings.ts +++ b/packages/pl-fe/src/schemas/pl-fe/settings.ts @@ -1,85 +1,89 @@ -import { z } from 'zod'; +import * as v from 'valibot'; import { locales } from 'pl-fe/messages'; import { coerceObject } from '../utils'; -const skinToneSchema = z.union([ - z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(6), -]); +const skinToneSchema = v.picklist([1, 2, 3, 4, 5, 6]); -const settingsSchema = z.object({ - onboarded: z.boolean().catch(false), - skinTone: skinToneSchema.catch(1), - reduceMotion: z.boolean().catch(false), - underlineLinks: z.boolean().catch(false), - autoPlayGif: z.boolean().catch(true), - displayMedia: z.enum(['default', 'hide_all', 'show_all']).catch('default'), - displaySpoilers: z.boolean().catch(false), - unfollowModal: z.boolean().catch(true), - boostModal: z.boolean().catch(false), - deleteModal: z.boolean().catch(true), - missingDescriptionModal: z.boolean().catch(true), - defaultPrivacy: z.enum(['public', 'unlisted', 'private', 'direct']).catch('public'), - defaultContentType: z.enum(['text/plain', 'text/markdown']).catch('text/plain'), - themeMode: z.enum(['system', 'light', 'dark', 'black']).catch('system'), - locale: z.string().catch(navigator.language).pipe(z.enum(locales)).catch('en'), - showExplanationBox: z.boolean().catch(true), - explanationBox: z.boolean().catch(true), - autoloadTimelines: z.boolean().catch(true), - autoloadMore: z.boolean().catch(true), - preserveSpoilers: z.boolean().catch(false), - autoTranslate: z.boolean().catch(false), - knownLanguages: z.array(z.string()).catch([]), +const settingsSchema = v.object({ + onboarded: v.fallback(v.boolean(), false), + skinTone: v.fallback(skinToneSchema, 1), + reduceMotion: v.fallback(v.boolean(), false), + underlineLinks: v.fallback(v.boolean(), false), + autoPlayGif: v.fallback(v.boolean(), true), + displayMedia: v.fallback(v.picklist(['default', 'hide_all', 'show_all']), 'default'), + displaySpoilers: v.fallback(v.boolean(), false), + unfollowModal: v.fallback(v.boolean(), true), + boostModal: v.fallback(v.boolean(), false), + deleteModal: v.fallback(v.boolean(), true), + missingDescriptionModal: v.fallback(v.boolean(), true), + defaultPrivacy: v.fallback(v.picklist(['public', 'unlisted', 'private', 'direct']), 'public'), + defaultContentType: v.fallback(v.picklist(['text/plain', 'text/markdown']), 'text/plain'), + themeMode: v.fallback(v.picklist(['system', 'light', 'dark', 'black']), 'system'), + locale: v.fallback( + v.pipe( + v.fallback(v.string(), navigator.language), + v.picklist(locales), + ), + 'en', + ), + showExplanationBox: v.fallback(v.boolean(), true), + explanationBox: v.fallback(v.boolean(), true), + autoloadTimelines: v.fallback(v.boolean(), true), + autoloadMore: v.fallback(v.boolean(), true), + preserveSpoilers: v.fallback(v.boolean(), false), + autoTranslate: v.fallback(v.boolean(), false), + knownLanguages: v.fallback(v.array(v.string()), []), - systemFont: z.boolean().catch(false), - demetricator: z.boolean().catch(false), + systemFont: v.fallback(v.boolean(), false), + demetricator: v.fallback(v.boolean(), false), - isDeveloper: z.boolean().catch(false), + isDeveloper: v.fallback(v.boolean(), false), chats: coerceObject({ - mainWindow: z.enum(['minimized', 'open']).catch('minimized'), - sound: z.boolean().catch(true), + mainWindow: v.fallback(v.picklist(['minimized', 'open']), 'minimized'), + sound: v.fallback(v.boolean(), true), }), - timelines: z.record(coerceObject({ + timelines: v.fallback(v.record(v.string(), coerceObject({ shows: coerceObject({ - reblog: z.boolean().catch(true), - reply: z.boolean().catch(true), - direct: z.boolean().catch(false), + reblog: v.fallback(v.boolean(), true), + reply: v.fallback(v.boolean(), true), + direct: v.fallback(v.boolean(), false), }), other: coerceObject({ - onlyMedia: z.boolean().catch(false), + onlyMedia: v.fallback(v.boolean(), false), }), - })).catch({}), + })), {}), account_timeline: coerceObject({ shows: coerceObject({ - pinned: z.boolean().catch(true), + pinned: v.fallback(v.boolean(), true), }), }), remote_timeline: coerceObject({ - pinnedHosts: z.string().array().catch([]), + pinnedHosts: v.fallback(v.array(v.string()), []), }), notifications: coerceObject({ quickFilter: coerceObject({ - active: z.string().catch('all'), - advanced: z.boolean().catch(false), - show: z.boolean().catch(true), + active: v.fallback(v.string(), 'all'), + advanced: v.fallback(v.boolean(), false), + show: v.fallback(v.boolean(), true), }), - sounds: z.record(z.boolean()).catch({}), + sounds: v.fallback(v.record(v.string(), v.boolean()), {}), }), - frequentlyUsedEmojis: z.record(z.number()).catch({}), - frequentlyUsedLanguages: z.record(z.number()).catch({}), + frequentlyUsedEmojis: v.fallback(v.record(v.string(), v.number()), {}), + frequentlyUsedLanguages: v.fallback(v.record(v.string(), v.number()), {}), - saved: z.boolean().catch(true), + saved: v.fallback(v.boolean(), true), - demo: z.boolean().catch(false), + demo: v.fallback(v.boolean(), false), }); -type Settings = z.infer; +type Settings = v.InferOutput; export { settingsSchema, type Settings }; diff --git a/packages/pl-fe/src/schemas/pleroma.ts b/packages/pl-fe/src/schemas/pleroma.ts index c7fff80c2..78a3d4b90 100644 --- a/packages/pl-fe/src/schemas/pleroma.ts +++ b/packages/pl-fe/src/schemas/pleroma.ts @@ -1,20 +1,26 @@ -import { z } from 'zod'; +import * as v from 'valibot'; import { coerceObject } from './utils'; -const mrfSimpleSchema = 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([]), -}); +const mrfSimpleSchema = coerceObject(v.entriesFromList( + [ + 'accept', + 'avatar_removal', + 'banner_removal', + 'federated_timeline_removal', + 'followers_only', + 'media_nsfw', + 'media_removal', + 'reject', + 'reject_deletes', + 'report_removal', + ], + v.fallback(v.array(v.string()), []), +)); -type MRFSimple = z.infer; +(window as any).v = v; +(window as any).mrfSimpleSchema = mrfSimpleSchema; + +type MRFSimple = v.InferOutput; export { mrfSimpleSchema, type MRFSimple }; diff --git a/packages/pl-fe/src/schemas/utils.ts b/packages/pl-fe/src/schemas/utils.ts index 8af87e166..d4038009e 100644 --- a/packages/pl-fe/src/schemas/utils.ts +++ b/packages/pl-fe/src/schemas/utils.ts @@ -1,3 +1,4 @@ +import * as v from 'valibot'; import z from 'zod'; import type { CustomEmoji } from 'pl-api'; @@ -18,8 +19,13 @@ const makeCustomEmojiMap = (customEmojis: CustomEmoji[]) => result[`:${emoji.shortcode}:`] = emoji; return result; }, {}); + /** zod schema to force the value into an object, if it isn't already. */ -const coerceObject = (shape: T) => - z.object({}).passthrough().catch({}).pipe(z.object(shape)); +const coerceObject = (shape: T) => + v.pipe( + v.any(), + v.transform((input) => typeof input === 'object' ? input : {}), + v.object(shape), + ); export { filteredArray, makeCustomEmojiMap, coerceObject }; diff --git a/packages/pl-fe/src/stores/settings.ts b/packages/pl-fe/src/stores/settings.ts index 2b76c4da5..f04b4ce4c 100644 --- a/packages/pl-fe/src/stores/settings.ts +++ b/packages/pl-fe/src/stores/settings.ts @@ -1,4 +1,5 @@ import { produce } from 'immer'; +import * as v from 'valibot'; import { create } from 'zustand'; import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings'; @@ -6,7 +7,7 @@ import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings'; import type { Emoji } from 'pl-fe/features/emoji'; import type { APIEntity } from 'pl-fe/types/entities'; -const settingsSchemaPartial = settingsSchema.partial(); +const settingsSchemaPartial = v.partial(settingsSchema); type State = { defaultSettings: Settings; @@ -35,22 +36,22 @@ const changeSetting = (object: APIEntity, path: string[], value: any) => { const mergeSettings = (state: State) => state.settings = { ...state.defaultSettings, ...state.userSettings }; const useSettingsStore = create((set) => ({ - defaultSettings: settingsSchema.parse({}), + defaultSettings: v.parse(settingsSchema, {}), userSettings: {}, - settings: settingsSchema.parse({}), + settings: v.parse(settingsSchema, {}), loadDefaultSettings: (settings: APIEntity) => set(produce((state: State) => { if (typeof settings !== 'object') return; - state.defaultSettings = settingsSchema.parse(settings); + state.defaultSettings = v.parse(settingsSchema, settings); mergeSettings(state); })), loadUserSettings: (settings?: APIEntity) => set(produce((state: State) => { if (typeof settings !== 'object') return; - state.userSettings = settingsSchemaPartial.parse(settings); + state.userSettings = v.parse(settingsSchemaPartial, settings); mergeSettings(state); })), diff --git a/packages/pl-fe/src/utils/config-db.ts b/packages/pl-fe/src/utils/config-db.ts index 43dd66c0f..9eda99bae 100644 --- a/packages/pl-fe/src/utils/config-db.ts +++ b/packages/pl-fe/src/utils/config-db.ts @@ -4,8 +4,9 @@ import { Set as ImmutableSet, } from 'immutable'; import trimStart from 'lodash/trimStart'; +import * as v from 'valibot'; -import { type MRFSimple, mrfSimpleSchema } from 'pl-fe/schemas/pleroma'; +import { mrfSimpleSchema } from 'pl-fe/schemas/pleroma'; type Config = ImmutableMap; type Policy = Record; @@ -18,7 +19,7 @@ const find = ( config.isSuperset(ImmutableMap({ group, key })), ); -const toSimplePolicy = (configs: ImmutableList): MRFSimple => { +const toSimplePolicy = (configs: ImmutableList) => { const config = find(configs, ':pleroma', ':mrf_simple'); const reducer = (acc: ImmutableMap, curr: ImmutableMap) => { @@ -30,9 +31,9 @@ const toSimplePolicy = (configs: ImmutableList): MRFSimple => { if (config?.get) { const value = config.get('value', ImmutableList()); const result = value.reduce(reducer, ImmutableMap()); - return mrfSimpleSchema.parse(result.toJS()); + return v.parse(mrfSimpleSchema, result.toJS()); } else { - return mrfSimpleSchema.parse({}); + return v.parse(mrfSimpleSchema, {}); } };