import { h as OpenCloudErrorOptions, m as OpenCloudError } from "./types-C3Egi37J.mjs"; //#region src/errors/api-error.d.ts /** * Options for constructing an {@link ApiError}. * * @since 0.1.0 */ interface ApiErrorOptions extends OpenCloudErrorOptions { /** Parsed response body, when present. */ details?: JSONValue | undefined; /** * Wall-clock time the request was in flight before this error, in * milliseconds. Present for errors built by the transport; a long elapsed * time on an intermittent failure points at a load or timeout correlation. */ elapsedMs?: number | undefined; /** * Human-readable summary extracted from an HTML gateway error page, set * when the error body was such a page (an HAProxy-style load-balancer * rejection) rather than an Open Cloud response. When present, the raw HTML * is not retained on {@link ApiError.details}. */ gatewaySummary?: string | undefined; /** HTTP method of the request that produced this error. */ method?: string | undefined; /** * Allowlisted response headers useful for diagnosis and escalation (request * ids, edge/server identifiers). The full header set is never retained, to * avoid surfacing anything sensitive and to keep errors light. */ responseHeaders?: Readonly> | undefined; /** HTTP status code from the API response. */ statusCode: number; /** * Length, in decoded characters, of a 2xx body that could not be parsed as * JSON. Set only by the transport, and only for that failure — nothing else * builds an {@link ApiError} over a successful status — so its presence is * also how a body-parse failure is told apart from an API rejection. The * number is the diagnostic one: a body that stops mid-token at exactly the * length the edge delivered is a truncated read, not malformed JSON. */ unparsedBodyLength?: number | undefined; /** Fully-qualified URL of the request that produced this error. */ url?: string | undefined; } /** * Everything in {@link ApiErrorOptions} except what the API answered ({@link * ApiErrorOptions.code}, {@link ApiErrorOptions.details}, and {@link * ApiErrorOptions.statusCode}): the request the transport made, how long it was * in flight, and what the response carried alongside its body. Every field is * required, and every one accepts `undefined` for a field the transport did not * capture. * * @since 0.2.0 */ type ApiRequestContext = { [K in Exclude]-?: ApiErrorOptions[K] | undefined }; /** * Thrown when the Roblox Open Cloud API returns a non-2xx response * that is not a rate limit (429). * * @since 0.1.0 * * @example * * ```ts * import { ApiError } from "@bedrock-rbx/ocale"; * * const error = new ApiError("HTTP 404: Pass not found (code NotFound)", { * code: "NotFound", * details: { errorCode: "NotFound", message: "Pass not found" }, * statusCode: 404, * }); * * expect(error).toBeInstanceOf(ApiError); * expect(error.statusCode).toBe(404); * expect(error.code).toBe("NotFound"); * expect(error.details).toEqual({ * errorCode: "NotFound", * message: "Pass not found", * }); * ``` */ declare class ApiError extends OpenCloudError { readonly details: JSONValue | undefined; readonly elapsedMs: number | undefined; readonly gatewaySummary: string | undefined; readonly method: string | undefined; override readonly name: string; readonly responseHeaders: Readonly> | undefined; readonly statusCode: number; readonly unparsedBodyLength: number | undefined; readonly url: string | undefined; /** * Creates a new ApiError. * * @param message - Human-readable error description. * @param options - Error options including status code, optional error * code, the parsed response body when present, and the request context * (method, url, elapsed time, allowlisted response headers) when built by * the transport. */ constructor(message: string, options: ApiErrorOptions); } /** * Reads the request context off an {@link ApiError} so a replacement error can * carry it. Spread the result into the options of the new error. {@link * OpenCloudError.code}, {@link ApiError.details}, and {@link * ApiError.statusCode} describe the API's answer and are left to the caller. * * @since 0.2.0 * * @param err - The error to read the request context from. * @returns The transport-captured fields, each undefined when unset. * * @example * * ```ts * import { ApiError, requestContextOf } from "@bedrock-rbx/ocale"; * * const original = new ApiError("HTTP 404", { * elapsedMs: 512, * method: "GET", * statusCode: 404, * url: "https://apis.roblox.com/cloud/v2/universes/1", * }); * * const rewrapped = new ApiError("Universe 1 was not found; adoption failed", { * ...requestContextOf(original), * statusCode: 404, * }); * * expect(rewrapped.method).toBe("GET"); * expect(rewrapped.url).toBe("https://apis.roblox.com/cloud/v2/universes/1"); * expect(rewrapped.elapsedMs).toBe(512); * ``` */ declare function requestContextOf(err: ApiError): ApiRequestContext; //#endregion //#region src/errors/network-error.d.ts /** * Options for constructing a {@link NetworkError}. * * @since 0.1.0 */ interface NetworkErrorOptions extends ErrorOptions { /** HTTP method of the request that failed. */ method?: string | undefined; /** Fully-qualified URL of the request that failed. */ url?: string | undefined; } /** * Thrown when a network-level failure prevents the request from reaching * the Roblox Open Cloud API (e.g., DNS resolution failure, connection reset). * The `method` and `url` name the failing call so a transport failure that * survives every retry can be diagnosed; the underlying transport error is * carried on `cause`. * * @since 0.1.0 */ declare class NetworkError extends OpenCloudError { readonly method: string | undefined; override readonly name: string; readonly url: string | undefined; /** * Creates a new NetworkError. * * @param message - Human-readable error description. * @param options - Error options including the optional `cause` and the * `method` / `url` of the request that failed. */ constructor(message: string, options?: NetworkErrorOptions); } //#endregion //#region src/errors/rate-limit.d.ts /** * Options for constructing a {@link RateLimitError}. * * @since 0.1.0 */ interface RateLimitErrorOptions extends OpenCloudErrorOptions { /** * Parsed 429 response body, when present. Holds the server's 429 * explanation (JSON when the body parses, otherwise the truncated raw * text) so a rate limit stays diagnosable from the error alone. A literal * JSON `null` remains `null`; an absent body is `undefined`. */ details?: JSONValue | undefined; /** * Requests left in the reported rate-limit window. Read from * `x-ratelimit-remaining` using the smallest valid token. * * `undefined` when the header has no valid non-negative integer token. * * Parsed separately from `x-ratelimit-reset`; a valid value survives an * invalid reset. * * This is one budget reading, not a classifier for the cause of the 429. */ remaining?: number | undefined; /** * Allowlisted response headers useful for diagnosing the 429. Values are * preserved exactly as the Fetch API presents them, including comma-joined * multi-window values. The full header set is never retained. */ responseHeaders?: Readonly> | undefined; /** Seconds to wait before retrying the request. */ retryAfterSeconds: number; /** * HTTP status code that produced the error. Always `429` when minted by the * SDK transport; `undefined` when constructed without one. */ statusCode?: number | undefined; } /** * Thrown when the Roblox Open Cloud API returns a 429 Too Many Requests * response. Contains the server-suggested retry delay and safe, * machine-readable response evidence. Generic 429 evidence can be ambiguous: * no individual header, body code, or remaining-budget value guarantees the * upstream cause. * * @since 0.1.0 * * @example * * ```ts * import { RateLimitError } from "@bedrock-rbx/ocale"; * * const error = new RateLimitError("Too many requests", { * code: "RESOURCE_EXHAUSTED", * remaining: 3, * responseHeaders: { * "retry-after": "1856", * "x-ratelimit-limit": "5, 5;w=60, 5;w=60", * }, * retryAfterSeconds: 1856, * }); * * // Inspect the available evidence without assuming it identifies the cause. * const evidence = { * code: error.code, * limit: error.responseHeaders?.["x-ratelimit-limit"], * retryAfter: error.responseHeaders?.["retry-after"], * }; * * expect(evidence).toEqual({ * code: "RESOURCE_EXHAUSTED", * limit: "5, 5;w=60, 5;w=60", * retryAfter: "1856", * }); * ``` */ declare class RateLimitError extends OpenCloudError { /** * Parsed 429 response body. A literal JSON `null` remains `null`; an absent * body is `undefined`. */ readonly details: JSONValue | undefined; override readonly name = "RateLimitError"; /** * Requests left in the reported window, or `undefined` if not reported. */ readonly remaining: number | undefined; /** Allowlisted raw response headers, or `undefined` if not set. */ readonly responseHeaders: Readonly> | undefined; readonly retryAfterSeconds: number; /** HTTP status code that produced the error, or `undefined` if not set. */ readonly statusCode: number | undefined; /** * Creates a new RateLimitError. * * @param message - Human-readable error description. * @param options - Error options including the retry delay. */ constructor(message: string, options: RateLimitErrorOptions); } //#endregion //#region src/internal/http/retry.d.ts /** * Fully-resolved retry config shape that {@link mergeConfig} and * {@link shouldRetry} operate on. Fields are required because this represents * the post-defaulting, internal view; callers should supply every field (or * resolve them via a test factory / client constructor). The partial, * user-facing type lives on client construction options; method defaults and * per-request overrides use `Partial`. */ interface RetryResolvable { /** Roblox Open Cloud API key. */ readonly apiKey: string; /** Base URL for the Open Cloud API. */ readonly baseUrl: string; /** Maximum retry attempts before giving up. */ readonly maxRetries: number; /** Status codes that are eligible for retry. */ readonly retryableStatuses: ReadonlyArray; /** * Codes for transport-level failures eligible for retry: node-style * transport codes ({@link findErrorCode}) surfaced as a * {@link NetworkError}, plus the synthetic {@link GATEWAY_REJECTED} for a * response served by an edge gateway. Not all of them prove the request * went unprocessed — see {@link TRANSIENT_TRANSPORT_CODES}. Empty for * create operations by default; consumers opt a create in via a per-request * override. */ readonly retryableTransportCodes: ReadonlyArray; /** Fallback delay function when no server hint is available. */ readonly retryDelay: (attempt: number) => number; /** Per-request timeout in milliseconds. */ readonly timeout: number; } /** * Transient transport error codes that are safe to retry for idempotent * operations. Connection resets, timeouts, and DNS hiccups are recoverable on * a retry. A self-aborted request timeout carries no OS-level `code`, so * {@link shouldRetry} folds it into this set as `ETIMEDOUT` (via * {@link isTimeoutAbort}) for idempotent methods; create methods retry no * transport codes and so still never re-issue a timed-out write. * * `ERR_HTTP2_STREAM_ERROR`, `ERR_HTTP2_SESSION_ERROR`, and `UND_ERR_INFO` are * the same deaths as spelled by a runtime whose `fetch` negotiates HTTP/2 * (Node 26 and later). They differ from the socket codes in one way that * matters: they do not prove the request went unprocessed. `UND_ERR_INFO` * covers both a `GOAWAY` declaring a stream was never started and a stream * that was fully sent, and `UND_ERR_SOCKET` can fire once a response is * already streaming. Retrying them is therefore justified by the operation * being safe to repeat, never by the request having gone unseen — which is * why {@link UPLOAD_METHOD_DEFAULTS}, the one write policy that includes * them, documents its own grounds. * * @since 0.1.0 */ declare const TRANSIENT_TRANSPORT_CODES: ReadonlyArray; /** * Synthetic transport code for a response that came from an edge gateway * rather than Open Cloud ({@link ApiError.gatewaySummary}). Such a response * proves the request was rejected before any Open Cloud handler saw it, so it * is classified alongside {@link TRANSIENT_TRANSPORT_CODES} rather than by its * HTTP status: the status belongs to the gateway, not to the API, and a * gateway `400` says nothing about the validity of the request. * * Name it in a per-request `retryableTransportCodes` override to opt an * operation into (or out of) gateway-rejection retry. * * @since 0.1.2 */ declare const GATEWAY_REJECTED = "GATEWAY_REJECTED"; /** * Synthetic transport code for a 2xx whose body could not be parsed as JSON * ({@link ApiError.unparsedBodyLength}). Open Cloud does not answer a success * status with a malformed document; what this failure describes in practice is * a body the edge delivered short, ending mid-token at exactly the length that * arrived. The next read is a fresh body and usually a whole one. * * Classifying it by transport code rather than by HTTP status is what makes it * recoverable at all: the status is a 200, which no `retryableStatuses` list * contains, so a status-keyed decision could only ever say "do not retry". * * Retrying is justified by the operation being safe to repeat, never by the * request having gone unprocessed — a 200 proves it was processed. So this code * is in {@link IDEMPOTENT_METHOD_DEFAULTS} only. Creates and uploads leave it * out: their write landed, and re-issuing it to re-read the answer would risk a * second resource for a response body, which is the wrong trade. A consumer who * can tolerate that duplicate names this code in a per-request * `retryableTransportCodes` override. * * @since 0.1.5 */ declare const RESPONSE_UNPARSEABLE = "RESPONSE_UNPARSEABLE"; /** Kind of HTTP method the merge is being performed for. */ type MethodKind = "create" | "idempotent"; //#endregion export { TRANSIENT_TRANSPORT_CODES as a, NetworkError as c, ApiErrorOptions as d, ApiRequestContext as f, RetryResolvable as i, NetworkErrorOptions as l, MethodKind as n, RateLimitError as o, requestContextOf as p, RESPONSE_UNPARSEABLE as r, RateLimitErrorOptions as s, GATEWAY_REJECTED as t, ApiError as u }; //# sourceMappingURL=retry-DXEklFrt.d.mts.map