@askrjs/fetch 0.2.1 → 0.4.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/README.md CHANGED
@@ -83,10 +83,11 @@ await client.createDocument({
83
83
  An omitted or unknown media type produces a `request` failure, so declaration
84
84
  order never selects a multi-variant request format.
85
85
 
86
- A validator only needs a `safeParse(value)` method, so schema libraries with that contract can be
87
- used without an adapter. Validators run for request bodies, path parameters, query parameters,
88
- headers, and decoded response bodies. Successful validator transformations are used for
89
- serialization and returned data.
86
+ A validator only needs a `safeParse(value)` method whose failure result exposes either `error` or
87
+ `issues`, so `@askrjs/schema` and other schema libraries with that contract can be used without an
88
+ adapter. Validators run for request bodies, path parameters, query parameters, headers, and decoded
89
+ response bodies. Successful validator transformations are used for serialization and returned data;
90
+ the validator's `error` or `issues` value is preserved on failure.
90
91
 
91
92
  ```ts
92
93
  const positiveInteger = {
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AnyEndpointDescriptor, ApiDefinition, ClientOptions, Codec, EndpointDescriptor, FailureResult, FetchResult, HttpResult, InferCodec, ParameterMap, SuccessResult } from "./types.js";
2
2
  //#region src/client.d.ts
3
3
  /** A one-off, untyped request description accepted by {@link createFetch}'s returned function. */
4
- interface AdHocCall {
4
+ export interface AdHocCall {
5
5
  url: string;
6
6
  method?: string;
7
7
  headers?: HeadersInit;
@@ -25,7 +25,7 @@ interface AdHocCall {
25
25
  * matching codec. Used internally by {@link createClient}, and usable directly for
26
26
  * requests without a full {@link EndpointDescriptor}.
27
27
  */
28
- declare function createFetch(options?: ClientOptions): (call: AdHocCall) => Promise<FetchResult>;
28
+ export declare function createFetch(options?: ClientOptions): (call: AdHocCall) => Promise<FetchResult>;
29
29
  type InputParts<D> = D extends EndpointDescriptor<infer P, infer Q, infer H, infer B, any, any> ? {
30
30
  params: P;
31
31
  query: Q;
@@ -42,18 +42,18 @@ type EndpointInput<D extends AnyEndpointDescriptor> = Container<"params", InputP
42
42
  type Successes<D> = D extends EndpointDescriptor<any, any, any, any, infer R, any> ? { [S in keyof R & number]: SuccessResult<InferCodec<R[S]>, S>; }[keyof R & number] : never;
43
43
  type Errors<D> = D extends EndpointDescriptor<any, any, any, any, any, infer E> ? { [S in Exclude<keyof E, "default"> & number]: HttpResult<InferCodec<NonNullable<E[S]>>, S>; }[Exclude<keyof E, "default"> & number] | ("default" extends keyof E ? HttpResult<InferCodec<NonNullable<E["default"]>>, number> : never) : never;
44
44
  /** The possible results of calling a typed client method for endpoint descriptor `D`. */
45
- type ClientResult<D extends AnyEndpointDescriptor> = Successes<D> | Errors<D> | FailureResult;
45
+ export type ClientResult<D extends AnyEndpointDescriptor> = Successes<D> | Errors<D> | FailureResult;
46
46
  type ClientMethod<D extends AnyEndpointDescriptor> = RequiredKeys<EndpointInput<D>> extends never ? (input?: EndpointInput<D>) => Promise<ClientResult<D>> : (input: EndpointInput<D>) => Promise<ClientResult<D>>;
47
47
  /** A fully-typed client for an {@link ApiDefinition}, with one method per endpoint. */
48
- type ApiClient<A extends ApiDefinition> = Readonly<{ [K in keyof A["endpoints"]]: ClientMethod<A["endpoints"][K]>; }>;
48
+ export type ApiClient<A extends ApiDefinition> = Readonly<{ [K in keyof A["endpoints"]]: ClientMethod<A["endpoints"][K]>; }>;
49
49
  /**
50
50
  * Builds a typed {@link ApiClient} from an {@link ApiDefinition}. Each endpoint becomes
51
51
  * a method that fills in the path, query, header, and body parameters, executes the
52
52
  * request via {@link createFetch}, and returns a {@link ClientResult}.
53
53
  */
54
- declare function createClient<A extends ApiDefinition>(api: A, options?: ClientOptions): ApiClient<A>;
54
+ export declare function createClient<A extends ApiDefinition>(api: A, options?: ClientOptions): ApiClient<A>;
55
55
  /** An `Error` thrown by {@link unwrap} that wraps a failed (non-`ok`) {@link FetchResult}. */
56
- declare class FetchError extends Error {
56
+ export declare class FetchError extends Error {
57
57
  readonly result: Exclude<FetchResult, {
58
58
  ok: true;
59
59
  }>;
@@ -65,6 +65,5 @@ declare class FetchError extends Error {
65
65
  * Returns the data of a successful {@link FetchResult}, or throws a {@link FetchError}
66
66
  * wrapping the result if it was not `ok`.
67
67
  */
68
- declare function unwrap<T>(result: FetchResult<T>): T;
69
- //#endregion
70
- export { AdHocCall, ApiClient, ClientResult, FetchError, createClient, createFetch, unwrap };
68
+ export declare function unwrap<T>(result: FetchResult<T>): T;
69
+ //#endregion
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  //#region src/client.ts
2
2
  const media = (response) => response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() ?? null;
3
3
  const compatible = (codec, type) => codec.kind === "empty" || codec.kind === "blob" || codec.kind === "arrayBuffer" || codec.kind === "stream" || !!type && codec.mediaTypes.some((expected) => expected === "*/*" || expected === type || expected === "text/*" && type.startsWith("text/") || expected === "+json" && type.endsWith("+json"));
4
+ const validationFailure = (result) => "error" in result ? result.error : result.issues;
4
5
  async function decode(response, codec) {
5
6
  const type = media(response);
6
7
  const selected = codec.kind === "content" ? codec.variants?.[type ?? ""] : codec;
@@ -33,7 +34,7 @@ async function decode(response, codec) {
33
34
  }
34
35
  if (selected.validator) {
35
36
  const parsed = selected.validator.safeParse(value);
36
- if (!parsed.success) throw parsed.error;
37
+ if (!parsed.success) throw validationFailure(parsed);
37
38
  return parsed.data;
38
39
  }
39
40
  return value;
@@ -41,7 +42,7 @@ async function decode(response, codec) {
41
42
  function encode(value, codec, headers, requestedMediaType) {
42
43
  if (codec.validator) {
43
44
  const parsed = codec.validator.safeParse(value);
44
- if (!parsed.success) throw parsed.error;
45
+ if (!parsed.success) throw validationFailure(parsed);
45
46
  value = parsed.data;
46
47
  }
47
48
  switch (codec.kind) {
@@ -141,7 +142,7 @@ const parameterValue = (map, name, value) => {
141
142
  const validator = definition && typeof definition === "object" && "safeParse" in definition ? definition : definition?.validator;
142
143
  if (!validator) return value;
143
144
  const parsed = validator.safeParse(value);
144
- if (!parsed.success) throw parsed.error;
145
+ if (!parsed.success) throw validationFailure(parsed);
145
146
  return parsed.data;
146
147
  };
147
148
  /**
package/dist/codecs.d.ts CHANGED
@@ -1,27 +1,26 @@
1
- import { Codec, Validator } from "./types.js";
1
+ import { Codec, InferValidator, Validator } from "./types.js";
2
2
  //#region src/codecs.d.ts
3
3
  /** Creates a JSON codec, matching `application/json` and `+json` suffixed media types. */
4
- declare function json<T = unknown>(): Codec<T>;
4
+ export declare function json<T = unknown>(): Codec<T>;
5
5
  /** Creates a JSON codec that validates/parses the decoded value with the given schema. */
6
- declare function json<V extends Validator>(schema: V): Codec<V extends Validator<infer T> ? T : never>;
6
+ export declare function json<V extends Validator>(schema: V): Codec<InferValidator<V>>;
7
7
  /** Creates a codec for plain text bodies (`text/*`), decoded as a string. */
8
- declare const text: () => Codec<string>;
8
+ export declare const text: () => Codec<string>;
9
9
  /** Creates a codec for `application/x-www-form-urlencoded` bodies, decoded as `URLSearchParams`. */
10
- declare const urlEncoded: () => Codec<URLSearchParams>;
10
+ export declare const urlEncoded: () => Codec<URLSearchParams>;
11
11
  /** Creates a codec for `multipart/form-data` bodies, decoded as `FormData`. */
12
- declare const multipart: () => Codec<FormData>;
12
+ export declare const multipart: () => Codec<FormData>;
13
13
  /** Creates a codec that decodes any response body as a `Blob`. */
14
- declare const blob: () => Codec<Blob>;
14
+ export declare const blob: () => Codec<Blob>;
15
15
  /** Creates a codec that decodes any response body as an `ArrayBuffer`. */
16
- declare const arrayBuffer: () => Codec<ArrayBuffer>;
16
+ export declare const arrayBuffer: () => Codec<ArrayBuffer>;
17
17
  /** Creates a codec that exposes the raw response body as a `ReadableStream`, without buffering it. */
18
- declare const stream: () => Codec<ReadableStream<Uint8Array>>;
18
+ export declare const stream: () => Codec<ReadableStream<Uint8Array>>;
19
19
  /** Creates a codec for bodies expected to be empty (e.g. 204 No Content), decoded as `undefined`. */
20
- declare const empty: () => Codec<undefined>;
20
+ export declare const empty: () => Codec<undefined>;
21
21
  /**
22
22
  * Creates a content-negotiated codec that selects among `variants` by the response's
23
23
  * media type, decoding with whichever variant matches.
24
24
  */
25
- declare const content: <T extends Record<string, Codec>>(variants: T) => Codec<{ [K in keyof T]: T[K] extends Codec<infer V> ? V : never; }[keyof T]>;
26
- //#endregion
27
- export { arrayBuffer, blob, content, empty, json, multipart, stream, text, urlEncoded };
25
+ export declare const content: <T extends Record<string, Codec>>(variants: T) => Codec<{ [K in keyof T]: T[K] extends Codec<infer V> ? V : never; }[keyof T]>;
26
+ //#endregion
package/dist/dsl.d.ts CHANGED
@@ -6,7 +6,7 @@ import { AnyEndpointDescriptor, ApiDefinition, ApiMetadata, Codec, EndpointDescr
6
6
  * @throws {TypeError} If the path uses colon/wildcard syntax, has malformed
7
7
  * `{...}` placeholders, or contains a duplicate parameter name.
8
8
  */
9
- declare function pathNames(path: string): string[];
9
+ export declare function pathNames(path: string): string[];
10
10
  type Responses = Record<number, Codec>;
11
11
  type Errors = Partial<Record<number | "default", Codec>>;
12
12
  type State = Omit<EndpointDescriptor, "responses" | "errors"> & {
@@ -20,7 +20,7 @@ declare const STATE: unique symbol;
20
20
  * returns a new builder reflecting the added configuration; the accumulated
21
21
  * state is finalized into an {@link EndpointDescriptor} by {@link defineApi}.
22
22
  */
23
- interface EndpointBuilder<P = undefined, Q = undefined, H = undefined, B = undefined, R extends Responses = {}, E extends Errors = {}> {
23
+ export interface EndpointBuilder<P = undefined, Q = undefined, H = undefined, B = undefined, R extends Responses = {}, E extends Errors = {}> {
24
24
  readonly [STATE]: State;
25
25
  /**
26
26
  * Declares path parameter types and optional runtime validators. Generic types are erased;
@@ -36,19 +36,19 @@ interface EndpointBuilder<P = undefined, Q = undefined, H = undefined, B = undef
36
36
  security(requirements: readonly Readonly<Record<string, readonly string[]>>[]): EndpointBuilder<P, Q, H, B, R, E>;
37
37
  }
38
38
  /** Starts building a GET endpoint at the given path. */
39
- declare const get: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
39
+ export declare const get: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
40
40
  /** Starts building a POST endpoint at the given path. */
41
- declare const post: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
41
+ export declare const post: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
42
42
  /** Starts building a PUT endpoint at the given path. */
43
- declare const put: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
43
+ export declare const put: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
44
44
  /** Starts building a PATCH endpoint at the given path. */
45
- declare const patch: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
45
+ export declare const patch: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
46
46
  /** Starts building a DELETE endpoint at the given path. */
47
- declare const del: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
47
+ export declare const del: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
48
48
  /** Starts building a HEAD endpoint at the given path. */
49
- declare const head: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
49
+ export declare const head: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
50
50
  /** Starts building an OPTIONS endpoint at the given path. */
51
- declare const options: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
51
+ export declare const options: (path: string) => EndpointBuilder<undefined, undefined, undefined, undefined, {}, {}>;
52
52
  type DescriptorOf<T> = T extends EndpointBuilder<infer P, infer Q, infer H, infer B, infer R, infer E> ? EndpointDescriptor<P, Q, H, B, R, E> : T extends AnyEndpointDescriptor ? T : never;
53
53
  type Defined<E extends Record<string, AnyEndpointDescriptor | EndpointBuilder>> = { [K in keyof E]: DescriptorOf<E[K]>; };
54
54
  /**
@@ -61,6 +61,5 @@ type Defined<E extends Record<string, AnyEndpointDescriptor | EndpointBuilder>>
61
61
  * getUser: get("/users/{id}").returns(json()),
62
62
  * });
63
63
  */
64
- declare function defineApi<E extends Record<string, AnyEndpointDescriptor | EndpointBuilder>>(endpoints: E, metadata?: ApiMetadata): ApiDefinition<Defined<E>>;
65
- //#endregion
66
- export { EndpointBuilder, defineApi, del, get, head, options, patch, pathNames, post, put };
64
+ export declare function defineApi<E extends Record<string, AnyEndpointDescriptor | EndpointBuilder>>(endpoints: E, metadata?: ApiMetadata): ApiDefinition<Defined<E>>;
65
+ //#endregion
@@ -4,7 +4,7 @@ import { FetchResult, Middleware, RequestContext } from "./types.js";
4
4
  * Middleware that attaches an `Authorization: Bearer <token>` header to every request.
5
5
  * `token` may be a static string or a (possibly async) function resolved on each request.
6
6
  */
7
- declare const bearerAuth: ({ token }: {
7
+ export declare const bearerAuth: ({ token }: {
8
8
  token: string | (() => string | Promise<string>);
9
9
  }) => Middleware;
10
10
  /**
@@ -13,13 +13,13 @@ declare const bearerAuth: ({ token }: {
13
13
  * async) function resolved on each request. The key is marked sensitive so the
14
14
  * {@link logging} middleware redacts it.
15
15
  */
16
- declare function apiKeyAuth({ key, value, in: location }: {
16
+ export declare function apiKeyAuth({ key, value, in: location }: {
17
17
  key: string;
18
18
  value: string | (() => string | Promise<string>);
19
19
  in?: "header" | "query";
20
20
  }): Middleware;
21
21
  /** Options controlling the {@link retry} middleware's behavior. */
22
- interface RetryOptions {
22
+ export interface RetryOptions {
23
23
  /** Maximum number of attempts, including the first. Defaults to 3. */
24
24
  attempts?: number;
25
25
  /** HTTP methods eligible for retry. Defaults to idempotent-by-convention methods. */
@@ -38,18 +38,18 @@ interface RetryOptions {
38
38
  * is replayed only when it can be cloned before the first attempt; streaming, already-consumed,
39
39
  * or otherwise non-cloneable bodies are sent once without retrying.
40
40
  */
41
- declare function retry(options?: RetryOptions): Middleware;
41
+ export declare function retry(options?: RetryOptions): Middleware;
42
42
  /**
43
43
  * Middleware that logs a `"request"` event before and a `"response"` event after each
44
44
  * request, via the given logger (defaults to `console`). Headers and query parameters
45
45
  * marked sensitive (e.g. by {@link apiKeyAuth}) or matching common sensitive-name
46
46
  * patterns (authorization, cookie, token, secret, password, api key) are redacted.
47
47
  */
48
- declare function logging(logger?: {
48
+ export declare function logging(logger?: {
49
49
  log(event: Record<string, unknown>): void;
50
50
  }): Middleware;
51
51
  /** Hooks invoked by the {@link telemetry} middleware around each request. */
52
- interface TelemetryHooks {
52
+ export interface TelemetryHooks {
53
53
  /** Called before the request proceeds; its return value (a "span") is passed to `end`/`error`. */
54
54
  start?(context: RequestContext): unknown;
55
55
  /** Called after the request completes successfully (from this middleware's perspective). */
@@ -61,6 +61,5 @@ interface TelemetryHooks {
61
61
  * Middleware that wraps each request with {@link TelemetryHooks}, calling `start` before
62
62
  * the request, `end` after it completes, and `error` (then rethrowing) if it throws.
63
63
  */
64
- declare function telemetry(hooks: TelemetryHooks): Middleware;
65
- //#endregion
66
- export { RetryOptions, TelemetryHooks, apiKeyAuth, bearerAuth, logging, retry, telemetry };
64
+ export declare function telemetry(hooks: TelemetryHooks): Middleware;
65
+ //#endregion
package/dist/types.d.ts CHANGED
@@ -1,25 +1,36 @@
1
1
  //#region src/types.d.ts
2
2
  /**
3
- * A minimal schema-validation contract, compatible with libraries such as Zod
4
- * that expose a `safeParse` method (e.g. via a thin adapter).
3
+ * A minimal schema-validation contract, compatible with `safeParse` methods
4
+ * that report failures through either `error` or `issues`.
5
5
  */
6
- interface Validator<T = unknown> {
6
+ export interface Validator<T = unknown> {
7
7
  safeParse(value: unknown): {
8
- success: true;
9
- data: T;
8
+ readonly success: true;
9
+ readonly data: T;
10
10
  } | {
11
- success: false;
12
- error: unknown;
11
+ readonly success: false;
12
+ readonly error: unknown;
13
+ readonly issues?: unknown;
14
+ } | {
15
+ readonly success: false;
16
+ readonly error?: unknown;
17
+ readonly issues: unknown;
13
18
  };
14
19
  }
15
20
  /** Infers the parsed output type `T` from a {@link Validator}. */
16
- type InferValidator<V> = V extends Validator<infer T> ? T : never;
21
+ export type InferValidator<V> = V extends {
22
+ safeParse(value: unknown): infer Result;
23
+ } ? Extract<Result, {
24
+ readonly success: true;
25
+ }> extends {
26
+ readonly data: infer T;
27
+ } ? T : never : never;
17
28
  /**
18
29
  * Describes how a request or response body is serialized/deserialized:
19
30
  * which wire format (`kind`), which media types it matches, and optionally
20
31
  * a {@link Validator} to parse/validate the decoded value.
21
32
  */
22
- interface Codec<T = unknown> {
33
+ export interface Codec<T = unknown> {
23
34
  readonly kind: "json" | "text" | "urlEncoded" | "multipart" | "blob" | "arrayBuffer" | "stream" | "empty" | "content";
24
35
  readonly mediaTypes: readonly string[];
25
36
  readonly validator?: Validator<T>;
@@ -27,13 +38,13 @@ interface Codec<T = unknown> {
27
38
  readonly variants?: Readonly<Record<string, Codec<unknown>>>;
28
39
  }
29
40
  /** Infers the decoded value type `T` from a {@link Codec}. */
30
- type InferCodec<C> = C extends Codec<infer T> ? T : never;
41
+ export type InferCodec<C> = C extends Codec<infer T> ? T : never;
31
42
  /** The set of HTTP methods supported by endpoint descriptors. */
32
- type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
43
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
33
44
  /** OpenAPI-style serialization styles for path, query, and header parameters. */
34
- type ParameterStyle = "simple" | "label" | "matrix" | "form" | "spaceDelimited" | "pipeDelimited" | "deepObject";
45
+ export type ParameterStyle = "simple" | "label" | "matrix" | "form" | "spaceDelimited" | "pipeDelimited" | "deepObject";
35
46
  /** Serialization and validation settings for a single path, query, or header parameter. */
36
- interface ParameterSpec<T = unknown> {
47
+ export interface ParameterSpec<T = unknown> {
37
48
  readonly validator?: Validator<T>;
38
49
  readonly style?: ParameterStyle;
39
50
  readonly explode?: boolean;
@@ -42,9 +53,9 @@ interface ParameterSpec<T = unknown> {
42
53
  * Maps parameter names to either a bare {@link Validator} or a full
43
54
  * {@link ParameterSpec} describing serialization and validation.
44
55
  */
45
- type ParameterMap = Readonly<Record<string, Validator<unknown> | ParameterSpec | undefined>>;
56
+ export type ParameterMap = Readonly<Record<string, Validator<unknown> | ParameterSpec | undefined>>;
46
57
  /** Describes a single API endpoint: its method, path, parameters, body, and possible responses. */
47
- interface EndpointDescriptor<P = undefined, Q = undefined, H = undefined, B = undefined, R extends Record<number, Codec> = Record<number, Codec>, E extends Partial<Record<number | "default", Codec>> = Partial<Record<number | "default", Codec>>> {
58
+ export interface EndpointDescriptor<P = undefined, Q = undefined, H = undefined, B = undefined, R extends Record<number, Codec> = Record<number, Codec>, E extends Partial<Record<number | "default", Codec>> = Partial<Record<number | "default", Codec>>> {
48
59
  readonly method: HttpMethod;
49
60
  readonly path: string;
50
61
  readonly params?: ParameterMap;
@@ -63,9 +74,9 @@ interface EndpointDescriptor<P = undefined, Q = undefined, H = undefined, B = un
63
74
  };
64
75
  }
65
76
  /** An {@link EndpointDescriptor} with its type parameters erased, for use in generic contexts. */
66
- type AnyEndpointDescriptor = EndpointDescriptor<any, any, any, any, any, any>;
77
+ export type AnyEndpointDescriptor = EndpointDescriptor<any, any, any, any, any, any>;
67
78
  /** API-level (rather than per-endpoint) metadata, such as servers and security schemes. */
68
- interface ApiMetadata {
79
+ export interface ApiMetadata {
69
80
  readonly servers?: readonly (string | {
70
81
  readonly url: string;
71
82
  })[];
@@ -73,12 +84,12 @@ interface ApiMetadata {
73
84
  readonly securitySchemes?: Readonly<Record<string, unknown>>;
74
85
  }
75
86
  /** A named collection of endpoints plus optional API metadata, as produced by {@link defineApi}. */
76
- interface ApiDefinition<E extends Record<string, AnyEndpointDescriptor> = Record<string, AnyEndpointDescriptor>> {
87
+ export interface ApiDefinition<E extends Record<string, AnyEndpointDescriptor> = Record<string, AnyEndpointDescriptor>> {
77
88
  readonly endpoints: E;
78
89
  readonly metadata?: ApiMetadata;
79
90
  }
80
91
  /** A successful (2xx) fetch outcome, carrying the decoded response data. */
81
- type SuccessResult<T = unknown, S extends number = number> = {
92
+ export type SuccessResult<T = unknown, S extends number = number> = {
82
93
  ok: true;
83
94
  kind: "success";
84
95
  status: S;
@@ -89,7 +100,7 @@ type SuccessResult<T = unknown, S extends number = number> = {
89
100
  response: Response;
90
101
  };
91
102
  /** A non-2xx fetch outcome where the server responded with a decodable error body. */
92
- type HttpResult<T = unknown, S extends number = number> = {
103
+ export type HttpResult<T = unknown, S extends number = number> = {
93
104
  ok: false;
94
105
  kind: "http";
95
106
  status: S;
@@ -100,9 +111,9 @@ type HttpResult<T = unknown, S extends number = number> = {
100
111
  response: Response;
101
112
  };
102
113
  /** Categorizes why a fetch could not produce an {@link HttpResult} or {@link SuccessResult}. */
103
- type FailureKind = "request" | "network" | "timeout" | "abort" | "decode" | "middleware";
114
+ export type FailureKind = "request" | "network" | "timeout" | "abort" | "decode" | "middleware";
104
115
  /** A fetch outcome that failed before or independently of receiving a decodable HTTP response. */
105
- type FailureResult = {
116
+ export type FailureResult = {
106
117
  ok: false;
107
118
  kind: FailureKind;
108
119
  status: number;
@@ -112,9 +123,9 @@ type FailureResult = {
112
123
  response?: Response;
113
124
  };
114
125
  /** The outcome of a fetch call: success, an HTTP-level error, or another kind of failure. */
115
- type FetchResult<T = unknown> = SuccessResult<T> | HttpResult<T> | FailureResult;
126
+ export type FetchResult<T = unknown> = SuccessResult<T> | HttpResult<T> | FailureResult;
116
127
  /** The mutable-by-replacement state threaded through the middleware chain for a single request. */
117
- interface RequestContext {
128
+ export interface RequestContext {
118
129
  readonly request: Request;
119
130
  readonly endpoint?: AnyEndpointDescriptor;
120
131
  readonly operationId?: string;
@@ -127,11 +138,11 @@ interface RequestContext {
127
138
  readonly deadline?: number;
128
139
  }
129
140
  /** Invokes the next middleware in the chain, optionally passing a replacement context. */
130
- type Next = (context?: RequestContext) => Promise<FetchResult>;
141
+ export type Next = (context?: RequestContext) => Promise<FetchResult>;
131
142
  /** A function that can inspect/replace the request context and/or the result of calling `next`. */
132
- type Middleware = (context: RequestContext, next: Next) => Promise<FetchResult>;
143
+ export type Middleware = (context: RequestContext, next: Next) => Promise<FetchResult>;
133
144
  /** Options controlling how a {@link createClient} or {@link createFetch} client makes requests. */
134
- interface ClientOptions {
145
+ export interface ClientOptions {
135
146
  baseUrl?: string;
136
147
  /** Custom transport, defaulting to the global `fetch`. */
137
148
  fetch?: (request: Request) => Promise<Response>;
@@ -142,5 +153,4 @@ interface ClientOptions {
142
153
  /** Middleware chain applied to every request, in order. */
143
154
  middleware?: readonly Middleware[];
144
155
  }
145
- //#endregion
146
- export { AnyEndpointDescriptor, ApiDefinition, ApiMetadata, ClientOptions, Codec, EndpointDescriptor, FailureKind, FailureResult, FetchResult, HttpMethod, HttpResult, InferCodec, InferValidator, Middleware, Next, ParameterMap, ParameterSpec, ParameterStyle, RequestContext, SuccessResult, Validator };
156
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askrjs/fetch",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Function-first typed HTTP contracts and clients for Askr.",
5
5
  "keywords": [
6
6
  "askr",
@@ -61,11 +61,12 @@
61
61
  "prepublishOnly": "npm run check"
62
62
  },
63
63
  "devDependencies": {
64
- "@types/node": "^26.2.0",
65
- "publint": "^0.3.23",
64
+ "@askrjs/schema": "^0.3.0",
65
+ "@types/node": "^26.3.0",
66
+ "publint": "^0.3.24",
66
67
  "typescript": "^7.0.2",
67
- "vite-plus": "^0.2.8",
68
- "vitest": "^4.1.10"
68
+ "vite-plus": "^0.3.1",
69
+ "vitest": "^4.1.11"
69
70
  },
70
71
  "engines": {
71
72
  "node": ">=24.0.0"