1/**
2 * Web-standard helpers for parsing HTTP `multipart/form-data` request bodies.
3 *
4 * This module mirrors `BunMultipart`, adapting a web `Request` body and headers
5 * into the shared `Multipart` model. `stream` returns multipart parts as a
6 * `Stream`, while `persisted` collects the form and writes file parts to scoped
7 * temporary files through the current `FileSystem`, `Path`, and `Scope`
8 * services.
9 *
10 * @since 4.0.0
11 */
12import type * as Effect from "effect/Effect"
13import type { FileSystem } from "effect/FileSystem"
14import * as Multipart from "effect/http/Multipart"
15import type { Path } from "effect/Path"
16import type * as Scope from "effect/Scope"
17import * as Stream from "effect/Stream"
18
19/**
20 * Parses a web `Request` body as multipart data and returns a stream of multipart parts.
21 *
22 * @category constructors
23 * @since 4.0.0
24 */
25export const stream = (source: Request): Stream.Stream<Multipart.Part, Multipart.MultipartError> =>
26 Stream.fromReadableStream({
27 evaluate: () =>
28 source.body ?? new ReadableStream({
29 start(controller) {
30 controller.enqueue(new Uint8Array())
31 controller.close()
32 }
33 }),
34 onError: (cause) => Multipart.MultipartError.fromReason("InternalError", cause)
35 }).pipe(
36 Stream.pipeThroughChannel(Multipart.makeChannel(Object.fromEntries(source.headers)))
37 )
38
39/**
40 * Parses and persists multipart data from a web `Request`, requiring file-system, path, and scope services.
41 *
42 * @category constructors
43 * @since 4.0.0
44 */
45export const persisted = (
46 source: Request
47): Effect.Effect<
48 Multipart.Persisted,
49 Multipart.MultipartError,
50 | FileSystem
51 | Path
52 | Scope.Scope
53> => Multipart.toPersisted(stream(source))