Files
supabase/apps/studio/components/interfaces/Storage/BucketDataProtectionFields.schema.ts
T
Claude 93ee4b82b8 fix(storage): validate expiration inputs inline on change, simplify error
- Call trigger() after each field.onChange so zod validation runs
  immediately as the user types, surfacing errors via FormMessage
- Simplify the S3 cap error message to 'Cannot exceed 100 versions'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 14:05:54 +00:00

79 lines
2.7 KiB
TypeScript

import { z } from 'zod'
import type { ExpirationMode, VersioningPlanLimits } from './StorageProtection.constants'
/**
* Shared zod fields for the object-versioning controls in the create/edit
* bucket modals. `''` is the in-form empty sentinel (per the react-hook-form
* convention) so the number inputs stay fully deletable/controlled; it's
* coerced to a real number on parse.
*/
const versioningNumberField = z.union([z.literal(''), z.coerce.number().int()])
/**
* S3 lifecycle policies support at most 100 noncurrent versions per rule.
* This is an absolute ceiling independent of the org's billing plan.
*/
export const S3_MAX_NONCURRENT_VERSIONS = 100
export const bucketProtectionFormFields = {
enable_versioning: z.boolean().default(false),
version_expiry_days: versioningNumberField.default(''),
max_noncurrent_versions: versioningNumberField.default(''),
expiration_mode: z.enum(['and', 'or']).default('and'),
}
export interface BucketProtectionFormValues {
enable_versioning: boolean
version_expiry_days: '' | number
max_noncurrent_versions: '' | number
expiration_mode: ExpirationMode
}
/**
* Validates the versioning fields against the org's plan limits. Call this
* from the parent modal's `.superRefine` so errors surface through the usual
* `FormItemLayout`/`FormMessage` rendering.
*
* Both fields are optional — an empty value means "no limit" for that
* condition. When a value IS provided it must fall within the plan's bounds.
*/
export const superRefineBucketProtection = (
data: BucketProtectionFormValues,
ctx: z.RefinementCtx,
planLimits: VersioningPlanLimits | null
) => {
if (!data.enable_versioning || !planLimits) return
if (
data.version_expiry_days !== '' &&
(data.version_expiry_days < planLimits.minRetentionDays ||
data.version_expiry_days > planLimits.maxRetentionDays)
) {
ctx.addIssue({
path: ['version_expiry_days'],
code: z.ZodIssueCode.custom,
message: `Must be between ${planLimits.minRetentionDays} and ${planLimits.maxRetentionDays} days on your plan`,
})
}
if (data.max_noncurrent_versions !== '') {
if (data.max_noncurrent_versions > S3_MAX_NONCURRENT_VERSIONS) {
ctx.addIssue({
path: ['max_noncurrent_versions'],
code: z.ZodIssueCode.custom,
message: `Cannot exceed ${S3_MAX_NONCURRENT_VERSIONS} versions`,
})
} else if (
data.max_noncurrent_versions < planLimits.minVersions ||
data.max_noncurrent_versions > planLimits.maxVersions
) {
ctx.addIssue({
path: ['max_noncurrent_versions'],
code: z.ZodIssueCode.custom,
message: `Must be between ${planLimits.minVersions} and ${planLimits.maxVersions} versions on your plan`,
})
}
}
}