arthron 1.2.0 → 2.1.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/dist/index.d.cts CHANGED
@@ -1,13 +1,14 @@
1
- import { _ as ArthronMeta, a as logging$1, b as RequestContext, d as Middleware, f as Plugin, g as isArthronError, h as ArthronError, i as retry$1, m as ResponseInterceptor, n as timeout$1, o as LogEvent$1, p as RequestInterceptor, r as TimeoutOptions$1, s as LoggingOptions$1, u as ErrorInterceptor, v as HeadersOption, x as RetryOptions, y as QueryParams } from "./index-C3lylSNd.cjs";
1
+ import { a as ResponseInterceptor, c as isArthronError, d as RequestContext, i as RequestInterceptor, l as ArthronMeta, n as Middleware, o as ArthronError, r as Plugin, s as ArthronErrorOptions, t as ErrorInterceptor, u as QueryParams } from "./shared.cjs";
2
2
  //#region src/types/response.types.d.ts
3
- interface ArthronPromise extends Promise<Response> {
3
+ interface ResponsePromise extends Promise<Response> {
4
4
  /** The body as JSON. `T` is a cast — nothing checks it. */
5
5
  json<T = unknown>(): Promise<T>;
6
6
  /**
7
- * The body as JSON, validated by a Standard Schema (Zod, Valibot, ArkType…)
8
- * and typed by it. A mismatch rejects with a `SchemaError`.
7
+ * The body as JSON, checked by a schema from Zod, Valibot, ArkType or any
8
+ * other Standard Schema library — passed as is — and typed by it. A body
9
+ * that does not match rejects with a `SchemaMismatchError`.
9
10
  */
10
- json<S extends StandardSchemaV1>(schema: S): Promise<InferOutput<S>>;
11
+ json<S extends ArthronSchema>(schema: S): Promise<ArthronSchema.Infer<S>>;
11
12
  text(): Promise<string>;
12
13
  blob(): Promise<Blob>;
13
14
  arrayBuffer(): Promise<ArrayBuffer>;
@@ -17,10 +18,17 @@ interface ArthronPromise extends Promise<Response> {
17
18
  /** Iterate a newline-delimited JSON body, one parsed value per line. */
18
19
  ndjson<T = unknown>(options?: NdjsonOptions): AsyncIterable<T>;
19
20
  }
20
- /** One server-sent event, as described by the WHATWG EventSource spec. */
21
+ /**
22
+ * One server-sent event, as described by the WHATWG EventSource spec, with
23
+ * the names a browser `MessageEvent` uses (`type`, `data`, `lastEventId`).
24
+ */
21
25
  type SseEvent = {
22
- /** The `event:` field, when the server sent one. */
23
- event?: string;
26
+ /**
27
+ * The `event:` field; `"message"` when the server sent none, as in a
28
+ * browser. Any other value is the server's own name (`"update"`,
29
+ * `"content_block_delta"`, …).
30
+ */
31
+ type: "message" | (string & {});
24
32
  /** All `data:` lines of the block, joined with `\n`. */
25
33
  data: string;
26
34
  /** The `id:` field of this event's own block, when it had one. */
@@ -31,8 +39,12 @@ type SseEvent = {
31
39
  * the server sends one. Send it as `Last-Event-ID` to resume a stream.
32
40
  */
33
41
  lastEventId: string;
34
- /** The reconnection time in ms the server last set with `retry:`; carried on every event after it. */
35
- retry?: number;
42
+ /**
43
+ * How long in ms the server asked a client to wait before reconnecting,
44
+ * from its last `retry:` field (the spec's "reconnection time"); carried
45
+ * on every event after it.
46
+ */
47
+ reconnectionTime?: number;
36
48
  };
37
49
  type NdjsonOptions = {
38
50
  /**
@@ -41,45 +53,60 @@ type NdjsonOptions = {
41
53
  */
42
54
  maxLineLength?: number;
43
55
  };
44
- type SseOptions = NdjsonOptions & {
56
+ type SseOptions = {
57
+ /**
58
+ * Longest line accepted, in characters. A longer one rejects the iteration
59
+ * with a RangeError and cancels the body. Default 8 Mi.
60
+ */
61
+ maxLineLength?: number;
45
62
  /**
46
- * Most `data` one event may collect before its blank line, in characters.
47
- * More rejects the iteration with a RangeError and cancels the body.
48
- * Default 8 Mi.
63
+ * Longest `data` one event may collect before its blank line, in
64
+ * characters. More rejects the iteration with a RangeError and cancels the
65
+ * body. Default 8 Mi.
49
66
  */
50
- maxEventSize?: number;
67
+ maxDataLength?: number;
51
68
  };
52
69
  /**
53
- * The Standard Schema interface (https://standardschema.dev), vendored as
54
- * types only so arthron keeps zero dependencies. Zod, Valibot, ArkType and
55
- * others implement it, so their schemas can be passed to `.json(schema)`.
70
+ * A schema `.json(schema)` checks the body against. It is `StandardSchemaV1`
71
+ * from `@standard-schema/spec` (https://standardschema.dev) under arthron's
72
+ * name, copied as types so arthron keeps zero dependencies: pass a Zod,
73
+ * Valibot, ArkType or any other Standard Schema as is — there is no
74
+ * arthron-specific format.
56
75
  */
57
- interface StandardSchemaV1<Input = unknown, Output = Input> {
76
+ interface ArthronSchema<Input = unknown, Output = Input> {
58
77
  readonly "~standard": {
59
78
  readonly version: 1;
60
79
  readonly vendor: string;
61
- readonly validate: (value: unknown) => SchemaResult<Output> | Promise<SchemaResult<Output>>;
80
+ readonly validate: (value: unknown) => ArthronSchema.ValidationResult<Output> | Promise<ArthronSchema.ValidationResult<Output>>;
62
81
  readonly types?: {
63
82
  readonly input: Input;
64
83
  readonly output: Output;
65
84
  } | undefined;
66
85
  };
67
86
  }
68
- type SchemaResult<Output> = {
69
- readonly value: Output;
70
- readonly issues?: undefined;
71
- } | {
72
- readonly issues: readonly SchemaIssue[];
73
- };
74
- interface SchemaIssue {
75
- readonly message: string;
76
- readonly path?: readonly (PropertyKey | {
77
- readonly key: PropertyKey;
78
- })[] | undefined;
87
+ declare namespace ArthronSchema {
88
+ /** The type a schema produces — what `.json(schema)` resolves with. */
89
+ type Infer<S extends ArthronSchema> = NonNullable<S["~standard"]["types"]>["output"];
90
+ /** One way the body does not match, with the path to it (`StandardSchemaV1.Issue`). */
91
+ interface Issue {
92
+ readonly message: string;
93
+ readonly path?: readonly (PropertyKey | {
94
+ readonly key: PropertyKey;
95
+ })[] | undefined;
96
+ }
97
+ /** What a schema's `validate` returns: the value, or the issues (`StandardSchemaV1.Result`). */
98
+ type ValidationResult<Output> = {
99
+ readonly value: Output;
100
+ readonly issues?: undefined;
101
+ } | {
102
+ readonly issues: readonly Issue[];
103
+ };
79
104
  }
80
- /** The type a schema produces. */
81
- type InferOutput<S extends StandardSchemaV1> = NonNullable<S["~standard"]["types"]>["output"];
82
- type ErrorDetailResolver = (data: unknown, response: Response) => string | undefined;
105
+ /**
106
+ * Extracts `ArthronError.description` from a failed response's payload
107
+ * (parsed: JSON if it parses, else the text).
108
+ */
109
+ type DescriptionExtractor = (payload: unknown, response: Response) => string | undefined;
83
110
  //#endregion
84
111
  //#region src/client/client.types.d.ts
85
112
  /**
@@ -93,128 +120,144 @@ type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTION
93
120
  * its own options by augmentation (`declare module "arthron"`).
94
121
  */
95
122
  interface RequestOptions extends Omit<RequestInit, "body" | "method" | "headers"> {
96
- /** Per-call headers; a `null`/`undefined` value drops a client default. */
97
- headers?: HeadersOption;
98
- params?: QueryParams;
123
+ /**
124
+ * Per-call headers: anything fetch's `headers` takes, or a record whose
125
+ * `null`/`undefined` values drop a client default. Not typed with
126
+ * `HeadersInit`: that name exists only in the DOM lib, and a Node project
127
+ * typed with `@types/node` alone does not have it.
128
+ */
129
+ headers?: RequestInit["headers"] | Record<string, string | null | undefined>;
130
+ /**
131
+ * The query string: `null`/`undefined` values are skipped, an array becomes
132
+ * repeated keys (`ids=1&ids=2`), and a `URLSearchParams` is used as is.
133
+ */
134
+ queryParams?: QueryParams;
99
135
  /**
100
136
  * Values for `{name}` placeholders in the path, each encoded into its own
101
- * segment: `get("/users/{id}", { path: { id } })`. Interpolating ids into
102
- * the path string yourself lets "../" or "?" escape the segment.
137
+ * segment: `get("/users/{id}", { pathParams: { id } })`. Interpolating ids
138
+ * into the path string yourself lets "../" or "?" escape the segment.
103
139
  */
104
- path?: Record<string, string | number>;
140
+ pathParams?: Record<string, string | number>;
105
141
  /** Settings for plugins on this call only (see `ArthronMeta`). Not sent. */
106
142
  meta?: ArthronMeta;
107
143
  /** Which non-ok responses reject on this call; overrides the client's `throwOn`. */
108
- throwOn?: (res: Response) => boolean;
144
+ throwOn?: (response: Response) => boolean;
109
145
  }
110
146
  /** `request()`'s options: any method, with the same body rules as `post`. */
111
- interface RequestInitWithBody extends RequestOptions {
147
+ interface RequestOptionsWithMethod extends RequestOptions {
112
148
  method: HttpMethod;
113
149
  body?: unknown;
114
150
  }
115
151
  /**
116
- * The fetch options a client can set as defaults: the same `RequestInit`
117
- * fields per-call options take, minus what only makes sense per call —
152
+ * Client options. Besides arthron's own keys it takes any fetch option as a
153
+ * default (`credentials`, `cache`, `redirect`, `mode`, …), flat like per-call
154
+ * options; a per-call value wins. Left out: what only makes sense per call —
118
155
  * `body`, `method`, `signal` (one client-wide signal, once aborted, would
119
156
  * fail every later call) and `window`. `headers` has its own merge rules.
120
157
  */
121
- type FetchDefaults = Omit<RequestInit, "body" | "method" | "headers" | "signal" | "window">;
122
- /**
123
- * Client config. Besides arthron's own keys it takes any fetch option as a
124
- * default (`credentials`, `cache`, `redirect`, `mode`, …), flat like per-call
125
- * options; a per-call value wins.
126
- */
127
- interface ArthronConfig extends FetchDefaults {
158
+ interface ArthronClientOptions extends Omit<RequestInit, "body" | "method" | "headers" | "signal" | "window"> {
128
159
  /** Prefix for relative paths. Without it only absolute URLs are accepted. */
129
160
  baseURL?: string;
130
161
  /**
131
- * Let an absolute URL on another origin than `baseURL` through. Off by
132
- * default, so a path from user input or an API response cannot send the
133
- * client's headers elsewhere; config `headers` are never sent to another
134
- * origin even when this is on.
162
+ * Origins an absolute URL may go to besides `baseURL`'s, as URLs of which
163
+ * only the origin counts: `["https://cdn.example.com"]`. Any other origin
164
+ * is a TypeError, so a path from user input or an API response cannot send
165
+ * requests elsewhere. Config `headers` go to `baseURL`'s origin only, never
166
+ * to these — calls there carry per-call headers alone. Without a
167
+ * `baseURL` this list alone limits absolute URLs, and config `headers`
168
+ * are refused (they would have nowhere to go); with neither, any http(s)
169
+ * URL goes, config headers included.
170
+ */
171
+ allowedOrigins?: readonly string[];
172
+ /**
173
+ * Default headers, sent to baseURL's origin only: anything fetch's
174
+ * `headers` takes, or a record. Per-call headers win, compared
175
+ * case-insensitively; `extend` can drop one with `null`.
176
+ */
177
+ headers?: RequestInit["headers"] | Record<string, string | null | undefined>;
178
+ /**
179
+ * The function requests are sent with: anything with fetch's signature,
180
+ * `(input, init) => Promise<Response>`. Default: the global `fetch`, read
181
+ * per call. Pass a fake in tests, a polyfill, or a framework's fetch
182
+ * (SvelteKit's `event.fetch`). Plugins see it as `requestContext.fetchFn`.
135
183
  */
136
- allowAbsoluteUrls?: boolean;
137
- /** Default headers. Per-call headers win, compared case-insensitively; `extend` can drop one with `null`. */
138
- headers?: HeadersOption;
139
- /** Injection point for tests and polyfills. Defaults to `globalThis.fetch`. */
140
- fetch?: typeof fetch;
141
- use?: Plugin[];
142
- /** How to pick a message out of an error body. */
143
- errorDetail?: ErrorDetailResolver;
184
+ fetchFn?: typeof fetch;
185
+ /**
186
+ * Plugins, outermost first. One with the pluginName of an earlier one
187
+ * replaces it in place.
188
+ */
189
+ plugins?: readonly Plugin[];
190
+ /**
191
+ * Extracts `ArthronError.description` from a failed response's payload, for
192
+ * an API that puts it somewhere else:
193
+ * `(payload) => (payload as { errors?: { msg?: string }[] }).errors?.[0]?.msg`.
194
+ * Called only for failed responses; if it throws, the error simply has no
195
+ * description. Default: the payload's `message`, else `error` (a string),
196
+ * else `error.message`, else `detail`.
197
+ */
198
+ extractDescription?: DescriptionExtractor;
144
199
  /**
145
200
  * Which non-ok responses reject with an ArthronError — return `false` to
146
201
  * get the Response instead (a 404 that is data, say). An ok response never
147
202
  * rejects. Default: every non-ok response except an opaque one
148
203
  * (`mode: "no-cors"`) and the 3xx of `redirect: "manual"`.
149
204
  */
150
- throwOn?: (res: Response) => boolean;
205
+ throwOn?: (response: Response) => boolean;
151
206
  }
152
- interface Arthron {
153
- get(path: string, init?: RequestOptions): ArthronPromise;
154
- delete(path: string, init?: RequestOptions): ArthronPromise;
155
- head(path: string, init?: RequestOptions): ArthronPromise;
156
- post(path: string, body?: unknown, init?: RequestOptions): ArthronPromise;
157
- put(path: string, body?: unknown, init?: RequestOptions): ArthronPromise;
158
- patch(path: string, body?: unknown, init?: RequestOptions): ArthronPromise;
207
+ interface ArthronClient {
208
+ get(path: string, requestOptions?: RequestOptions): ResponsePromise;
209
+ delete(path: string, requestOptions?: RequestOptions): ResponsePromise;
210
+ head(path: string, requestOptions?: RequestOptions): ResponsePromise;
211
+ post(path: string, body?: unknown, requestOptions?: RequestOptions): ResponsePromise;
212
+ put(path: string, body?: unknown, requestOptions?: RequestOptions): ResponsePromise;
213
+ patch(path: string, body?: unknown, requestOptions?: RequestOptions): ResponsePromise;
159
214
  /** RFC 10008 QUERY: safe and idempotent like GET, but the query travels in the body. */
160
- query(path: string, body?: unknown, init?: RequestOptions): ArthronPromise;
215
+ query(path: string, body?: unknown, requestOptions?: RequestOptions): ResponsePromise;
161
216
  /** Any method — the extension point for verbs without their own helper. */
162
- request(path: string, init: RequestInitWithBody): ArthronPromise;
217
+ request(path: string, requestOptions: RequestOptionsWithMethod): ResponsePromise;
163
218
  /**
164
- * New client with these plugins added: a named one replaces the plugin of
165
- * that name in place, an unnamed one appends. The receiver is untouched.
219
+ * New client with these plugins added: a plugin replaces the one with its
220
+ * pluginName in place; other names append. The receiver is untouched.
166
221
  */
167
- use(...plugins: Plugin[]): Arthron;
168
- /** New client with config shallow-merged, headers merged, plugins added as `use` adds them. */
169
- extend(config: Partial<ArthronConfig>): Arthron;
170
- /** New client without the plugins of these names. The receiver is untouched. */
171
- without(...names: string[]): Arthron;
222
+ use(...plugins: Plugin[]): ArthronClient;
223
+ /** New client with these options shallow-merged, headers merged, plugins added as `use` adds them. */
224
+ extend(clientOptions: Partial<ArthronClientOptions>): ArthronClient;
225
+ /** New client without the plugins of these pluginNames. The receiver is untouched. */
226
+ without(...pluginNames: string[]): ArthronClient;
172
227
  }
173
228
  //#endregion
174
229
  //#region src/client/client.d.ts
175
230
  /**
176
- * Build a client from config. The result is immutable — `use` and `extend`
231
+ * Build a client from its options. The result is immutable — `use` and `extend`
177
232
  * return new clients and never touch the one they were called on.
178
233
  */
179
- export declare function arthron(config?: ArthronConfig): Arthron;
234
+ export declare function arthron(clientOptions?: ArthronClientOptions): ArthronClient;
180
235
  //#endregion
181
- //#region src/pipeline/define-plugin.d.ts
182
- /** Identity helper: gives a middleware written in its own file the right parameter types. */
183
- export declare const defineMiddleware: (middleware: Middleware) => Middleware;
184
- /** Identity helpers for each interceptor kind — `defineInterceptor.request(fn)` etc. */
185
- export declare const defineInterceptor: {
186
- request: (interceptor: RequestInterceptor) => RequestInterceptor;
187
- response: (interceptor: ResponseInterceptor) => ResponseInterceptor;
188
- error: (interceptor: ErrorInterceptor) => ErrorInterceptor;
189
- };
190
- /** Identity helper: type-checks a plugin object literal without an annotation. */
191
- export declare const definePlugin: (plugin: Plugin) => Plugin;
236
+ //#region src/pipeline/create-plugin.d.ts
237
+ /**
238
+ * A plugin, checked where it is written and typed without annotations:
239
+ * `createPlugin({ pluginName: "trace", requestInterceptor: (requestContext) => … })`.
240
+ */
241
+ export declare function createPlugin(plugin: Plugin): Plugin;
242
+ /** A middleware written on its own, typed without annotations. */
243
+ export declare const createMiddleware: (middleware: Middleware) => Middleware;
244
+ /** A request interceptor written on its own, typed without annotations. */
245
+ export declare const createRequestInterceptor: (interceptor: RequestInterceptor) => RequestInterceptor;
246
+ /** A response interceptor written on its own, typed without annotations. */
247
+ export declare const createResponseInterceptor: (interceptor: ResponseInterceptor) => ResponseInterceptor;
248
+ /** An error interceptor written on its own, typed without annotations. */
249
+ export declare const createErrorInterceptor: (interceptor: ErrorInterceptor) => ErrorInterceptor;
192
250
  //#endregion
193
251
  //#region src/response/schema-validation.d.ts
194
252
  /**
195
- * `.json(schema)` got a body that does not match. Not an ArthronError: the
196
- * request succeeded — the data is what is wrong.
253
+ * `.json(schema)` got a body that does not match the schema. Not an
254
+ * ArthronError: the request succeeded — the data is what is wrong.
197
255
  */
198
- export declare class SchemaError extends Error {
199
- readonly name = "SchemaError";
256
+ export declare class SchemaMismatchError extends Error {
257
+ readonly name = "SchemaMismatchError";
200
258
  /** Every problem the validator reported, with the path to it. */
201
- readonly issues: readonly SchemaIssue[];
202
- constructor(issues: readonly SchemaIssue[]);
259
+ readonly issues: readonly ArthronSchema.Issue[];
260
+ constructor(issues: readonly ArthronSchema.Issue[]);
203
261
  }
204
262
  //#endregion
205
- //#region src/index.d.ts
206
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
207
- export declare const logging: typeof logging$1;
208
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
209
- export declare const retry: typeof retry$1;
210
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
211
- export declare const timeout: typeof timeout$1;
212
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
213
- export type LogEvent = LogEvent$1;
214
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
215
- export type LoggingOptions = LoggingOptions$1;
216
- /** @deprecated Import from "arthron/plugins"; this export goes in the next major. */
217
- export type TimeoutOptions = TimeoutOptions$1;
218
- //#endregion
219
- export { type Arthron, type ArthronConfig, ArthronError, type ArthronMeta, type ArthronPromise, type ErrorDetailResolver, type ErrorInterceptor, type HeadersOption, type HttpMethod, type InferOutput, type Middleware, type NdjsonOptions, type Plugin, type QueryParams, type RequestContext, type RequestInitWithBody, type RequestInterceptor, type RequestOptions, type ResponseInterceptor, type RetryOptions, type SchemaIssue, type SseEvent, type SseOptions, type StandardSchemaV1, isArthronError };
220
- //# sourceMappingURL=index.d.cts.map
263
+ export { type ArthronClient, type ArthronClientOptions, ArthronError, type ArthronErrorOptions, type ArthronMeta, type ArthronSchema, type DescriptionExtractor, type ErrorInterceptor, type HttpMethod, type Middleware, type NdjsonOptions, type Plugin, type QueryParams, type RequestContext, type RequestInterceptor, type RequestOptions, type RequestOptionsWithMethod, type ResponseInterceptor, type ResponsePromise, type SseEvent, type SseOptions, isArthronError };