fix(storage): enforce S3 max 100 versions via zod, remove native HTML validation

- Add S3_MAX_NONCURRENT_VERSIONS constant (100) and enforce it in
  superRefineBucketProtection independently of plan limits
- Remove HTML min attributes from both inputs so only Supabase inline
  errors (FormMessage via FormItemLayout) appear — no browser tooltips
- Plan-specific bounds still enforced as before; the S3 cap is checked
  first with a distinct error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
This commit is contained in:
Claude committed 2026-08-11 13:53:56 +00:00
1 parent 57b359af4e
commit 54c9f84cb3
2 files changed
+23 -12

No files matched your search

@@ -10,6 +10,12 @@ import type { ExpirationMode, VersioningPlanLimits } from './StorageProtection.c
*/
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(''),
@@ -51,15 +57,22 @@ export const superRefineBucketProtection = (
})
}
if (
data.max_noncurrent_versions !== '' &&
(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`,
})
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 (S3 lifecycle policy limit)`,
})
} 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`,
})
}
}
}
@@ -247,7 +247,6 @@ const ExpirationPolicySection = ({
<FormInputGroupInput
{...field}
type="number"
min={1}
placeholder="—"
value={field.value === '' ? '' : field.value}
onChange={(e) => {
@@ -278,7 +277,6 @@ const ExpirationPolicySection = ({
<FormInputGroupInput
{...field}
type="number"
min={1}
placeholder="—"
value={field.value === '' ? '' : field.value}
onChange={(e) => {