@atlasent/sdk 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +133 -0
- package/dist/behavior.cjs +175 -0
- package/dist/behavior.cjs.map +1 -0
- package/dist/behavior.d.cts +241 -0
- package/dist/behavior.d.ts +241 -0
- package/dist/behavior.js +143 -0
- package/dist/behavior.js.map +1 -0
- package/dist/hono.cjs +580 -0
- package/dist/hono.cjs.map +1 -0
- package/dist/hono.d.cts +109 -0
- package/dist/hono.d.ts +109 -0
- package/dist/hono.js +550 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.cjs +763 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +620 -0
- package/dist/index.d.ts +620 -0
- package/dist/index.js +723 -0
- package/dist/index.js.map +1 -0
- package/dist/protect-BKxcoR_2.d.cts +159 -0
- package/dist/protect-BKxcoR_2.d.ts +159 -0
- package/package.json +101 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/auditBundle.ts","../src/protect.ts","../src/retry.ts"],"sourcesContent":["/**\n * @atlasent/sdk — execution-time authorization for AI agents.\n *\n * Primary API is the default export:\n *\n * ```ts\n * import atlasent from \"@atlasent/sdk\";\n *\n * const permit = await atlasent.protect({\n * agent: \"deploy-bot\",\n * action: \"deploy_to_production\",\n * context: { commit, approver },\n * });\n * ```\n *\n * Named exports remain available for the lower-level\n * {@link AtlaSentClient} and the error taxonomy.\n */\n\nimport { AtlaSentClient } from \"./client.js\";\nimport { verifyBundle } from \"./auditBundle.js\";\nimport { AtlaSentDeniedError, AtlaSentError } from \"./errors.js\";\nimport { configure, protect } from \"./protect.js\";\n\nexport { AtlaSentClient } from \"./client.js\";\nexport {\n AtlaSentDeniedError,\n AtlaSentError,\n type AtlaSentDecision,\n type AtlaSentDeniedErrorInit,\n type AtlaSentErrorCode,\n type AtlaSentErrorInit,\n} from \"./errors.js\";\nexport {\n configure,\n protect,\n type ConfigureOptions,\n type Permit,\n type ProtectRequest,\n} from \"./protect.js\";\nexport type {\n ApiKeySelfResponse,\n AtlaSentClientOptions,\n AuditEventsResult,\n AuditExportRequest,\n AuditExportResult,\n Decision,\n EvaluateRequest,\n EvaluateResponse,\n RateLimitState,\n VerifyPermitRequest,\n VerifyPermitResponse,\n} from \"./types.js\";\nexport {\n canonicalJSON,\n signedBytesFor,\n verifyAuditBundle,\n verifyBundle,\n type AuditBundle,\n type BundleVerificationResult,\n type VerifyBundleOptions,\n type VerifyKey,\n} from \"./auditBundle.js\";\nexport type {\n AuditDecision,\n AuditEvent,\n AuditEventsPage,\n AuditEventsQuery,\n AuditExport,\n AuditExportSignatureStatus,\n} from \"./audit.js\";\nexport {\n DEFAULT_RETRY_POLICY,\n computeBackoffMs,\n hasAttemptsLeft,\n isRetryable,\n mergePolicy,\n type RetryPolicy,\n} from \"./retry.js\";\n\n/**\n * Default export. The opinionated, category-defining entry point:\n *\n * ```ts\n * import atlasent from \"@atlasent/sdk\";\n * await atlasent.protect({ ... });\n * ```\n */\nconst atlasent = {\n protect,\n configure,\n verifyBundle,\n AtlaSentClient,\n AtlaSentError,\n AtlaSentDeniedError,\n} as const;\n\nexport default atlasent;\n","/**\n * Error types for the AtlaSent TypeScript SDK.\n *\n * The SDK follows a fail-closed design: a clean policy DENY is\n * returned as `EvaluateResponse.decision === \"DENY\"` (not thrown),\n * but any failure to confirm authorization — network, timeout,\n * bad response, invalid key, rate limit — throws an\n * {@link AtlaSentError}.\n */\n\n/** Discriminator for {@link AtlaSentError.code}. */\nexport type AtlaSentErrorCode =\n | \"invalid_api_key\"\n | \"forbidden\"\n | \"rate_limited\"\n | \"timeout\"\n | \"network\"\n | \"bad_response\"\n | \"bad_request\"\n | \"server_error\";\n\n/** Initialization options for {@link AtlaSentError}. */\nexport interface AtlaSentErrorInit {\n status?: number;\n code?: AtlaSentErrorCode;\n requestId?: string;\n retryAfterMs?: number;\n cause?: unknown;\n}\n\n/**\n * The only error type this SDK throws.\n *\n * Flat top-level properties mirror the convention used by Stripe,\n * Octokit, and Supabase. `cause` is forwarded to the standard\n * ES2022 `Error` constructor.\n */\nexport class AtlaSentError extends Error {\n // Subclasses override to their own literal (e.g. \"AtlaSentDeniedError\");\n // keep this assignable rather than pinned to a single literal.\n override name: string = \"AtlaSentError\";\n\n /** HTTP status code, when the error originated from an API response. */\n readonly status: number | undefined;\n /** Coarse category — useful for `switch` statements at call sites. */\n readonly code: AtlaSentErrorCode | undefined;\n /** Correlation ID echoed from the `X-Request-ID` header the SDK sent. */\n readonly requestId: string | undefined;\n /** Parsed `Retry-After` header value, in milliseconds. Only set for 429. */\n readonly retryAfterMs: number | undefined;\n\n constructor(message: string, init: AtlaSentErrorInit = {}) {\n super(\n message,\n init.cause !== undefined ? { cause: init.cause } : undefined,\n );\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.retryAfterMs = init.retryAfterMs;\n }\n}\n\n/**\n * Outcome of a denied decision.\n *\n * `\"deny\"` is what the current `/v1-evaluate` API returns. `\"hold\"`\n * and `\"escalate\"` are reserved for forthcoming API decisions that\n * put a permit into a pending state requiring human review; the\n * union is declared now so call sites can `switch` exhaustively\n * from the start and adopt new decisions without a breaking change.\n */\nexport type AtlaSentDecision = \"deny\" | \"hold\" | \"escalate\";\n\n/** Initialization options for {@link AtlaSentDeniedError}. */\nexport interface AtlaSentDeniedErrorInit {\n decision: AtlaSentDecision;\n evaluationId: string;\n reason?: string;\n requestId?: string;\n auditHash?: string;\n}\n\n/**\n * Thrown by {@link atlasent.protect} when the policy engine refuses\n * the action, or when a permit fails end-to-end verification.\n *\n * This is the **fail-closed boundary** of the SDK: every code path\n * that short-circuits an action because authorization was not\n * confirmed raises an `AtlaSentDeniedError`. Callers cannot silently\n * proceed on a denial by forgetting to branch on a return value.\n *\n * Extends {@link AtlaSentError} so `instanceof AtlaSentError`\n * catches denials as part of the SDK's single exception family;\n * use `instanceof AtlaSentDeniedError` to distinguish a policy\n * denial from a transport/auth error.\n */\nexport class AtlaSentDeniedError extends AtlaSentError {\n override name: string = \"AtlaSentDeniedError\";\n\n /** Policy decision — `\"deny\"` today; `\"hold\"` / `\"escalate\"` reserved. */\n readonly decision: AtlaSentDecision;\n /** Opaque permit/decision id from `/v1-evaluate`. */\n readonly evaluationId: string;\n /** Human-readable explanation from the policy engine, if provided. */\n readonly reason: string | undefined;\n /** Hash-chained audit-trail entry associated with the decision. */\n readonly auditHash: string | undefined;\n\n constructor(init: AtlaSentDeniedErrorInit) {\n const msg = init.reason\n ? `AtlaSent ${init.decision}: ${init.reason}`\n : `AtlaSent ${init.decision}`;\n const errInit: AtlaSentErrorInit = { status: 200 };\n if (init.requestId !== undefined) errInit.requestId = init.requestId;\n super(msg, errInit);\n this.decision = init.decision;\n this.evaluationId = init.evaluationId;\n this.reason = init.reason;\n this.auditHash = init.auditHash;\n }\n}\n","/**\n * AtlaSent HTTP client.\n *\n * Two public methods, both backed by native `fetch`:\n * - {@link AtlaSentClient.evaluate} → POST {baseUrl}/v1-evaluate\n * - {@link AtlaSentClient.verifyPermit} → POST {baseUrl}/v1-verify-permit\n *\n * Fail-closed: a clean policy DENY is returned (not thrown), but\n * network, timeout, bad response, 4xx/5xx, and rate-limit conditions\n * all throw {@link AtlaSentError}.\n */\n\nimport type {\n AuditEventsPage,\n AuditEventsQuery,\n AuditExport,\n} from \"./audit.js\";\nimport {\n AtlaSentError,\n type AtlaSentErrorCode,\n type AtlaSentErrorInit,\n} from \"./errors.js\";\nimport type {\n ApiKeySelfResponse,\n AtlaSentClientOptions,\n AuditEventsResult,\n AuditExportRequest,\n AuditExportResult,\n EvaluateRequest,\n EvaluateResponse,\n RateLimitState,\n VerifyPermitRequest,\n VerifyPermitResponse,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.atlasent.io\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst SDK_VERSION = \"0.1.0\";\n\n/** Raw JSON shape received from `POST /v1-evaluate`. */\ninterface EvaluateWire {\n permitted: boolean;\n decision_id: string;\n reason?: string;\n audit_hash?: string;\n timestamp?: string;\n}\n\n/** Raw JSON shape received from `GET /v1-api-key-self`. */\ninterface ApiKeySelfWire {\n key_id: string;\n organization_id: string;\n environment: string;\n scopes?: string[];\n allowed_cidrs?: string[] | null;\n rate_limit_per_minute: number;\n client_ip?: string | null;\n expires_at?: string | null;\n}\n\n/** Raw JSON shape received from `POST /v1-verify-permit`. */\ninterface VerifyPermitWire {\n verified: boolean;\n outcome?: string;\n permit_hash?: string;\n timestamp?: string;\n}\n\nexport class AtlaSentClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: AtlaSentClientOptions) {\n if (!options.apiKey || typeof options.apiKey !== \"string\") {\n throw new AtlaSentError(\"apiKey is required\", {\n code: \"invalid_api_key\",\n });\n }\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Ask the policy engine whether an agent action is permitted.\n *\n * A \"DENY\" is **not** thrown — it is returned in\n * `response.decision`. Network errors, invalid API key, rate\n * limits, timeouts, and malformed responses throw\n * {@link AtlaSentError}.\n */\n async evaluate(input: EvaluateRequest): Promise<EvaluateResponse> {\n const body = {\n action: input.action,\n agent: input.agent,\n context: input.context ?? {},\n api_key: this.apiKey,\n };\n const { body: wire, rateLimit } = await this.post<EvaluateWire>(\"/v1-evaluate\", body);\n\n if (typeof wire.permitted !== \"boolean\" || typeof wire.decision_id !== \"string\") {\n throw new AtlaSentError(\n \"Malformed response from /v1-evaluate: missing `permitted` or `decision_id`\",\n { code: \"bad_response\" },\n );\n }\n\n return {\n decision: wire.permitted ? \"ALLOW\" : \"DENY\",\n permitId: wire.decision_id,\n reason: wire.reason ?? \"\",\n auditHash: wire.audit_hash ?? \"\",\n timestamp: wire.timestamp ?? \"\",\n rateLimit,\n };\n }\n\n /**\n * Verify that a previously issued permit is still valid.\n *\n * A `verified: false` response is **not** thrown — inspect the\n * returned object. Only transport / server errors throw.\n */\n async verifyPermit(\n input: VerifyPermitRequest,\n ): Promise<VerifyPermitResponse> {\n const body = {\n decision_id: input.permitId,\n action: input.action ?? \"\",\n agent: input.agent ?? \"\",\n context: input.context ?? {},\n api_key: this.apiKey,\n };\n const { body: wire, rateLimit } = await this.post<VerifyPermitWire>(\n \"/v1-verify-permit\",\n body,\n );\n\n if (typeof wire.verified !== \"boolean\") {\n throw new AtlaSentError(\n \"Malformed response from /v1-verify-permit: missing `verified`\",\n { code: \"bad_response\" },\n );\n }\n\n return {\n verified: wire.verified,\n outcome: wire.outcome ?? \"\",\n permitHash: wire.permit_hash ?? \"\",\n timestamp: wire.timestamp ?? \"\",\n rateLimit,\n };\n }\n\n /**\n * Self-introspection: ask the server to describe the API key this\n * client was constructed with. Returns the key's ID, organization,\n * environment, scopes, IP allowlist, per-minute rate limit, the\n * client IP the server observed, and the expiry (if any).\n *\n * Never includes the raw key or its hash. Safe to surface in operator\n * dashboards. Useful for `IP_NOT_ALLOWED` debugging (the server tells\n * you exactly which IP it saw) and for proactive expiry warnings.\n *\n * Throws {@link AtlaSentError} on transport / auth failures — same\n * taxonomy as {@link AtlaSentClient.evaluate}.\n */\n async keySelf(): Promise<ApiKeySelfResponse> {\n const { body: wire, rateLimit } = await this.get<ApiKeySelfWire>(\n \"/v1-api-key-self\",\n );\n\n if (typeof wire.key_id !== \"string\" || typeof wire.organization_id !== \"string\") {\n throw new AtlaSentError(\n \"Malformed response from /v1-api-key-self: missing `key_id` or `organization_id`\",\n { code: \"bad_response\" },\n );\n }\n\n return {\n keyId: wire.key_id,\n organizationId: wire.organization_id,\n environment: wire.environment,\n scopes: wire.scopes ?? [],\n allowedCidrs: wire.allowed_cidrs ?? null,\n rateLimitPerMinute: wire.rate_limit_per_minute,\n clientIp: wire.client_ip ?? null,\n expiresAt: wire.expires_at ?? null,\n rateLimit,\n };\n }\n\n /**\n * List persisted audit events for the authenticated organization\n * (`GET /v1-audit/events`). Returned rows are wire-identical with\n * the server: snake_case field names, including `previous_hash` and\n * the `hash` chain, so the response can be fed straight into the\n * offline verifier when paired with a signed export.\n *\n * `query.types` is a comma-joined list (e.g.\n * `\"evaluate.allow,policy.updated\"`). `cursor` is the opaque\n * `next_cursor` from the prior page. All fields are optional; the\n * server defaults `limit` to 50 (capped at 500).\n *\n * Throws {@link AtlaSentError} on transport / auth failures — same\n * taxonomy as {@link AtlaSentClient.evaluate}.\n */\n async listAuditEvents(\n query: AuditEventsQuery = {},\n ): Promise<AuditEventsResult> {\n const { body: wire, rateLimit } = await this.get<AuditEventsPage>(\n \"/v1-audit/events\",\n buildAuditEventsQuery(query),\n );\n\n if (!Array.isArray(wire.events) || typeof wire.total !== \"number\") {\n throw new AtlaSentError(\n \"Malformed response from /v1-audit/events: missing `events` or `total`\",\n { code: \"bad_response\" },\n );\n }\n\n return { ...wire, rateLimit };\n }\n\n /**\n * Request a signed audit export bundle\n * (`POST /v1-audit/exports`). The returned object is wire-identical\n * with the server — `signature`, `chain_head_hash`, `events`, and\n * friends survive untouched so the bundle can be persisted to disk\n * and handed to the offline verifier (`verifyBundle` /\n * `verifyAuditBundle`) without any reshaping.\n *\n * Pass `filter.types`, `filter.from`, `filter.to`, or `filter.actor_id`\n * to narrow the export; omit for a full-org bundle. `rateLimit` is\n * attached alongside the wire fields for observability.\n *\n * Throws {@link AtlaSentError} on transport / auth failures — same\n * taxonomy as {@link AtlaSentClient.evaluate}.\n */\n async createAuditExport(\n filter: AuditExportRequest = {},\n ): Promise<AuditExportResult> {\n const { body: wire, rateLimit } = await this.post<AuditExport>(\n \"/v1-audit/exports\",\n filter,\n );\n\n if (\n typeof wire.export_id !== \"string\" ||\n typeof wire.chain_head_hash !== \"string\" ||\n !Array.isArray(wire.events)\n ) {\n throw new AtlaSentError(\n \"Malformed response from /v1-audit/exports: missing `export_id`, `chain_head_hash`, or `events`\",\n { code: \"bad_response\" },\n );\n }\n\n return { ...wire, rateLimit };\n }\n\n private async post<T>(\n path: string,\n body: unknown,\n ): Promise<{ body: T; rateLimit: RateLimitState | null }> {\n return this.request<T>(path, \"POST\", body, undefined);\n }\n\n private async get<T>(\n path: string,\n query?: URLSearchParams,\n ): Promise<{ body: T; rateLimit: RateLimitState | null }> {\n return this.request<T>(path, \"GET\", undefined, query);\n }\n\n private async request<T>(\n path: string,\n method: \"GET\" | \"POST\",\n body: unknown,\n query: URLSearchParams | undefined,\n ): Promise<{ body: T; rateLimit: RateLimitState | null }> {\n const qs = query && Array.from(query).length > 0 ? `?${query.toString()}` : \"\";\n const url = `${this.baseUrl}${path}${qs}`;\n const requestId = globalThis.crypto.randomUUID();\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n \"User-Agent\": `@atlasent/sdk/${SDK_VERSION} node/${process.version}`,\n \"X-Request-ID\": requestId,\n };\n if (method === \"POST\") headers[\"Content-Type\"] = \"application/json\";\n\n const init: RequestInit = {\n method,\n headers,\n signal: AbortSignal.timeout(this.timeoutMs),\n };\n if (method === \"POST\") init.body = JSON.stringify(body);\n\n let response: Response;\n try {\n response = await this.fetchImpl(url, init);\n } catch (err) {\n throw mapFetchError(err, requestId);\n }\n\n if (!response.ok) {\n throw await buildHttpError(response, requestId);\n }\n\n let parsed: unknown;\n try {\n parsed = await response.json();\n } catch (err) {\n throw new AtlaSentError(\"Invalid JSON response from AtlaSent API\", {\n code: \"bad_response\",\n status: response.status,\n requestId,\n cause: err,\n });\n }\n\n if (parsed === null || typeof parsed !== \"object\") {\n throw new AtlaSentError(\"Expected a JSON object from AtlaSent API\", {\n code: \"bad_response\",\n status: response.status,\n requestId,\n });\n }\n\n return {\n body: parsed as T,\n rateLimit: parseRateLimitHeaders(response.headers),\n };\n }\n}\n\n/**\n * Parse the server's `X-RateLimit-*` header triple into a typed\n * {@link RateLimitState}. Returns `null` when any of the three headers\n * is missing or unparseable — callers treat that as \"the server didn't\n * emit rate-limit state\" rather than \"the window is empty\".\n *\n * `X-RateLimit-Reset` is accepted as either unix-seconds (what the\n * AtlaSent edge functions emit today) or an ISO 8601 timestamp.\n */\nfunction parseRateLimitHeaders(headers: Headers): RateLimitState | null {\n const rawLimit = headers.get(\"x-ratelimit-limit\");\n const rawRemaining = headers.get(\"x-ratelimit-remaining\");\n const rawReset = headers.get(\"x-ratelimit-reset\");\n if (rawLimit === null || rawRemaining === null || rawReset === null) {\n return null;\n }\n const limit = Number(rawLimit);\n const remaining = Number(rawRemaining);\n if (!Number.isFinite(limit) || !Number.isFinite(remaining)) {\n return null;\n }\n const resetAt = parseResetHeader(rawReset);\n if (resetAt === null) {\n return null;\n }\n return { limit, remaining, resetAt };\n}\n\nfunction parseResetHeader(raw: string): Date | null {\n const seconds = Number(raw);\n if (Number.isFinite(seconds)) {\n // Standard shape: unix seconds. 10-digit values are in the valid\n // range ~2001–2286 so this heuristic won't confuse a tiny\n // `remaining`-like number for an epoch.\n return new Date(seconds * 1000);\n }\n const ms = Date.parse(raw);\n if (Number.isFinite(ms)) {\n return new Date(ms);\n }\n return null;\n}\n\nfunction mapFetchError(err: unknown, requestId: string): AtlaSentError {\n if (err instanceof AtlaSentError) return err;\n if (err instanceof DOMException && err.name === \"TimeoutError\") {\n return new AtlaSentError(\"Request to AtlaSent API timed out\", {\n code: \"timeout\",\n requestId,\n cause: err,\n });\n }\n if (err instanceof Error && err.name === \"AbortError\") {\n return new AtlaSentError(\"Request to AtlaSent API timed out\", {\n code: \"timeout\",\n requestId,\n cause: err,\n });\n }\n const message = err instanceof Error ? err.message : \"network error\";\n return new AtlaSentError(`Failed to reach AtlaSent API: ${message}`, {\n code: \"network\",\n requestId,\n cause: err,\n });\n}\n\nasync function buildHttpError(\n response: Response,\n requestId: string,\n): Promise<AtlaSentError> {\n const status = response.status;\n const classified = await classifyHttpStatus(response);\n const init: AtlaSentErrorInit = {\n status,\n code: classified.code,\n requestId,\n };\n if (classified.retryAfterMs !== undefined) {\n init.retryAfterMs = classified.retryAfterMs;\n }\n return new AtlaSentError(classified.message, init);\n}\n\nasync function classifyHttpStatus(response: Response): Promise<{\n message: string;\n code: AtlaSentErrorCode;\n retryAfterMs: number | undefined;\n}> {\n const status = response.status;\n const serverMessage = await readServerMessage(response);\n\n if (status === 401) {\n return {\n message: serverMessage ?? \"Invalid API key\",\n code: \"invalid_api_key\",\n retryAfterMs: undefined,\n };\n }\n if (status === 403) {\n return {\n message:\n serverMessage ?? \"Access forbidden — check your API key permissions\",\n code: \"forbidden\",\n retryAfterMs: undefined,\n };\n }\n if (status === 429) {\n return {\n message: serverMessage ?? \"Rate limited by AtlaSent API\",\n code: \"rate_limited\",\n retryAfterMs: parseRetryAfter(response.headers.get(\"retry-after\")),\n };\n }\n if (status >= 500) {\n return {\n message: serverMessage ?? `AtlaSent API returned HTTP ${status}`,\n code: \"server_error\",\n retryAfterMs: undefined,\n };\n }\n return {\n message: serverMessage ?? `AtlaSent API returned HTTP ${status}`,\n code: \"bad_request\",\n retryAfterMs: undefined,\n };\n}\n\nasync function readServerMessage(response: Response): Promise<string | null> {\n try {\n const text = await response.text();\n if (!text) return null;\n try {\n const parsed = JSON.parse(text);\n if (parsed && typeof parsed === \"object\") {\n const msg = (parsed as Record<string, unknown>).message;\n const reason = (parsed as Record<string, unknown>).reason;\n if (typeof msg === \"string\" && msg.length > 0) return msg;\n if (typeof reason === \"string\" && reason.length > 0) return reason;\n }\n } catch {\n // Fall through — treat as plain text.\n }\n return text.length > 500 ? `${text.slice(0, 500)}…` : text;\n } catch {\n return null;\n }\n}\n\n/**\n * Translate an {@link AuditEventsQuery} into `URLSearchParams`. The\n * server expects snake_case keys (`actor_id`) and accepts\n * comma-joined values for `types`; numeric `limit` serializes via\n * `String(n)`. Undefined / empty fields are dropped so the query\n * string stays minimal.\n */\nfunction buildAuditEventsQuery(query: AuditEventsQuery): URLSearchParams {\n const params = new URLSearchParams();\n if (query.types !== undefined && query.types !== \"\") {\n params.set(\"types\", query.types);\n }\n if (query.actor_id !== undefined && query.actor_id !== \"\") {\n params.set(\"actor_id\", query.actor_id);\n }\n if (query.from !== undefined && query.from !== \"\") {\n params.set(\"from\", query.from);\n }\n if (query.to !== undefined && query.to !== \"\") {\n params.set(\"to\", query.to);\n }\n if (query.limit !== undefined) {\n params.set(\"limit\", String(query.limit));\n }\n if (query.cursor !== undefined && query.cursor !== \"\") {\n params.set(\"cursor\", query.cursor);\n }\n return params;\n}\n\nfunction parseRetryAfter(raw: string | null): number | undefined {\n if (!raw) return undefined;\n const seconds = Number(raw);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(raw);\n if (Number.isFinite(date)) {\n const delta = date - Date.now();\n return delta > 0 ? delta : 0;\n }\n return undefined;\n}\n","/**\n * Offline verification for audit export bundles.\n *\n * Mirrors `atlasent-api/supabase/functions/v1-audit/verify.ts`. The\n * reference verifier there is the source of truth; this module must\n * stay byte-identical with it on the canonicalization + signing path\n * so a bundle that verifies in the backend verifies here (and vice\n * versa).\n *\n * Primary entry point:\n *\n * ```ts\n * import { verifyBundle } from \"@atlasent/sdk\";\n * const result = await verifyBundle(\"export.json\", { publicKeysPem: [pem] });\n * ```\n *\n * Node 20+ ships Ed25519 in `crypto.webcrypto.subtle`, so no extra\n * dependencies are required.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { webcrypto } from \"node:crypto\";\n\nconst GENESIS_HASH = \"0\".repeat(64);\n\nconst subtle = webcrypto.subtle;\n\n/** Node's webcrypto CryptoKey — kept local so the module doesn't depend on DOM types. */\ntype WebCryptoKey = webcrypto.CryptoKey;\n\n/** Public key candidate the verifier will try, tagged with its registry id. */\nexport interface VerifyKey {\n keyId: string;\n publicKey: WebCryptoKey;\n}\n\nexport interface BundleVerificationResult {\n /**\n * AND of three checks: adjacency (each event's `previous_hash`\n * equals the prior event's `hash`), per-event hash recomputation\n * from the canonical payload, and `chain_head_hash` matching the\n * last event's stored hash.\n */\n chainIntegrityOk: boolean;\n /** Ed25519 signature verified against one of the supplied public keys. */\n signatureValid: boolean;\n /** `chain_head_hash` equals the last event's stored `hash`. */\n headHashMatches: boolean;\n /** Event ids whose recomputed hash != stored hash. */\n tamperedEventIds: string[];\n /** Which registry key id matched, when `signatureValid` is true. */\n matchedKeyId?: string | undefined;\n /** Non-fatal explanation when a flag is false. */\n reason?: string | undefined;\n /** Convenience: `chainIntegrityOk && signatureValid`. */\n verified: boolean;\n}\n\n/** Parsed bundle shape the verifier consumes. Fields beyond these are ignored. */\nexport interface AuditBundle {\n export_id?: unknown;\n org_id?: unknown;\n chain_head_hash?: unknown;\n event_count?: unknown;\n signed_at?: unknown;\n events?: unknown;\n signature?: unknown;\n signing_key_id?: unknown;\n [k: string]: unknown;\n}\n\nexport interface VerifyBundleOptions {\n /** SPKI-PEM strings (one per key in the active trust set). */\n publicKeysPem?: readonly string[];\n /** Already-imported keys, paired with registry ids (rotation hint). */\n keys?: readonly VerifyKey[];\n}\n\n// ─── Canonicalization ─────────────────────────────────────────────────────────\n\n/**\n * Reproduces `_shared/rules.ts::canonicalJSON` byte-for-byte:\n * - object keys sorted at every depth\n * - no whitespace\n * - `null`, `undefined`, `NaN`, `±Infinity` all render as `\"null\"`\n * - strings use standard `JSON.stringify` escapes\n */\nexport function canonicalJSON(value: unknown): string {\n if (value === null || value === undefined) return \"null\";\n if (typeof value === \"number\") return Number.isFinite(value) ? String(value) : \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"string\") return JSON.stringify(value);\n if (Array.isArray(value)) return \"[\" + value.map(canonicalJSON).join(\",\") + \"]\";\n if (typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n return \"{\" + keys.map((k) => JSON.stringify(k) + \":\" + canonicalJSON(obj[k])).join(\",\") + \"}\";\n }\n return \"null\";\n}\n\nasync function sha256Hex(input: string): Promise<string> {\n const buf = await subtle.digest(\"SHA-256\", new TextEncoder().encode(input));\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n// ─── Envelope reconstruction ──────────────────────────────────────────────────\n\n/**\n * Recreate the exact bytes `handleExport` signed. Key order is\n * load-bearing — must match the object literal in\n * `v1-audit/index.ts::handleExport`. V8 preserves insertion order, so\n * the literal below is byte-identical with what the backend signs.\n */\nexport function signedBytesFor(bundle: AuditBundle): Uint8Array<ArrayBuffer> {\n const envelope = {\n export_id: bundle.export_id,\n org_id: bundle.org_id,\n chain_head_hash: bundle.chain_head_hash,\n event_count: bundle.event_count,\n signed_at: bundle.signed_at,\n events: bundle.events,\n };\n return new TextEncoder().encode(JSON.stringify(envelope));\n}\n\n// ─── Chain verification ───────────────────────────────────────────────────────\n\ninterface ChainEvent {\n id?: unknown;\n hash?: unknown;\n previous_hash?: unknown;\n payload?: unknown;\n}\n\nasync function verifyChainEvents(\n events: ChainEvent[],\n): Promise<{ adjacencyOk: boolean; tamperedIds: string[] }> {\n const tamperedIds: string[] = [];\n let adjacencyOk = true;\n const first = events[0];\n let prevHash =\n first && typeof first.previous_hash === \"string\" ? first.previous_hash : GENESIS_HASH;\n\n for (let i = 0; i < events.length; i++) {\n const e = events[i];\n if (!e || typeof e.hash !== \"string\" || typeof e.previous_hash !== \"string\") {\n tamperedIds.push(String(e?.id ?? `index_${i}`));\n adjacencyOk = false;\n continue;\n }\n if (e.previous_hash !== prevHash) adjacencyOk = false;\n\n const canonical = canonicalJSON(e.payload ?? {});\n const recomputed = await sha256Hex(prevHash + canonical);\n if (recomputed !== e.hash) tamperedIds.push(String(e.id));\n\n prevHash = e.hash;\n }\n\n return { adjacencyOk, tamperedIds };\n}\n\n// ─── Signature verification ───────────────────────────────────────────────────\n\nfunction base64UrlDecode(s: string): Uint8Array<ArrayBuffer> {\n const pad = s.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (s.length % 4));\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\") + pad;\n const bin = Buffer.from(b64, \"base64\");\n const out = new Uint8Array(bin.byteLength);\n out.set(bin);\n return out;\n}\n\nasync function importSpkiPem(pem: string): Promise<WebCryptoKey> {\n const b64 = pem\n .replace(/-----BEGIN PUBLIC KEY-----/, \"\")\n .replace(/-----END PUBLIC KEY-----/, \"\")\n .replace(/\\s+/g, \"\");\n const bytes = Uint8Array.from(Buffer.from(b64, \"base64\"));\n return subtle.importKey(\"spki\", bytes, { name: \"Ed25519\" }, false, [\"verify\"]);\n}\n\nasync function resolveKeys(options: VerifyBundleOptions | undefined): Promise<VerifyKey[]> {\n const out: VerifyKey[] = [];\n if (options?.keys) out.push(...options.keys);\n if (options?.publicKeysPem) {\n for (let i = 0; i < options.publicKeysPem.length; i++) {\n const pem = options.publicKeysPem[i];\n if (!pem) continue;\n try {\n const pk = await importSpkiPem(pem);\n out.push({ keyId: `pem_${i}`, publicKey: pk });\n } catch {\n // Malformed PEM — skip it, try the rest.\n }\n }\n }\n return out;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport async function verifyAuditBundle(\n bundle: AuditBundle,\n keys: readonly VerifyKey[],\n): Promise<BundleVerificationResult> {\n const events: ChainEvent[] = Array.isArray(bundle.events) ? (bundle.events as ChainEvent[]) : [];\n\n const { adjacencyOk, tamperedIds } = await verifyChainEvents(events);\n\n const last = events[events.length - 1];\n const lastHash = last && typeof last.hash === \"string\" ? last.hash : GENESIS_HASH;\n const headHashMatches =\n typeof bundle.chain_head_hash === \"string\" ? bundle.chain_head_hash === lastHash : false;\n\n const chainIntegrityOk = adjacencyOk && tamperedIds.length === 0 && headHashMatches;\n\n let signatureValid = false;\n let matchedKeyId: string | undefined;\n let reason: string | undefined;\n\n if (keys.length === 0) {\n reason =\n \"no signing keys configured (signing_keys table empty and ATLASENT_EXPORT_SIGNING_KEY_PUBLIC unset)\";\n } else if (typeof bundle.signature !== \"string\" || bundle.signature.length === 0) {\n reason = \"bundle carries no signature\";\n } else {\n try {\n const sigBytes = base64UrlDecode(bundle.signature);\n const envelopeBytes = signedBytesFor(bundle);\n const hint = typeof bundle.signing_key_id === \"string\" ? bundle.signing_key_id : null;\n const ordered = hint\n ? [\n ...keys.filter((k) => k.keyId === hint),\n ...keys.filter((k) => k.keyId !== hint),\n ]\n : Array.from(keys);\n for (const k of ordered) {\n const ok = await subtle.verify(\"Ed25519\", k.publicKey, sigBytes, envelopeBytes);\n if (ok) {\n signatureValid = true;\n matchedKeyId = k.keyId;\n break;\n }\n }\n if (!signatureValid) {\n reason = `signature did not verify under any of ${keys.length} configured public key(s)`;\n }\n } catch (err) {\n reason = `signature check failed: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n\n if (!chainIntegrityOk && reason === undefined) {\n if (tamperedIds.length > 0) reason = `hash mismatch for ${tamperedIds.length} event(s)`;\n else if (!adjacencyOk) reason = \"chain adjacency broken\";\n else if (!headHashMatches) reason = \"chain_head_hash does not match last event\";\n }\n\n return {\n chainIntegrityOk,\n signatureValid,\n headHashMatches,\n tamperedEventIds: tamperedIds,\n matchedKeyId,\n reason,\n verified: chainIntegrityOk && signatureValid,\n };\n}\n\n/**\n * Load a bundle from disk (or a parsed object) and verify it.\n *\n * `publicKeysPem` is the active SPKI-PEM set from\n * `GET /v1-signing-keys`. When omitted, the chain check still runs\n * but `signatureValid` will be false with an explanatory `reason` —\n * callers that want a complete offline check MUST supply the trust\n * set.\n */\nexport async function verifyBundle(\n pathOrBundle: string | AuditBundle,\n options?: VerifyBundleOptions,\n): Promise<BundleVerificationResult> {\n let bundle: AuditBundle;\n if (typeof pathOrBundle === \"string\") {\n const raw = await readFile(pathOrBundle, \"utf8\");\n const parsed = JSON.parse(raw);\n // Fixture wrapper shape: { description, bundle }. Accepted for ergonomics.\n bundle =\n parsed && typeof parsed === \"object\" && \"bundle\" in parsed && typeof parsed.bundle === \"object\"\n ? (parsed.bundle as AuditBundle)\n : (parsed as AuditBundle);\n } else {\n bundle = pathOrBundle;\n }\n const keys = await resolveKeys(options);\n return verifyAuditBundle(bundle, keys);\n}\n","/**\n * `atlasent.protect(...)` — the one-call, fail-closed execution-time\n * authorization boundary.\n *\n * ```ts\n * import atlasent from \"@atlasent/sdk\";\n *\n * const permit = await atlasent.protect({\n * agent: \"deploy-bot\",\n * action: \"deploy_to_production\",\n * context: { commit, approver },\n * });\n * // …run the action. If we got here, AtlaSent authorized it\n * // end-to-end (evaluate + verifyPermit).\n * ```\n *\n * Unlike {@link AtlaSentClient.evaluate}, `protect` never returns a\n * denied decision. On deny, it throws {@link AtlaSentDeniedError};\n * on transport / auth / server failure it throws\n * {@link AtlaSentError}. The action cannot execute unless a valid\n * {@link Permit} is returned — this is the SDK's category boundary,\n * not a helper.\n */\n\nimport { AtlaSentClient } from \"./client.js\";\nimport {\n AtlaSentDeniedError,\n AtlaSentError,\n type AtlaSentDecision,\n} from \"./errors.js\";\nimport type { AtlaSentClientOptions } from \"./types.js\";\n\n/** Input to {@link protect}. Same shape as `EvaluateRequest`. */\nexport interface ProtectRequest {\n agent: string;\n action: string;\n context?: Record<string, unknown>;\n}\n\n/**\n * Success return from {@link protect}. The action is authorized\n * end-to-end — evaluation allowed AND the resulting permit verified.\n */\nexport interface Permit {\n /** Opaque permit / decision identifier. */\n permitId: string;\n /** Verification hash bound to the permit. */\n permitHash: string;\n /** Audit-trail entry associated with the decision (hash-chained). */\n auditHash: string;\n /** Human-readable reason from the policy engine. */\n reason: string;\n /** ISO 8601 timestamp of the verification. */\n timestamp: string;\n}\n\n/** Configuration for the process-wide singleton used by {@link protect}. */\nexport interface ConfigureOptions {\n /** Overrides `ATLASENT_API_KEY` env var. */\n apiKey?: string;\n /** Overrides the default `https://api.atlasent.io`. */\n baseUrl?: string;\n /** Per-request timeout in ms. */\n timeoutMs?: number;\n /** Inject a custom fetch (primarily for tests). */\n fetch?: typeof fetch;\n}\n\nlet sharedClient: AtlaSentClient | null = null;\nlet overrides: ConfigureOptions = {};\n\n/**\n * Configure the singleton client used by {@link protect}. Optional —\n * if `ATLASENT_API_KEY` is set in the environment, `protect` works\n * without any configuration. Calling `configure` again replaces the\n * singleton; subsequent `protect` calls use the new settings.\n */\nexport function configure(options: ConfigureOptions): void {\n overrides = { ...overrides, ...options };\n sharedClient = null;\n}\n\n/** Reset the singleton. Exported for tests; not part of the public API. */\nexport function __resetSharedClientForTests(): void {\n sharedClient = null;\n overrides = {};\n}\n\nfunction getClient(): AtlaSentClient {\n if (sharedClient) return sharedClient;\n\n const apiKey = overrides.apiKey ?? process.env.ATLASENT_API_KEY;\n if (!apiKey) {\n throw new AtlaSentError(\n \"AtlaSent is not configured. Set ATLASENT_API_KEY in the environment, or call atlasent.configure({ apiKey }).\",\n { code: \"invalid_api_key\" },\n );\n }\n const options: AtlaSentClientOptions = { apiKey };\n if (overrides.baseUrl !== undefined) options.baseUrl = overrides.baseUrl;\n if (overrides.timeoutMs !== undefined) options.timeoutMs = overrides.timeoutMs;\n if (overrides.fetch !== undefined) options.fetch = overrides.fetch;\n sharedClient = new AtlaSentClient(options);\n return sharedClient;\n}\n\nfunction wireDecisionToDenied(serverDecision: string): AtlaSentDecision {\n // The current /v1-evaluate contract returns \"ALLOW\" | \"DENY\". Map\n // \"DENY\" → \"deny\"; any future lowercase-or-new value passes through\n // so forthcoming \"hold\" / \"escalate\" responses don't break callers.\n const lower = serverDecision.toLowerCase();\n if (lower === \"hold\" || lower === \"escalate\") return lower;\n return \"deny\";\n}\n\n/**\n * Authorize an action end-to-end. On allow, returns a verified\n * {@link Permit}. On anything else, throws:\n *\n * - {@link AtlaSentDeniedError} — policy denied, or the permit\n * failed verification. Fail-closed: if this throws, the action\n * MUST NOT proceed.\n * - {@link AtlaSentError} — transport, timeout, auth, rate-limit,\n * or server error. Same fail-closed contract: do not proceed.\n */\nexport async function protect(request: ProtectRequest): Promise<Permit> {\n const client = getClient();\n const evaluation = await client.evaluate(request);\n\n if (evaluation.decision !== \"ALLOW\") {\n throw new AtlaSentDeniedError({\n decision: wireDecisionToDenied(evaluation.decision),\n evaluationId: evaluation.permitId,\n reason: evaluation.reason,\n auditHash: evaluation.auditHash,\n });\n }\n\n const verifyRequest: {\n permitId: string;\n agent: string;\n action: string;\n context?: Record<string, unknown>;\n } = {\n permitId: evaluation.permitId,\n agent: request.agent,\n action: request.action,\n };\n if (request.context !== undefined) verifyRequest.context = request.context;\n const verification = await client.verifyPermit(verifyRequest);\n\n if (!verification.verified) {\n throw new AtlaSentDeniedError({\n decision: \"deny\",\n evaluationId: evaluation.permitId,\n reason: `Permit failed verification (${verification.outcome})`,\n auditHash: evaluation.auditHash,\n });\n }\n\n return {\n permitId: evaluation.permitId,\n permitHash: verification.permitHash,\n auditHash: evaluation.auditHash,\n reason: evaluation.reason,\n timestamp: verification.timestamp,\n };\n}\n","/**\n * Retry-policy helpers for the AtlaSent TypeScript SDK.\n *\n * This module is **pure**: no I/O, no network, no globals beyond\n * `Math.random`. The intent is that {@link AtlaSentClient} (and any\n * caller wrapping `protect()` / `evaluate()`) can ask\n * {@link isRetryable} whether to retry a given {@link AtlaSentError},\n * then ask {@link computeBackoffMs} how long to sleep before the next\n * attempt.\n *\n * Wire-up into the client itself is intentionally deferred — see\n * ROADMAP item #7 (Post-GA). Sentry breadcrumb emission is also\n * deferred; both will land together once a transport-level retry\n * loop is wired into `client.ts`.\n *\n * Retry classification (matches the server's documented contract):\n * - `network` / `timeout` → retry (transient transport)\n * - `server_error` (HTTP 5xx) → retry\n * - `rate_limited` (HTTP 429) → retry, honour `retryAfterMs`\n * - `bad_response` → retry (likely truncated body)\n * - `invalid_api_key`/`forbidden`/`bad_request` → never retry\n *\n * Backoff: capped exponential with full jitter.\n * delay = min(maxDelayMs, baseDelayMs * 2^attempt) * random[0, 1)\n *\n * \"Full jitter\" is the AWS-recommended scheme — see\n * https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/.\n * It avoids thundering-herd retries from many SDK instances that hit\n * a 429 in the same window.\n */\n\nimport { AtlaSentError, type AtlaSentErrorCode } from \"./errors.js\";\n\n/** Defaults for {@link RetryPolicy}. Conservative — three retries, ~7s ceiling. */\nexport const DEFAULT_RETRY_POLICY: Required<RetryPolicy> = {\n maxAttempts: 3,\n baseDelayMs: 250,\n maxDelayMs: 7_000,\n};\n\n/**\n * Caller-tunable retry policy. All fields optional; missing fields\n * fall back to {@link DEFAULT_RETRY_POLICY}.\n */\nexport interface RetryPolicy {\n /**\n * Total attempts including the first try. `1` disables retries\n * entirely. Must be `>= 1`; values below are clamped to `1`.\n */\n maxAttempts?: number;\n /**\n * Initial backoff for `attempt = 0`. Doubles per attempt up to\n * `maxDelayMs`. Must be `>= 0`.\n */\n baseDelayMs?: number;\n /**\n * Hard ceiling on the per-attempt sleep, applied **before** jitter.\n * The actual sleep is uniformly distributed in `[0, ceiling]`.\n */\n maxDelayMs?: number;\n}\n\n/**\n * Error codes the SDK considers transient. A `Set` (rather than a\n * `switch`) keeps callers free to extend the policy in the future\n * without forking this module.\n */\nconst RETRYABLE_CODES: ReadonlySet<AtlaSentErrorCode> = new Set([\n \"network\",\n \"timeout\",\n \"rate_limited\",\n \"server_error\",\n \"bad_response\",\n]);\n\n/**\n * Decide whether `err` is worth a retry. Anything that isn't an\n * {@link AtlaSentError} is treated as non-retryable — the SDK's\n * transport layer always wraps fetch failures in `AtlaSentError`,\n * so a non-AtlaSent throwable is by definition a programmer bug\n * (a bad input, an assertion in user code) and should propagate.\n */\nexport function isRetryable(err: unknown): boolean {\n if (!(err instanceof AtlaSentError)) return false;\n if (err.code === undefined) return false;\n return RETRYABLE_CODES.has(err.code);\n}\n\n/**\n * Compute how long to sleep before retry attempt `attempt`\n * (zero-indexed: `attempt = 0` is the first retry, i.e. the second\n * total request). Uses capped exponential backoff with full jitter.\n *\n * When `err` carries a `retryAfterMs` (server-provided `Retry-After`\n * header), the result is `max(retryAfterMs, jitteredDelay)` — the\n * server's hint is treated as a floor so we never retry sooner than\n * the server asked.\n *\n * @param attempt Zero-indexed retry attempt (0, 1, 2, ...).\n * @param policy Optional override of {@link DEFAULT_RETRY_POLICY}.\n * @param err Optional error whose `retryAfterMs` is honoured.\n * @param random Injectable RNG, defaults to `Math.random`. Must\n * return values in `[0, 1)` to preserve the\n * distribution.\n */\nexport function computeBackoffMs(\n attempt: number,\n policy: RetryPolicy = {},\n err?: unknown,\n random: () => number = Math.random,\n): number {\n const merged = mergePolicy(policy);\n const safeAttempt = Math.max(0, Math.floor(attempt));\n // 2^attempt grows exponentially; cap before multiplying to keep the\n // intermediate value bounded for very large `attempt`.\n const exp = Math.min(safeAttempt, 30);\n const ceiling = Math.min(merged.maxDelayMs, merged.baseDelayMs * 2 ** exp);\n const jittered = Math.floor(ceiling * clampUnit(random()));\n\n const retryAfterMs =\n err instanceof AtlaSentError && typeof err.retryAfterMs === \"number\"\n ? Math.max(0, err.retryAfterMs)\n : 0;\n\n return Math.max(retryAfterMs, jittered);\n}\n\n/**\n * Returns `true` when `attempt` (zero-indexed) is below the policy's\n * `maxAttempts - 1` ceiling — i.e. when there is still budget for at\n * least one more try after this one. Convenience wrapper so retry\n * loops read top-to-bottom:\n *\n * ```ts\n * for (let attempt = 0; ; attempt++) {\n * try { return await op(); }\n * catch (err) {\n * if (!isRetryable(err) || !hasAttemptsLeft(attempt, policy)) throw err;\n * await sleep(computeBackoffMs(attempt, policy, err));\n * }\n * }\n * ```\n */\nexport function hasAttemptsLeft(\n attempt: number,\n policy: RetryPolicy = {},\n): boolean {\n const merged = mergePolicy(policy);\n return attempt + 1 < merged.maxAttempts;\n}\n\n/**\n * Merge a partial policy with {@link DEFAULT_RETRY_POLICY} and clamp\n * each field into a sensible range. Exported for tests and for\n * callers that want to log the resolved policy.\n */\nexport function mergePolicy(policy: RetryPolicy): Required<RetryPolicy> {\n const maxAttempts = Math.max(\n 1,\n Math.floor(policy.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts),\n );\n const baseDelayMs = Math.max(\n 0,\n policy.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,\n );\n const maxDelayMs = Math.max(\n baseDelayMs,\n policy.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,\n );\n return { maxAttempts, baseDelayMs, maxDelayMs };\n}\n\n/**\n * Clamp `n` into `[0, 1)`. Defends against a misbehaving injected\n * RNG returning `NaN`, `Infinity`, or a negative number.\n */\nfunction clampUnit(n: number): number {\n if (!Number.isFinite(n)) return 0;\n if (n < 0) return 0;\n if (n >= 1) return 0.999_999_999;\n return n;\n}\n"],"mappings":"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","names":[]}
|