/** * @since 1.0.0 */ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" import * as Option from "effect/Option" import type * as ParseResult from "effect/ParseResult" import * as Schema from "effect/Schema" import type { ParseOptions } from "effect/SchemaAST" import type * as Stream from "effect/Stream" import * as FileSystem from "./FileSystem.js" import type * as Headers from "./Headers.js" import * as UrlParams from "./UrlParams.js" /** * @since 1.0.0 * @category type ids */ export const TypeId: unique symbol = Symbol.for("@effect/platform/HttpIncomingMessage") /** * @since 1.0.0 * @category type ids */ export type TypeId = typeof TypeId /** * @since 1.0.0 * @category models */ export interface HttpIncomingMessage extends Inspectable.Inspectable { readonly [TypeId]: TypeId readonly headers: Headers.Headers readonly remoteAddress: Option.Option readonly json: Effect.Effect readonly text: Effect.Effect readonly urlParamsBody: Effect.Effect readonly arrayBuffer: Effect.Effect readonly stream: Stream.Stream } /** * @since 1.0.0 * @category schema */ export const schemaBodyJson = (schema: Schema.Schema, options?: ParseOptions | undefined) => { const parse = Schema.decodeUnknown(schema, options) return (self: HttpIncomingMessage): Effect.Effect => Effect.flatMap(self.json, parse) } /** * @since 1.0.0 * @category schema */ export const schemaBodyUrlParams = < A, I extends Readonly | undefined>>, R >( schema: Schema.Schema, options?: ParseOptions | undefined ) => { const decode = UrlParams.schemaStruct(schema, options) return (self: HttpIncomingMessage): Effect.Effect => Effect.flatMap(self.urlParamsBody, decode) } /** * @since 1.0.0 * @category schema */ export const schemaHeaders = >, R>( schema: Schema.Schema, options?: ParseOptions | undefined ) => { const parse = Schema.decodeUnknown(schema, options) return (self: HttpIncomingMessage): Effect.Effect => parse(self.headers) } /** * @since 1.0.0 * @category fiber refs */ export class MaxBodySize extends Context.Reference()("@effect/platform/HttpIncomingMessage/MaxBodySize", { defaultValue: Option.none }) {} /** * @since 1.0.0 * @category fiber refs */ export const withMaxBodySize = dual< /** * @since 1.0.0 * @category fiber refs */ (size: Option.Option) => (effect: Effect.Effect) => Effect.Effect, /** * @since 1.0.0 * @category fiber refs */ (effect: Effect.Effect, size: Option.Option) => Effect.Effect >(2, (effect, size) => Effect.provideService(effect, MaxBodySize, Option.map(size, FileSystem.Size))) /** * @since 1.0.0 */ export const inspect = (self: HttpIncomingMessage, that: object): object => { const contentType = self.headers["content-type"] ?? "" let body: unknown if (contentType.includes("application/json")) { try { body = Effect.runSync(self.json) } catch { // } } else if (contentType.includes("text/") || contentType.includes("urlencoded")) { try { body = Effect.runSync(self.text) } catch { // } } const obj: any = { ...that, headers: Inspectable.redact(self.headers), remoteAddress: self.remoteAddress.toJSON() } if (body !== undefined) { obj.body = body } return obj }