import { n as IamPrimitives, t as AccessControl } from "../../access-control-CxeWQI64.js"; import { t as IamAdapter } from "../../adapter-DeNTUcdv.js"; import { t as IamRequest } from "../../request-BouexCSW.js"; //#region src/adapters/http/index.d.ts /** HTTP adapter integration types. Type-only namespace - zero bundle cost. */ declare namespace IamHttp { /** * Describes the configuration for {@link IamHttpAdapter}. * * Covers endpoint, fetch overrides, retry, and circuit-breaker tuning. */ interface IConfig { /** Specifies the base URL of the duck-iam API (e.g. `https://api.example.com/access`). */ baseUrl: string; /** Overrides the default `globalThis.fetch` implementation. */ fetch?: typeof globalThis.fetch; /** Provides headers (e.g. auth tokens) merged into every request. */ headers?: Record | (() => Record | Promise>); /** * Sets the per-request timeout in milliseconds. * * Layered with the engine's `adapterTimeoutMs`; whichever fires first wins. * Defaults to `5_000`. Set to `0` to rely solely on the engine timeout. */ timeoutMs?: number; /** * Caps retry attempts on transient failures (5xx, network errors, or * `AbortError` from a per-request timeout). * * 4xx responses are never retried. Defaults to `2` (3 total attempts). */ retries?: number; /** * Sets the base delay in ms for exponential backoff between retries. * * Attempt N waits `backoffMs * 2^(N-1)` plus jitter. Defaults to `100`. */ backoffMs?: number; /** * Opens the circuit after this many consecutive transient failures. * * Once open, requests reject immediately until the cooldown elapses. Default * `5`. Set to `0` to disable. */ circuitBreakerThreshold?: number; /** * Sets the half-open cooldown in ms. * * After this window, the next request is allowed through as a probe; success * closes the circuit, failure re-opens it. Default `30_000` (30 s). */ circuitBreakerCooldownMs?: number; /** * Restricts the set of acceptable hosts for `baseUrl`. When set, the * parsed URL must match an entry in the list or construction throws. * * Matching rules: * - Comparison is case-insensitive on both sides - `Example.COM` in the * list matches `example.com` in `baseUrl` and vice versa. * - Entries may be bare hostnames (`example.com`) or host:port pairs * (`example.com:8080`). * - A bare-host entry matches the URL's hostname regardless of port - so * `allowedHosts: ['example.com']` accepts `example.com`, `example.com:80`, * and `example.com:8443`. * - A host:port entry matches only that exact port - `example.com:8080` * rejects `example.com` (no port) and `example.com:9090`. * - Precedence: bare hostname is tried first, then full `host` (with port). * * Defaults to `undefined` (allow any host); when omitted a one-time * `console.warn` is emitted at construction recommending a list. */ allowedHosts?: string[]; /** * Permits `baseUrl` whose hostname is an IP literal in a private/loopback * range (`127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, * `169.254.0.0/16`, `::1`, `fc00::/7`, `fe80::/10`). Defaults to `false`. * DNS names are not resolved (would require sync I/O at init); they pass * through and are only constrained by `allowedHosts`. */ allowPrivateHosts?: boolean; } } /** * Backs the access store with a remote [@gentleduck/iam:http] HTTP API. * * Useful for client-side engines that delegate storage to a backend service. * Adds per-request timeout, exponential-backoff retry, and a circuit breaker. * * @template TAction - Constrains valid action strings. * @template TResource - Constrains valid resource strings. * @template TRole - Constrains valid role strings. * @template TScope - Constrains valid scope strings. * @example * ```ts * const adapter = new IamHttpAdapter({ * baseUrl: 'https://api.example.com/access', * headers: { Authorization: 'Bearer ...' }, * }) * ``` */ declare class IamHttpAdapter implements IamAdapter.IAdapter { private _baseUrl; private _fetch; private _headers; private _timeoutMs; private _retries; private _backoffMs; private _cbThreshold; private _cbCooldownMs; private _cbConsecutiveFailures; private _cbOpenedAt; private _cbHalfOpenInFlight; /** * Creates a new HTTP adapter. * * @param config - Provides endpoint, fetch overrides, retry, and breaker tuning. */ constructor(config: IamHttp.IConfig); /** * Validates `baseUrl` at construction time. Rejects non-`http(s)` schemes, * trailing query/fragment, hosts not in the allow-list, and private/loopback * IP literals when `allowPrivateHosts` is `false`. Emits a one-time warn * recommending `allowedHosts` when omitted. Returns the canonical base URL * with any trailing `/` stripped (for back-compat with the previous behaviour). */ private static _validateBaseUrl; /** * Gates fetch attempts based on circuit state. * * - closed: pass through. * - open: reject immediately until the cooldown elapses. * - half-open: allow exactly one probe; concurrent callers reject while the * probe is in flight. The probe's outcome closes or re-opens the circuit. */ private _circuitState; private _onCircuitSuccess; private _onCircuitFailure; /** Sends an HTTP request to the API, merging headers and parsing the JSON response. */ private _request; /** * Same as {@link _request} but treats `404 Not Found` as a missing-resource * signal and returns `null` instead of throwing. The `IamAdapter.IAdapter` * contract for `getPolicy`/`getRole` is "the role, or null if not found"; * the previous throw-on-every-non-2xx behaviour broke that contract and * caused engine.resolve() to bubble up a hard error on every cold miss. */ private _requestOrNull; /** * Fetches with per-request timeout and exponential-backoff retry on transient * failures. * * Transient covers 5xx, network errors, or our own timeout abort. 4xx is * treated as a definitive answer and returned without retry. */ private _fetchWithRetry; private _fetchOnce; private _timeoutSignal; /** * Lists every policy from the remote API. * * @param opts - Optional read options forwarded to fetch. * @returns Array of policies returned by `GET /policies`. */ listPolicies(opts?: IamAdapter.IReadOptions): Promise[]>; /** * Fetches a single policy by ID. * * @param id - Identifies the policy to look up. * @param opts - Optional read options forwarded to fetch. * @returns The matching policy or `null` when the API returns 404. */ getPolicy(id: string, opts?: IamAdapter.IReadOptions): Promise | null>; /** * Stores or overwrites a policy via PUT. * * @param p - Provides the policy to persist. * @returns Resolves once the API acknowledges the write. */ savePolicy(p: AccessControl.IPolicy): Promise; /** * Removes a policy by ID via DELETE. * * @param id - Identifies the policy to delete. * @returns Resolves once the API acknowledges the delete. */ deletePolicy(id: string): Promise; /** * Lists every role from the remote API. * * @param opts - Optional read options forwarded to fetch. * @returns Array of roles returned by `GET /roles`. */ listRoles(opts?: IamAdapter.IReadOptions): Promise[]>; /** * Fetches a single role by ID. * * @param id - Identifies the role to look up. * @param opts - Optional read options forwarded to fetch. * @returns The matching role or `null` when the API returns 404. */ getRole(id: string, opts?: IamAdapter.IReadOptions): Promise | null>; /** * Stores or overwrites a role via PUT. * * @param r - Provides the role to persist. * @returns Resolves once the API acknowledges the write. */ saveRole(r: AccessControl.IRole): Promise; /** * Removes a role by ID via DELETE. * * @param id - Identifies the role to delete. * @returns Resolves once the API acknowledges the delete. */ deleteRole(id: string): Promise; /** * Lists role IDs assigned to a subject. * * **Contract:** the response MUST contain GLOBAL (unscoped) role IDs * only. Scoped role assignments must be returned * from `GET /subjects/{id}/scoped-roles` (see {@link getSubjectScopedRoles}). * The HTTP adapter cannot enforce this - it forwards whatever the server * returns - so the operator's API server is responsible for filtering * out scoped roles. A server that collapses scoped+unscoped into one list * will cause subjects to evaluate with MORE permissions than they would * against any other adapter (memory, file, redis, drizzle, prisma), * silently breaking authorization parity across backends. * * @param subjectId - Identifies the subject whose roles are read. * @param opts - Optional read options forwarded to fetch. * @returns Array of UNSCOPED role IDs returned by `GET /subjects/{id}/roles`. */ getSubjectRoles(subjectId: string, opts?: IamAdapter.IReadOptions): Promise; /** * Lists scoped role assignments for a subject. * * @param subjectId - Identifies the subject whose scoped roles are read. * @param opts - Optional read options forwarded to fetch. * @returns Array of `(role, scope)` pairs. */ getSubjectScopedRoles(subjectId: string, opts?: IamAdapter.IReadOptions): Promise[]>; /** * Grants a role to a subject, optionally restricted to a scope. * * @param subjectId - Identifies the subject receiving the role. * @param roleId - Specifies the role being granted. * @param scope - Optional scope binding the assignment. * @returns Resolves once the API acknowledges the write. */ assignRole(subjectId: string, roleId: TRole, scope?: TScope): Promise; /** * Removes a role assignment from a subject. * * @param subjectId - Identifies the subject losing the role. * @param roleId - Specifies the role being revoked. * @param scope - Optional scope filter passed as a query param. * @returns Resolves once the API acknowledges the delete. */ revokeRole(subjectId: string, roleId: TRole, scope?: TScope): Promise; /** * Fetches the attribute bag stored for a subject. * * @param subjectId - Identifies the subject whose attributes are read. * @param opts - Optional read options forwarded to fetch. * @returns The subject's attribute map. */ getSubjectAttributes(subjectId: string, opts?: IamAdapter.IReadOptions): Promise; /** * Shallow-merges new attributes into the subject's existing bag via PATCH. * * @param subjectId - Identifies the subject whose attributes are written. * @param attrs - Provides the partial attribute patch to merge in. * @returns Resolves once the API acknowledges the write. */ setSubjectAttributes(subjectId: string, attrs: IamPrimitives.Attributes): Promise; } //#endregion export { IamHttp, IamHttpAdapter }; //# sourceMappingURL=index.d.ts.map