## Demo: file-upload ### FileUploadDemo.svelte ```svelte

With {'{@attach enhance(...)}'}

Plain HTML

``` ### FileUpload.svelte ```svelte

{label}

{#if errorMessage} {/if} {#if fileResult}

{fileResult.filename} — {fileResult.size} bytes

{fileResult.content}
{/if}
{#if isHydratable} {selectedName ?? 'No file chosen'} {/if}
``` ### routes.ts ```ts import { Mochi, fail, success } from 'mochi-framework'; import type { MochiRouteValue } from 'mochi-framework'; export const routes: Record = { '/demos/file-upload': Mochi.page('./src/demos/file-upload/FileUploadDemo.svelte', { actions: { uploadFile: async ({ formData }) => { const file = formData.get('file'); if (!(file instanceof File) || file.size === 0) { return fail(400, { error: 'No file selected' }); } const ext = file.name.split('.').pop()?.toLowerCase() ?? ''; if (ext !== 'txt' && ext !== 'md') { return fail(400, { error: 'Only .txt and .md files are accepted' }); } if (file.size > 100 * 1024) { return fail(400, { error: 'File too large (max 100 KB)' }); } const content = await file.text(); return success({ filename: file.name, content, size: file.size }); }, }, }), }; ``` ### index.ts ```ts import { Mochi, logger } from 'mochi-framework'; await Mochi.serve({ port: 3333, development: process.env.MODE === 'development', routes: { '/': Mochi.page('./src/Home.svelte'), }, }); logger.info('Server running at http://localhost:3333'); ```