@zap-studio/fetch 0.5.6 → 1.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/CHANGELOG.md CHANGED
@@ -1,44 +1,67 @@
1
- ## @zap-studio/fetch@0.5.6
1
+ # Changelog
2
2
 
3
- ### Tree-shakeable root re-exports
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.1.0]
8
+
9
+ ### Added
10
+
11
+ `createFetch(...)` gains an optional `logger?: Logger` option (from `@zap-studio/logger`). When provided, it logs outgoing requests at `debug`, response status at `debug` (2xx) or `warn` (non-2xx), and schema validation failures at `error`. Omitting it keeps zero logging overhead. See [Logging](https://www.zapstudio.dev/fetch/logging).
12
+
13
+ ## [1.0.0]
14
+
15
+ ### Changed
16
+
17
+ - Clarified tree-shakeable design in the package description and README (no code change).
18
+
19
+ ### Removed
20
+
21
+ Collapsed the internal request pipeline (`_internal.ts`, `_methods.ts`, `constants.ts`, `headers.ts`, `request.ts`, `url.ts`) into implementation-only files. `mergeHeaders`, `normalizeRequest`, and `resolveRequestUrl` are no longer public API — they were pipeline internals, not meant for standalone use.
22
+
23
+ - Removed the `./constants`, `./headers`, `./request`, `./url`, and `./fetch` subpath exports. Use the root `@zap-studio/fetch` entry instead.
24
+ - `GLOBAL_DEFAULTS`, `FetchError`, `$fetch`, `api`, `createFetch`, and all public types are unaffected and still exported from `.`; `./errors` and `./types` subpaths are unaffected.
25
+
26
+ ## [0.5.6]
27
+
28
+ ### Added
4
29
 
5
30
  The package root now re-exports the full public API, so everything can be imported from `@zap-studio/fetch` directly (`$fetch`, `api`, `createFetch`, `FetchError`, `mergeHeaders`, `GLOBAL_DEFAULTS`, `normalizeRequest`, `resolveRequestUrl`, and all public types). All exports are side-effect free and tree-shakeable; granular subpath imports keep working.
6
31
 
7
32
  - The `$fetch`/`api`/`createFetch` implementation moved from the entrypoint into its own module, available as the new `./fetch` subpath.
33
+
34
+ ### Removed
35
+
8
36
  - Removed the `./internal` and `./methods` subpath exports. Both were implementation details (`fetchInternal`, `createMethod`) and are no longer part of the public API.
9
37
 
10
- ## @zap-studio/fetch@0.5.5
38
+ ## [0.5.5]
11
39
 
12
- ### Migrate to ultracite lint/format
40
+ ### Changed
13
41
 
14
42
  Internal formatting and lint cleanup only. No public API or behavior change.
15
43
 
16
- # @zap-studio/fetch
17
-
18
- ## 0.5.4
44
+ ## [0.5.4]
19
45
 
20
46
  ### Changed
21
47
 
22
48
  - Added fetch ecosystem benchmarks and grouped benchmark output for easier cross-library comparisons.
23
49
  - Applied small internal performance optimizations in request preparation, URL resolution, and header merging without changing public API behavior.
24
50
 
25
- ## 0.5.3
51
+ ## [0.5.3]
26
52
 
27
53
  ### Changed
28
54
 
29
55
  - Refactor `createFetch(...)` to derive fallback defaults from `GLOBAL_DEFAULTS` instead of re-defining primitive defaults inline.
30
56
 
31
- ## 0.5.2
57
+ ## [0.5.2]
32
58
 
33
59
  ### Changed
34
60
 
35
61
  - Expand TSDoc coverage across fetch modules and exported contracts for stronger JSR documentation completeness.
36
-
37
- ### Dependencies
38
-
39
62
  - Updated dependency `@zap-studio/validation` to `0.3.4`.
40
63
 
41
- ## 0.5.1
64
+ ## [0.5.1]
42
65
 
43
66
  ### Fixed
44
67
 
@@ -47,19 +70,18 @@ Internal formatting and lint cleanup only. No public API or behavior change.
47
70
  ### Changed
48
71
 
49
72
  - 2ea1a70: Cleaned up public option typings by removing redundant `| undefined` unions from fetch configuration types and overloads.
50
-
51
- ### Dependencies
52
-
53
73
  - Updated dependency `@zap-studio/validation` to `0.3.3`.
54
74
 
55
- ## 0.5.0
75
+ ## [0.5.0]
56
76
 
57
- ### Breaking
77
+ ### Added
58
78
 
59
- - Request bodies are no longer auto-serialized from plain objects; use the explicit `json` option (or set `body` yourself). `body` and `json` are mutually exclusive at the type level and enforced at runtime.
79
+ - Documented the throwable error surface for `$fetch`, `createFetch`, and internal request execution with explicit `@throws` tags (for example `FetchError`, `ValidationError`, `TypeError`, `DOMException`, `SyntaxError`, and validator-thrown errors).
80
+ - Added full package test coverage across statements, branches, functions, and lines.
60
81
 
61
82
  ### Changed
62
83
 
84
+ - **Breaking:** Request bodies are no longer auto-serialized from plain objects; use the explicit `json` option (or set `body` yourself). `body` and `json` are mutually exclusive at the type level and enforced at runtime.
63
85
  - Simplified the request API around web platform types.
64
86
  - The first argument is named `input` and typed as `FetchInput` (`Parameters<typeof fetch>[0]` from `lib.dom`), exported from `@zap-studio/fetch/types`, so allowed inputs track global `fetch` when DOM typings change.
65
87
  - Non-`Request` values (including `URL`) are normalized to a string URL before query merge.
@@ -71,46 +93,35 @@ Internal formatting and lint cleanup only. No public API or behavior change.
71
93
  - `body` and `json` are mutually exclusive in TypeScript and guarded at runtime.
72
94
  - Reworked URL handling to use the platform `URL` and `URLSearchParams` APIs while preserving relative URL output when no `baseURL` is configured.
73
95
  - Simplified the internal module structure and removed the old `utils` module.
96
+ - Reworked tests to mirror the `src` module structure.
97
+ - JSR dependency mapping now pins `@zap-studio/validation` to `0.3.2`.
74
98
 
75
99
  ### Fixed
76
100
 
77
101
  - Fixed absolute URL handling when no `baseURL` is configured.
78
102
  - `resolveSearchParams` keeps a trailing `#` when the input URL had an empty fragment (for example `.../path#`), matching typical `URL` serialization instead of dropping the delimiter.
79
103
 
80
- ### Tests
81
-
82
- - Reworked tests to mirror the `src` module structure.
83
- - Added full package coverage across statements, branches, functions, and lines.
84
-
85
- ### Documentation
86
-
87
- - Documented the throwable error surface for `$fetch`, `createFetch`, and internal request execution with explicit `@throws` tags (for example `FetchError`, `ValidationError`, `TypeError`, `DOMException`, `SyntaxError`, and validator-thrown errors).
88
-
89
- ### Dependencies
104
+ ## [0.4.7]
90
105
 
91
- - JSR dependency mapping now pins `@zap-studio/validation` to `0.3.2`.
92
-
93
- ## 0.4.7
94
-
95
- ### Patch Changes
106
+ ### Changed
96
107
 
97
108
  - e26293e: Updated dependencies.
98
109
  - @zap-studio/validation@0.3.2
99
110
 
100
- ## 0.4.6
111
+ ## [0.4.6]
101
112
 
102
- ### Patch Changes
113
+ ### Changed
103
114
 
104
115
  - 5ea3d3b: Updated dependencies.
105
116
  - @zap-studio/validation@0.3.1
106
117
 
107
- ## 0.4.5
118
+ ## [0.4.5]
108
119
 
109
- ### Dependencies
120
+ ### Changed
110
121
 
111
122
  - f75b984: Updated dependency `@zap-studio/validation` to `0.3.0`.
112
123
 
113
- ## 0.4.4
124
+ ## [0.4.4]
114
125
 
115
126
  ### Fixed
116
127
 
@@ -119,39 +130,33 @@ Internal formatting and lint cleanup only. No public API or behavior change.
119
130
  - Auto-stringify plain JSON body values even when no response schema is provided.
120
131
  - Set `Content-Type: application/json` only when auto-stringifying and no explicit content type is set.
121
132
 
122
- ## 0.4.3
133
+ ## [0.4.3]
123
134
 
124
135
  ### Changed
125
136
 
126
137
  - e4542bb: Refined `standardValidate` typings so return types depend on `throwOnError`, and updated `@zap-studio/fetch` integration while preserving the existing boolean configuration API.
127
-
128
- ### Dependencies
129
-
130
138
  - e4542bb: Updated dependency `@zap-studio/validation` to `0.2.1`.
131
139
 
132
- ## 0.4.2
140
+ ## [0.4.2]
133
141
 
134
- ### Dependencies
142
+ ### Changed
135
143
 
136
144
  - 2de8183: Updated dependency `@zap-studio/validation` to `0.2.0`.
137
145
 
138
- ## 0.4.1
146
+ ## [0.4.1]
139
147
 
140
148
  ### Changed
141
149
 
142
150
  - 447dbda: Switched shared Standard Schema validation utilities to `@zap-studio/validation`.
143
-
144
- ### Dependencies
145
-
146
151
  - 447dbda: Updated dependency `@zap-studio/validation` to `0.1.0`.
147
152
 
148
- ## 0.4.0
153
+ ## [0.4.0]
149
154
 
150
155
  ### Added
151
156
 
152
157
  - 69057cd: Exposed fetch defaults constants and utility helpers as public exports.
153
158
 
154
- ## 0.3.1
159
+ ## [0.3.1]
155
160
 
156
161
  ### Changed
157
162
 
@@ -160,31 +165,26 @@ Internal formatting and lint cleanup only. No public API or behavior change.
160
165
  - `throwOnValidationError: false` returns `Promise<StandardSchemaV1.Result<TSchema>>`.
161
166
  - This improves type safety and removes manual narrowing in default usage.
162
167
 
163
- ## 0.3.0
168
+ ## [0.3.0]
164
169
 
165
170
  ### Added
166
171
 
167
172
  - 659621c: Added `searchParams` support in `createFetch` for factory-level default query parameters.
168
173
  - Per-request `searchParams` still override factory defaults.
169
174
 
170
- ## 0.2.2
175
+ ## [0.2.2]
171
176
 
172
177
  ### Changed
173
178
 
174
179
  - 5c3abbf: Prepared JSR publish and `isolatedDeclarations` support with explicit `$Fetch` and `ApiMethods` types.
175
180
 
176
- ## 0.2.1
181
+ ## [0.2.1]
177
182
 
178
183
  ### Changed
179
184
 
180
185
  - 82bac5c: Replaced regex-based slash trimming with more efficient string manipulation for URL normalization.
181
186
 
182
- ## 0.2.0
183
-
184
- ### Changed
185
-
186
- - 78afb76: Migrated from Zod-only validation to Standard Schema v1 for broader validator compatibility.
187
- - Supported libraries include Zod, Valibot, ArkType, and other Standard Schema-compliant validators.
187
+ ## [0.2.0]
188
188
 
189
189
  ### Added
190
190
 
@@ -192,25 +192,27 @@ Internal formatting and lint cleanup only. No public API or behavior change.
192
192
  - 78afb76: Added smart URL behavior so absolute URLs bypass `baseURL`.
193
193
  - 78afb76: Added automatic JSON body serialization and `Content-Type` handling for schema-based requests.
194
194
 
195
- ### Breaking
195
+ ### Changed
196
196
 
197
- - 78afb76: Standard Schema-compliant validator libraries are now required (for example Zod 3.23+, Valibot 1.0+, ArkType 2.0+).
198
- - 78afb76: Internal file structure was reorganized (affects deep imports).
199
- - 78afb76: `FetchError` constructor now requires `(message, response)`.
197
+ - 78afb76: Migrated from Zod-only validation to Standard Schema v1 for broader validator compatibility.
198
+ - Supported libraries include Zod, Valibot, ArkType, and other Standard Schema-compliant validators.
199
+ - 78afb76: **Breaking:** Standard Schema-compliant validator libraries are now required (for example Zod 3.23+, Valibot 1.0+, ArkType 2.0+).
200
+ - 78afb76: **Breaking:** Internal file structure was reorganized (affects deep imports).
201
+ - 78afb76: **Breaking:** `FetchError` constructor now requires `(message, response)`.
200
202
 
201
- ## 0.1.2
203
+ ## [0.1.2]
202
204
 
203
205
  ### Changed
204
206
 
205
207
  - 69c2b21: Renamed `safeFetch` to `$fetch` while preserving `safeFetch` compatibility for legacy usage.
206
208
 
207
- ## 0.1.1
209
+ ## [0.1.1]
208
210
 
209
211
  ### Changed
210
212
 
211
213
  - 5f1812b: Updated `files` in `package.json` to publish only required artifacts.
212
214
 
213
- ## 0.1.0
215
+ ## [0.1.0]
214
216
 
215
217
  ### Added
216
218
 
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 alexandretrotel
3
+ Copyright (c) 2026 Alexandre Trotel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,76 +2,52 @@
2
2
 
3
3
  A small fetch wrapper with [**Standard Schema**](https://standardschema.dev/schema) response validation.
4
4
 
5
+ Full documentation: [zapstudio.dev/fetch](https://www.zapstudio.dev/fetch)
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
- nub add @zap-studio/fetch
9
- # or
10
10
  npm install @zap-studio/fetch
11
- # or
12
- pnpm add @zap-studio/fetch
13
11
  ```
14
12
 
15
- ## Runtime Support
13
+ You also need a schema library that implements [Standard Schema](https://standardschema.dev/schema), such as Zod, Valibot, or ArkType.
16
14
 
17
- | Runtime | Minimum version |
18
- | ------------------ | ------------------------------------------------ |
19
- | Node.js | 18.0.0 (ships native `fetch`) |
20
- | Bun | 1.0.0 |
21
- | Deno | 1.42 |
22
- | Cloudflare Workers | Any current release |
23
- | Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
15
+ ## Features
24
16
 
25
- The package relies on the global `fetch` API and ships standard ESM only. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/fetch`).
17
+ - **Raw fetch mode** through `$fetch(input, options)` behaves like native `fetch` and returns the `Response`.
18
+ - **Validated fetch mode** through `$fetch(input, schema, options)` — parses and validates the JSON response.
19
+ - **HTTP method helpers** through `api.get`, `api.post`, `api.put`, `api.patch`, and `api.delete`.
20
+ - **Configured clients** through `createFetch(...)` with shared `baseURL`, headers, query params, and error defaults.
21
+ - **JSON convenience** through the `json` option, which serializes the request body and sets `Content-Type`.
22
+ - **Structured errors** with `FetchError` for HTTP failures and `ValidationError` for schema failures.
23
+ - **Validator-agnostic** — works with any library that implements Standard Schema.
24
+ - **Optional logging** through `createFetch({ logger })` ([`@zap-studio/logger`](https://www.npmjs.com/package/@zap-studio/logger)) — omit it and there's zero logging overhead.
25
+ - **Tree-shakeable** — every export is a standalone function with no shared internal state; unused exports are dropped by any modern bundler.
26
26
 
27
- ## Quick Example
27
+ ## Quick Start
28
28
 
29
29
  ```ts
30
- import { api } from "@zap-studio/fetch";
30
+ import { ConsoleLogger } from "@zap-studio/logger";
31
+ import { createFetch } from "@zap-studio/fetch";
31
32
  import { z } from "zod";
32
33
 
33
34
  const UserSchema = z.object({
34
35
  id: z.number(),
35
36
  name: z.string(),
36
- email: z.string().email(),
37
+ email: z.email(),
37
38
  });
38
39
 
39
- const user = await api.get("https://api.example.com/users/1", UserSchema);
40
+ const logger = new ConsoleLogger({ minLevel: "debug" });
41
+ const { api } = createFetch({ logger });
40
42
 
41
- console.log(user.name);
42
- ```
43
-
44
- ## Before
45
-
46
- Without response validation, fetch code often needs unchecked assertions:
47
-
48
- ```ts
49
- type User = {
50
- id: number;
51
- name: string;
52
- };
53
-
54
- const response = await fetch("/api/users/1");
55
- const data = await response.json();
56
-
57
- const user = data as User;
58
- ```
59
-
60
- ## After
61
-
62
- With `@zap-studio/fetch`, the response is validated before it is returned:
63
-
64
- ```ts
65
- import { api } from "@zap-studio/fetch";
43
+ const user = await api.get("https://api.example.com/users/1", UserSchema);
66
44
 
67
- const user = await api.get("/api/users/1", UserSchema);
45
+ console.log(user.name); // typed as string, validated at runtime
68
46
  ```
69
47
 
70
- The returned value is inferred from the schema.
71
-
72
- ## Raw Fetch Usage
48
+ ## Raw Fetch Mode
73
49
 
74
- Use `$fetch` without a schema when you want the normal `Response` object.
50
+ Behaves like native `fetch` and returns the `Response`.
75
51
 
76
52
  ```ts
77
53
  import { $fetch } from "@zap-studio/fetch";
@@ -80,21 +56,9 @@ const response = await $fetch("/api/users/1");
80
56
  const user = await response.json();
81
57
  ```
82
58
 
83
- Native fetch options are passed through as `RequestInit`.
84
-
85
- ```ts
86
- const response = await $fetch("/api/users", {
87
- method: "POST",
88
- body: JSON.stringify({ name: "Ada" }),
89
- headers: {
90
- "Content-Type": "application/json",
91
- },
92
- });
93
- ```
94
-
95
- ## Validated Fetch Usage
59
+ ## Validated Fetch Mode
96
60
 
97
- Pass a Standard Schema as the second argument to validate the JSON response.
61
+ Parses and validates the JSON response.
98
62
 
99
63
  ```ts
100
64
  import { $fetch } from "@zap-studio/fetch";
@@ -102,213 +66,93 @@ import { $fetch } from "@zap-studio/fetch";
102
66
  const user = await $fetch("/api/users/1", UserSchema);
103
67
  ```
104
68
 
105
- The schema validates the response body, not the request body.
106
-
107
- When validation fails, a `ValidationError` is thrown by default.
69
+ ## HTTP Method Helpers
108
70
 
109
- ## API Method Helpers
110
-
111
- The `api` export provides common HTTP methods.
71
+ `api.get`, `api.post`, `api.put`, `api.patch`, and `api.delete`.
112
72
 
113
73
  ```ts
114
74
  import { api } from "@zap-studio/fetch";
115
75
 
116
- const user = await api.get("/api/users/1", UserSchema);
117
-
118
76
  const created = await api.post("/api/users", UserSchema, {
119
- json: {
120
- name: "Ada",
121
- email: "ada@example.com",
122
- },
123
- });
124
- ```
125
-
126
- In this example, `UserSchema` validates the response from `/api/users`. The `json` value is the outgoing request body and is not validated by `UserSchema`.
127
-
128
- These helpers set the HTTP method for you. For raw responses without validation, use `$fetch`.
129
-
130
- ## JSON Request Bodies
131
-
132
- Use `json` when you want the package to serialize a JSON request body.
133
-
134
- ```ts
135
- await api.post("/api/users", UserSchema, {
136
- json: {
137
- name: "Ada",
138
- },
139
- });
140
- ```
141
-
142
- The schema argument still validates the response. Validate request bodies separately before passing them to `json` if your application needs outgoing payload validation.
143
-
144
- This sets:
145
-
146
- - `body` to `JSON.stringify(json)`
147
- - `Content-Type` to `application/json` when no content type was already provided
148
-
149
- Use native `body` for standard fetch behavior.
150
-
151
- ```ts
152
- await $fetch("/api/upload", {
153
- method: "POST",
154
- body: formData,
77
+ json: { name: "Ada", email: "ada@example.com" },
155
78
  });
156
79
  ```
157
80
 
158
- `body` and `json` are mutually exclusive.
81
+ ## Configured Clients
159
82
 
160
- ## Query Params
161
-
162
- Use `searchParams` for per-request query params.
163
-
164
- ```ts
165
- await api.get("/api/users", UserListSchema, {
166
- searchParams: {
167
- page: "1",
168
- limit: "20",
169
- },
170
- });
171
- ```
172
-
173
- `searchParams` accepts the same input shapes as `new URLSearchParams(...)`.
174
-
175
- ```ts
176
- await api.get("/api/users", UserListSchema, {
177
- searchParams: new URLSearchParams({ q: "ada" }),
178
- });
179
-
180
- await api.get("/api/users", UserListSchema, {
181
- searchParams: "q=ada&page=1",
182
- });
183
-
184
- await api.get("/api/users", UserListSchema, {
185
- searchParams: [["q", "ada"]],
186
- });
187
- ```
188
-
189
- When defaults, URL query params, and request params overlap, later values win:
190
-
191
- 1. `createFetch({ searchParams })`
192
- 2. query params already present in the URL
193
- 3. per-request `searchParams`
194
-
195
- ## Creating a Client
196
-
197
- Use `createFetch` to configure shared defaults.
83
+ Shared `baseURL`, headers, query params, and error defaults.
198
84
 
199
85
  ```ts
200
86
  import { createFetch } from "@zap-studio/fetch";
201
87
 
202
- const { $fetch, api } = createFetch({
88
+ const { api } = createFetch({
203
89
  baseURL: "https://api.example.com",
204
- headers: {
205
- Authorization: `Bearer ${token}`,
206
- },
207
- searchParams: {
208
- locale: "en",
209
- },
90
+ headers: { Authorization: `Bearer ${token}` },
210
91
  });
211
92
 
212
93
  const user = await api.get("/users/1", UserSchema);
213
- const response = await $fetch("/health");
214
94
  ```
215
95
 
216
- Supported defaults:
96
+ ## JSON Convenience
217
97
 
218
- | Option | Description |
219
- | ------------------------ | ----------------------------------------------- |
220
- | `baseURL` | Base URL for relative request URLs |
221
- | `headers` | Headers applied to every request |
222
- | `searchParams` | Query params applied to every request |
223
- | `throwOnFetchError` | Throw `FetchError` for non-ok responses |
224
- | `throwOnValidationError` | Throw `ValidationError` for validation failures |
225
-
226
- ## Non-throwing Validation
227
-
228
- Set `throwOnValidationError: false` to receive the raw Standard Schema result.
98
+ Serializes the request body and sets `Content-Type`.
229
99
 
230
100
  ```ts
231
- const result = await api.get("/api/users/1", UserSchema, {
232
- throwOnValidationError: false,
101
+ await api.post("/api/users", UserSchema, {
102
+ json: { name: "Ada" }, // mutually exclusive with `body`
233
103
  });
234
-
235
- if (result.issues) {
236
- console.error("Validation failed:", result.issues);
237
- } else {
238
- console.log("Validation passed:", result.value);
239
- }
240
104
  ```
241
105
 
242
- ## HTTP Errors
106
+ ## Structured Errors
243
107
 
244
- By default, non-ok responses throw a `FetchError`.
108
+ `FetchError` for HTTP failures and `ValidationError` for schema failures.
245
109
 
246
110
  ```ts
247
111
  import { FetchError } from "@zap-studio/fetch";
112
+ import { ValidationError } from "@zap-studio/validation";
248
113
 
249
114
  try {
250
- await api.get("/api/users/404", UserSchema);
115
+ await api.get("/api/users/1", UserSchema);
251
116
  } catch (error) {
252
- if (error instanceof FetchError) {
253
- console.error(error.status);
254
- console.error(error.response);
255
- }
117
+ if (error instanceof FetchError) console.error(error.status);
118
+ if (error instanceof ValidationError) console.error(error.issues);
256
119
  }
257
120
  ```
258
121
 
259
- Disable this behavior when you want to handle the raw response yourself.
122
+ ## Validator-Agnostic
260
123
 
261
- ```ts
262
- const response = await $fetch("/api/users/404", {
263
- throwOnFetchError: false,
264
- });
124
+ Works with any library that implements Standard Schema.
265
125
 
266
- console.log(response.status);
126
+ ```ts
127
+ // UserSchema can come from Zod, Valibot, ArkType, or any Standard Schema-compatible library
128
+ import type { StandardSchemaV1 } from "@zap-studio/validation";
267
129
  ```
268
130
 
269
- ## Request Objects
131
+ ## Logging
270
132
 
271
- The first argument (`input`) uses the same type as global `fetch`.
133
+ Pass a `logger?: Logger` from [`@zap-studio/logger`](https://www.npmjs.com/package/@zap-studio/logger) to `createFetch(...)` to observe requests, responses, and validation failures. Omit it and nothing is logged.
272
134
 
273
135
  ```ts
274
- const request = new Request("https://api.example.com/users/1", {
275
- headers: {
276
- Authorization: `Bearer ${token}`,
277
- },
278
- });
136
+ import { ConsoleLogger } from "@zap-studio/logger";
137
+ import { createFetch } from "@zap-studio/fetch";
279
138
 
280
- const user = await $fetch(request, UserSchema);
139
+ const logger = new ConsoleLogger({ minLevel: "debug" });
140
+ const { api } = createFetch({ baseURL: "https://api.example.com", logger });
281
141
  ```
282
142
 
283
- Per-request options are merged on top of the `Request`.
284
-
285
- ## Standard Schema
286
-
287
- `@zap-studio/fetch` validates responses through `@zap-studio/validation`, so it works with any validation library that implements Standard Schema.
288
-
289
- Examples include:
290
-
291
- - [Zod](https://zod.dev/)
292
- - [Valibot](https://valibot.dev/)
293
- - [ArkType](https://arktype.io/)
143
+ Outgoing requests log at `debug`, response status logs at `debug` (2xx) or `warn` (non-2xx), and schema validation failures log at `error`.
294
144
 
295
- You can also import Standard Schema types from `@zap-studio/validation` when needed.
145
+ ## Runtime Support
296
146
 
297
- ```ts
298
- import type { StandardSchemaV1 } from "@zap-studio/validation";
299
- ```
147
+ | Runtime | Minimum version |
148
+ | ------------------ | ------------------------------------------------ |
149
+ | Node.js | 18.0.0 (ships native `fetch`) |
150
+ | Bun | 1.0.0 |
151
+ | Deno | 1.42 |
152
+ | Cloudflare Workers | Any current release |
153
+ | Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
300
154
 
301
- ## Choosing the Right API
302
-
303
- | API | Use when |
304
- | ------------- | ------------------------------------------------- |
305
- | `$fetch` | You want raw fetch behavior or a custom method |
306
- | `api.get` | You want a validated GET request |
307
- | `api.post` | You want a validated POST request |
308
- | `api.put` | You want a validated PUT request |
309
- | `api.patch` | You want a validated PATCH request |
310
- | `api.delete` | You want a validated DELETE request |
311
- | `createFetch` | You want shared defaults like base URL or headers |
155
+ The package relies on the global `fetch` API and ships standard ESM only. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/fetch`).
312
156
 
313
157
  ## License
314
158