MIME/HTTP helpers — structured headers (RFC 7231 §3.1.1, RFC 5987) and
multipart/*bodies (form-data, mixed).
Module mime | Source packages/front/fw/src/io/codec/mime.js | Deps utf8, random | Worker-safe yes
Scope: JS web framework. Covers practical client/server-in-browser needs:
- Parse / format header values with parameters (
Content-Type,Content-Disposition,Accept…) - Parse / format a header block (HTTP response, multipart part)
- Encode / decode
multipart/form-databodies (upload) andmultipart/mixed(multi-resource response) - Random boundary generation
Out of scope: quoted-printable, base64 transfer-encoding (→ b64), RFC 2047 encoded-word (email).
Resolve
const mime = runtime.resolve('mime');
// Returns: { parseHeader, formatHeader, parseHeaders, formatHeaders,
// encode, decode, randomBoundary }
API
| Method | Signature | Returns |
|---|---|---|
parseHeader |
(s: string) => {value, params} |
Parses a structured header |
formatHeader |
(value | {value, params}, params?) => string |
Serializes (quotes when needed) |
parseHeaders |
(text: string) => Object |
Parses a CRLF block (name → value, lowercased) |
formatHeaders |
(headers: Object) => string |
Emits Name: value\r\n (Title-Case) |
encode |
(parts, options?) => {body, boundary, contentType} |
Builds a multipart body |
decode |
(bytes: Uint8Array, boundary: string) => Part[] |
Decomposes a multipart body |
randomBoundary |
() => string |
Unique random boundary |
Type Part : { headers: Object, body: Uint8Array }.
Examples
Parse a structured header
mime.parseHeader('multipart/form-data; boundary=abc; charset=utf-8');
// { value: 'multipart/form-data', params: { boundary: 'abc', charset: 'utf-8' } }
mime.parseHeader('form-data; name="file"; filename="a.txt"');
// { value: 'form-data', params: { name: 'file', filename: 'a.txt' } }
// RFC 5987 extended param (UTF-8 encoded) — automatically decoded
mime.parseHeader("attachment; filename*=UTF-8''caf%C3%A9.txt");
// { value: 'attachment', params: { filename: 'café.txt' } }
Parameter names are lowercased. If both name*= and name= are present, the extended form (RFC 5987) takes precedence.
Format a header
mime.formatHeader('text/plain', { charset: 'utf-8' });
// 'text/plain; charset=utf-8'
// Automatic quoting when the value contains a non-token character
mime.formatHeader('form-data', { name: 'a b', filename: 'my "file".txt' });
// 'form-data; name="a b"; filename="my \\"file\\".txt"'
Upload form-data
const { utf8, mime } = fw.runtime.resolveAll(['utf8', 'mime']);
const { body, contentType } = mime.encode([
{
headers: { 'content-disposition': 'form-data; name="user"' },
body: 'alice'
},
{
headers: {
'content-disposition': 'form-data; name="avatar"; filename="me.png"',
'content-type': 'image/png'
},
body: pngBytes // Uint8Array
}
]);
await fetch('/upload', {
method: 'POST',
headers: { 'Content-Type': contentType },
body
});
Parse a multipart response
const response = await fetch('/byteranges-resource');
const ct = mime.parseHeader(response.headers.get('content-type'));
// ct.value === 'multipart/byteranges'
// ct.params.boundary === 'xyz'
const bytes = new Uint8Array(await response.arrayBuffer());
const parts = mime.decode(bytes, ct.params.boundary);
for (const part of parts) {
const range = part.headers['content-range'];
console.log(range, part.body.length, 'bytes');
}
Parse a header block
const raw = 'Content-Type: text/plain\r\nContent-Length: 42\r\nX-Foo: a\r\nX-Foo: b';
mime.parseHeaders(raw);
// { 'content-type': 'text/plain', 'content-length': '42', 'x-foo': 'a, b' }
Names are always lowercased. Multiple values for the same name are joined with ', ' (RFC 7230 §3.2.2). Folded lines (obsolete line-folding) are tolerated during decoding.
Worker Usage
const worker = fw.createWorker(
function ({ libs, args }) {
const [bytesArray, boundary] = args;
const parts = libs.mime.decode(new Uint8Array(bytesArray), boundary);
self.postMessage(parts.map(p => ({
headers: p.headers,
bodyLength: p.body.length
})));
},
{ dependencies: ['mime'], args: [Array.from(bodyBytes), boundary] }
);
Notes
- The
decodeparser accepts a preamble before the first boundary (RFC 2046 spec). decodetolerates LF or CRLF after a boundary;encodealways emits CRLF (strict RFC).- For uploads where native
FormDatasuffices, prefer the browser API —mime.encodeis useful when you need precise control over framing, custom per-part headers, ormultipart/mixedbodies. randomBoundaryuses the CSPRNG from therandommodule (8 bytes viarandom.bytes(8)→ 16 hex chars). If the entropy source is unavailable,randomBoundaryreturnsfalse;encode(parts, { boundary })lets you supply a custom boundary.- The
multipart/byterangesfamily (HTTPRangemulti-range response) is decoded with the samedecodeby passing the boundary extracted fromContent-Type.