Ctrl K
ComunidadGuías…

src/features/route-gates.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * The gates and policies that take their roles from a declared route. A gate
 * names its route by the declaration's id, or by the path its route table key
 * uses (see "Keep methods flat" in AGENTS.md). `admin-surface/areas.ts` stays
 * the one place a role is written down, and
 * `test/integration/admin-role-matrix.test.ts` proves each gate still agrees
 * with its declaration.
 */

import { adminDestinationAt } from "#shared/admin-surface.ts";
import type { Flash } from "#shared/flash-context.ts";
import type { FormParams } from "#shared/form-data.ts";
import type { RequestRoute, ResponseHandler } from "#shared/response-steps.ts";
import {
  type AuthPolicy,
  type AuthSession,
  authPage,
  authResponsePage,
  formGuard,
  formPolicyFor,
  type Guard,
  gatedPost,
  pageGuardFor,
  type SessionGuard,
} from "./auth.ts";

/** A GET page's gate at a declared route. */
export const pageGuardAt = (route: string): SessionGuard<AuthSession> =>
  pageGuardFor(adminDestinationAt(route));

/** A form POST's policy at a declared route. */
export const formPolicyAt = (route: string): AuthPolicy<"form"> =>
  formPolicyFor(adminDestinationAt(route));

/** A form POST's gate at a declared route. */
export const formGuardAt = (
  route: string,
): Guard<[session: AuthSession, form: FormParams]> =>
  formGuard(formPolicyAt(route));

/** A POST route at a declared route whose handler returns the full Response.
 * The roles come from the route's declaration. */
export const gatedPostAt = (route: string) => gatedPost(formPolicyAt(route));

/** Run a session handler under a page's gate, the audience from the route's
 * declaration. */
export const pageGuardRun = (
  route: string,
  request: Request,
  run: (session: AuthSession) => Promise<Response>,
): Promise<Response> => pageGuardAt(route)(request, run);

/** A GET page at a declared route, guarded by the roles that route
 * declares. */
export const authPageAt = (route: string) => authPage(pageGuardAt(route));

/** What a response page's builder returns from: the session, the request, and
 * the flashed message values. */
type ResponsePageBuilder = ResponseHandler<
  [session: AuthSession, request: Request, flash: Flash]
>;

/** One guarded GET route, keyed by the pattern it serves, ready to spread into
 * a route table. */
type AuthResponsePageRoutes<Pattern extends string> = {
  [K in `GET ${Pattern}`]: RequestRoute;
};

/** A GET page whose builder returns the full Response. The page can then 404
 * or redirect instead of rendering one. The route key comes from the pattern,
 * so a page is always bound where its gate read the roles. */
export const authResponsePageRoute =
  <Pattern extends string>(pattern: Pattern) =>
  (build: ResponsePageBuilder): AuthResponsePageRoutes<Pattern> =>
    ({
      [`GET ${pattern}`]: authResponsePage(pageGuardAt(pattern))(build),
    }) as AuthResponsePageRoutes<Pattern>;