diff --git a/.changeset/gentle-ears-pay.md b/.changeset/gentle-ears-pay.md new file mode 100644 index 00000000000..e6108bde2f6 --- /dev/null +++ b/.changeset/gentle-ears-pay.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +accept Headers.Input in HttpServerResponse constructors diff --git a/.changeset/gold-seals-suffer.md b/.changeset/gold-seals-suffer.md new file mode 100644 index 00000000000..6358a759c15 --- /dev/null +++ b/.changeset/gold-seals-suffer.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +add HttpServerResponse.redirect api diff --git a/packages/platform/src/HttpServerResponse.ts b/packages/platform/src/HttpServerResponse.ts index 283de3adf0d..67a1773d37b 100644 --- a/packages/platform/src/HttpServerResponse.ts +++ b/packages/platform/src/HttpServerResponse.ts @@ -50,7 +50,7 @@ export interface HttpServerResponse extends Effect.Effect, I export interface Options { readonly status?: number | undefined readonly statusText?: string | undefined - readonly headers?: Headers.Headers | undefined + readonly headers?: Headers.Input | undefined readonly cookies?: Cookies | undefined readonly contentType?: string | undefined readonly contentLength?: number | undefined @@ -84,6 +84,13 @@ export const isServerResponse: (u: unknown) => u is HttpServerResponse = interna */ export const empty: (options?: Options.WithContent | undefined) => HttpServerResponse = internal.empty +/** + * @since 1.0.0 + * @category constructors + */ +export const redirect: (location: string, options?: Options.WithContentType | undefined) => HttpServerResponse = + internal.redirect + /** * @since 1.0.0 * @category constructors diff --git a/packages/platform/src/internal/httpPlatform.ts b/packages/platform/src/internal/httpPlatform.ts index 394a81b727b..3194524be0e 100644 --- a/packages/platform/src/internal/httpPlatform.ts +++ b/packages/platform/src/internal/httpPlatform.ts @@ -49,7 +49,11 @@ export const make = (impl: { Effect.map(({ etag, info }) => { const start = Number(options?.offset ?? 0) const end = options?.bytesToRead !== undefined ? start + Number(options.bytesToRead) : undefined - const headers = Headers.set(options?.headers ?? Headers.empty, "etag", Etag.toString(etag)) + const headers = Headers.set( + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + "etag", + Etag.toString(etag) + ) if (info.mtime._tag === "Some") { ;(headers as any)["last-modified"] = info.mtime.value.toUTCString() } @@ -69,7 +73,7 @@ export const make = (impl: { fileWebResponse(file, options) { return Effect.map(etagGen.fromFileWeb(file), (etag) => { const headers = Headers.merge( - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, Headers.unsafeFromRecord({ etag: Etag.toString(etag), "last-modified": new Date(file.lastModified).toUTCString() diff --git a/packages/platform/src/internal/httpServerResponse.ts b/packages/platform/src/internal/httpServerResponse.ts index 0d652f0b239..ec7a414fbd1 100644 --- a/packages/platform/src/internal/httpServerResponse.ts +++ b/packages/platform/src/internal/httpServerResponse.ts @@ -91,36 +91,60 @@ export const empty = (options?: ServerResponse.Options.WithContent | undefined): new ServerResponseImpl( options?.status ?? 204, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, internalBody.empty ) +/** @internal */ +export const redirect = ( + location: string, + options?: ServerResponse.Options.WithContentType | undefined +): ServerResponse.HttpServerResponse => { + const headers = Headers.unsafeFromRecord({ location }) + return new ServerResponseImpl( + options?.status ?? 301, + options?.statusText, + options?.headers ? + Headers.merge( + headers, + Headers.fromInput(options.headers) + ) : + headers, + options?.cookies ?? Cookies.empty, + internalBody.empty + ) +} + /** @internal */ export const uint8Array = ( body: Uint8Array, options?: ServerResponse.Options.WithContentType -): ServerResponse.HttpServerResponse => - new ServerResponseImpl( +): ServerResponse.HttpServerResponse => { + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + return new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + headers, options?.cookies ?? Cookies.empty, - internalBody.uint8Array(body, getContentType(options)) + internalBody.uint8Array(body, getContentType(options, headers)) ) +} /** @internal */ export const text = ( body: string, options?: ServerResponse.Options.WithContentType -): ServerResponse.HttpServerResponse => - new ServerResponseImpl( +): ServerResponse.HttpServerResponse => { + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + return new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + headers, options?.cookies ?? Cookies.empty, - internalBody.text(body, getContentType(options)) + internalBody.text(body, getContentType(options, headers)) ) +} /** @internal */ export const html: { @@ -177,7 +201,7 @@ export const json = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, body )) @@ -190,7 +214,7 @@ export const unsafeJson = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, internalBody.unsafeJson(body) ) @@ -209,7 +233,7 @@ export const schemaJson = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, body )) @@ -245,7 +269,7 @@ export const urlParams = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, internalBody.text(UrlParams.toString(UrlParams.fromInput(body)), "application/x-www-form-urlencoded") ) @@ -255,7 +279,7 @@ export const raw = (body: unknown, options?: ServerResponse.Options | undefined) new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, internalBody.raw(body) ) @@ -268,7 +292,7 @@ export const formData = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty, options?.cookies ?? Cookies.empty, internalBody.formData(body) ) @@ -277,21 +301,26 @@ export const formData = ( export const stream = ( body: Stream.Stream, options?: ServerResponse.Options | undefined -): ServerResponse.HttpServerResponse => - new ServerResponseImpl( +): ServerResponse.HttpServerResponse => { + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + return new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ?? Headers.empty, + headers, options?.cookies ?? Cookies.empty, - internalBody.stream(body, getContentType(options), options?.contentLength) + internalBody.stream(body, getContentType(options, headers), options?.contentLength) ) +} /** @internal */ -export const getContentType = (options?: ServerResponse.Options | undefined): string | undefined => { +export const getContentType = ( + options: ServerResponse.Options | undefined, + headers: Headers.Headers +): string | undefined => { if (options?.contentType) { return options.contentType } else if (options?.headers) { - return options.headers["content-type"] + return headers["content-type"] } else { return }