/** * Serializable GraphQL client errors with an operation name and a * `Schema.TaggedError` reason. * * @stability experimental * @since 4.0.0 */ import type * as Duration from "../Duration.ts"; import * as Headers from "../http/Headers.ts"; import type * as HttpClientError from "../http/HttpClientError.ts"; import * as Schema from "../Schema.ts"; declare const TypeId = "~effect/graphql/GraphQLClientError"; /** * One entry of a GraphQL response's `errors` array, as described by the * GraphQL specification. * * **Details** * * `path[0]` is the response key the error belongs to, which is the alias when * the document used one, so callers can match an error to the field it hit. * Unknown top-level fields are preserved as JSON values, including GitHub's * `type` field. * * **Example** (Finding which alias an error belongs to) * * ```ts import.meta.vitest * import { Schema } from "effect" * import { GraphQLClientError } from "effect/graphql" * * const error = Schema.decodeUnknownSync(GraphQLClientError.GraphQLError)({ * message: "Could not resolve to a Repository", * path: ["missing", "issues"], * type: "NOT_FOUND" * }) * * error.path?.[0] // => "missing" * ``` * * @stability experimental * @category schemas * @since 4.0.0 */ export declare const GraphQLError: Schema.StructWithRest>>; readonly path: Schema.optional>>; readonly extensions: Schema.optional>>; }>, readonly [Schema.$Record>]>; /** * The decoded type of {@link GraphQLError}. * * @stability experimental * @category models * @since 4.0.0 */ export type GraphQLError = typeof GraphQLError.Type; declare const ResponseError_base: Schema.Class>>; readonly path: Schema.optional>>; readonly extensions: Schema.optional>>; }>, readonly [Schema.$Record>]>>; readonly data: Schema.optional>; readonly extensions: Schema.optional>>; readonly status: Schema.optional; readonly headers: Schema.optional, Schema.$Record, never, never>>; }>, import("../Cause.ts").YieldableError>; /** * The server answered with a non-empty `errors` array. * * **Details** * * Queries and mutations fail even when partial `data` is present, since a * field error's `null` cannot be distinguished from a genuine `null`. Use * `{ partial: true }` to receive decoded `{ data, errors }` instead. * * On this error, `data` is raw. `GraphQLError.path[0]` identifies the response * key (or alias). Non-2xx HTTP responses with a GraphQL body also use this * reason rather than `TransportError`. * * `status` and `headers` are the HTTP status and response headers, as the * transport reported them and middleware left them. Headers use * `Headers.Headers` at runtime and encode as a plain string record. * Decoding restores the normalized header collection. Both are unset over * graphql-ws, and `status` is unset for graphql-sse events. `retryAfter` * parses the `Retry-After` response header as whole seconds or an HTTP date, * measured from `Date.now()` when the error is constructed. `isRetryable` is * true for HTTP status `429` or `500`–`599`, and for any response with a * valid `Retry-After`, whatever its status. It is false otherwise, including * when there is no status. * * For queries and mutations, callers opt into retries with `Effect.retry`, * and should wait for `retryAfter` when it is set. With * `GraphQLClient.pages`, retry each page's call, not the stream: * `Stream.retry` starts again from the first page. Subscriptions retry a * retryable `ResponseError` on their own, by resubscribing. graphql-sse events * carry the stream response's headers, so an event with `errors` is retried * when that response had a valid `Retry-After`, and ends the stream * otherwise. * * **Example** (Inspecting the errors of a failed call) * * ```ts import.meta.vitest * import { GraphQLClientError } from "effect/graphql" * * const reason = new GraphQLClientError.ResponseError({ * errors: [{ message: "Not found", path: ["repository"] }], * data: { repository: null }, * status: 200 * }) * * reason.isRetryable // => false * reason.status // => 200 * reason.errors.map((error) => error.path?.[0]) // => ["repository"] * ``` * * **Example** (Retrying a call on retryable errors) * * ```ts import.meta.vitest * import { Effect } from "effect" * import { GraphQLClientError } from "effect/graphql" * * const call = Effect.fail(new GraphQLClientError.GraphQLClientError({ * operation: "Viewer", * reason: new GraphQLClientError.ResponseError({ * errors: [{ message: "Query timed out" }], * status: 502 * }) * })) * * const retried = Effect.retry(call, { * while: (error) => error.reason.isRetryable, * times: 3 * }) * ``` * * @stability experimental * @category errors * @since 4.0.0 */ export declare class ResponseError extends ResponseError_base { #private; /** * `true` for HTTP status `429` or `500`–`599`, or when the response has a * `Retry-After` header that parses, at any status, including a graphql-sse * event whose stream response had one; `false` otherwise, including when * there is no status and no `Retry-After`. Use this as the `while` predicate * of `Effect.retry` on the call, and {@link retryAfter} as the delay when it * is set. Subscriptions resubscribe on it. * * @since 4.0.0 */ get isRetryable(): boolean; /** * The `Retry-After` response header parsed as whole seconds or an HTTP * date. An HTTP date is measured from `Date.now()` when the error is * constructed, so every read returns the same delay. Missing or invalid * values yield `undefined`. `Effect.retry` does not apply this delay * automatically. * * @since 4.0.0 */ get retryAfter(): Duration.Duration | undefined; get message(): string; } /** * Tests whether a WebSocket close code is fatal, meaning a `TransportError` * carrying it is not retryable. * * **Details** * * Fatal codes are every standard `1xxx` code except `1000`, `1001`, `1005`, * `1006`, `1012`, `1013` and `1014`, plus the graphql-ws codes `4400` (bad * request), `4401` (unauthorized), `4403` (forbidden), `4406` (subprotocol * not acceptable), `4409` (subscriber already exists), `4429` (too many * initialisation requests) and `4500` (internal server error). Any other code * is retryable. * * `4403` is fatal because reconnecting with the same credentials is refused * again. * * **Example** (Checking close codes) * * ```ts import.meta.vitest * import { GraphQLClientError } from "effect/graphql" * * GraphQLClientError.isFatalCloseCode(1006) // => false * GraphQLClientError.isFatalCloseCode(1011) // => true * GraphQLClientError.isFatalCloseCode(4401) // => true * ``` * * @stability experimental * @category predicates * @since 4.0.0 */ export declare const isFatalCloseCode: (code: number) => boolean; declare const TransportError_base: Schema.Class; readonly headers: Schema.optional, Schema.$Record, never, never>>; readonly retryAfter: Schema.optional; readonly closeCode: Schema.optional; readonly fatal: Schema.optional; readonly cause: Schema.optional; }>, import("../Cause.ts").YieldableError>; /** * The client could not get a GraphQL response from the server: a network * failure, an HTTP response that is not a GraphQL response, or a closed * WebSocket connection. * * **Details** * * - `status` and `headers` are only set for an HTTP response that is not a * GraphQL response. Headers use `Headers.Headers` at runtime and encode * as a plain string record; decoding restores the header collection. * - `retryAfter` comes from the `Retry-After` header (whole seconds or an * HTTP date) and is encoded as milliseconds. When it is set, the error is * retryable at any status, unless the close code is fatal. * - `closeCode` is set when a WebSocket connection closed. See * {@link isFatalCloseCode} for the codes that are not retryable. * - `fatal: true` marks a failure that retrying cannot fix, such as an * operation on a graphql-ws transport whose scope has closed. * - `cause` holds the original error, such as the `HttpClientError`. Once * encoded it shrinks to `{ name, message }`, the standard `Schema.Defect` * behaviour. * * **Example** (Retryability of transport failures) * * ```ts import.meta.vitest * import { GraphQLClientError } from "effect/graphql" * * new GraphQLClientError.TransportError({ description: "ECONNREFUSED" }).isRetryable // => true * new GraphQLClientError.TransportError({ description: "503", status: 503 }).isRetryable // => true * new GraphQLClientError.TransportError({ description: "404", status: 404 }).isRetryable // => false * new GraphQLClientError.TransportError({ description: "closed", closeCode: 4401 }).isRetryable // => false * new GraphQLClientError.TransportError({ description: "shut down", fatal: true }).isRetryable // => false * ``` * * @stability experimental * @category errors * @since 4.0.0 */ export declare class TransportError extends TransportError_base { /** * `true` for a network failure (no `status`), a `429` or `500`–`599` * status, a `retryAfter` at any status, and any close code that is not * fatal (see {@link isFatalCloseCode}). An error marked `fatal` or with a * fatal close code is never retryable. When retrying, wait for * `retryAfter` when it is set. * * @since 4.0.0 */ get isRetryable(): boolean; get message(): string; /** * Converts an `HttpClientError` into a `TransportError`, reading `status`, * `headers` (as `Headers.Headers`) and `Retry-After` from the response * when there is one. * * **Details** * * `Retry-After` is read as whole seconds or an HTTP date; any other value * is ignored. An HTTP date is measured from `options.now` (epoch * milliseconds), which defaults to `Date.now()`. Transports pass the Effect * clock's time when the response arrives. * * @since 4.0.0 */ static fromHttpClientError(error: HttpClientError.HttpClientError, options?: { readonly now?: number | undefined; }): TransportError; } declare const EncodeError_base: Schema.Class, import("../Cause.ts").YieldableError>; /** * The variables Schema rejected the input, so nothing was sent. * * @stability experimental * @category errors * @since 4.0.0 */ export declare class EncodeError extends EncodeError_base { /** * An `EncodeError` is never retryable. * * @since 4.0.0 */ get isRetryable(): boolean; get message(): string; } declare const DecodeError_base: Schema.Class, import("../Cause.ts").YieldableError>; /** * The response was not a GraphQL response, or `data` did not match the result * Schema. * * **Details** * * `data: null` without `errors` also produces a `DecodeError`, since * `ResponseError` requires a non-empty errors array. * * @stability experimental * @category errors * @since 4.0.0 */ export declare class DecodeError extends DecodeError_base { /** * A `DecodeError` is never retryable. * * @since 4.0.0 */ get isRetryable(): boolean; get message(): string; } declare const PaginationError_base: Schema.Class; }>, import("../Cause.ts").YieldableError>; /** * Cursor paging could not continue. Only `GraphQLClient.pages` and * `GraphQLClient.items` raise it, when a connection becomes `null` on a page * after the first, or when `hasNextPage` is `true` but `endCursor` is `null` * or did not advance. * * **Details** * * `cursor` is the last cursor that was sent, `null` on the first page. * * @stability experimental * @category errors * @since 4.0.0 */ export declare class PaginationError extends PaginationError_base { /** * A `PaginationError` is never retryable. * * @since 4.0.0 */ get isRetryable(): boolean; get message(): string; } /** * The union of every reason a {@link GraphQLClientError} can carry. * * @stability experimental * @category models * @since 4.0.0 */ export type Reason = ResponseError | TransportError | EncodeError | DecodeError | PaginationError; declare const GraphQLClientError_base: Schema.Class; }>, import("../Cause.ts").YieldableError>; /** * The error every `GraphQLClient` method fails with. It names the operation * and carries one of the five reasons; `cause` is the reason itself. * * **Example** (Retrying only what is retryable) * * ```ts import.meta.vitest * import { Duration, Schema } from "effect" * import { GraphQLClientError } from "effect/graphql" * * const error = new GraphQLClientError.GraphQLClientError({ * operation: "Viewer", * reason: new GraphQLClientError.TransportError({ * description: "503 Service Unavailable", * status: 503, * retryAfter: Duration.seconds(30) * }) * }) * * error.isRetryable // => true * error.retryAfter // => Duration.seconds(30) * * // Errors round-trip through their Schema * const encoded = Schema.encodeSync(GraphQLClientError.GraphQLClientError)(error) * Schema.decodeUnknownSync(GraphQLClientError.GraphQLClientError)(encoded).reason._tag // => "TransportError" * ``` * * @stability experimental * @category errors * @since 4.0.0 */ export declare class GraphQLClientError extends GraphQLClientError_base { /** * Marks this value as a GraphQL client error for runtime guards. * * @stability experimental * @since 4.0.0 */ readonly [TypeId] = "~effect/graphql/GraphQLClientError"; /** * The reason, exposed as the error's cause. * * @since 4.0.0 */ readonly cause: DecodeError | EncodeError | PaginationError | ResponseError | TransportError; /** * Delegates to the reason's `isRetryable`. * * @since 4.0.0 */ get isRetryable(): boolean; /** * The `Retry-After` hint of a `ResponseError` or `TransportError` reason, * if any. Use it as the delay when retrying on {@link isRetryable}. * * @since 4.0.0 */ get retryAfter(): Duration.Duration | undefined; /** * The HTTP status of a `ResponseError` or `TransportError` reason, if any. * * @since 4.0.0 */ get status(): number | undefined; /** * The response headers of a `ResponseError` or `TransportError` reason, if * any. * * @since 4.0.0 */ get headers(): Headers.Headers | undefined; get message(): string; } /** * Tests whether a value is a {@link GraphQLClientError}. * * @stability experimental * @category guards * @since 4.0.0 */ export declare const isGraphQLClientError: (u: unknown) => u is GraphQLClientError; export {}; //# sourceMappingURL=GraphQLClientError.d.ts.map