diff --git a/packages/schema/src/primitives/FileValidators.ts b/packages/schema/src/primitives/FileValidators.ts index 38825b3d0..c54212474 100644 --- a/packages/schema/src/primitives/FileValidators.ts +++ b/packages/schema/src/primitives/FileValidators.ts @@ -59,7 +59,7 @@ export function normalizeFilename(value: string): string { let normalized = normalizeString(value); normalized = normalized.replaceAll(String.fromCharCode(0), ''); normalized = normalized.replace(/[/\\]/g, '_'); - normalized = normalized.replace(/\.{2,}/g, '_'); + normalized = normalized.replace(/\.{2,}/g, '.'); while (normalized.includes('..')) { normalized = normalized.replace(/\.\./g, '_'); } diff --git a/packages/schema/src/primitives/tests/FileValidators.test.ts b/packages/schema/src/primitives/tests/FileValidators.test.ts index 04d4aa82c..536d67e9a 100644 --- a/packages/schema/src/primitives/tests/FileValidators.test.ts +++ b/packages/schema/src/primitives/tests/FileValidators.test.ts @@ -38,11 +38,18 @@ describe('FilenameType', () => { expect(result.data).toBe('path_to_file.txt'); } }); - it('replaces double dots with underscores', () => { + it('collapses repeated dots into a single dot', () => { const result = FilenameType.safeParse('..file..txt'); expect(result.success).toBe(true); if (result.success) { - expect(result.data).not.toContain('..'); + expect(result.data).toBe('.file.txt'); + } + }); + it('keeps the extension when the name contains repeated dots', () => { + const result = FilenameType.safeParse('video..mp4'); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data).toBe('video.mp4'); } }); it('handles Windows reserved names by prefixing', () => {