{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/adapters/http/index.ts"],"sourcesContent":["import type { AccessControl, IamAdapter, IamPrimitives, IamRequest } from '../../core/types'\n\n/** Brand symbol marking an error as retry-eligible. Internal to this adapter. */\nconst TRANSIENT = Symbol('duck-iam.http.transient')\n\n/** Tags an Error as transient so `isTransientError` will pick it up. Returns the same instance. */\nfunction makeTransient<T extends Error>(err: T): T {\n  Reflect.set(err, TRANSIENT, true)\n  return err\n}\n\n/**\n * Returns `true` if the error should trigger a retry: anything tagged with\n * {@link TRANSIENT}, fetch `AbortError`/`TypeError`, or common Node socket\n * codes (`ECONNRESET`, `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND`).\n */\nfunction isTransientError(err: unknown): boolean {\n  if (!err || typeof err !== 'object') return false\n  if (Reflect.get(err, TRANSIENT) === true) return true\n  const name = Reflect.get(err, 'name')\n  if (name === 'AbortError' || name === 'TypeError') return true\n  const code = Reflect.get(err, 'code')\n  return code === 'ECONNRESET' || code === 'ECONNREFUSED' || code === 'ETIMEDOUT' || code === 'ENOTFOUND'\n}\n\n/**\n * Composes multiple AbortSignals into one that aborts when any source aborts.\n *\n * Used to layer the per-request fetch timeout with the engine's\n * `adapterTimeoutMs` and any user-supplied `IReadOptions.signal`.\n */\nfunction anySignal(signals: AbortSignal[]): AbortSignal | undefined {\n  if (signals.length === 0) return undefined\n  if (signals.length === 1) return signals[0]\n  const ctrl = new AbortController()\n  const onAbort = (reason: unknown) => ctrl.abort(reason)\n  for (const sig of signals) {\n    if (sig.aborted) {\n      ctrl.abort(sig.reason)\n      break\n    }\n    sig.addEventListener('abort', () => onAbort(sig.reason), { once: true })\n  }\n  return ctrl.signal\n}\n/** HTTP adapter integration types. Type-only namespace - zero bundle cost. */\nexport namespace IamHttp {\n  /**\n   * Describes the configuration for {@link IamHttpAdapter}.\n   *\n   * Covers endpoint, fetch overrides, retry, and circuit-breaker tuning.\n   */\n  export interface IConfig {\n    /** Specifies the base URL of the duck-iam API (e.g. `https://api.example.com/access`). */\n    baseUrl: string\n    /** Overrides the default `globalThis.fetch` implementation. */\n    fetch?: typeof globalThis.fetch\n    /** Provides headers (e.g. auth tokens) merged into every request. */\n    headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)\n    /**\n     * Sets the per-request timeout in milliseconds.\n     *\n     * Layered with the engine's `adapterTimeoutMs`; whichever fires first wins.\n     * Defaults to `5_000`. Set to `0` to rely solely on the engine timeout.\n     */\n    timeoutMs?: number\n    /**\n     * Caps retry attempts on transient failures (5xx, network errors, or\n     * `AbortError` from a per-request timeout).\n     *\n     * 4xx responses are never retried. Defaults to `2` (3 total attempts).\n     */\n    retries?: number\n    /**\n     * Sets the base delay in ms for exponential backoff between retries.\n     *\n     * Attempt N waits `backoffMs * 2^(N-1)` plus jitter. Defaults to `100`.\n     */\n    backoffMs?: number\n    /**\n     * Opens the circuit after this many consecutive transient failures.\n     *\n     * Once open, requests reject immediately until the cooldown elapses. Default\n     * `5`. Set to `0` to disable.\n     */\n    circuitBreakerThreshold?: number\n    /**\n     * Sets the half-open cooldown in ms.\n     *\n     * After this window, the next request is allowed through as a probe; success\n     * closes the circuit, failure re-opens it. Default `30_000` (30 s).\n     */\n    circuitBreakerCooldownMs?: number\n    /**\n     * Restricts the set of acceptable hosts for `baseUrl`. When set, the\n     * parsed URL must match an entry in the list or construction throws.\n     *\n     * Matching rules:\n     * - Comparison is case-insensitive on both sides - `Example.COM` in the\n     *   list matches `example.com` in `baseUrl` and vice versa.\n     * - Entries may be bare hostnames (`example.com`) or host:port pairs\n     *   (`example.com:8080`).\n     * - A bare-host entry matches the URL's hostname regardless of port - so\n     *   `allowedHosts: ['example.com']` accepts `example.com`, `example.com:80`,\n     *   and `example.com:8443`.\n     * - A host:port entry matches only that exact port - `example.com:8080`\n     *   rejects `example.com` (no port) and `example.com:9090`.\n     * - Precedence: bare hostname is tried first, then full `host` (with port).\n     *\n     * Defaults to `undefined` (allow any host); when omitted a one-time\n     * `console.warn` is emitted at construction recommending a list.\n     */\n    allowedHosts?: string[]\n    /**\n     * Permits `baseUrl` whose hostname is an IP literal in a private/loopback\n     * range (`127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`,\n     * `169.254.0.0/16`, `::1`, `fc00::/7`, `fe80::/10`). Defaults to `false`.\n     * DNS names are not resolved (would require sync I/O at init); they pass\n     * through and are only constrained by `allowedHosts`.\n     */\n    allowPrivateHosts?: boolean\n  }\n}\n\n/**\n * One-time warning latch for omitted `allowedHosts`. Module-level so repeated\n * adapter constructions during tests / per-request scopes don't spam stderr.\n */\nconst _ALLOWED_HOSTS_WARNED = { fired: false }\n\n/**\n * Converts a 32-bit IPv4 tail expressed as two colon-separated hex groups\n * (e.g. `7f00:1`) into dotted-quad form (`127.0.0.1`). Returns `null` if the\n * input is not a well-formed 32-bit hex tail. Both groups may be 1-4 hex\n * digits; the second group may be omitted leading zeros (`7f00:1` ==\n * `7f00:0001`).\n */\nfunction _hexTailToDottedQuad(tail: string): string | null {\n  const m = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(tail)\n  if (!m || m[1] === undefined || m[2] === undefined) return null\n  const hi = parseInt(m[1], 16)\n  const lo = parseInt(m[2], 16)\n  if (!Number.isFinite(hi) || !Number.isFinite(lo)) return null\n  if (hi < 0 || hi > 0xffff || lo < 0 || lo > 0xffff) return null\n  return `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`\n}\n\n/**\n * Returns `true` when the given hostname is an IP literal in a private,\n * loopback, link-local, or unique-local range. DNS names return `false`\n * (caller controls them via `allowedHosts`).\n */\nfunction _isPrivateHost(hostname: string): boolean {\n  // Strip surrounding brackets from IPv6 literals and a single trailing FQDN\n  // dot so `127.0.0.1.` / `example.com.` normalise to their bare form before\n  // the rest of the checks fire.\n  let h = hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n  if (h.endsWith('.')) h = h.slice(0, -1)\n  // IPv4 dotted-quad\n  const v4 = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(h)\n  if (v4) {\n    const a = Number(v4[1])\n    const b = Number(v4[2])\n    if (a === 127) return true // loopback\n    if (a === 10) return true // RFC1918\n    if (a === 192 && b === 168) return true // RFC1918\n    if (a === 172 && b >= 16 && b <= 31) return true // RFC1918\n    if (a === 169 && b === 254) return true // link-local\n    if (a === 0) return true // \"this network\" - includes 0.0.0.0 unspecified\n    return false\n  }\n  // IPv6 literal\n  if (h.includes(':')) {\n    const lower = h.toLowerCase()\n    if (lower === '::1' || lower === '0:0:0:0:0:0:0:1') return true\n    // IPv6 unspecified `::` (kernel wildcard, often resolves to a local\n    // interface). Block its expanded form too.\n    if (lower === '::' || lower === '0:0:0:0:0:0:0:0') return true\n    // fc00::/7 - first byte 0xfc or 0xfd\n    if (/^f[cd][0-9a-f]{0,2}:/.test(lower)) return true\n    // fe80::/10 - fe8x, fe9x, feax, febx\n    if (/^fe[89ab][0-9a-f]?:/.test(lower)) return true\n    // IPv4-mapped IPv6 - `::ffff:a.b.c.d` (dotted-quad tail) or\n    // `::ffff:hhhh:hhhh` (hex tail, canonical form Node's URL parser emits).\n    // Also accept the fully expanded `0:0:0:0:0:ffff:...` form.\n    let mappedTail: string | null = null\n    if (lower.startsWith('::ffff:')) mappedTail = lower.slice(7)\n    else if (lower.startsWith('0:0:0:0:0:ffff:')) mappedTail = lower.slice(15)\n    if (mappedTail !== null) {\n      // Dotted-quad tail (`::ffff:127.0.0.1`).\n      if (mappedTail.includes('.')) return _isPrivateHost(mappedTail)\n      // Hex tail (`::ffff:7f00:1`) - convert to dotted-quad then re-check.\n      const dotted = _hexTailToDottedQuad(mappedTail)\n      if (dotted) return _isPrivateHost(dotted)\n      return false\n    }\n    // IPv4-compatible IPv6 (deprecated RFC4291 section 2.5.5.1) - `::a.b.c.d`.\n    // Node canonicalises these to hex too, but cover textual form for\n    // completeness.\n    if (lower.startsWith('::') && lower.includes('.')) {\n      const tail = lower.slice(2)\n      if (/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.test(tail)) return _isPrivateHost(tail)\n    }\n    // 6to4 prefix `2002::/16` carries an inner IPv4 in the next two 16-bit\n    // groups (`2002:AABB:CCDD::` -> `A.B.C.D` with bytes AA,BB,CC,DD). Linux\n    // ships 6to4 by default - `2002:7f00:1::` carries `127.0.0.1`.\n    if (lower.startsWith('2002:')) {\n      const m = /^2002:([0-9a-f]{1,4}):([0-9a-f]{1,4})(?::|$)/.exec(lower)\n      if (m) {\n        const dotted = _hexTailToDottedQuad(`${m[1]}:${m[2]}`)\n        if (dotted) return _isPrivateHost(dotted)\n      }\n    }\n    // NAT64 `64:ff9b::/96`; accept both canonical and `0064:ff9b:` spellings.\n    if (lower.startsWith('64:ff9b:') || lower.startsWith('0064:ff9b:')) {\n      const tail = lower.startsWith('0064:') ? lower.slice(10) : lower.slice(8)\n      // Dotted-quad tail (`64:ff9b::127.0.0.1`).\n      if (tail.includes('.')) {\n        const v4match = /(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$/.exec(tail)\n        if (v4match?.[1]) return _isPrivateHost(v4match[1])\n      }\n      // Hex tail - last two non-empty hex groups form the 32-bit v4.\n      const groups = tail.split(':').filter((g) => g.length > 0)\n      if (groups.length >= 2) {\n        const dotted = _hexTailToDottedQuad(`${groups[groups.length - 2]}:${groups[groups.length - 1]}`)\n        if (dotted) return _isPrivateHost(dotted)\n      }\n    }\n    return false\n  }\n  return false\n}\n\n/** Lowercase, strip trailing FQDN dot, IDN -> punycode via `new URL`; invalid input falls back to lowercased input. */\nfunction _normaliseHostForAllowlist(host: string): string {\n  let h = host.toLowerCase()\n  if (h.endsWith('.')) h = h.slice(0, -1)\n  // Fast path: pure ASCII needs no IDN conversion.\n  // biome-ignore lint/suspicious/noControlCharactersInRegex: explicit ASCII range check.\n  if (/^[\\x00-\\x7f]*$/.test(h)) return h\n  try {\n    return new URL(`http://${h}`).hostname\n  } catch {\n    return h\n  }\n}\n\n/**\n * Backs the access store with a remote [@gentleduck/iam:http] HTTP API.\n *\n * Useful for client-side engines that delegate storage to a backend service.\n * Adds per-request timeout, exponential-backoff retry, and a circuit breaker.\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 * @example\n * ```ts\n * const adapter = new IamHttpAdapter({\n *   baseUrl: 'https://api.example.com/access',\n *   headers: { Authorization: 'Bearer ...' },\n * })\n * ```\n */\nexport class IamHttpAdapter<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n> implements IamAdapter.IAdapter<TAction, TResource, TRole, TScope>\n{\n  private _baseUrl: string\n  private _fetch: typeof globalThis.fetch\n  private _headers: IamHttp.IConfig['headers']\n  private _timeoutMs: number\n  private _retries: number\n  private _backoffMs: number\n  private _cbThreshold: number\n  private _cbCooldownMs: number\n  // Circuit-breaker state. closed -> too many transients -> open -> cooldown\n  // expires -> half-open -> success closes / failure re-opens.\n  private _cbConsecutiveFailures = 0\n  private _cbOpenedAt: number | null = null\n  private _cbHalfOpenInFlight = false\n\n  /**\n   * Creates a new HTTP adapter.\n   *\n   * @param config - Provides endpoint, fetch overrides, retry, and breaker tuning.\n   */\n  constructor(config: IamHttp.IConfig) {\n    this._baseUrl = IamHttpAdapter._validateBaseUrl(config)\n    this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis)\n    this._headers = config.headers\n    this._timeoutMs = config.timeoutMs ?? 5_000\n    this._retries = config.retries ?? 2\n    this._backoffMs = config.backoffMs ?? 100\n    this._cbThreshold = config.circuitBreakerThreshold ?? 5\n    this._cbCooldownMs = config.circuitBreakerCooldownMs ?? 30_000\n  }\n\n  /**\n   * Validates `baseUrl` at construction time. Rejects non-`http(s)` schemes,\n   * trailing query/fragment, hosts not in the allow-list, and private/loopback\n   * IP literals when `allowPrivateHosts` is `false`. Emits a one-time warn\n   * recommending `allowedHosts` when omitted. Returns the canonical base URL\n   * with any trailing `/` stripped (for back-compat with the previous behaviour).\n   */\n  private static _validateBaseUrl(config: IamHttp.IConfig): string {\n    let parsed: URL\n    try {\n      parsed = new URL(config.baseUrl)\n    } catch {\n      throw new Error(`[@gentleduck/iam:http] invalid baseUrl ${JSON.stringify(config.baseUrl)}`)\n    }\n    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n      throw new Error(`[@gentleduck/iam:http] baseUrl scheme must be http: or https:, got ${parsed.protocol}`)\n    }\n    if (parsed.search || parsed.hash) {\n      throw new Error('[@gentleduck/iam:http] baseUrl must not contain a query string or fragment')\n    }\n    if (config.allowedHosts && config.allowedHosts.length > 0) {\n      // Two arms: bare hostname (any port) vs `hostname:port` (exact match).\n      // Both sides normalised: lower-case, no FQDN trailing dot, IDN punycoded.\n      const urlHostname = _normaliseHostForAllowlist(parsed.hostname)\n      // For host (with port) we split, normalise host, then re-attach port.\n      const rawHost = parsed.host.toLowerCase()\n      const colonIdx = rawHost.lastIndexOf(':')\n      const urlHost =\n        colonIdx > 0 && !rawHost.startsWith('[')\n          ? `${_normaliseHostForAllowlist(rawHost.slice(0, colonIdx))}:${rawHost.slice(colonIdx + 1)}`\n          : _normaliseHostForAllowlist(rawHost)\n      const normEntries = config.allowedHosts.map((h) => {\n        const lower = h.toLowerCase()\n        const ci = lower.lastIndexOf(':')\n        if (ci > 0 && !lower.startsWith('[') && /^\\d+$/.test(lower.slice(ci + 1))) {\n          return `${_normaliseHostForAllowlist(lower.slice(0, ci))}:${lower.slice(ci + 1)}`\n        }\n        return _normaliseHostForAllowlist(lower)\n      })\n      const matched = normEntries.some((entry) => entry === urlHostname || entry === urlHost)\n      if (!matched) {\n        throw new Error(`[@gentleduck/iam:http] baseUrl host ${JSON.stringify(parsed.host)} not in allowedHosts`)\n      }\n    } else if (!_ALLOWED_HOSTS_WARNED.fired) {\n      _ALLOWED_HOSTS_WARNED.fired = true\n      console.warn(\n        '[@gentleduck/iam:http] `allowedHosts` not set - any host accepted. Pass `init.allowedHosts` for SSRF defense in depth.',\n      )\n    }\n    if (!config.allowPrivateHosts && _isPrivateHost(parsed.hostname)) {\n      throw new Error(\n        `[@gentleduck/iam:http] baseUrl host ${JSON.stringify(parsed.hostname)} resolves to a private/loopback range - set allowPrivateHosts: true to opt in`,\n      )\n    }\n    // Strip a single trailing `/` for back-compat with previous behaviour.\n    return config.baseUrl.replace(/\\/$/, '')\n  }\n\n  /**\n   * Gates fetch attempts based on circuit state.\n   *\n   * - closed: pass through.\n   * - open: reject immediately until the cooldown elapses.\n   * - half-open: allow exactly one probe; concurrent callers reject while the\n   *   probe is in flight. The probe's outcome closes or re-opens the circuit.\n   */\n  private _circuitState(): 'closed' | 'open' | 'half-open' {\n    if (this._cbThreshold <= 0 || this._cbOpenedAt === null) return 'closed'\n    return Date.now() - this._cbOpenedAt < this._cbCooldownMs ? 'open' : 'half-open'\n  }\n\n  private _onCircuitSuccess(): void {\n    this._cbConsecutiveFailures = 0\n    this._cbOpenedAt = null\n    this._cbHalfOpenInFlight = false\n  }\n\n  private _onCircuitFailure(): void {\n    this._cbConsecutiveFailures++\n    this._cbHalfOpenInFlight = false\n    if (this._cbThreshold > 0 && this._cbConsecutiveFailures >= this._cbThreshold) {\n      this._cbOpenedAt = Date.now()\n    }\n  }\n\n  /** Sends an HTTP request to the API, merging headers and parsing the JSON response. */\n  private async _request<T>(path: string, init?: RequestInit, readOpts?: IamAdapter.IReadOptions): Promise<T> {\n    const res = await this._fetchWithRetry(path, init, readOpts)\n    if (!res.ok) {\n      throw new Error(`[@gentleduck/iam:http] HTTP ${res.status}: ${await readBodyCapped(res)}`)\n    }\n    return readJsonCapped<T>(res)\n  }\n\n  /**\n   * Same as {@link _request} but treats `404 Not Found` as a missing-resource\n   * signal and returns `null` instead of throwing. The `IamAdapter.IAdapter`\n   * contract for `getPolicy`/`getRole` is \"the role, or null if not found\";\n   * the previous throw-on-every-non-2xx behaviour broke that contract and\n   * caused engine.resolve() to bubble up a hard error on every cold miss.\n   */\n  private async _requestOrNull<T>(\n    path: string,\n    init?: RequestInit,\n    readOpts?: IamAdapter.IReadOptions,\n  ): Promise<T | null> {\n    const res = await this._fetchWithRetry(path, init, readOpts)\n    if (res.status === 404) return null\n    if (!res.ok) {\n      throw new Error(`[@gentleduck/iam:http] HTTP ${res.status}: ${await readBodyCapped(res)}`)\n    }\n    return readJsonCapped<T>(res)\n  }\n\n  /**\n   * Fetches with per-request timeout and exponential-backoff retry on transient\n   * failures.\n   *\n   * Transient covers 5xx, network errors, or our own timeout abort. 4xx is\n   * treated as a definitive answer and returned without retry.\n   */\n  private async _fetchWithRetry(\n    path: string,\n    init: RequestInit | undefined,\n    readOpts?: IamAdapter.IReadOptions,\n  ): Promise<Response> {\n    const state = this._circuitState()\n    if (state === 'open') {\n      throw new Error('[@gentleduck/iam:http] circuit open - refusing request')\n    }\n    if (state === 'half-open') {\n      if (this._cbHalfOpenInFlight) {\n        throw new Error('[@gentleduck/iam:http] circuit half-open probe in flight')\n      }\n      this._cbHalfOpenInFlight = true\n    }\n\n    let attempt = 0\n    let lastError: unknown\n    while (attempt <= this._retries) {\n      try {\n        const res = await this._fetchOnce(path, init, readOpts)\n        this._onCircuitSuccess()\n        return res\n      } catch (err) {\n        lastError = err\n        if (!isTransientError(err) || attempt === this._retries) {\n          this._onCircuitFailure()\n          throw err\n        }\n        // Cap exponential backoff so misconfigured `_retries` cannot drive the\n        // delay past 2^31 ms (setTimeout overflow).\n        const exp = Math.min(this._backoffMs * 2 ** attempt, 60_000)\n        const delay = exp + Math.floor(Math.random() * Math.min(this._backoffMs, 5000))\n        await new Promise((r) => setTimeout(r, delay))\n        attempt++\n      }\n    }\n    this._onCircuitFailure()\n    throw lastError as Error\n  }\n\n  private async _fetchOnce(\n    path: string,\n    init: RequestInit | undefined,\n    readOpts?: IamAdapter.IReadOptions,\n  ): Promise<Response> {\n    const headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      ...(typeof this._headers === 'function' ? await this._headers() : (this._headers ?? {})),\n      ...((init?.headers as Record<string, string>) ?? {}),\n    }\n    const controllers = [readOpts?.signal, this._timeoutSignal()].filter((s): s is AbortSignal => !!s)\n    const signal = anySignal(controllers)\n    // SSRF defence: `redirect: 'error'` keeps the validated base URL.\n    const res = await this._fetch(`${this._baseUrl}${path}`, { ...init, headers, signal, redirect: 'error' })\n    if (res.status >= 500) {\n      const body = await readBodyCapped(res)\n      throw makeTransient(new Error(`[@gentleduck/iam:http] HTTP ${res.status}: ${body}`))\n    }\n    return res\n  }\n\n  private _timeoutSignal(): AbortSignal | undefined {\n    if (this._timeoutMs <= 0) return undefined\n    const ctrl = new AbortController()\n    setTimeout(\n      () => ctrl.abort(makeTransient(new Error(`IamHttpAdapter request timed out after ${this._timeoutMs}ms`))),\n      this._timeoutMs,\n    )\n    return ctrl.signal\n  }\n\n  /**\n   * Lists every policy from the remote API.\n   *\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns Array of policies returned by `GET /policies`.\n   */\n  async listPolicies(opts?: IamAdapter.IReadOptions): Promise<AccessControl.IPolicy<TAction, TResource, TRole>[]> {\n    return this._request('/policies', undefined, opts)\n  }\n  /**\n   * Fetches a single policy by ID.\n   *\n   * @param id - Identifies the policy to look up.\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns The matching policy or `null` when the API returns 404.\n   */\n  async getPolicy(\n    id: string,\n    opts?: IamAdapter.IReadOptions,\n  ): Promise<AccessControl.IPolicy<TAction, TResource, TRole> | null> {\n    if (typeof id !== 'string' || id.length === 0 || id.length > 1024) return null\n    return this._requestOrNull(`/policies/${encodeURIComponent(id)}`, undefined, opts)\n  }\n  /**\n   * Stores or overwrites a policy via PUT.\n   *\n   * @param p - Provides the policy to persist.\n   * @returns Resolves once the API acknowledges the write.\n   */\n  async savePolicy(p: AccessControl.IPolicy<TAction, TResource, TRole>): Promise<void> {\n    await this._request('/policies', {\n      method: 'PUT',\n      body: JSON.stringify(p),\n    })\n  }\n  /**\n   * Removes a policy by ID via DELETE.\n   *\n   * @param id - Identifies the policy to delete.\n   * @returns Resolves once the API acknowledges the delete.\n   */\n  async deletePolicy(id: string): Promise<void> {\n    await this._request(`/policies/${encodeURIComponent(id)}`, { method: 'DELETE' })\n  }\n\n  /**\n   * Lists every role from the remote API.\n   *\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns Array of roles returned by `GET /roles`.\n   */\n  async listRoles(opts?: IamAdapter.IReadOptions): Promise<AccessControl.IRole<TAction, TResource, TRole, TScope>[]> {\n    return this._request('/roles', undefined, opts)\n  }\n  /**\n   * Fetches a single role by ID.\n   *\n   * @param id - Identifies the role to look up.\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns The matching role or `null` when the API returns 404.\n   */\n  async getRole(\n    id: string,\n    opts?: IamAdapter.IReadOptions,\n  ): Promise<AccessControl.IRole<TAction, TResource, TRole, TScope> | null> {\n    if (typeof id !== 'string' || id.length === 0 || id.length > 1024) return null\n    return this._requestOrNull(`/roles/${encodeURIComponent(id)}`, undefined, opts)\n  }\n  /**\n   * Stores or overwrites a role via PUT.\n   *\n   * @param r - Provides the role to persist.\n   * @returns Resolves once the API acknowledges the write.\n   */\n  async saveRole(r: AccessControl.IRole<TAction, TResource, TRole, TScope>): Promise<void> {\n    await this._request('/roles', { method: 'PUT', body: JSON.stringify(r) })\n  }\n  /**\n   * Removes a role by ID via DELETE.\n   *\n   * @param id - Identifies the role to delete.\n   * @returns Resolves once the API acknowledges the delete.\n   */\n  async deleteRole(id: string): Promise<void> {\n    await this._request(`/roles/${encodeURIComponent(id)}`, { method: 'DELETE' })\n  }\n\n  /**\n   * Lists role IDs assigned to a subject.\n   *\n   * **Contract:** the response MUST contain GLOBAL (unscoped) role IDs\n   * only. Scoped role assignments must be returned\n   * from `GET /subjects/{id}/scoped-roles` (see {@link getSubjectScopedRoles}).\n   * The HTTP adapter cannot enforce this - it forwards whatever the server\n   * returns - so the operator's API server is responsible for filtering\n   * out scoped roles. A server that collapses scoped+unscoped into one list\n   * will cause subjects to evaluate with MORE permissions than they would\n   * against any other adapter (memory, file, redis, drizzle, prisma),\n   * silently breaking authorization parity across backends.\n   *\n   * @param subjectId - Identifies the subject whose roles are read.\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns Array of UNSCOPED role IDs returned by `GET /subjects/{id}/roles`.\n   */\n  async getSubjectRoles(subjectId: string, opts?: IamAdapter.IReadOptions): Promise<TRole[]> {\n    if (typeof subjectId !== 'string' || subjectId.length === 0 || subjectId.length > 1024) return []\n    const raw: unknown = await this._request(`/subjects/${encodeURIComponent(subjectId)}/roles`, undefined, opts)\n    return parseHttpSubjectRoles<TRole>(raw, subjectId)\n  }\n  /**\n   * Lists scoped role assignments for a subject.\n   *\n   * @param subjectId - Identifies the subject whose scoped roles are read.\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns Array of `(role, scope)` pairs.\n   */\n  async getSubjectScopedRoles(\n    subjectId: string,\n    opts?: IamAdapter.IReadOptions,\n  ): Promise<IamRequest.IScopedRole<TRole, TScope>[]> {\n    if (typeof subjectId !== 'string' || subjectId.length === 0 || subjectId.length > 1024) return []\n    const raw: unknown = await this._request(`/subjects/${encodeURIComponent(subjectId)}/scoped-roles`, undefined, opts)\n    return parseHttpSubjectScopedRoles<TRole, TScope>(raw, subjectId)\n  }\n  /**\n   * Grants a role to a subject, optionally restricted to a scope.\n   *\n   * @param subjectId - Identifies the subject receiving the role.\n   * @param roleId - Specifies the role being granted.\n   * @param scope - Optional scope binding the assignment.\n   * @returns Resolves once the API acknowledges the write.\n   */\n  async assignRole(subjectId: string, roleId: TRole, scope?: TScope): Promise<void> {\n    await this._request(`/subjects/${encodeURIComponent(subjectId)}/roles`, {\n      method: 'POST',\n      body: JSON.stringify({ roleId, scope }),\n    })\n  }\n  /**\n   * Removes a role assignment from a subject.\n   *\n   * @param subjectId - Identifies the subject losing the role.\n   * @param roleId - Specifies the role being revoked.\n   * @param scope - Optional scope filter passed as a query param.\n   * @returns Resolves once the API acknowledges the delete.\n   */\n  async revokeRole(subjectId: string, roleId: TRole, scope?: TScope): Promise<void> {\n    const params = scope ? `?scope=${encodeURIComponent(scope)}` : ''\n    await this._request(`/subjects/${encodeURIComponent(subjectId)}/roles/${encodeURIComponent(roleId)}${params}`, {\n      method: 'DELETE',\n    })\n  }\n  /**\n   * Fetches the attribute bag stored for a subject.\n   *\n   * @param subjectId - Identifies the subject whose attributes are read.\n   * @param opts - Optional read options forwarded to fetch.\n   * @returns The subject's attribute map.\n   */\n  async getSubjectAttributes(subjectId: string, opts?: IamAdapter.IReadOptions): Promise<IamPrimitives.Attributes> {\n    if (typeof subjectId !== 'string' || subjectId.length === 0 || subjectId.length > 1024) return {}\n    const raw: unknown = await this._request(`/subjects/${encodeURIComponent(subjectId)}/attributes`, undefined, opts)\n    return parseHttpSubjectAttributes(raw, subjectId)\n  }\n  /**\n   * Shallow-merges new attributes into the subject's existing bag via PATCH.\n   *\n   * @param subjectId - Identifies the subject whose attributes are written.\n   * @param attrs - Provides the partial attribute patch to merge in.\n   * @returns Resolves once the API acknowledges the write.\n   */\n  async setSubjectAttributes(subjectId: string, attrs: IamPrimitives.Attributes): Promise<void> {\n    await this._request(`/subjects/${encodeURIComponent(subjectId)}/attributes`, {\n      method: 'PATCH',\n      body: JSON.stringify(attrs),\n    })\n  }\n}\n\n/** Read up to 200 chars of the response body for error messages. */\nasync function readBodyCapped(res: Response): Promise<string> {\n  // Streaming read with a 4KB hard cap; `res.text()` would buffer the entire\n  // body first - a hostile remote sending a multi-GB body would exhaust\n  // memory before the cap-and-slice ever ran. We only need a short prefix\n  // for the error-context line, so cap during ingest.\n  const MAX_BYTES = 4096\n  const reader = res.body?.getReader()\n  if (!reader) {\n    try {\n      const t = await res.text()\n      return t.length <= 200 ? t : `${t.slice(0, 200)}...(truncated)`\n    } catch {\n      return ''\n    }\n  }\n  const decoder = new TextDecoder()\n  let acc = ''\n  let bytes = 0\n  try {\n    while (bytes < MAX_BYTES) {\n      const { value, done } = await reader.read()\n      if (done) break\n      bytes += value.byteLength\n      acc += decoder.decode(value, { stream: true })\n      if (bytes >= MAX_BYTES) break\n    }\n    acc += decoder.decode()\n  } catch {\n    /* swallow stream errors; partial body is fine for diagnostics */\n  } finally {\n    void reader.cancel().catch(() => {})\n  }\n  if (acc.length <= 200) return acc\n  return `${acc.slice(0, 200)}...(truncated)`\n}\n\n/**\n * Stream-and-cap JSON reader: refuses bodies past 4 MiB so a hostile remote\n * cannot OOM us before we ever reach JSON.parse. Real IAM payloads are\n * <100 KiB; 4 MiB is generous for bulk-policy fetches.\n */\nasync function readJsonCapped<T>(res: Response): Promise<T> {\n  const MAX_BYTES = 4 * 1024 * 1024\n  const reader = res.body?.getReader()\n  if (!reader) {\n    return (await res.json()) as T\n  }\n  const decoder = new TextDecoder()\n  let text = ''\n  let bytes = 0\n  try {\n    while (bytes < MAX_BYTES) {\n      const { value, done } = await reader.read()\n      if (done) break\n      bytes += value.byteLength\n      text += decoder.decode(value, { stream: true })\n      if (bytes >= MAX_BYTES) {\n        throw new Error('[@gentleduck/iam:http] response body exceeds 4 MiB cap')\n      }\n    }\n    text += decoder.decode()\n  } finally {\n    void reader.cancel().catch(() => {})\n  }\n  return JSON.parse(text) as T\n}\n\nfunction parseHttpSubjectAttributes(value: unknown, subjectId: string): IamPrimitives.Attributes {\n  if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n    const got = value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value\n    throw new Error(\n      `[@gentleduck/iam:http] getSubjectAttributes for \"${subjectId}\" returned ${got} (expected JSON object)`,\n    )\n  }\n  return value as IamPrimitives.Attributes\n}\n\nfunction parseHttpSubjectRoles<TRole extends string>(value: unknown, subjectId: string): TRole[] {\n  if (!Array.isArray(value)) {\n    const got = value === null ? 'null' : typeof value\n    throw new Error(`[@gentleduck/iam:http] getSubjectRoles for \"${subjectId}\" returned ${got} (expected JSON array)`)\n  }\n  const roles: TRole[] = []\n  for (const entry of value) {\n    if (typeof entry === 'string' && entry.length > 0) {\n      roles.push(entry as TRole)\n    }\n  }\n  return roles\n}\n\nfunction parseHttpSubjectScopedRoles<TRole extends string, TScope extends string>(\n  value: unknown,\n  subjectId: string,\n): IamRequest.IScopedRole<TRole, TScope>[] {\n  if (!Array.isArray(value)) {\n    const got = value === null ? 'null' : typeof value\n    throw new Error(\n      `[@gentleduck/iam:http] getSubjectScopedRoles for \"${subjectId}\" returned ${got} (expected JSON array)`,\n    )\n  }\n  const out: IamRequest.IScopedRole<TRole, TScope>[] = []\n  for (const entry of value) {\n    if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) continue\n    const role = Reflect.get(entry, 'role')\n    const scope = Reflect.get(entry, 'scope')\n    if (typeof role !== 'string' || role.length === 0) continue\n    if (typeof scope !== 'string' || scope.length === 0) continue\n    out.push({ role: role as TRole, scope: scope as TScope })\n  }\n  return out\n}\n"],"mappings":"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"}