{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/server/generic/index.ts"],"sourcesContent":["import type { IamEngine } from '../../core'\nimport type { IamClient, IamRequest } from '../../core/types'\n\n/**\n * Shared admin-mutation audit event shape.\n *\n * Every framework adapter (express, hono, next, nest) accepts an optional\n * `onAdminMutation` callback in its admin-router options. The callback fires\n * once per mutation (PUT/POST/DELETE/PATCH) after the handler completes,\n * regardless of success or failure. It is fire-and-forget - adapters never\n * `await` it inline - so a slow or throwing hook can never block, fail, or\n * leak timing information back to the caller. Errors inside the hook are\n * caught and one-line-logged via `console.error`.\n *\n * GET (read) handlers never fire the hook.\n *\n * Rate-limit throttling is out of scope; callers compose their own rate-limit\n * middleware around the admin router. See each adapter's JSDoc for a pattern.\n */\nexport namespace IamAdminAudit {\n  /** Categorical action describing what changed. */\n  export type Action = 'create' | 'update' | 'delete' | 'replace'\n  /** Categorical target describing what kind of object was changed. */\n  export type Target = 'policy' | 'role' | 'assignment' | 'role-assignment' | 'attributes'\n\n  /**\n   * Describes a single admin mutation event.\n   *\n   * Field-level semantics worth calling out:\n   *\n   * - `path` - By default carries the request URL **including any expanded\n   *   route parameters** (e.g. `/admin/policies/policy-123/tenant-acme`).\n   *   That string therefore can contain tenant IDs, subject IDs, role IDs,\n   *   and other potentially sensitive identifiers. To redact, pass\n   *   {@link IOptions.redactPath} on the adapter's admin options.\n   * - `error` - By default this is the **error class name only** (e.g.\n   *   `'TypeError'`, `'PolicyValidationError'`), NOT `err.message`. The\n   *   message can leak credentials, query fragments, or SQL when the\n   *   downstream throw originates in a DB driver. To restore the full\n   *   message, pass {@link IOptions.includeErrorMessage} `true` on the\n   *   adapter's admin options.\n   */\n  export interface IEvent {\n    /** Whatever the adapter's `authorize` callback returned (often a user/JWT claims object). */\n    actor?: unknown\n    /** Semantic verb. */\n    action: Action\n    /** Semantic noun. */\n    target: Target\n    /** Optional identifier of the target object (e.g. policy id, subject id). */\n    targetId?: string\n    /** Event timestamp from `Date.now()`. */\n    ts: number\n    /** HTTP method that triggered the mutation. */\n    method: string\n    /**\n     * HTTP path that triggered the mutation. By default this is the raw\n     * request path with route parameters already expanded (so it may include\n     * tenant IDs, subject IDs, etc.). Use {@link IOptions.redactPath} to\n     * strip or rewrite identifiers before the hook sees the value.\n     */\n    path: string\n    /** Whether the handler completed without throwing. */\n    success: boolean\n    /**\n     * Stringified error indicator when `success === false`. Defaults to the\n     * thrown value's class name (e.g. `'TypeError'`). Set\n     * {@link IOptions.includeErrorMessage} `true` on the adapter options to\n     * write `err.message` instead.\n     */\n    error?: string\n  }\n\n  /** Audit-hook signature. Sync or async; never awaited by the adapter. */\n  export type Hook = (event: IEvent) => void | Promise<void>\n\n  /**\n   * Shared audit-hook hardening options.\n   *\n   * Every framework adapter's admin-router options interface composes this\n   * shape, so the hardening surface is identical across express/hono/next/\n   * nest. All fields are optional and additive - the legacy hook behaviour\n   * is preserved when none are supplied.\n   */\n  export interface IOptions {\n    /**\n     * Optional redactor applied to {@link IEvent.path} before the hook\n     * receives the event.\n     *\n     * The default `event.path` carries the request URL including expanded\n     * route parameters - e.g. `/admin/policies/policy-123/tenant-acme` -\n     * which means tenant IDs, subject IDs and role IDs can flow into audit\n     * sinks unredacted. Supply a redactor when your audit sink lives outside\n     * your trust boundary.\n     *\n     * @example\n     * ```ts\n     * // Replace the last segment with `:id`.\n     * redactPath: (p) => p.replace(/\\/[^/]+$/, '/:id')\n     * ```\n     */\n    redactPath?: (path: string) => string\n    /**\n     * Invoked when the hook itself throws (sync or async). The default sink\n     * is `console.error`; supply this to route hook failures into your\n     * logger or metrics pipeline. Errors thrown by `onAuditHookError` itself\n     * are caught and last-resort-logged via `console.error` - they never\n     * propagate.\n     */\n    onAuditHookError?: (err: unknown, event: IEvent) => void\n    /**\n     * When `true`, populate {@link IEvent.error} with `err.message`. The\n     * default is the error **class name** because downstream DB-driver\n     * errors can carry credentials, query fragments, or SQL inside their\n     * message. Only enable this if you control the throw sites and the\n     * audit sink.\n     */\n    includeErrorMessage?: boolean\n    /**\n     * CSRF guard for state-changing admin mutations.\n     *\n     * Default (`undefined`): a built-in `Sec-Fetch-Site` check rejects\n     * cross-site browser requests - browsers populate the header\n     * automatically; its absence indicates a non-browser caller (curl,\n     * server-to-server, native app) and is allowed. This default closes\n     * the most common cookie-auth admin-CSRF vector without operator\n     * action.\n     *\n     * Pass `false` to disable entirely (server-to-server with bearer\n     * tokens / mTLS that intentionally posts cross-site). Pass a function\n     * to supply a stricter check - e.g. an Origin allowlist:\n     *\n     * @example\n     * ```ts\n     * // Default - uses built-in Sec-Fetch-Site check\n     * adminRouter(engine, { authorize })\n     *\n     * // Disable (bearer-token API, no browser involved)\n     * adminRouter(engine, { authorize, csrfCheck: false })\n     *\n     * // Stricter: Origin allowlist\n     * const ADMIN_ORIGINS = new Set(['https://admin.example.com'])\n     * adminRouter(engine, {\n     *   authorize,\n     *   csrfCheck: (req) => ADMIN_ORIGINS.has(req.headers.origin),\n     * })\n     * ```\n     */\n    csrfCheck?: ((req: unknown) => boolean) | false\n  }\n}\n\n/** Per-process latch so the CSRF default notice fires at most once. */\nlet _CSRF_DEFAULT_NOTICED = false\n\n/**\n * Log a one-time notice on first admin-router construction so operators\n * upgrading from 2.0.x are explicitly told the default changed. Suppressed\n * when the operator passed `csrfCheck` (any value including `false`).\n *\n * Called by every framework adapter exactly once at construction.\n */\nexport function iamNoticeCsrfDefaultIfNeeded(csrfCheckPassed: boolean): void {\n  if (csrfCheckPassed || _CSRF_DEFAULT_NOTICED) return\n  _CSRF_DEFAULT_NOTICED = true\n  // eslint-disable-next-line no-console\n  console.info(\n    '[@gentleduck/iam] admin router: default CSRF check enabled - ' +\n      'rejecting browser requests with Sec-Fetch-Site: cross-site|cross-origin. ' +\n      'Pass `csrfCheck: false` for bearer-token/mTLS APIs, or supply a custom ' +\n      'predicate. See SECURITY.md \"Admin router CSRF\" section. (2.1.0 behavior change)',\n  )\n}\n\n/**\n * Default CSRF predicate: reject browser requests whose `Sec-Fetch-Site`\n * header is `'cross-site'` or `'cross-origin'`. Same-origin and same-site\n * requests pass; non-browser callers (no header set) pass.\n *\n * @param req - Any object the adapter can extract a header from.\n * @returns `true` to allow, `false` to reject (403).\n */\n\nexport function iamDefaultCsrfCheck(req: unknown): boolean {\n  const r = req as\n    | {\n        headers?: Record<string, string | string[] | undefined> | { get?: (n: string) => string | null }\n        req?: { header?: (n: string) => string | undefined }\n      }\n    | undefined\n  let site: string | undefined\n  // IamExpress/IamNest-style: req.headers is a Record.\n  const recordHeaders = r?.headers\n  if (recordHeaders && typeof (recordHeaders as { get?: unknown }).get !== 'function') {\n    const v = (recordHeaders as Record<string, string | string[] | undefined>)['sec-fetch-site']\n    site = Array.isArray(v) ? v[0] : v\n  }\n  // Next/fetch-API style: req.headers.get(...).\n  if (!site && recordHeaders && typeof (recordHeaders as { get?: unknown }).get === 'function') {\n    site = (recordHeaders as { get: (n: string) => string | null }).get('sec-fetch-site') ?? undefined\n  }\n  // IamHono style: c.req.header(...).\n  if (!site && r?.req?.header) {\n    site = r.req.header('sec-fetch-site')\n  }\n  if (!site) return true // non-browser caller; let bearer/mTLS auth decide\n  return site !== 'cross-site' && site !== 'cross-origin'\n}\n\n/**\n * Composable admin-mutation audit wrapper. Runs `handler` inside\n * try/catch/finally, capturing success/failure for the audit event and\n * surfacing the operator-friendly error string.\n *\n * Re-throws the original error so the caller's catch can build the\n * framework-specific error response. The audit always fires (via finally).\n *\n * @template T - Handler return type.\n * @param ctx - Audit payload + hooks shared across framework adapters.\n * @param handler - The actual mutation function (e.g. `engine.admin.savePolicy`).\n */\nexport async function iamWithAdminAudit<T>(\n  ctx: {\n    actor: unknown\n    action: IamAdminAudit.Action\n    target: IamAdminAudit.Target\n    targetId?: string\n    method: string\n    path: string\n    onAdminMutation?: IamAdminAudit.Hook\n    redactPath?: (path: string) => string\n    onAuditHookError?: (err: unknown, event: IamAdminAudit.IEvent) => void\n    includeErrorMessage?: boolean\n  },\n  handler: () => Promise<T>,\n): Promise<T> {\n  let success = false\n  let errorMessage: string | undefined\n  try {\n    const out = await handler()\n    success = true\n    return out\n  } catch (err) {\n    errorMessage = iamErrorToAuditString(err, ctx.includeErrorMessage)\n    throw err\n  } finally {\n    iamFireAdminMutation(\n      ctx.onAdminMutation,\n      {\n        actor: ctx.actor,\n        action: ctx.action,\n        target: ctx.target,\n        targetId: ctx.targetId,\n        ts: Date.now(),\n        method: ctx.method,\n        path: ctx.path,\n        success,\n        error: errorMessage,\n      },\n      { redactPath: ctx.redactPath, onAuditHookError: ctx.onAuditHookError },\n    )\n  }\n}\n\n/**\n * Result of {@link iamRunAdminAuthz}. Discriminated union so the framework\n * adapter can branch on the phase and produce its own response.\n */\nexport type IamIAdminAuthzResult =\n  | { phase: 'forbidden' }\n  | { phase: 'unauthorized' }\n  | { phase: 'error'; error: Error }\n  | { phase: 'ok'; actor: unknown }\n\n/**\n * Run the CSRF + authorize phases shared by every admin route.\n * Discriminated-union return lets each framework adapter map to its own\n * response shape (express writes to `res`, hono/next return `Response`, nest\n * throws). The catch arm wraps thrown values into a normal `Error`.\n */\nexport async function iamRunAdminAuthz<TReq>(\n  req: TReq,\n  csrfCheck: ((req: TReq) => boolean) | null,\n  authorize: (req: TReq) => unknown | Promise<unknown>,\n): Promise<IamIAdminAuthzResult> {\n  if (csrfCheck && !csrfCheck(req)) return { phase: 'forbidden' }\n  let actor: unknown\n  try {\n    actor = await authorize(req)\n  } catch (err) {\n    return { phase: 'error', error: err instanceof Error ? err : new Error(String(err)) }\n  }\n  if (!actor) return { phase: 'unauthorized' }\n  return { phase: 'ok', actor }\n}\n\n/**\n * Derive an audit-friendly string from an unknown thrown value.\n *\n * By default returns the constructor name of the thrown value (e.g.\n * `'Error'`, `'TypeError'`, `'PolicyValidationError'`) so credential-bearing\n * `err.message` strings never leak into audit sinks. When\n * `includeMessage === true`, returns `err.message` for `Error` instances and\n * `String(err)` otherwise. Non-Error throws (`undefined`, strings, numbers)\n * are handled defensively.\n *\n * @param err - The thrown value; may not be an `Error` instance.\n * @param includeMessage - When `true`, return the full message instead of the class name.\n * @returns A stable string suitable for {@link IamAdminAudit.IEvent.error}.\n */\nexport function iamErrorToAuditString(err: unknown, includeMessage?: boolean): string {\n  if (includeMessage) {\n    if (err instanceof Error) return err.message\n    if (err === undefined) return 'undefined'\n    if (err === null) return 'null'\n    // Tag non-Error throws and cap length so audit sinks never receive an\n    // unbounded raw value (e.g. a thrown secret string).\n    const raw = typeof err === 'string' ? err : safeStringify(err)\n    const capped = raw.length > NON_ERROR_MESSAGE_CAP ? `${raw.slice(0, NON_ERROR_MESSAGE_CAP)}...` : raw\n    return `<non-Error ${typeof err}> ${capped}`\n  }\n  if (err instanceof Error) {\n    return err.constructor?.name ?? 'Error'\n  }\n  if (err === undefined) return 'undefined'\n  if (err === null) return 'null'\n  // Primitive throw: report its JS typeof so the sink still sees something\n  // categorical (e.g. 'string', 'number') rather than the value itself.\n  return typeof err\n}\n\n/** 256 chars is enough to identify a thrown shape without exfil. */\nconst NON_ERROR_MESSAGE_CAP = 256\n\n/**\n * Safely coerce a non-Error throw to string. Plain `String(obj)` returns\n * `[object Object]` for most objects; we try `JSON.stringify` first to surface\n * useful detail, but swallow circular-ref throws and fall back to `String()`.\n */\nfunction safeStringify(v: unknown): string {\n  try {\n    return JSON.stringify(v) ?? String(v)\n  } catch {\n    return String(v)\n  }\n}\n\n/**\n * Fire-and-forget invoker for an {@link IamAdminAudit.Hook}.\n *\n * Resolves any returned promise off the request critical path. Applies\n * {@link IamAdminAudit.IOptions.redactPath} to `event.path` before invoking the\n * hook so route parameters never reach the sink. Routes thrown errors (sync\n * or async) to {@link IamAdminAudit.IOptions.onAuditHookError} when configured,\n * falling back to `console.error` with a one-line tag. The hook can never\n * block, fail, or destabilise the response.\n *\n * @param hook - Optional caller-supplied hook; no-op when absent.\n * @param event - Event payload describing the mutation.\n * @param opts - Optional hardening options (path redaction, hook-error sink).\n */\nexport function iamFireAdminMutation(\n  hook: IamAdminAudit.Hook | undefined,\n  event: IamAdminAudit.IEvent,\n  opts?: Pick<IamAdminAudit.IOptions, 'redactPath' | 'onAuditHookError'>,\n): void {\n  if (!hook) return\n\n  // Redact path before the hook ever sees it.\n  if (opts?.redactPath) {\n    try {\n      event.path = opts.redactPath(event.path)\n    } catch (err) {\n      // Redactor itself blew up - treat as a hook error.\n      reportAuditHookError(err, event, opts.onAuditHookError)\n      return\n    }\n  }\n\n  try {\n    Promise.resolve(hook(event)).catch((err) => reportAuditHookError(err, event, opts?.onAuditHookError))\n  } catch (err) {\n    reportAuditHookError(err, event, opts?.onAuditHookError)\n  }\n}\n\n/**\n * Routes a hook failure to the caller-supplied\n * {@link IamAdminAudit.IOptions.onAuditHookError} when configured, otherwise to\n * `console.error`. Errors from `onAuditHookError` itself never propagate;\n * they fall through to a last-resort `console.error`.\n */\nfunction reportAuditHookError(\n  err: unknown,\n  event: IamAdminAudit.IEvent,\n  sink: IamAdminAudit.IOptions['onAuditHookError'],\n): void {\n  if (sink) {\n    try {\n      sink(err, event)\n      return\n    } catch (sinkErr) {\n      // Sink itself threw - last-resort log, then stop.\n      try {\n        console.error(\n          '[@gentleduck/iam] onAuditHookError sink threw:',\n          sinkErr instanceof Error ? sinkErr.message : String(sinkErr),\n        )\n      } catch {\n        // console.error itself failed (extremely unusual) - give up silently.\n      }\n      return\n    }\n  }\n  try {\n    console.error('[@gentleduck/iam] onAdminMutation hook threw:', err instanceof Error ? err.message : String(err))\n  } catch {\n    // ignore\n  }\n}\n/**\n * Builds a server-side permission map for a subject and a list of checks.\n *\n * Call once per request and forward the map to the client.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n * @param engine - Provides the access engine to consult.\n * @param subjectId - Identifies the subject whose permissions are computed.\n * @param checks - Lists the permission tuples to evaluate.\n * @param environment - Optional environment context shared across checks.\n * @returns A permission map keyed by `(action, resource, scope)` tuple.\n */\nexport async function generateIamPermissionMap<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n>(\n  engine: IamEngine<TAction, TResource, TRole, TScope>,\n  subjectId: string,\n  checks: readonly IamClient.IPermissionCheck<TAction, TResource, TScope>[],\n  environment?: IamRequest.IEnvironment,\n): Promise<IamClient.PermissionMap<TAction, TResource, TScope>> {\n  return engine.permissions(subjectId, checks, environment)\n}\n\n/**\n * Builds a typed `can(action, resourceType, ...)` function bound to a subject.\n *\n * Useful inside request handlers for terse permission checks.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n * @param engine - Provides the access engine to consult.\n * @param subjectId - Identifies the subject the returned function checks.\n * @param environment - Optional environment context applied to every check.\n * @returns A `(action, resourceType, resourceId?, scope?) => Promise<boolean>` checker.\n * @example\n * ```ts\n * const can = createIamSubjectCan(engine, req.user.id)\n * if (await can('delete', 'post')) { ... }\n * ```\n */\nexport function createIamSubjectCan<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n>(engine: IamEngine<TAction, TResource, TRole, TScope>, subjectId: string, environment?: IamRequest.IEnvironment) {\n  return (action: TAction, resourceType: TResource, resourceId?: string, scope?: TScope) =>\n    engine.can(subjectId, action, { type: resourceType, id: resourceId, attributes: {} }, environment, scope)\n}\n\n/**\n * Extracts an environment object from common request shapes.\n *\n * Looks at `req.ip`, `x-forwarded-for`, `x-real-ip`, and `user-agent`, and\n * stamps the current timestamp.\n *\n * @param req - Provides any request-like object with `ip` and/or `headers`.\n * @returns The extracted {@link IamRequest.IEnvironment}.\n */\nexport function iamExtractEnvironment(req: {\n  ip?: string\n  headers?: Record<string, string | string[] | undefined> | Headers\n  method?: string\n  url?: string\n}): IamRequest.IEnvironment {\n  const getHeader = (name: string): string | undefined => {\n    if (!req.headers) return undefined\n    if (req.headers instanceof Headers) return req.headers.get(name) ?? undefined\n    const val = req.headers[name]\n    return Array.isArray(val) ? val[0] : val\n  }\n\n  return {\n    // XFF can carry multiple comma-separated values (one per proxy hop,\n    // leftmost is the original client). Apps behind multiple trusted\n    // proxies should bypass this helper and assemble `env.ip` themselves.\n    ip: req.ip ?? normalizeForwardedFor(getHeader('x-forwarded-for')) ?? normalizeForwardedFor(getHeader('x-real-ip')),\n    userAgent: getHeader('user-agent'),\n    timestamp: Date.now(),\n  }\n}\n\n/**\n * Extract the leftmost client IP from an `X-Forwarded-For` / `X-Real-IP`\n * header. Returns undefined for missing, empty, or oversized input.\n */\nfunction normalizeForwardedFor(raw: string | undefined): string | undefined {\n  if (typeof raw !== 'string') return undefined\n  if (raw.length === 0 || raw.length > 4096) return undefined\n  const first = raw.split(',', 1)[0]\n  if (first === undefined) return undefined\n  const trimmed = first.trim()\n  if (trimmed.length === 0) return undefined\n  if (trimmed.length > 256) return undefined\n  return trimmed\n}\n\n/** Maps HTTP methods to default access actions used by the framework adapters. */\nexport const IAM_METHOD_ACTION_MAP: Readonly<Record<string, string>> = {\n  GET: 'read',\n  HEAD: 'read',\n  OPTIONS: 'read',\n  POST: 'create',\n  PUT: 'update',\n  PATCH: 'update',\n  DELETE: 'delete',\n}\n"],"mappings":"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"}