Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"license": "ISC",
"description": "",
"dependencies": {
"@hono/zod-validator": "^0.7.3",
"@hono/zod-validator": "^0.7.4",
"@interledger/open-payments": "^7.1.3",
"@noble/ed25519": "^3.0.0",
"@paralleldrive/cuid2": "^2.2.2",
Expand All @@ -24,7 +24,7 @@
"hono": "^4.9.8",
"http-message-signatures": "^1.0.4",
"httpbis-digest-headers": "^1.0.0",
"zod": "^3.25.76"
"zod": "^4.1.12"
},
"types": "./src/types.ts",
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ app.onError((error, c) => {
message: 'Validation failed',
code: 'VALIDATION_ERROR',
details: {
issues: error.errors.map((err) => ({
issues: error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code
Expand Down
6 changes: 3 additions & 3 deletions api/src/routes/get-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { zValidator } from '@hono/zod-validator'
import { HTTPException } from 'hono/http-exception'
import { z } from 'zod'
import { ConfigStorageService } from '@shared/config-storage-service'
import { AWS_PREFIX } from '@shared/defines'
import { PRESET_IDS, TOOLS } from '@shared/types'
Expand All @@ -14,6 +12,8 @@ import type {
} from '@shared/types'
import { app } from '../app.js'
import { createHTTPException } from '../utils/utils.js'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod/v4'

app.get(
'/config/:tool',
Expand All @@ -26,7 +26,7 @@ app.get(
zValidator(
'query',
z.object({
wa: z.string().url(),
wa: z.url(),
preset: z.enum(PRESET_IDS)
})
),
Expand Down
6 changes: 3 additions & 3 deletions api/src/routes/probabilistic-revshare.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { HTTPException } from 'hono/http-exception'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import type { ContentfulStatusCode } from 'hono/utils/http-status'
import type { WalletAddress } from '@interledger/open-payments'
import { decode, pickWeightedRandom } from '@shared/probabilistic-revenue-share'
import { isWalletAddress, validateWalletAddressOrPointer } from '@shared/utils'
import { createHTTPException } from '../utils/utils'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod/v4'

import { app } from '../app.js'

Expand All @@ -14,7 +14,7 @@ app.get(
zValidator(
'param',
z.object({
payload: z.string().base64url().max(50_000).min(20)
payload: z.base64url().max(50_000).min(20)
})
),
async ({ req, json }) => {
Expand Down
22 changes: 11 additions & 11 deletions api/src/schemas/payment.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as z from 'zod/v4'
import { z } from 'zod/v4'

export const PaymentQuoteSchema = z.object({
senderWalletAddress: z.string().url('Invalid sender wallet address'),
receiverWalletAddress: z.string().url('Invalid receiver wallet address'),
senderWalletAddress: z.url('Invalid sender wallet address'),
receiverWalletAddress: z.url('Invalid receiver wallet address'),
amount: z.number().positive('Amount must be positive'),
note: z.string().optional()
})
Expand Down Expand Up @@ -35,11 +35,11 @@ export const PaymentFinalizeSchema = z.object({
walletAddress: WalletAddressSchema,
pendingGrant: z.object({
interact: z.object({
redirect: z.string().url(),
redirect: z.url(),
finish: z.string()
}),
continue: z.object({
uri: z.string().url(),
uri: z.url(),
access_token: z.object({
value: z.string()
}),
Expand All @@ -48,18 +48,18 @@ export const PaymentFinalizeSchema = z.object({
}),
quote: z.object({
id: z.string(),
walletAddress: z.string().url('Invalid wallet address'),
receiver: z.string().url(),
walletAddress: z.url('Invalid wallet address'),
receiver: z.url(),
receiveAmount: AmountSchema,
debitAmount: AmountSchema,
method: z.literal('ilp'),
createdAt: z.string().datetime(),
expiresAt: z.string().datetime().optional()
createdAt: z.iso.datetime(),
expiresAt: z.iso.datetime().optional()
}),
incomingPaymentGrant: z.object({
access_token: z.object({
value: z.string(),
manage: z.string().url(),
manage: z.url(),
expires_in: z.number().int(),
access: z.array(
z.object({
Expand All @@ -73,7 +73,7 @@ export const PaymentFinalizeSchema = z.object({
access_token: z.object({
value: z.string()
}),
uri: z.string().url(),
uri: z.url(),
wait: z.number().int().optional()
})
}),
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { z } from 'zod'
import type {
createBannerSchema,
createButtonSchema,
createWidgetSchema
} from '../utils/validate.server.js'
import type { ElementConfigType } from '@shared/types'
import type { z } from 'zod/v4'

export type ModalType = {
type:
Expand Down Expand Up @@ -37,7 +37,7 @@ export type SanitizedFields = Pick<
>

export type JSONError<T extends z.ZodTypeAny> = {
errors: z.typeToFlattenedError<z.infer<T>>
errors: z.ZodFlattenedError<z.infer<T>>
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
21 changes: 10 additions & 11 deletions frontend/app/utils/validate.client.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { z } from 'zod'
import {
bannerFieldsSchema,
buttonFieldsSchema,
widgetFieldsSchema
} from './validate.shared'
import type { ElementConfigType } from '@shared/types'
import { z } from 'zod/v4'

export const elementConfigStorageSchema = z
.object({
versionName: z.string(),
tag: z.string().optional(),
// can be undefined initially
walletAddress: z.string().optional()
})
.merge(buttonFieldsSchema)
.merge(bannerFieldsSchema)
.merge(widgetFieldsSchema)
export const elementConfigStorageSchema = z.object({
versionName: z.string(),
tag: z.string().optional(),
// can be undefined initially
walletAddress: z.string().optional(),
...buttonFieldsSchema.shape,
...bannerFieldsSchema.shape,
...widgetFieldsSchema.shape
})

/**
* Validates configurations from localStorage.
Expand Down
58 changes: 31 additions & 27 deletions frontend/app/utils/validate.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { z } from 'zod'
import {
checkHrefFormat,
getWalletAddress,
Expand All @@ -10,6 +9,7 @@ import {
buttonFieldsSchema,
widgetFieldsSchema
} from './validate.shared'
import { z } from 'zod/v4'

export const walletSchema = z.object({
walletAddress: z
Expand All @@ -24,7 +24,7 @@ export const walletSchema = z.object({
await getWalletAddress(updatedUrl)
} catch (e) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
code: 'custom',
message:
e instanceof WalletAddressFormatError
? e.message
Expand All @@ -43,29 +43,26 @@ export const fullConfigSchema = z.object({
fullconfig: z.string().min(1, { message: 'Unknown error' })
})

export const createButtonSchema = z
.object({
elementType: z.literal('button')
})
.merge(buttonFieldsSchema)
.merge(walletSchema)
.merge(versionSchema)
export const createButtonSchema = z.object({
elementType: z.literal('button'),
...buttonFieldsSchema.shape,
...walletSchema.shape,
...versionSchema.shape
})

export const createBannerSchema = z
.object({
elementType: z.literal('banner')
})
.merge(bannerFieldsSchema)
.merge(walletSchema)
.merge(versionSchema)
export const createBannerSchema = z.object({
elementType: z.literal('banner'),
...bannerFieldsSchema.shape,
...walletSchema.shape,
...versionSchema.shape
})

export const createWidgetSchema = z
.object({
elementType: z.literal('widget')
})
.merge(widgetFieldsSchema)
.merge(walletSchema)
.merge(versionSchema)
export const createWidgetSchema = z.object({
elementType: z.literal('widget'),
...widgetFieldsSchema.shape,
...walletSchema.shape,
...versionSchema.shape
})

export const getElementSchema = (type: string) => {
switch (type) {
Expand All @@ -90,7 +87,10 @@ export const validateForm = async (
if (intent === 'import' || intent === 'delete') {
result = await walletSchema.safeParseAsync(formData)
} else if (intent === 'newversion') {
const newVersionSchema = versionSchema.merge(walletSchema)
const newVersionSchema = z.object({
...versionSchema.shape,
...walletSchema.shape
})
result = await newVersionSchema.safeParseAsync(formData)
} else {
let currentSchema
Expand All @@ -106,9 +106,13 @@ export const validateForm = async (
default:
currentSchema = createBannerSchema
}
result = await currentSchema
.merge(fullConfigSchema)
.safeParseAsync(Object.assign(formData, { ...{ elementType } }))
const mergedSchema = z.object({
...currentSchema.shape,
...fullConfigSchema.shape
})
result = await mergedSchema.safeParseAsync(
Object.assign(formData, { ...{ elementType } })
)
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const payload = result.data as unknown as any
Expand Down
14 changes: 7 additions & 7 deletions frontend/app/utils/validate.shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { z } from 'zod'
import {
CORNER_OPTION,
BANNER_POSITION,
Expand All @@ -12,6 +11,7 @@ import {
WIDGET_TITLE_MAX_LENGTH,
WIDGET_DESCRIPTION_MAX_LENGTH
} from '@shared/types'
import { z } from 'zod/v4'

const bannerFontSizeError = {
message: `Font size must be between ${BANNER_FONT_SIZES.min} and ${BANNER_FONT_SIZES.max}`
Expand All @@ -23,7 +23,7 @@ const widgetFontSizeError = {
export const buttonFieldsSchema = z.object({
buttonFontName: z.string().min(1, { message: 'Choose a font' }),
buttonText: z.string().min(1, { message: 'Button label cannot be empty' }),
buttonBorder: z.nativeEnum(CORNER_OPTION),
buttonBorder: z.enum(CORNER_OPTION),
buttonTextColor: z.string().min(6),
buttonBackgroundColor: z.string().min(6),
buttonDescriptionText: z.string().optional()
Expand All @@ -48,10 +48,10 @@ export const bannerFieldsSchema = z.object({
bannerDescriptionVisible: z.coerce.boolean().optional(),
bannerTextColor: z.string().min(6),
bannerBackgroundColor: z.string().min(6),
bannerSlideAnimation: z.nativeEnum(SLIDE_ANIMATION),
bannerSlideAnimation: z.enum(SLIDE_ANIMATION),
bannerThumbnail: z.string().optional(),
bannerPosition: z.nativeEnum(BANNER_POSITION),
bannerBorder: z.nativeEnum(CORNER_OPTION)
bannerPosition: z.enum(BANNER_POSITION),
bannerBorder: z.enum(CORNER_OPTION)
})

export const widgetFieldsSchema = z.object({
Expand All @@ -71,14 +71,14 @@ export const widgetFieldsSchema = z.object({
})
.optional(),
widgetDescriptionVisible: z.coerce.boolean().optional(),
widgetPosition: z.nativeEnum(WIDGET_POSITION),
widgetPosition: z.enum(WIDGET_POSITION),
widgetDonateAmount: z.coerce
.number()
.min(0, { message: 'Donate amount must be positive' }),
widgetButtonText: z
.string()
.min(1, { message: 'Button text cannot be empty' }),
widgetButtonBorder: z.nativeEnum(CORNER_OPTION),
widgetButtonBorder: z.enum(CORNER_OPTION),
widgetButtonBackgroundColor: z.string().min(1),
widgetButtonTextColor: z.string().min(1),
widgetTextColor: z.string().min(1),
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"sanitize-html": "^2.17.0",
"tailwindcss": "^3.4.17",
"valtio": "^2.1.8",
"zod": "^3.25.76"
"zod": "^4.1.12"
},
"devDependencies": {
"@lit/react": "^1.0.8",
Expand Down
Loading