Skip to content

perf(web): optimize document upload validation with static sets, MIME checks, and 50MB size guard - #1560

Open
Sruhvx-jpg wants to merge 1 commit into
supermemoryai:mainfrom
Sruhvx-jpg:perf/web-file-upload-validation
Open

perf(web): optimize document upload validation with static sets, MIME checks, and 50MB size guard#1560
Sruhvx-jpg wants to merge 1 commit into
supermemoryai:mainfrom
Sruhvx-jpg:perf/web-file-upload-validation

Conversation

@Sruhvx-jpg

Copy link
Copy Markdown

Description

Fixes #1559

Optimizes client-side document file validation in apps/web/components/add-document/file.tsx during batch uploads and drag-and-drop operations.

Changes

  1. Module-level Static Allocation: Extracted validation logic into apps/web/lib/document-file-validation.ts with ALLOWED_EXTENSIONS and ALLOWED_MIME_TYPES Sets allocated once at module scope rather than allocating dynamic Sets on every single file check.
  2. Fail-Fast 50MB Size Guard: Added early rejection for files exceeding the 50MB limit with a dedicated toast notification ("One file exceeds the 50MB limit"), preventing wasted upload bandwidth.
  3. MIME & Fallback Extension Matching: Supported direct O(1) MIME verification for application/pdf, image/*, and standard document types alongside clean fallback extension parsing.
  4. Unit Tests: Added apps/web/lib/document-file-validation.test.ts testing standard extensions, uppercase multi-dot filenames, extensionless MIME blobs, >50MB bounds, and zero-byte/unsupported files.

Verification

  • Ran bun test apps/web/lib/document-file-validation.test.ts (6/6 tests passing).
  • Ran bunx biome check apps/web/components/add-document/file.tsx apps/web/lib/document-file-validation.ts apps/web/lib/document-file-validation.test.ts (clean).

Comment on lines +8 to +17
return {
name,
size,
type,
lastModified: Date.now(),
slice: () => new Blob(),
stream: () => new ReadableStream(),
text: () => Promise.resolve(""),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
} as unknown as File

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The object literal returned from createMockFile uses a type assertion (as unknown as File) instead of a type annotation. According to the 'Type assertions and casting' rule, you should use type annotations instead of assertions for object literals. Consider defining a typed variable with an explicit annotation, e.g., const mock: File = { name, size, type, ... }, or extracting a typed interface for the mock, rather than casting with as unknown as File.

Suggested change
return {
name,
size,
type,
lastModified: Date.now(),
slice: () => new Blob(),
stream: () => new ReadableStream(),
text: () => Promise.resolve(""),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
} as unknown as File
const mock: File = Object.assign(new File([], name), {
name,
size,
type,
lastModified: Date.now(),
slice: () => new Blob(),
stream: () => new ReadableStream(),
text: () => Promise.resolve(""),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
})
return mock

Spotted by Graphite (based on custom rule: TypeScript style guide (Google))

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +86 to +94
for (const file of incoming) {
if (file.size > MAX_DOCUMENT_FILE_BYTES) {
oversizedCount++
} else if (!isAcceptedFile(file)) {
unsupportedCount++
} else {
accepted.push(file)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty files (size = 0) are incorrectly categorized as "unsupported" rather than being handled separately. An empty PDF file will show "One file type is not supported" even though the file type is valid.

Impact: Misleading user feedback - users will think they uploaded the wrong file type when they actually uploaded an empty file.

Fix: Add a separate check for empty files:

for (const file of incoming) {
  if (file.size <= 0) {
    emptyCount++
  } else if (file.size > MAX_DOCUMENT_FILE_BYTES) {
    oversizedCount++
  } else if (!isAcceptedFile(file)) {
    unsupportedCount++
  } else {
    accepted.push(file)
  }
}

Then add appropriate toast message for empty files.

Suggested change
for (const file of incoming) {
if (file.size > MAX_DOCUMENT_FILE_BYTES) {
oversizedCount++
} else if (!isAcceptedFile(file)) {
unsupportedCount++
} else {
accepted.push(file)
}
}
for (const file of incoming) {
if (file.size <= 0) {
emptyCount++
} else if (file.size > MAX_DOCUMENT_FILE_BYTES) {
oversizedCount++
} else if (!isAcceptedFile(file)) {
unsupportedCount++
} else {
accepted.push(file)
}
}

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

… checks, and 50MB size guard

- Hoist extension and MIME type Set allocations to module scope in document-file-validation
- Add fail-fast 50MB size check to reject oversized uploads prior to network requests
- Support direct MIME lookup for PDFs, images, and standard office documents alongside extension fallback
- Add unit test suite covering extension, MIME, size, and corrupt/empty file validation

Fixes supermemoryai#1559
@Sruhvx-jpg
Sruhvx-jpg force-pushed the perf/web-file-upload-validation branch from 89f3e36 to 0bf6801 Compare August 19, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(web): optimize document upload validation with static sets, MIME checks, and 50MB size guard

1 participant