chore: update to zod@4

This commit is contained in:
diced
2025-09-02 23:38:23 -07:00
parent 8e778d4178
commit 0a59298fa0
5 changed files with 64 additions and 57 deletions

View File

@@ -89,7 +89,7 @@
"swr": "^2.3.6",
"typescript-eslint": "^8.40.0",
"vite": "^7.1.3",
"zod": "^3.25.67",
"zod": "^4.1.5",
"zustand": "^5.0.8"
},
"devDependencies": {

10
pnpm-lock.yaml generated
View File

@@ -183,8 +183,8 @@ importers:
specifier: ^7.1.3
version: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(sass@1.90.0)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.4)
zod:
specifier: ^3.25.67
version: 3.25.67
specifier: ^4.1.5
version: 4.1.5
zustand:
specifier: ^5.0.8
version: 5.0.8(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))
@@ -4895,8 +4895,8 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
zod@3.25.67:
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
zod@4.1.5:
resolution: {integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==}
zustand@5.0.8:
resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==}
@@ -10437,7 +10437,7 @@ snapshots:
yocto-queue@0.1.0: {}
zod@3.25.67: {}
zod@4.1.5: {}
zustand@5.0.8(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)):
optionalDependencies:

View File

@@ -1,6 +1,6 @@
import { tmpdir } from 'os';
import { join, resolve } from 'path';
import { type ZodIssue, z } from 'zod';
import { z } from 'zod';
import { log } from '../logger';
import { ParsedConfig } from './read';
import { PROP_TO_ENV } from './read/env';
@@ -19,7 +19,7 @@ declare global {
export const discordContent = z
.object({
webhookUrl: z.string().url().nullable().default(null),
webhookUrl: z.url().nullable().default(null),
username: z.string().nullable().default(null),
avatarUrl: z.string().nullable().default(null),
content: z.string().nullable().default(null),
@@ -63,10 +63,11 @@ export const schema = z.object({
inclusive: true,
message: 'Secret must contain at least 32 characters',
exact: false,
origin: 'string',
});
}
}),
databaseUrl: z.string().url(),
databaseUrl: z.url(),
returnHttpsUrls: z.boolean().default(false),
defaultDomain: z.string().nullable().default(null),
tempDirectory: z
@@ -135,7 +136,7 @@ export const schema = z.object({
if (s.type === 's3' && !s.s3) {
for (const key of ['accessKeyId', 'secretAccessKey', 'region', 'bucket']) {
c.addIssue({
code: z.ZodIssueCode.invalid_type,
code: 'invalid_type',
expected: 'string',
received: 'unknown',
path: ['s3', key],
@@ -143,7 +144,7 @@ export const schema = z.object({
}
} else if (s.type === 'local' && !s.local) {
c.addIssue({
code: z.ZodIssueCode.invalid_type,
code: 'invalid_type',
expected: 'string',
received: 'unknown',
path: ['local', 'directory'],
@@ -168,7 +169,7 @@ export const schema = z.object({
showUserSpecific: z.boolean().default(true),
}),
versionChecking: z.boolean().default(true),
versionAPI: z.string().url().default('https://zipline-version.diced.sh/'),
versionAPI: z.url().default('https://zipline-version.diced.sh/'),
}),
domains: z.array(z.string()).default([]),
invites: z.object({
@@ -177,12 +178,12 @@ export const schema = z.object({
}),
website: z.object({
title: z.string().default('Zipline'),
titleLogo: z.string().url().nullable().default(null),
titleLogo: z.url().nullable().default(null),
externalLinks: z
.array(
z.object({
name: z.string(),
url: z.string().url(),
url: z.url(),
}),
)
.default([
@@ -195,7 +196,7 @@ export const schema = z.object({
url: 'https://zipline.diced.sh',
},
]),
loginBackground: z.string().url().nullable().default(null),
loginBackground: z.url().nullable().default(null),
loginBackgroundBlur: z.boolean().default(true),
defaultAvatar: z
.string()
@@ -228,7 +229,7 @@ export const schema = z.object({
.object({
clientId: z.string(),
clientSecret: z.string(),
redirectUri: z.string().url().nullable().default(null),
redirectUri: z.url().nullable().default(null),
allowedIds: z.array(z.string()).default([]),
deniedIds: z.array(z.string()).default([]),
})
@@ -245,7 +246,7 @@ export const schema = z.object({
.object({
clientId: z.string(),
clientSecret: z.string(),
redirectUri: z.string().url().nullable().default(null),
redirectUri: z.url().nullable().default(null),
})
.or(
z.object({
@@ -258,7 +259,7 @@ export const schema = z.object({
.object({
clientId: z.string(),
clientSecret: z.string(),
redirectUri: z.string().url().nullable().default(null),
redirectUri: z.url().nullable().default(null),
})
.or(
z.object({
@@ -271,10 +272,10 @@ export const schema = z.object({
.object({
clientId: z.string(),
clientSecret: z.string(),
authorizeUrl: z.string().url(),
userinfoUrl: z.string().url(),
tokenUrl: z.string().url(),
redirectUri: z.string().url().nullable().default(null),
authorizeUrl: z.url(),
userinfoUrl: z.url(),
tokenUrl: z.url(),
redirectUri: z.url().nullable().default(null),
})
.or(
z.object({
@@ -289,9 +290,9 @@ export const schema = z.object({
}),
discord: z
.object({
webhookUrl: z.string().url().nullable().default(null),
webhookUrl: z.url().nullable().default(null),
username: z.string().nullable().default(null),
avatarUrl: z.string().url().nullable().default(null),
avatarUrl: z.url().nullable().default(null),
onUpload: discordContent,
onShorten: discordContent,
})
@@ -309,8 +310,8 @@ export const schema = z.object({
allowList: z.array(z.string()).default([]),
}),
httpWebhook: z.object({
onUpload: z.string().url().nullable().default(null),
onShorten: z.string().url().nullable().default(null),
onUpload: z.url().nullable().default(null),
onShorten: z.url().nullable().default(null),
}),
ssl: z.object({
key: z
@@ -351,7 +352,7 @@ export function validateConfigObject(env: ParsedConfig): Config {
if (!validated.success) {
logger.error('There was an error while validating the environment.');
for (const error of validated.error.errors) {
for (const error of validated.error.issues) {
handleError(error);
}
@@ -363,12 +364,12 @@ export function validateConfigObject(env: ParsedConfig): Config {
return validated.data;
}
function handleError(error: ZodIssue) {
function handleError(error: z.core.$ZodIssue) {
logger.debug(JSON.stringify(error));
if (error.code === 'invalid_union') {
for (const unionError of error.unionErrors) {
for (const subError of unionError.issues) {
for (const unionError of error.errors) {
for (const subError of unionError) {
handleError(subError);
}
}
@@ -378,7 +379,7 @@ function handleError(error: ZodIssue) {
const path =
error.path[1] === 'externalLinks'
? `WEBSITE_EXTERNAL_LINKS[${error.path[2]}]`
? `WEBSITE_EXTERNAL_LINKS[${String(error.path[2])}]`
: (PROP_TO_ENV[<keyof typeof PROP_TO_ENV>error.path.join('.')] ?? error.path.join('.'));
logger.error(`${path}: ${error.message}`);

View File

@@ -167,17 +167,18 @@ export const export3Schema = z.object({
hostname: z.string(),
release: z.string(),
}),
env: z.record(z.string()),
env: z.record(z.string(), z.string()),
}),
user_map: z.record(z.string()),
thumbnail_map: z.record(z.string()),
folder_map: z.record(z.string()),
file_map: z.record(z.string()),
url_map: z.record(z.string()),
invite_map: z.record(z.string()),
user_map: z.record(z.string(), z.string()),
thumbnail_map: z.record(z.string(), z.string()),
folder_map: z.record(z.string(), z.string()),
file_map: z.record(z.string(), z.string()),
url_map: z.record(z.string(), z.string()),
invite_map: z.record(z.string(), z.string()),
users: z.record(
z.string(),
z.object({
username: z.string(),
password: z.string().optional().nullable(),
@@ -204,6 +205,7 @@ export const export3Schema = z.object({
),
files: z.record(
z.string(),
z.object({
name: z.string(),
original_name: z.string().optional().nullable(),
@@ -221,6 +223,7 @@ export const export3Schema = z.object({
),
thumbnails: z.record(
z.string(),
z.object({
name: z.string(),
created_at: z.string(),
@@ -228,6 +231,7 @@ export const export3Schema = z.object({
),
folders: z.record(
z.string(),
z.object({
name: z.string(),
public: z.boolean(),
@@ -238,6 +242,7 @@ export const export3Schema = z.object({
),
urls: z.record(
z.string(),
z.object({
destination: z.string(),
vanity: z.string().optional().nullable(),
@@ -250,6 +255,7 @@ export const export3Schema = z.object({
),
invites: z.record(
z.string(),
z.object({
code: z.string(),
expires_at: z.string().optional().nullable(),

View File

@@ -189,25 +189,25 @@ export default fastifyPlugin(
featuresMetricsShowUserSpecific: z.boolean(),
featuresVersionChecking: z.boolean(),
featuresVersionAPI: z.string().url(),
featuresVersionAPI: z.url(),
invitesEnabled: z.boolean(),
invitesLength: z.number().min(1).max(64),
websiteTitle: z.string(),
websiteTitleLogo: z.string().url().nullable(),
websiteTitleLogo: z.url().nullable(),
websiteExternalLinks: z
.union([
z.array(
z.object({
name: z.string(),
url: z.string().url(),
url: z.url(),
}),
),
z.string(),
])
.transform((value) => (typeof value === 'string' ? JSON.parse(value) : value)),
websiteLoginBackground: z.string().url().nullable(),
websiteLoginBackground: z.url().nullable(),
websiteLoginBackgroundBlur: z.boolean(),
websiteDefaultAvatar: z
.string()
@@ -241,7 +241,7 @@ export default fastifyPlugin(
oauthDiscordClientId: z.string().nullable(),
oauthDiscordClientSecret: z.string().nullable(),
oauthDiscordRedirectUri: z.string().url().endsWith('/api/auth/oauth/discord').nullable(),
oauthDiscordRedirectUri: z.url().endsWith('/api/auth/oauth/discord').nullable(),
oauthDiscordAllowedIds: z
.union([
z.array(z.string().refine((s) => /^\d+$/.test(s), 'Discord ID must be a number')),
@@ -265,18 +265,18 @@ export default fastifyPlugin(
oauthGoogleClientId: z.string().nullable(),
oauthGoogleClientSecret: z.string().nullable(),
oauthGoogleRedirectUri: z.string().url().endsWith('/api/auth/oauth/google').nullable(),
oauthGoogleRedirectUri: z.url().endsWith('/api/auth/oauth/google').nullable(),
oauthGithubClientId: z.string().nullable(),
oauthGithubClientSecret: z.string().nullable(),
oauthGithubRedirectUri: z.string().url().endsWith('/api/auth/oauth/github').nullable(),
oauthGithubRedirectUri: z.url().endsWith('/api/auth/oauth/github').nullable(),
oauthOidcClientId: z.string().nullable(),
oauthOidcClientSecret: z.string().nullable(),
oauthOidcAuthorizeUrl: z.string().url().nullable(),
oauthOidcTokenUrl: z.string().url().nullable(),
oauthOidcUserinfoUrl: z.string().url().nullable(),
oauthOidcRedirectUri: z.string().url().endsWith('/api/auth/oauth/oidc').nullable(),
oauthOidcAuthorizeUrl: z.url().nullable(),
oauthOidcTokenUrl: z.url().nullable(),
oauthOidcUserinfoUrl: z.url().nullable(),
oauthOidcRedirectUri: z.url().endsWith('/api/auth/oauth/oidc').nullable(),
mfaTotpEnabled: z.boolean(),
mfaTotpIssuer: z.string(),
@@ -290,22 +290,22 @@ export default fastifyPlugin(
.union([z.array(z.string()), z.string()])
.transform((value) => (typeof value === 'string' ? value.split(',') : value)),
httpWebhookOnUpload: z.string().url().nullable(),
httpWebhookOnShorten: z.string().url().nullable(),
httpWebhookOnUpload: z.url().nullable(),
httpWebhookOnShorten: z.url().nullable(),
discordWebhookUrl: z.string().url().nullable(),
discordWebhookUrl: z.url().nullable(),
discordUsername: z.string().nullable(),
discordAvatarUrl: z.string().url().nullable(),
discordAvatarUrl: z.url().nullable(),
discordOnUploadWebhookUrl: z.string().url().nullable(),
discordOnUploadWebhookUrl: z.url().nullable(),
discordOnUploadUsername: z.string().nullable(),
discordOnUploadAvatarUrl: z.string().url().nullable(),
discordOnUploadAvatarUrl: z.url().nullable(),
discordOnUploadContent: z.string().nullable(),
discordOnUploadEmbed: discordEmbed,
discordOnShortenWebhookUrl: z.string().url().nullable(),
discordOnShortenWebhookUrl: z.url().nullable(),
discordOnShortenUsername: z.string().nullable(),
discordOnShortenAvatarUrl: z.string().url().nullable(),
discordOnShortenAvatarUrl: z.url().nullable(),
discordOnShortenContent: z.string().nullable(),
discordOnShortenEmbed: discordEmbed,