| 688 | } |
| 689 | |
| 690 | function decodePayload( |
| 691 | payloadBy: Map<string, PayloadDecoder>, |
| 692 | httpRequest: HttpServerRequest, |
| 693 | query: Record<string, string | Array<string>> |
| 694 | ): Effect.Effect<unknown, Schema.SchemaError, unknown> | HttpServerResponse | undefined { |
| 695 | const hasBody = HttpMethod.hasBody(httpRequest.method) |
| 696 | const contentType = hasBody |
| 697 | ? MediaType.normalize(httpRequest.headers["content-type"] ?? "application/json") |
| 698 | : "application/x-www-form-urlencoded" |
| 699 | const existing = payloadBy.get(contentType) |
| 700 | if (!existing) { |
| 701 | return Response.text(`Unsupported content-type: ${contentType}`, { status: 415 }) |
| 702 | } |
| 703 | const { _tag, decode } = existing |
| 704 | switch (_tag) { |
| 705 | case "Multipart": { |
| 706 | if (existing.mode === "buffered") { |
| 707 | let eff = Effect.orDie(httpRequest.multipart) |
| 708 | if (existing.limits) { |
| 709 | eff = Effect.provideContext(eff, Multipart.limitsServices(existing.limits)) |
| 710 | } |
| 711 | return Effect.flatMap(eff, decode) |
| 712 | } |
| 713 | return Effect.succeed( |
| 714 | existing.limits |
| 715 | ? Stream.provideContext(httpRequest.multipartStream, Multipart.limitsServices(existing.limits)) |
| 716 | : httpRequest.multipartStream |
| 717 | ) |
| 718 | } |
| 719 | case "Json": |
| 720 | return Effect.flatMap(Effect.orDie(httpRequest.text), (text) => { |
| 721 | if (text === "") { |
| 722 | return decode(existing.nullOnEmpty ? null : undefined) |
| 723 | } |
| 724 | try { |
| 725 | return decode(JSON.parse(text)) |
| 726 | } catch { |
| 727 | return Effect.fail( |
| 728 | new Schema.SchemaError( |
| 729 | new SchemaIssue.InvalidValue({ message: "Expected a valid JSON body" }) |
| 730 | ) |
| 731 | ) |
| 732 | } |
| 733 | }) |
| 734 | case "Text": |
| 735 | return Effect.flatMap(Effect.orDie(httpRequest.text), decode) |
| 736 | case "FormUrlEncoded": { |
| 737 | const source = hasBody |
| 738 | ? Effect.map(Effect.orDie(httpRequest.urlParamsBody), UrlParams.toRecord) |
| 739 | : Effect.succeed(query) |
| 740 | return Effect.flatMap(source, decode) |
| 741 | } |
| 742 | case "Uint8Array": |
| 743 | return Effect.flatMap( |
| 744 | Effect.map(Effect.orDie(httpRequest.arrayBuffer), (buffer) => new Uint8Array(buffer)), |
| 745 | decode |
| 746 | ) |
| 747 | } |