/**
 * The `HttpApiMiddleware` module defines middleware service keys for
 * declarative HTTP APIs. Middleware wraps endpoint execution on the server and,
 * when required by an API, can also wrap requests made by generated clients.
 *
 * Middleware is where you put cross-cutting behavior that belongs to the API
 * contract: authentication, authorization, request logging, tracing, rate
 * limiting, request-scoped services, schema-error normalization, and client
 * request decoration.
 *
 * **Mental model**
 *
 * - A middleware is declared with {@link Service}; the declaration is both a
 *   `Context.Service` key and a typed description of the middleware contract.
 * - Server implementations receive the endpoint response effect plus endpoint
 *   and group metadata. Security middleware also receives decoded credentials
 *   from the configured `HttpApiSecurity` scheme.
 * - The service metadata records provided services, required services, declared
 *   error schemas, client error types, security schemes, and whether generated
 *   clients must install a matching client middleware.
 * - Client middleware is published through the {@link ForClient} marker by
 *   {@link layerClient} and participates in the generated client's request
 *   pipeline.
 *
 * **Common tasks**
 *
 * - Declare middleware services with {@link Service}.
 * - Detect security middleware with {@link isSecurity}.
 * - Convert `HttpApiSchemaError` failures into declared API errors with
 *   {@link layerSchemaErrorTransform}.
 * - Install a generated-client middleware with {@link layerClient}.
 *
 * **Gotchas**
 *
 * - Middleware error declarations must be `Schema` values, or arrays of
 *   `Schema` values, because builders and clients encode and decode them as
 *   part of the endpoint error surface.
 * - A security middleware must declare at least one security scheme. Ordinary
 *   middleware does not receive credentials.
 * - A schema-error transform must fail with an error covered by the middleware's
 *   declared error schema.
 * - `requiredForClient` adds a client-side requirement. Provide it with
 *   {@link layerClient}; the layer captures its surrounding context.
 *
 * **See also**
 *
 * - {@link Service} for declaring middleware service keys.
 * - {@link layerSchemaErrorTransform} for mapping schema failures.
 * - {@link layerClient} for generated-client middleware.
 *
 * @since 4.0.0
 */
/** @effect-diagnostics floatingEffect:skip-file */
/** @effect-diagnostics classSelfMismatch:off */
import * as Context from "../../Context.js";
import * as Effect from "../../Effect.js";
import * as Layer from "../../Layer.js";
import { hasProperty } from "../../Predicate.js";
import { Scope } from "../../Scope.js";
import { HttpApiSchemaError } from "./HttpApiError.js";
const TypeId = "~effect/httpapi/HttpApiMiddleware";
const SecurityTypeId = "~effect/httpapi/HttpApiMiddleware/Security";
/**
 * Returns `true` when an HTTP API middleware service is security middleware.
 *
 * @category guards
 * @since 4.0.0
 */
export const isSecurity = u => hasProperty(u, SecurityTypeId);
/**
 * Creates a `Context.Service` class for an HTTP API middleware implementation.
 *
 * **When to use**
 *
 * Use when you use the optional configuration to declare required services, provided services,
 * typed error schemas, security schemes, client errors, and whether generated
 * clients must provide a matching client middleware.
 *
 * @category schemas
 * @since 4.0.0
 */
export const Service = () => (id, options) => {
  const Err = globalThis.Error;
  const limit = Err.stackTraceLimit;
  Err.stackTraceLimit = 2;
  const creationError = new Err();
  Err.stackTraceLimit = limit;
  class Service extends Context.Service()(id) {}
  const self = Service;
  Object.defineProperty(Service, "stack", {
    get() {
      return creationError.stack;
    }
  });
  self[TypeId] = TypeId;
  self.error = getError(options?.error);
  self.requiredForClient = options?.requiredForClient ?? false;
  if (options?.security !== undefined) {
    if (Object.keys(options.security).length === 0) {
      throw new Error("HttpApiMiddleware.Service: security object must not be empty");
    }
    self[SecurityTypeId] = SecurityTypeId;
    self.security = options.security;
  }
  return self;
};
function getError(error) {
  if (error === undefined) return new Set();
  return new Set(Array.isArray(error) ? error : [error]);
}
/**
 * Creates a middleware layer that transforms `HttpApiSchemaError` failures.
 *
 * **Details**
 *
 * The middleware catches schema errors produced while running an endpoint and uses
 * the supplied `transform` function to convert them into the middleware's declared
 * error schema.
 *
 * **Example** (Mapping schema errors to custom errors)
 *
 * ```ts
 * import { Effect, Schema } from "effect"
 * import { HttpApiMiddleware } from "effect/unstable/httpapi"
 *
 * export class CustomError extends Schema.TaggedErrorClass<CustomError>()("CustomError", {}) {}
 *
 * export class ErrorHandler extends HttpApiMiddleware.Service<ErrorHandler>()("api/ErrorHandler", {
 *   error: CustomError
 * }) {}
 *
 * export const ErrorHandlerLayer = HttpApiMiddleware.layerSchemaErrorTransform(
 *   ErrorHandler,
 *   (schemaError) =>
 *     Effect.log("Got SchemaError", schemaError).pipe(
 *       Effect.andThen(Effect.fail(new CustomError()))
 *     )
 * )
 * ```
 *
 * @category SchemaError transform
 * @since 4.0.0
 */
export const layerSchemaErrorTransform = (service, transform) => Layer.succeed(service, (httpEffect, options) => Effect.catch(httpEffect, e => HttpApiSchemaError.is(e) ? transform(e, options) : Effect.fail(e)));
/**
 * Provides a client-side middleware implementation for a middleware that is required by generated clients.
 *
 * **Details**
 *
 * The layer captures the surrounding services and makes the middleware available
 * through the `ForClient` service marker used by HTTP API clients.
 *
 * @category client
 * @since 4.0.0
 */
export const layerClient = (tag, service) => Layer.effectContext(Effect.gen(function* () {
  const services = (yield* Effect.context()).pipe(Context.omit(Scope));
  const middleware = Effect.isEffect(service) ? yield* service : service;
  return Context.makeUnsafe(new Map([[`${tag.key}/Client`, options => Effect.updateContext(middleware(options), requestContext => Context.merge(services, requestContext))]]));
}));
//# sourceMappingURL=HttpApiMiddleware.js.map