import * as Arr from "effect/Array" import * as Effect from "effect/Effect" import * as Effectable from "effect/Effectable" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" import type * as App from "../HttpApp.js" import type * as Multiplex from "../HttpMultiplex.js" import * as Error from "../HttpServerError.js" import * as ServerRequest from "../HttpServerRequest.js" import type * as ServerResponse from "../HttpServerResponse.js" /** @internal */ export const TypeId: Multiplex.TypeId = Symbol.for("@effect/platform/HttpMultiplex") as Multiplex.TypeId class MultiplexImpl extends Effectable.Class< ServerResponse.HttpServerResponse, E | Error.RouteNotFound, R | ServerRequest.HttpServerRequest > implements Multiplex.HttpMultiplex { readonly [TypeId]: Multiplex.TypeId constructor( readonly apps: ReadonlyArray< readonly [ predicate: (request: ServerRequest.HttpServerRequest) => Effect.Effect, app: App.Default ] > ) { super() this[TypeId] = TypeId let execute: (request: ServerRequest.HttpServerRequest) => App.Default = (request) => Effect.fail(new Error.RouteNotFound({ request })) for (let i = apps.length - 1; i >= 0; i--) { const [predicate, app] = apps[i] const previous = execute execute = (request) => Effect.flatMap( predicate(request), (match) => match ? app : previous(request) ) } this.execute = Effect.flatMap(ServerRequest.HttpServerRequest, execute) } execute: App.Default commit() { return this.execute } [Inspectable.NodeInspectSymbol]() { return Inspectable.toJSON(this) } toString(): string { return Inspectable.format(this) } toJSON(): unknown { return { _id: "@effect/platform/HttpMultiplex" } } } /** @internal */ export const empty: Multiplex.HttpMultiplex = new MultiplexImpl([]) /** @internal */ export const make = ( apps: Iterable< readonly [ predicate: (request: ServerRequest.HttpServerRequest) => Effect.Effect, app: App.Default ] > ): Multiplex.HttpMultiplex => new MultiplexImpl(Arr.fromIterable(apps)) /** @internal */ export const add = dual< ( predicate: (request: ServerRequest.HttpServerRequest) => Effect.Effect, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, predicate: (request: ServerRequest.HttpServerRequest) => Effect.Effect, app: App.Default ) => Multiplex.HttpMultiplex >( 3, (self, predicate, app) => make([ ...self.apps, [predicate, app] ] as any) ) /** @internal */ export const headerExact = dual< ( header: string, value: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, header: string, value: string, app: App.Default ) => Multiplex.HttpMultiplex >( 4, (self, header, value, app) => add(self, (req) => req.headers[header] !== undefined ? Effect.succeed(req.headers[header] === value) : Effect.succeed(false), app) ) /** @internal */ export const headerRegex = dual< ( header: string, regex: RegExp, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, header: string, regex: RegExp, app: App.Default ) => Multiplex.HttpMultiplex >( 4, (self, header, regex, app) => add(self, (req) => req.headers[header] !== undefined ? Effect.succeed(regex.test(req.headers[header])) : Effect.succeed(false), app) ) /** @internal */ export const headerStartsWith = dual< ( header: string, prefix: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, header: string, prefix: string, app: App.Default ) => Multiplex.HttpMultiplex >( 4, (self, header, prefix, app) => add(self, (req) => req.headers[header] !== undefined ? Effect.succeed(req.headers[header].startsWith(prefix)) : Effect.succeed(false), app) ) /** @internal */ export const headerEndsWith = dual< ( header: string, suffix: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, header: string, suffix: string, app: App.Default ) => Multiplex.HttpMultiplex >( 4, (self, header, suffix, app) => add(self, (req) => req.headers[header] !== undefined ? Effect.succeed(req.headers[header].endsWith(suffix)) : Effect.succeed(false), app) ) /** @internal */ export const hostRegex = dual< ( regex: RegExp, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, regex: RegExp, app: App.Default ) => Multiplex.HttpMultiplex >(3, (self, regex, app) => headerRegex(self, "host", regex, app)) /** @internal */ export const hostStartsWith = dual< ( prefix: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, prefix: string, app: App.Default ) => Multiplex.HttpMultiplex >(3, (self, prefix, app) => headerStartsWith(self, "host", prefix, app)) /** @internal */ export const hostEndsWith = dual< ( suffix: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, suffix: string, app: App.Default ) => Multiplex.HttpMultiplex >(3, (self, suffix, app) => headerEndsWith(self, "host", suffix, app)) /** @internal */ export const hostExact = dual< ( host: string, app: App.Default ) => (self: Multiplex.HttpMultiplex) => Multiplex.HttpMultiplex, ( self: Multiplex.HttpMultiplex, host: string, app: App.Default ) => Multiplex.HttpMultiplex >(3, (self, host, app) => headerExact(self, "host", host, app))