@zap-studio/fetch 0.5.5 → 0.5.6

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +1 -1
  3. package/README.md +17 -1
  4. package/dist/{constants.d.mts → constants.d.ts} +2 -2
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/{constants.mjs → constants.js} +1 -1
  7. package/dist/constants.js.map +1 -0
  8. package/dist/{errors.d.mts → errors.d.ts} +1 -1
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/{errors.mjs → errors.js} +1 -1
  11. package/dist/errors.js.map +1 -0
  12. package/dist/fetch-D0IXjsUD.js +222 -0
  13. package/dist/fetch-D0IXjsUD.js.map +1 -0
  14. package/dist/{index.d.mts → fetch.d.ts} +3 -3
  15. package/dist/fetch.d.ts.map +1 -0
  16. package/dist/fetch.js +3 -0
  17. package/dist/{headers.d.mts → headers.d.ts} +1 -1
  18. package/dist/headers.d.ts.map +1 -0
  19. package/dist/{headers.mjs → headers.js} +1 -1
  20. package/dist/headers.js.map +1 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.js +7 -0
  23. package/dist/{request.d.mts → request.d.ts} +2 -2
  24. package/dist/request.d.ts.map +1 -0
  25. package/dist/{request.mjs → request.js} +2 -2
  26. package/dist/request.js.map +1 -0
  27. package/dist/{types.d.mts → types.d.ts} +1 -1
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +0 -0
  30. package/dist/{url.d.mts → url.d.ts} +2 -2
  31. package/dist/url.d.ts.map +1 -0
  32. package/dist/{url.mjs → url.js} +1 -1
  33. package/dist/url.js.map +1 -0
  34. package/package.json +19 -27
  35. package/dist/constants.d.mts.map +0 -1
  36. package/dist/constants.mjs.map +0 -1
  37. package/dist/errors.d.mts.map +0 -1
  38. package/dist/errors.mjs.map +0 -1
  39. package/dist/headers.d.mts.map +0 -1
  40. package/dist/headers.mjs.map +0 -1
  41. package/dist/index.d.mts.map +0 -1
  42. package/dist/index.mjs +0 -103
  43. package/dist/index.mjs.map +0 -1
  44. package/dist/internal.d.mts +0 -32
  45. package/dist/internal.d.mts.map +0 -1
  46. package/dist/internal.mjs +0 -75
  47. package/dist/internal.mjs.map +0 -1
  48. package/dist/methods.d.mts +0 -22
  49. package/dist/methods.d.mts.map +0 -1
  50. package/dist/methods.mjs +0 -58
  51. package/dist/methods.mjs.map +0 -1
  52. package/dist/request.d.mts.map +0 -1
  53. package/dist/request.mjs.map +0 -1
  54. package/dist/types.d.mts.map +0 -1
  55. package/dist/types.mjs +0 -1
  56. package/dist/url.d.mts.map +0 -1
  57. package/dist/url.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## @zap-studio/fetch@0.5.6
2
+
3
+ ### Tree-shakeable root re-exports
4
+
5
+ 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
+
7
+ - The `$fetch`/`api`/`createFetch` implementation moved from the entrypoint into its own module, available as the new `./fetch` subpath.
8
+ - Removed the `./internal` and `./methods` subpath exports. Both were implementation details (`fetchInternal`, `createMethod`) and are no longer part of the public API.
9
+
1
10
  ## @zap-studio/fetch@0.5.5
2
11
 
3
12
  ### Migrate to ultracite lint/format
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Alexandre Trotel
3
+ Copyright (c) 2026 alexandretrotel
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
@@ -5,9 +5,25 @@ A small fetch wrapper with [**Standard Schema**](https://standardschema.dev/sche
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
+ nub add @zap-studio/fetch
9
+ # or
8
10
  npm install @zap-studio/fetch
11
+ # or
12
+ pnpm add @zap-studio/fetch
9
13
  ```
10
14
 
15
+ ## Runtime Support
16
+
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) |
24
+
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`).
26
+
11
27
  ## Quick Example
12
28
 
13
29
  ```ts
@@ -228,7 +244,7 @@ if (result.issues) {
228
244
  By default, non-ok responses throw a `FetchError`.
229
245
 
230
246
  ```ts
231
- import { FetchError } from "@zap-studio/fetch/errors";
247
+ import { FetchError } from "@zap-studio/fetch";
232
248
 
233
249
  try {
234
250
  await api.get("/api/users/404", UserSchema);
@@ -1,4 +1,4 @@
1
- import { FetchDefaults } from "./types.mjs";
1
+ import { FetchDefaults } from "./types.js";
2
2
  //#region src/constants.d.ts
3
3
  /**
4
4
  * Default options for the global $fetch
@@ -14,4 +14,4 @@ import { FetchDefaults } from "./types.mjs";
14
14
  declare const GLOBAL_DEFAULTS: FetchDefaults;
15
15
  //#endregion
16
16
  export { GLOBAL_DEFAULTS };
17
- //# sourceMappingURL=constants.d.mts.map
17
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","names":[],"sources":["../src/constants.ts"],"mappings":";;;;;;;;;;;;;cAmBa,iBAAiB"}
@@ -18,4 +18,4 @@ const GLOBAL_DEFAULTS = {
18
18
  //#endregion
19
19
  export { GLOBAL_DEFAULTS };
20
20
 
21
- //# sourceMappingURL=constants.mjs.map
21
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Shared defaults and constants for fetch behavior.\n *\n * @module @zap-studio/fetch/constants\n */\n\nimport type { FetchDefaults } from \"./types.js\";\n\n/**\n * Default options for the global $fetch\n *\n * These defaults are used by the top-level `$fetch` export.\n * Use `createFetch(...)` when you need per-client defaults.\n *\n * @example\n * import { GLOBAL_DEFAULTS } from \"@zap-studio/fetch/constants\";\n *\n * console.log(GLOBAL_DEFAULTS.throwOnFetchError); // true\n */\nexport const GLOBAL_DEFAULTS: FetchDefaults = {\n baseURL: \"\",\n throwOnFetchError: true,\n throwOnValidationError: true,\n};\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAa,kBAAiC;CAC5C,SAAS;CACT,mBAAmB;CACnB,wBAAwB;AAC1B"}
@@ -38,4 +38,4 @@ declare class FetchError extends Error {
38
38
  }
39
39
  //#endregion
40
40
  export { FetchError };
41
- //# sourceMappingURL=errors.d.mts.map
41
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAwBa,mBAAmB;;;;EAI9B,QAAQ;;;;EAIR,UAAU;;;;EAKV,YAAY,iBAAiB,UAAU"}
@@ -44,4 +44,4 @@ var FetchError = class extends Error {
44
44
  //#endregion
45
45
  export { FetchError };
46
46
 
47
- //# sourceMappingURL=errors.mjs.map
47
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Custom error types used by the fetch package.\n *\n * @module @zap-studio/fetch/errors\n */\n\n/**\n * Error thrown for HTTP errors (non-2xx responses)\n *\n * Includes the failing `Response` object and status code for\n * downstream error handling and response inspection.\n *\n * @example\n * import { FetchError } from \"@zap-studio/fetch/errors\";\n *\n * try {\n * await $fetch(\"/users/404\");\n * } catch (error) {\n * if (error instanceof FetchError) {\n * console.error(error.status);\n * console.error(await error.response.text());\n * }\n * }\n */\nexport class FetchError extends Error {\n /**\n * HTTP status code from the failing response.\n */\n status: Response[\"status\"];\n /**\n * Full response object for additional inspection.\n */\n response: Response;\n\n /**\n * Creates a FetchError from a non-ok response.\n */\n constructor(message: string, response: Response) {\n super(message);\n this.name = \"FetchError\";\n this.status = response.status;\n this.response = response;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;CAIA;;;;CAKA,YAAY,SAAiB,UAAoB;EAC/C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS,SAAS;EACvB,KAAK,WAAW;CAClB;AACF"}
@@ -0,0 +1,222 @@
1
+ import { GLOBAL_DEFAULTS } from "./constants.js";
2
+ import { FetchError } from "./errors.js";
3
+ import { mergeHeaders } from "./headers.js";
4
+ import { normalizeRequest } from "./request.js";
5
+ import { resolveRequestUrl } from "./url.js";
6
+ import { isStandardSchema, standardValidate } from "@zap-studio/validation";
7
+ //#region src/_internal.ts
8
+ /**
9
+ * Normalizes request-level options into a final RequestInit payload and runtime flags.
10
+ *
11
+ * @param options - Request-level options.
12
+ * @param defaults - Client-level defaults.
13
+ * @returns Fully merged request init payload and effective runtime flags.
14
+ */
15
+ const prepareRequestInit = (options, defaults) => {
16
+ const { headers, json, searchParams, throwOnFetchError = defaults.throwOnFetchError, throwOnValidationError = defaults.throwOnValidationError, ...rest } = options;
17
+ const init = { ...rest };
18
+ const mergedHeaders = mergeHeaders(defaults.headers, headers);
19
+ if (mergedHeaders !== void 0) init.headers = mergedHeaders;
20
+ if (json !== void 0) {
21
+ if (init.body !== void 0 && init.body !== null) throw new TypeError("Cannot provide both `body` and `json`.");
22
+ init.body = JSON.stringify(json);
23
+ if (init.headers === void 0) init.headers = new Headers({ "Content-Type": "application/json" });
24
+ else {
25
+ const requestHeaders = new Headers(init.headers);
26
+ if (!requestHeaders.has("Content-Type")) requestHeaders.set("Content-Type", "application/json");
27
+ init.headers = requestHeaders;
28
+ }
29
+ }
30
+ return {
31
+ init,
32
+ searchParams,
33
+ throwOnFetchError,
34
+ throwOnValidationError
35
+ };
36
+ };
37
+ /**
38
+ * Internal fetch implementation used by both $fetch and createFetch.
39
+ *
40
+ * This function normalizes request input, resolves final URL + query params,
41
+ * executes `fetch`, optionally throws `FetchError`, and optionally validates
42
+ * JSON response payloads using Standard Schema.
43
+ *
44
+ * @param input - Request URL, path, or Request object.
45
+ * @param schema - Optional Standard Schema for response validation.
46
+ * @param options - Optional request options and package-specific flags.
47
+ * @param defaults - Effective client defaults.
48
+ * @returns Raw `Response` when no schema is provided; otherwise validated output.
49
+ * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
50
+ * @throws {ValidationError} When a schema is provided, validation returns issues, and
51
+ * `throwOnValidationError` is `true`.
52
+ * @throws {TypeError} When both `body` and `json` are provided, when JSON request
53
+ * serialization fails, when request construction fails, when headers/search params are
54
+ * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`
55
+ * implementation rejects network-level failures as `TypeError`.
56
+ * @throws {DOMException} When the runtime rejects an aborted request or response body read
57
+ * as an `AbortError` DOMException.
58
+ * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the
59
+ * response body.
60
+ * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.
61
+ */
62
+ const fetchInternal = async (input, schema, options, defaults) => {
63
+ const request = normalizeRequest(input, options);
64
+ const { init, searchParams, throwOnFetchError, throwOnValidationError } = prepareRequestInit(request.options, defaults);
65
+ const url = resolveRequestUrl(request.url, defaults, searchParams);
66
+ const response = request.request ? await fetch(new Request(url, request.request), init) : await fetch(url, init);
67
+ if (throwOnFetchError && !response.ok) throw new FetchError(`HTTP ${response.status}: ${response.statusText}`, response);
68
+ if (schema === void 0) return response;
69
+ const raw = await response.json();
70
+ if (throwOnValidationError) return await standardValidate(schema, raw, { throwOnError: true });
71
+ return await standardValidate(schema, raw, { throwOnError: false });
72
+ };
73
+ //#endregion
74
+ //#region src/_methods.ts
75
+ /**
76
+ * Method helper factories used to build verb-specific fetch functions.
77
+ *
78
+ * @module @zap-studio/fetch/_methods (private)
79
+ */
80
+ /**
81
+ * Creates an HTTP method helper bound to a fetch function.
82
+ *
83
+ * The returned function mirrors `$Fetch` overloads but forces the provided
84
+ * HTTP method (`GET`, `POST`, etc.) into request options.
85
+ *
86
+ * @param fetchFn - Fetch function to wrap.
87
+ * @param method - HTTP method to enforce.
88
+ * @returns Method-bound fetch function.
89
+ * @throws {unknown} Any error thrown or rejected by `fetchFn` when the returned method-bound
90
+ * fetch function is called.
91
+ *
92
+ * @example
93
+ * const get = createMethod($fetch, "GET");
94
+ * const user = await get("/users/1", UserSchema);
95
+ */
96
+ const createMethod = (fetchFn, method) => {
97
+ /**
98
+ * Method-bound `$Fetch` implementation.
99
+ *
100
+ * Resolves schema/option overloads and injects the configured HTTP method.
101
+ */
102
+ async function methodFetch(input, schemaOrOptions, optionsOrUndefined) {
103
+ if (isStandardSchema(schemaOrOptions)) {
104
+ if (optionsOrUndefined?.throwOnValidationError === false) return await fetchFn(input, schemaOrOptions, {
105
+ ...optionsOrUndefined,
106
+ method,
107
+ throwOnValidationError: false
108
+ });
109
+ const { throwOnValidationError, ...restOptions } = optionsOrUndefined ?? {};
110
+ if (throwOnValidationError === true) return await fetchFn(input, schemaOrOptions, {
111
+ ...restOptions,
112
+ method,
113
+ throwOnValidationError: true
114
+ });
115
+ return await fetchFn(input, schemaOrOptions, {
116
+ ...restOptions,
117
+ method
118
+ });
119
+ }
120
+ return await fetchFn(input, {
121
+ ...schemaOrOptions,
122
+ method
123
+ });
124
+ }
125
+ return methodFetch;
126
+ };
127
+ //#endregion
128
+ //#region src/fetch.ts
129
+ /**
130
+ * Typed fetch client: `$fetch`, `api` method shortcuts, and the `createFetch`
131
+ * instance factory.
132
+ *
133
+ * @module @zap-studio/fetch/fetch
134
+ */
135
+ async function $fetch(input, schemaOrOptions, optionsOrUndefined) {
136
+ const [schema, options] = isStandardSchema(schemaOrOptions) ? [schemaOrOptions, optionsOrUndefined] : [void 0, schemaOrOptions];
137
+ return await fetchInternal(input, schema, options, GLOBAL_DEFAULTS);
138
+ }
139
+ /**
140
+ * Convenience methods for common HTTP verbs.
141
+ *
142
+ * These methods always require a schema for validation.
143
+ * For raw responses without validation, use `$fetch` directly.
144
+ *
145
+ * Each method has the same throw behavior as {@link $fetch}.
146
+ *
147
+ * @example
148
+ * import { z } from "zod";
149
+ * import { api } from "@zap-studio/fetch";
150
+ *
151
+ * const PostSchema = z.object({
152
+ * id: z.number(),
153
+ * title: z.string(),
154
+ * content: z.string(),
155
+ * });
156
+ *
157
+ * async function fetchPost(postId: number) {
158
+ * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);
159
+ * return post; // post is typed as { id: number; title: string; content: string; }
160
+ * }
161
+ */
162
+ const api = {
163
+ delete: createMethod($fetch, "DELETE"),
164
+ get: createMethod($fetch, "GET"),
165
+ patch: createMethod($fetch, "PATCH"),
166
+ post: createMethod($fetch, "POST"),
167
+ put: createMethod($fetch, "PUT")
168
+ };
169
+ /**
170
+ * Creates a custom fetch instance with pre-configured defaults.
171
+ *
172
+ * Use this factory to create API clients with a base URL, default headers,
173
+ * and other shared configuration. Each instance is independent.
174
+ *
175
+ * The returned `$fetch` and `api` methods have the same throw behavior as the
176
+ * top-level {@link $fetch} export.
177
+ *
178
+ * @example
179
+ * import { z } from "zod";
180
+ * import { createFetch } from "@zap-studio/fetch";
181
+ *
182
+ * // Create a configured instance
183
+ * const { $fetch, api } = createFetch({
184
+ * baseURL: "https://api.example.com",
185
+ * headers: { "Authorization": "Bearer token" },
186
+ * });
187
+ *
188
+ * const UserSchema = z.object({ id: z.number(), name: z.string() });
189
+ *
190
+ * // Now use relative paths - baseURL is prepended automatically
191
+ * const user = await api.get("/users/1", UserSchema);
192
+ *
193
+ * // Or use $fetch directly
194
+ * const response = await $fetch("/users", UserSchema, { method: "POST", json: { name: "John" } });
195
+ */
196
+ const createFetch = (factoryOptions = {}) => {
197
+ const defaults = {
198
+ ...GLOBAL_DEFAULTS,
199
+ ...factoryOptions,
200
+ baseURL: factoryOptions.baseURL ?? GLOBAL_DEFAULTS.baseURL,
201
+ throwOnFetchError: factoryOptions.throwOnFetchError ?? GLOBAL_DEFAULTS.throwOnFetchError,
202
+ throwOnValidationError: factoryOptions.throwOnValidationError ?? GLOBAL_DEFAULTS.throwOnValidationError
203
+ };
204
+ async function customFetch(input, schemaOrOptions, optionsOrUndefined) {
205
+ const [schema, options] = isStandardSchema(schemaOrOptions) ? [schemaOrOptions, optionsOrUndefined] : [void 0, schemaOrOptions];
206
+ return await fetchInternal(input, schema, options, defaults);
207
+ }
208
+ return {
209
+ $fetch: customFetch,
210
+ api: {
211
+ delete: createMethod(customFetch, "DELETE"),
212
+ get: createMethod(customFetch, "GET"),
213
+ patch: createMethod(customFetch, "PATCH"),
214
+ post: createMethod(customFetch, "POST"),
215
+ put: createMethod(customFetch, "PUT")
216
+ }
217
+ };
218
+ };
219
+ //#endregion
220
+ export { api as n, createFetch as r, $fetch as t };
221
+
222
+ //# sourceMappingURL=fetch-D0IXjsUD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-D0IXjsUD.js","names":[],"sources":["../src/_internal.ts","../src/_methods.ts","../src/fetch.ts"],"sourcesContent":["/**\n * Internal request execution and option preparation utilities.\n *\n * @module @zap-studio/fetch/_internal (private)\n */\n\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\nimport { standardValidate } from \"@zap-studio/validation\";\n\nimport { FetchError } from \"./errors.js\";\nimport { mergeHeaders } from \"./headers.js\";\nimport { normalizeRequest } from \"./request.js\";\nimport type {\n ExtendedRequestInit,\n FetchDefaults,\n FetchInput,\n} from \"./types.js\";\nimport { resolveRequestUrl } from \"./url.js\";\n\n/**\n * Normalizes request-level options into a final RequestInit payload and runtime flags.\n *\n * @param options - Request-level options.\n * @param defaults - Client-level defaults.\n * @returns Fully merged request init payload and effective runtime flags.\n */\nconst prepareRequestInit = (\n options: ExtendedRequestInit,\n defaults: FetchDefaults\n): {\n init: RequestInit;\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined;\n throwOnFetchError: boolean;\n throwOnValidationError: boolean;\n} => {\n const {\n headers,\n json,\n searchParams,\n throwOnFetchError = defaults.throwOnFetchError,\n throwOnValidationError = defaults.throwOnValidationError,\n ...rest\n } = options;\n\n const init: RequestInit = { ...rest };\n const mergedHeaders = mergeHeaders(defaults.headers, headers);\n if (mergedHeaders !== undefined) {\n init.headers = mergedHeaders;\n }\n\n if (json !== undefined) {\n if (init.body !== undefined && init.body !== null) {\n throw new TypeError(\"Cannot provide both `body` and `json`.\");\n }\n\n init.body = JSON.stringify(json);\n if (init.headers === undefined) {\n init.headers = new Headers({ \"Content-Type\": \"application/json\" });\n } else {\n const requestHeaders = new Headers(init.headers);\n if (!requestHeaders.has(\"Content-Type\")) {\n requestHeaders.set(\"Content-Type\", \"application/json\");\n }\n init.headers = requestHeaders;\n }\n }\n\n return {\n init,\n searchParams,\n throwOnFetchError,\n throwOnValidationError,\n };\n};\n\n/**\n * Internal fetch implementation used by both $fetch and createFetch.\n *\n * This function normalizes request input, resolves final URL + query params,\n * executes `fetch`, optionally throws `FetchError`, and optionally validates\n * JSON response payloads using Standard Schema.\n *\n * @param input - Request URL, path, or Request object.\n * @param schema - Optional Standard Schema for response validation.\n * @param options - Optional request options and package-specific flags.\n * @param defaults - Effective client defaults.\n * @returns Raw `Response` when no schema is provided; otherwise validated output.\n * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.\n * @throws {ValidationError} When a schema is provided, validation returns issues, and\n * `throwOnValidationError` is `true`.\n * @throws {TypeError} When both `body` and `json` are provided, when JSON request\n * serialization fails, when request construction fails, when headers/search params are\n * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`\n * implementation rejects network-level failures as `TypeError`.\n * @throws {DOMException} When the runtime rejects an aborted request or response body read\n * as an `AbortError` DOMException.\n * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the\n * response body.\n * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.\n */\nexport const fetchInternal = async (\n input: FetchInput,\n schema: StandardSchemaV1 | undefined,\n options: ExtendedRequestInit | undefined,\n defaults: FetchDefaults\n): Promise<unknown> => {\n const request = normalizeRequest(input, options);\n const { init, searchParams, throwOnFetchError, throwOnValidationError } =\n prepareRequestInit(request.options, defaults);\n const url = resolveRequestUrl(request.url, defaults, searchParams);\n const response = request.request\n ? await fetch(new Request(url, request.request), init)\n : await fetch(url, init);\n\n if (throwOnFetchError && !response.ok) {\n throw new FetchError(\n `HTTP ${response.status}: ${response.statusText}`,\n response\n );\n }\n\n if (schema === undefined) {\n return response;\n }\n\n const raw: unknown = await response.json();\n if (throwOnValidationError) {\n return await standardValidate(schema, raw, { throwOnError: true });\n }\n return await standardValidate(schema, raw, { throwOnError: false });\n};\n","/**\n * Method helper factories used to build verb-specific fetch functions.\n *\n * @module @zap-studio/fetch/_methods (private)\n */\n\nimport { isStandardSchema } from \"@zap-studio/validation\";\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\n\nimport type { $Fetch, ExtendedRequestInit, FetchInput } from \"./types.js\";\n\n/**\n * Creates an HTTP method helper bound to a fetch function.\n *\n * The returned function mirrors `$Fetch` overloads but forces the provided\n * HTTP method (`GET`, `POST`, etc.) into request options.\n *\n * @param fetchFn - Fetch function to wrap.\n * @param method - HTTP method to enforce.\n * @returns Method-bound fetch function.\n * @throws {unknown} Any error thrown or rejected by `fetchFn` when the returned method-bound\n * fetch function is called.\n *\n * @example\n * const get = createMethod($fetch, \"GET\");\n * const user = await get(\"/users/1\", UserSchema);\n */\nexport const createMethod = (fetchFn: $Fetch, method: string): $Fetch => {\n function methodFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & {\n throwOnValidationError: false;\n }\n ): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\n function methodFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & {\n throwOnValidationError?: true;\n }\n ): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\n function methodFetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n ): Promise<Response>;\n\n /**\n * Method-bound `$Fetch` implementation.\n *\n * Resolves schema/option overloads and injects the configured HTTP method.\n */\n async function methodFetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n ): Promise<unknown> {\n if (isStandardSchema(schemaOrOptions)) {\n if (optionsOrUndefined?.throwOnValidationError === false) {\n return await fetchFn(input, schemaOrOptions, {\n ...optionsOrUndefined,\n method,\n throwOnValidationError: false,\n });\n }\n\n const { throwOnValidationError, ...restOptions } =\n optionsOrUndefined ?? {};\n\n if (throwOnValidationError === true) {\n return await fetchFn(input, schemaOrOptions, {\n ...restOptions,\n method,\n throwOnValidationError: true,\n });\n }\n\n return await fetchFn(input, schemaOrOptions, {\n ...restOptions,\n method,\n });\n }\n\n return await fetchFn(input, {\n ...schemaOrOptions,\n method,\n });\n }\n\n return methodFetch;\n};\n","/**\n * Typed fetch client: `$fetch`, `api` method shortcuts, and the `createFetch`\n * instance factory.\n *\n * @module @zap-studio/fetch/fetch\n */\n\nimport { isStandardSchema } from \"@zap-studio/validation\";\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\n\nimport { fetchInternal } from \"./_internal.js\";\nimport { createMethod } from \"./_methods.js\";\nimport { GLOBAL_DEFAULTS } from \"./constants.js\";\nimport type {\n $Fetch,\n ApiMethods,\n ExtendedRequestInit,\n FetchDefaults,\n FetchInput,\n} from \"./types.js\";\n\n/**\n * Type-safe fetch wrapper with Standard Schema validation.\n *\n * - When `throwOnValidationError: true`: validated data of type `TSchema`\n * - When `throwOnValidationError: false`: Standard Schema Result object `{ value?, issues? }`\n * - When `throwOnFetchError: true`: throws `FetchError` on non-ok responses\n *\n * If no schema is provided, returns the raw `Response` object.\n *\n * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.\n * @throws {ValidationError} When a schema is provided, validation returns issues, and\n * `throwOnValidationError` is `true`.\n * @throws {TypeError} When both `body` and `json` are provided, when JSON request\n * serialization fails, when request construction fails, when headers/search params are\n * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`\n * implementation rejects network-level failures as `TypeError`.\n * @throws {DOMException} When the runtime rejects an aborted request or response body read\n * as an `AbortError` DOMException.\n * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the\n * response body.\n * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.\n *\n * @example\n * import { z } from \"zod\";\n * import { $fetch } from \"@zap-studio/fetch\";\n *\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n *\n * // Basic usage (schema validation)\n * const user = await $fetch(\"/api/users/1\", UserSchema, { headers: { \"Authorization\": \"Bearer token\" } });\n * console.log(\"Validated user:\", user);\n *\n * // Raw usage (no schema validation and typed Response object)\n * const result = await $fetch(\"/api/data\", { method: \"POST\", body: JSON.stringify({ key: \"value\" }) });\n * const json = await result.json() as ResultType;\n * console.log(\"Raw response data:\", json);\n *\n * // Usage with validation errors returned instead of thrown\n * const result = await $fetch(\"/api/users/1\", UserSchema, { throwOnValidationError: false });\n *\n * if (result.issues) {\n * console.error(\"Validation errors:\", result.issues);\n * } else {\n * console.log(\"Validated user:\", result.value);\n * }\n */\nexport async function $fetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & { throwOnValidationError: false }\n): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\nexport async function $fetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & { throwOnValidationError?: true }\n): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\nexport async function $fetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n): Promise<Response>;\n\nexport async function $fetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n): Promise<unknown> {\n const [schema, options] = isStandardSchema(schemaOrOptions)\n ? [schemaOrOptions, optionsOrUndefined]\n : [undefined, schemaOrOptions];\n\n return await fetchInternal(input, schema, options, GLOBAL_DEFAULTS);\n}\n\n/**\n * Convenience methods for common HTTP verbs.\n *\n * These methods always require a schema for validation.\n * For raw responses without validation, use `$fetch` directly.\n *\n * Each method has the same throw behavior as {@link $fetch}.\n *\n * @example\n * import { z } from \"zod\";\n * import { api } from \"@zap-studio/fetch\";\n *\n * const PostSchema = z.object({\n * id: z.number(),\n * title: z.string(),\n * content: z.string(),\n * });\n *\n * async function fetchPost(postId: number) {\n * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);\n * return post; // post is typed as { id: number; title: string; content: string; }\n * }\n */\nexport const api: ApiMethods = {\n delete: createMethod($fetch, \"DELETE\"),\n get: createMethod($fetch, \"GET\"),\n patch: createMethod($fetch, \"PATCH\"),\n post: createMethod($fetch, \"POST\"),\n put: createMethod($fetch, \"PUT\"),\n};\n\n/**\n * Creates a custom fetch instance with pre-configured defaults.\n *\n * Use this factory to create API clients with a base URL, default headers,\n * and other shared configuration. Each instance is independent.\n *\n * The returned `$fetch` and `api` methods have the same throw behavior as the\n * top-level {@link $fetch} export.\n *\n * @example\n * import { z } from \"zod\";\n * import { createFetch } from \"@zap-studio/fetch\";\n *\n * // Create a configured instance\n * const { $fetch, api } = createFetch({\n * baseURL: \"https://api.example.com\",\n * headers: { \"Authorization\": \"Bearer token\" },\n * });\n *\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n *\n * // Now use relative paths - baseURL is prepended automatically\n * const user = await api.get(\"/users/1\", UserSchema);\n *\n * // Or use $fetch directly\n * const response = await $fetch(\"/users\", UserSchema, { method: \"POST\", json: { name: \"John\" } });\n */\nexport const createFetch = (\n factoryOptions: Partial<FetchDefaults> = {}\n): {\n $fetch: $Fetch;\n api: ApiMethods;\n} => {\n const defaults: FetchDefaults = {\n ...GLOBAL_DEFAULTS,\n ...factoryOptions,\n baseURL: factoryOptions.baseURL ?? GLOBAL_DEFAULTS.baseURL,\n throwOnFetchError:\n factoryOptions.throwOnFetchError ?? GLOBAL_DEFAULTS.throwOnFetchError,\n throwOnValidationError:\n factoryOptions.throwOnValidationError ??\n GLOBAL_DEFAULTS.throwOnValidationError,\n };\n\n async function customFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & { throwOnValidationError: false }\n ): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\n async function customFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & {\n throwOnValidationError?: true;\n }\n ): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\n async function customFetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n ): Promise<Response>;\n\n async function customFetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n ): Promise<unknown> {\n const [schema, options] = isStandardSchema(schemaOrOptions)\n ? [schemaOrOptions, optionsOrUndefined]\n : [undefined, schemaOrOptions];\n\n return await fetchInternal(input, schema, options, defaults);\n }\n\n const customApi = {\n delete: createMethod(customFetch, \"DELETE\"),\n get: createMethod(customFetch, \"GET\"),\n patch: createMethod(customFetch, \"PATCH\"),\n post: createMethod(customFetch, \"POST\"),\n put: createMethod(customFetch, \"PUT\"),\n };\n\n return {\n $fetch: customFetch,\n api: customApi,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAM,sBACJ,SACA,aAMG;CACH,MAAM,EACJ,SACA,MACA,cACA,oBAAoB,SAAS,mBAC7B,yBAAyB,SAAS,wBAClC,GAAG,SACD;CAEJ,MAAM,OAAoB,EAAE,GAAG,KAAK;CACpC,MAAM,gBAAgB,aAAa,SAAS,SAAS,OAAO;CAC5D,IAAI,kBAAkB,KAAA,GACpB,KAAK,UAAU;CAGjB,IAAI,SAAS,KAAA,GAAW;EACtB,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,SAAS,MAC3C,MAAM,IAAI,UAAU,wCAAwC;EAG9D,KAAK,OAAO,KAAK,UAAU,IAAI;EAC/B,IAAI,KAAK,YAAY,KAAA,GACnB,KAAK,UAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;OAC5D;GACL,MAAM,iBAAiB,IAAI,QAAQ,KAAK,OAAO;GAC/C,IAAI,CAAC,eAAe,IAAI,cAAc,GACpC,eAAe,IAAI,gBAAgB,kBAAkB;GAEvD,KAAK,UAAU;EACjB;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,gBAAgB,OAC3B,OACA,QACA,SACA,aACqB;CACrB,MAAM,UAAU,iBAAiB,OAAO,OAAO;CAC/C,MAAM,EAAE,MAAM,cAAc,mBAAmB,2BAC7C,mBAAmB,QAAQ,SAAS,QAAQ;CAC9C,MAAM,MAAM,kBAAkB,QAAQ,KAAK,UAAU,YAAY;CACjE,MAAM,WAAW,QAAQ,UACrB,MAAM,MAAM,IAAI,QAAQ,KAAK,QAAQ,OAAO,GAAG,IAAI,IACnD,MAAM,MAAM,KAAK,IAAI;CAEzB,IAAI,qBAAqB,CAAC,SAAS,IACjC,MAAM,IAAI,WACR,QAAQ,SAAS,OAAO,IAAI,SAAS,cACrC,QACF;CAGF,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,MAAM,MAAe,MAAM,SAAS,KAAK;CACzC,IAAI,wBACF,OAAO,MAAM,iBAAiB,QAAQ,KAAK,EAAE,cAAc,KAAK,CAAC;CAEnE,OAAO,MAAM,iBAAiB,QAAQ,KAAK,EAAE,cAAc,MAAM,CAAC;AACpE;;;;;;;;;;;;;;;;;;;;;;;;ACvGA,MAAa,gBAAgB,SAAiB,WAA2B;;;;;;CA2BvE,eAAe,YACb,OACA,iBACA,oBACkB;EAClB,IAAI,iBAAiB,eAAe,GAAG;GACrC,IAAI,oBAAoB,2BAA2B,OACjD,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;IACA,wBAAwB;GAC1B,CAAC;GAGH,MAAM,EAAE,wBAAwB,GAAG,gBACjC,sBAAsB,CAAC;GAEzB,IAAI,2BAA2B,MAC7B,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;IACA,wBAAwB;GAC1B,CAAC;GAGH,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;GACF,CAAC;EACH;EAEA,OAAO,MAAM,QAAQ,OAAO;GAC1B,GAAG;GACH;EACF,CAAC;CACH;CAEA,OAAO;AACT;;;;;;;;;ACRA,eAAsB,OACpB,OACA,iBACA,oBACkB;CAClB,MAAM,CAAC,QAAQ,WAAW,iBAAiB,eAAe,IACtD,CAAC,iBAAiB,kBAAkB,IACpC,CAAC,KAAA,GAAW,eAAe;CAE/B,OAAO,MAAM,cAAc,OAAO,QAAQ,SAAS,eAAe;AACpE;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,MAAkB;CAC7B,QAAQ,aAAa,QAAQ,QAAQ;CACrC,KAAK,aAAa,QAAQ,KAAK;CAC/B,OAAO,aAAa,QAAQ,OAAO;CACnC,MAAM,aAAa,QAAQ,MAAM;CACjC,KAAK,aAAa,QAAQ,KAAK;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,eACX,iBAAyC,CAAC,MAIvC;CACH,MAAM,WAA0B;EAC9B,GAAG;EACH,GAAG;EACH,SAAS,eAAe,WAAW,gBAAgB;EACnD,mBACE,eAAe,qBAAqB,gBAAgB;EACtD,wBACE,eAAe,0BACf,gBAAgB;CACpB;CAqBA,eAAe,YACb,OACA,iBACA,oBACkB;EAClB,MAAM,CAAC,QAAQ,WAAW,iBAAiB,eAAe,IACtD,CAAC,iBAAiB,kBAAkB,IACpC,CAAC,KAAA,GAAW,eAAe;EAE/B,OAAO,MAAM,cAAc,OAAO,QAAQ,SAAS,QAAQ;CAC7D;CAUA,OAAO;EACL,QAAQ;EACR,KAAK;GATL,QAAQ,aAAa,aAAa,QAAQ;GAC1C,KAAK,aAAa,aAAa,KAAK;GACpC,OAAO,aAAa,aAAa,OAAO;GACxC,MAAM,aAAa,aAAa,MAAM;GACtC,KAAK,aAAa,aAAa,KAAK;EAKvB;CACf;AACF"}
@@ -1,6 +1,6 @@
1
- import { $Fetch, ApiMethods, ExtendedRequestInit, FetchDefaults, FetchInput } from "./types.mjs";
1
+ import { $Fetch, ApiMethods, ExtendedRequestInit, FetchDefaults, FetchInput } from "./types.js";
2
2
  import { StandardSchemaV1 } from "@zap-studio/validation";
3
- //#region src/index.d.ts
3
+ //#region src/fetch.d.ts
4
4
  /**
5
5
  * Type-safe fetch wrapper with Standard Schema validation.
6
6
  *
@@ -111,4 +111,4 @@ declare const createFetch: (factoryOptions?: Partial<FetchDefaults>) => {
111
111
  };
112
112
  //#endregion
113
113
  export { $fetch, api, createFetch };
114
- //# sourceMappingURL=index.d.mts.map
114
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","names":[],"sources":["../src/fetch.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmEsB,OAAO,gBAAgB,kBAC3C,OAAO,YACP,QAAQ,SACR,SAAS;EAAwB;IAChC,QAAQ,iBAAiB,OAAO,iBAAiB,YAAY;iBAE1C,OAAO,gBAAgB,kBAC3C,OAAO,YACP,QAAQ,SACR,UAAU;EAAwB;IACjC,QAAQ,iBAAiB,YAAY;iBAElB,OACpB,OAAO,YACP,UAAU,sBACT,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;cAqCE,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmCL,cACX,iBAAgB,QAAQ;EAExB,QAAQ;EACR,KAAK"}
package/dist/fetch.js ADDED
@@ -0,0 +1,3 @@
1
+ import "./constants.js";
2
+ import { n as api, r as createFetch, t as $fetch } from "./fetch-D0IXjsUD.js";
3
+ export { $fetch, api, createFetch };
@@ -23,4 +23,4 @@
23
23
  declare const mergeHeaders: (base?: HeadersInit, override?: HeadersInit) => Headers | undefined;
24
24
  //#endregion
25
25
  export { mergeHeaders };
26
- //# sourceMappingURL=headers.d.mts.map
26
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","names":[],"sources":["../src/headers.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;cAsBa,eACX,OAAO,aACP,WAAW,gBACV"}
@@ -31,4 +31,4 @@ const mergeHeaders = (base, override) => {
31
31
  //#endregion
32
32
  export { mergeHeaders };
33
33
 
34
- //# sourceMappingURL=headers.mjs.map
34
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","names":[],"sources":["../src/headers.ts"],"sourcesContent":["/**\n * Header utility helpers for request normalization and merging.\n *\n * @module @zap-studio/fetch/headers\n */\n\n/**\n * Merges two HeadersInit objects, with the second one taking precedence.\n *\n * @param base - Base/default headers.\n * @param override - Request-level override headers.\n * @returns A merged `Headers` object, or `undefined` when both inputs are empty.\n * @throws {TypeError} When either header input contains invalid header names or values.\n *\n * @example\n * const headers = mergeHeaders(\n * { Authorization: \"Bearer token\" },\n * { \"X-Trace-Id\": \"abc\" },\n * );\n *\n * console.log(headers?.get(\"Authorization\")); // Bearer token\n */\nexport const mergeHeaders = (\n base?: HeadersInit,\n override?: HeadersInit\n): Headers | undefined => {\n if (base === undefined && override === undefined) {\n return undefined;\n }\n\n if (base === undefined) {\n return new Headers(override);\n }\n\n if (override === undefined) {\n return new Headers(base);\n }\n\n const merged = new Headers(base);\n for (const [key, value] of new Headers(override).entries()) {\n merged.set(key, value);\n }\n return merged;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,gBACX,MACA,aACwB;CACxB,IAAI,SAAS,KAAA,KAAa,aAAa,KAAA,GACrC;CAGF,IAAI,SAAS,KAAA,GACX,OAAO,IAAI,QAAQ,QAAQ;CAG7B,IAAI,aAAa,KAAA,GACf,OAAO,IAAI,QAAQ,IAAI;CAGzB,MAAM,SAAS,IAAI,QAAQ,IAAI;CAC/B,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,QAAQ,QAAQ,CAAC,CAAC,QAAQ,GACvD,OAAO,IAAI,KAAK,KAAK;CAEvB,OAAO;AACT"}
@@ -0,0 +1,8 @@
1
+ import { $Fetch, ApiMethods, ExtendedRequestInit, FetchDefaults, FetchInput } from "./types.js";
2
+ import { GLOBAL_DEFAULTS } from "./constants.js";
3
+ import { FetchError } from "./errors.js";
4
+ import { $fetch, api, createFetch } from "./fetch.js";
5
+ import { mergeHeaders } from "./headers.js";
6
+ import { NormalizedRequest, normalizeRequest } from "./request.js";
7
+ import { resolveRequestUrl } from "./url.js";
8
+ export { type $Fetch, $fetch, type ApiMethods, type ExtendedRequestInit, type FetchDefaults, FetchError, type FetchInput, GLOBAL_DEFAULTS, type NormalizedRequest, api, createFetch, mergeHeaders, normalizeRequest, resolveRequestUrl };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { GLOBAL_DEFAULTS } from "./constants.js";
2
+ import { FetchError } from "./errors.js";
3
+ import { mergeHeaders } from "./headers.js";
4
+ import { normalizeRequest } from "./request.js";
5
+ import { resolveRequestUrl } from "./url.js";
6
+ import { n as api, r as createFetch, t as $fetch } from "./fetch-D0IXjsUD.js";
7
+ export { $fetch, FetchError, GLOBAL_DEFAULTS, api, createFetch, mergeHeaders, normalizeRequest, resolveRequestUrl };
@@ -1,4 +1,4 @@
1
- import { ExtendedRequestInit, FetchInput } from "./types.mjs";
1
+ import { ExtendedRequestInit, FetchInput } from "./types.js";
2
2
  //#region src/request.d.ts
3
3
  /**
4
4
  * Normalized representation used by internal request execution.
@@ -27,4 +27,4 @@ interface NormalizedRequest {
27
27
  declare const normalizeRequest: (input: FetchInput, options?: ExtendedRequestInit) => NormalizedRequest;
28
28
  //#endregion
29
29
  export { NormalizedRequest, normalizeRequest };
30
- //# sourceMappingURL=request.d.mts.map
30
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","names":[],"sources":["../src/request.ts"],"mappings":";;;;;;;;;UAkBiB;EACf;EACA,UAAU;EACV,SAAS;;;;;;;;;;;;;;cAeE,mBACX,OAAO,YACP,UAAU,wBACT"}
@@ -1,4 +1,4 @@
1
- import { mergeHeaders } from "./headers.mjs";
1
+ import { mergeHeaders } from "./headers.js";
2
2
  //#region src/request.ts
3
3
  /**
4
4
  * Request normalization helpers for fetch `input` values.
@@ -40,4 +40,4 @@ const normalizeRequest = (input, options) => {
40
40
  //#endregion
41
41
  export { normalizeRequest };
42
42
 
43
- //# sourceMappingURL=request.mjs.map
43
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","names":[],"sources":["../src/request.ts"],"sourcesContent":["/**\n * Request normalization helpers for fetch `input` values.\n *\n * @module @zap-studio/fetch/request\n */\n\nimport { mergeHeaders } from \"./headers.js\";\nimport type { ExtendedRequestInit, FetchInput } from \"./types.js\";\n\nconst EMPTY_OPTIONS = {} as ExtendedRequestInit;\n\n/**\n * Normalized representation used by internal request execution.\n *\n * - `url`: resolved string URL from the input (string or `URL`; `Request` uses `request.url`)\n * - `request`: original Request clone when input is a Request\n * - `options`: normalized request options merged with Request headers\n */\nexport interface NormalizedRequest {\n url: string;\n request?: Request;\n options: ExtendedRequestInit;\n}\n\n/**\n * Normalizes fetch `input` and request-level options into a consistent internal shape.\n *\n * @param input - Request URL/path or Request instance.\n * @param options - Optional request options.\n * @returns A normalized request structure for internal processing.\n * @throws {TypeError} When cloning a `Request` fails or the merged headers are invalid.\n *\n * @example\n * const normalized = normalizeRequest(\"/users\", { method: \"GET\" });\n * console.log(normalized.url); // \"/users\"\n */\nexport const normalizeRequest = (\n input: FetchInput,\n options?: ExtendedRequestInit\n): NormalizedRequest => {\n if (!(input instanceof Request)) {\n const url = input instanceof URL ? input.href : input;\n return {\n options: options ?? EMPTY_OPTIONS,\n url,\n };\n }\n\n const request = new Request(input);\n const { headers, ...rest } = options ?? {};\n const mergedHeaders = mergeHeaders(request.headers, headers);\n const normalizedOptions = { ...rest } as ExtendedRequestInit;\n\n if (mergedHeaders !== undefined) {\n normalizedOptions.headers = mergedHeaders;\n }\n\n return {\n options: normalizedOptions,\n request,\n url: request.url,\n };\n};\n"],"mappings":";;;;;;;AASA,MAAM,gBAAgB,CAAC;;;;;;;;;;;;;AA2BvB,MAAa,oBACX,OACA,YACsB;CACtB,IAAI,EAAE,iBAAiB,UAAU;EAC/B,MAAM,MAAM,iBAAiB,MAAM,MAAM,OAAO;EAChD,OAAO;GACL,SAAS,WAAW;GACpB;EACF;CACF;CAEA,MAAM,UAAU,IAAI,QAAQ,KAAK;CACjC,MAAM,EAAE,SAAS,GAAG,SAAS,WAAW,CAAC;CACzC,MAAM,gBAAgB,aAAa,QAAQ,SAAS,OAAO;CAC3D,MAAM,oBAAoB,EAAE,GAAG,KAAK;CAEpC,IAAI,kBAAkB,KAAA,GACpB,kBAAkB,UAAU;CAG9B,OAAO;EACL,SAAS;EACT;EACA,KAAK,QAAQ;CACf;AACF"}
@@ -165,4 +165,4 @@ interface ApiMethods {
165
165
  }
166
166
  //#endregion
167
167
  export { $Fetch, ApiMethods, ExtendedRequestInit, FetchDefaults, FetchInput };
168
- //# sourceMappingURL=types.d.mts.map
168
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;KAWY,aAAa,kBAAkB;KAEtC,uBAAuB,6BAA6B;KAEpD,kBAAkB;EACrB;;KAGG,eAAe,KAAK;;;;;EAKvB;EACA;;UAGQ;;;;;EAKR,eAAe;;;;;EAKf;;;;;EAKA;;;;;;;;;;;;KAaU,uBAAuB,kBAAkB,gBACnD;;;;;;;;;;;UAYe;;;;;EAKf;;;;;EAKA,UAAU;;;;;EAKV,eAAe;;;;;EAKf;;;;;EAKA;;;;;UAMe;;;;;;;;;;;;;;;;GAgBd,gBAAgB,kBACf,OAAO,YACP,QAAQ,SACR,SAAS;IAAwB;MAChC,QAAQ,iBAAiB,OAAO,iBAAiB,YAAY;;;;;;;;;;;;;;;;;GAkB/D,gBAAgB,kBACf,OAAO,YACP,QAAQ,SACR,UAAU;IACR;MAED,QAAQ,iBAAiB,YAAY;;;;;;;;;;;;GAavC,OAAO,YAAY,UAAU,sBAAsB,QAAQ;;;;;;;;UAS7C;;;;EAIf,QAAQ;;;;EAIR,KAAK;;;;EAIL,OAAO;;;;EAIP,MAAM;;;;EAIN,KAAK"}
package/dist/types.js ADDED
File without changes
@@ -1,4 +1,4 @@
1
- import { ExtendedRequestInit, FetchDefaults } from "./types.mjs";
1
+ import { ExtendedRequestInit, FetchDefaults } from "./types.js";
2
2
  //#region src/url.d.ts
3
3
  /**
4
4
  * Resolves final request URL by applying baseURL and layered search params.
@@ -23,4 +23,4 @@ import { ExtendedRequestInit, FetchDefaults } from "./types.mjs";
23
23
  declare const resolveRequestUrl: (resourceUrl: string, defaults: FetchDefaults, searchParams?: ExtendedRequestInit["searchParams"]) => string;
24
24
  //#endregion
25
25
  export { resolveRequestUrl };
26
- //# sourceMappingURL=url.d.mts.map
26
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","names":[],"sources":["../src/url.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;cAmFa,oBACX,qBACA,UAAU,eACV,eAAe"}
@@ -57,4 +57,4 @@ const resolveRequestUrl = (resourceUrl, defaults, searchParams) => {
57
57
  //#endregion
58
58
  export { resolveRequestUrl };
59
59
 
60
- //# sourceMappingURL=url.mjs.map
60
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","names":[],"sources":["../src/url.ts"],"sourcesContent":["/**\n * URL resolution and query merging utilities.\n *\n * @module @zap-studio/fetch/url\n */\n\nimport type { ExtendedRequestInit, FetchDefaults } from \"./types.js\";\n\n/**\n * Copies search params into target, overriding duplicate keys.\n */\nconst mergeSearchParams = (\n target: URLSearchParams,\n source: ExtendedRequestInit[\"searchParams\"] | undefined\n): void => {\n for (const [key, value] of new URLSearchParams(source)) {\n target.set(key, value);\n }\n};\n\n/**\n * Ensures a URL has a trailing slash for relative URL resolution.\n */\nconst ensureTrailingSlash = (url: string): string =>\n url.endsWith(\"/\") ? url : `${url}/`;\n\n/**\n * Resolves search params by applying default params, URL params, then request params.\n */\nconst resolveSearchParams = (\n url: string,\n defaultSearchParams: FetchDefaults[\"searchParams\"] | undefined,\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined\n): string => {\n if (defaultSearchParams === undefined && searchParams === undefined) {\n return url;\n }\n\n const hashIndex = url.indexOf(\"#\");\n const hasFragment = hashIndex !== -1;\n const urlWithoutHash = hasFragment ? url.slice(0, hashIndex) : url;\n const hash = hasFragment ? url.slice(hashIndex + 1) : \"\";\n const queryIndex = urlWithoutHash.indexOf(\"?\");\n const pathname =\n queryIndex === -1 ? urlWithoutHash : urlWithoutHash.slice(0, queryIndex);\n const urlSearchParams =\n queryIndex === -1 ? undefined : urlWithoutHash.slice(queryIndex + 1);\n const resolvedSearchParams = new URLSearchParams();\n\n mergeSearchParams(resolvedSearchParams, defaultSearchParams);\n mergeSearchParams(resolvedSearchParams, urlSearchParams);\n mergeSearchParams(resolvedSearchParams, searchParams);\n\n const resolvedSearch = resolvedSearchParams.toString();\n const fragmentSuffix = hasFragment ? `#${hash}` : \"\";\n\n if (resolvedSearch.length === 0) {\n return `${pathname}${fragmentSuffix}`;\n }\n\n return `${pathname}?${resolvedSearch}${fragmentSuffix}`;\n};\n\n/**\n * Resolves final request URL by applying baseURL and layered search params.\n *\n * Search param precedence:\n * 1. `defaults.searchParams`\n * 2. search params already present in `resourceUrl`\n * 3. per-request `searchParams`\n *\n * @example\n * const finalUrl = resolveRequestUrl(\n * \"/users?page=2\",\n * { ...defaults, baseURL: \"https://api.example.com\", searchParams: { locale: \"en\" } },\n * { page: \"3\" },\n * );\n * // https://api.example.com/users?locale=en&page=3\n *\n * @throws {TypeError} When `baseURL` and `resourceUrl` cannot be resolved by\n * `URL`, or when default/per-request search params cannot be converted by\n * `URLSearchParams`.\n */\nexport const resolveRequestUrl = (\n resourceUrl: string,\n defaults: FetchDefaults,\n searchParams?: ExtendedRequestInit[\"searchParams\"]\n): string => {\n const url = defaults.baseURL\n ? new URL(resourceUrl, ensureTrailingSlash(defaults.baseURL)).toString()\n : resourceUrl;\n\n return resolveSearchParams(url, defaults.searchParams, searchParams);\n};\n"],"mappings":";;;;AAWA,MAAM,qBACJ,QACA,WACS;CACT,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,gBAAgB,MAAM,GACnD,OAAO,IAAI,KAAK,KAAK;AAEzB;;;;AAKA,MAAM,uBAAuB,QAC3B,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,IAAI;;;;AAKnC,MAAM,uBACJ,KACA,qBACA,iBACW;CACX,IAAI,wBAAwB,KAAA,KAAa,iBAAiB,KAAA,GACxD,OAAO;CAGT,MAAM,YAAY,IAAI,QAAQ,GAAG;CACjC,MAAM,cAAc,cAAc;CAClC,MAAM,iBAAiB,cAAc,IAAI,MAAM,GAAG,SAAS,IAAI;CAC/D,MAAM,OAAO,cAAc,IAAI,MAAM,YAAY,CAAC,IAAI;CACtD,MAAM,aAAa,eAAe,QAAQ,GAAG;CAC7C,MAAM,WACJ,eAAe,KAAK,iBAAiB,eAAe,MAAM,GAAG,UAAU;CACzE,MAAM,kBACJ,eAAe,KAAK,KAAA,IAAY,eAAe,MAAM,aAAa,CAAC;CACrE,MAAM,uBAAuB,IAAI,gBAAgB;CAEjD,kBAAkB,sBAAsB,mBAAmB;CAC3D,kBAAkB,sBAAsB,eAAe;CACvD,kBAAkB,sBAAsB,YAAY;CAEpD,MAAM,iBAAiB,qBAAqB,SAAS;CACrD,MAAM,iBAAiB,cAAc,IAAI,SAAS;CAElD,IAAI,eAAe,WAAW,GAC5B,OAAO,GAAG,WAAW;CAGvB,OAAO,GAAG,SAAS,GAAG,iBAAiB;AACzC;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,qBACX,aACA,UACA,iBACW;CACX,MAAM,MAAM,SAAS,UACjB,IAAI,IAAI,aAAa,oBAAoB,SAAS,OAAO,CAAC,CAAC,CAAC,SAAS,IACrE;CAEJ,OAAO,oBAAoB,KAAK,SAAS,cAAc,YAAY;AACrE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-studio/fetch",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "private": false,
5
5
  "description": "A type-safe fetch wrapper for HTTP requests with runtime schema validation.",
6
6
  "keywords": [
@@ -35,42 +35,34 @@
35
35
  ],
36
36
  "type": "module",
37
37
  "sideEffects": false,
38
- "types": "./dist/index.d.mts",
38
+ "types": "./dist/index.d.ts",
39
39
  "exports": {
40
- ".": "./dist/index.mjs",
41
- "./constants": "./dist/constants.mjs",
42
- "./errors": "./dist/errors.mjs",
43
- "./headers": "./dist/headers.mjs",
44
- "./internal": "./dist/internal.mjs",
45
- "./methods": "./dist/methods.mjs",
46
- "./request": "./dist/request.mjs",
47
- "./types": "./dist/types.mjs",
48
- "./url": "./dist/url.mjs",
40
+ ".": "./dist/index.js",
41
+ "./constants": "./dist/constants.js",
42
+ "./errors": "./dist/errors.js",
43
+ "./fetch": "./dist/fetch.js",
44
+ "./headers": "./dist/headers.js",
45
+ "./request": "./dist/request.js",
46
+ "./types": "./dist/types.js",
47
+ "./url": "./dist/url.js",
49
48
  "./package.json": "./package.json"
50
49
  },
51
50
  "publishConfig": {
52
51
  "access": "public"
53
52
  },
54
53
  "dependencies": {
55
- "@zap-studio/validation": "0.3.5"
54
+ "@zap-studio/validation": "workspace:*"
56
55
  },
57
56
  "devDependencies": {
58
- "@better-fetch/fetch": "^1.3.1",
59
- "arktype": "^2.2.3",
60
- "axios": "^1.18.1",
61
- "ky": "^2.0.2",
62
- "ofetch": "^1.5.1",
63
- "tsdown": "^0.22.4",
64
- "typescript": "^7.0.2",
65
- "valibot": "^1.4.2",
66
- "vitest": "^4.1.10",
67
- "zod": "^4.4.3",
68
- "@zap-studio/typescript": "0.0.0"
57
+ "@zap-studio/typescript": "workspace:*",
58
+ "arktype": "catalog:",
59
+ "tsdown": "catalog:",
60
+ "typescript": "catalog:",
61
+ "valibot": "catalog:",
62
+ "vitest": "catalog:",
63
+ "zod": "catalog:"
69
64
  },
70
65
  "engines": {
71
66
  "node": ">=18.0.0"
72
- },
73
- "scripts": {
74
- "build": "tsdown --config ./tsdown.config.ts"
75
67
  }
76
- }
68
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.mts","names":[],"sources":["../src/constants.ts"],"mappings":";;;;;;;;;;;;;cAmBa,iBAAiB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.mjs","names":[],"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Shared defaults and constants for fetch behavior.\n *\n * @module @zap-studio/fetch/constants\n */\n\nimport type { FetchDefaults } from \"./types.js\";\n\n/**\n * Default options for the global $fetch\n *\n * These defaults are used by the top-level `$fetch` export.\n * Use `createFetch(...)` when you need per-client defaults.\n *\n * @example\n * import { GLOBAL_DEFAULTS } from \"@zap-studio/fetch/constants\";\n *\n * console.log(GLOBAL_DEFAULTS.throwOnFetchError); // true\n */\nexport const GLOBAL_DEFAULTS: FetchDefaults = {\n baseURL: \"\",\n throwOnFetchError: true,\n throwOnValidationError: true,\n};\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAa,kBAAiC;CAC5C,SAAS;CACT,mBAAmB;CACnB,wBAAwB;AAC1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAwBa,mBAAmB;;;;EAI9B,QAAQ;;;;EAIR,UAAU;;;;EAKV,YAAY,iBAAiB,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Custom error types used by the fetch package.\n *\n * @module @zap-studio/fetch/errors\n */\n\n/**\n * Error thrown for HTTP errors (non-2xx responses)\n *\n * Includes the failing `Response` object and status code for\n * downstream error handling and response inspection.\n *\n * @example\n * import { FetchError } from \"@zap-studio/fetch/errors\";\n *\n * try {\n * await $fetch(\"/users/404\");\n * } catch (error) {\n * if (error instanceof FetchError) {\n * console.error(error.status);\n * console.error(await error.response.text());\n * }\n * }\n */\nexport class FetchError extends Error {\n /**\n * HTTP status code from the failing response.\n */\n status: Response[\"status\"];\n /**\n * Full response object for additional inspection.\n */\n response: Response;\n\n /**\n * Creates a FetchError from a non-ok response.\n */\n constructor(message: string, response: Response) {\n super(message);\n this.name = \"FetchError\";\n this.status = response.status;\n this.response = response;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;CAIA;;;;CAKA,YAAY,SAAiB,UAAoB;EAC/C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS,SAAS;EACvB,KAAK,WAAW;CAClB;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"headers.d.mts","names":[],"sources":["../src/headers.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;cAsBa,eACX,OAAO,aACP,WAAW,gBACV"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"headers.mjs","names":[],"sources":["../src/headers.ts"],"sourcesContent":["/**\n * Header utility helpers for request normalization and merging.\n *\n * @module @zap-studio/fetch/headers\n */\n\n/**\n * Merges two HeadersInit objects, with the second one taking precedence.\n *\n * @param base - Base/default headers.\n * @param override - Request-level override headers.\n * @returns A merged `Headers` object, or `undefined` when both inputs are empty.\n * @throws {TypeError} When either header input contains invalid header names or values.\n *\n * @example\n * const headers = mergeHeaders(\n * { Authorization: \"Bearer token\" },\n * { \"X-Trace-Id\": \"abc\" },\n * );\n *\n * console.log(headers?.get(\"Authorization\")); // Bearer token\n */\nexport const mergeHeaders = (\n base?: HeadersInit,\n override?: HeadersInit\n): Headers | undefined => {\n if (base === undefined && override === undefined) {\n return undefined;\n }\n\n if (base === undefined) {\n return new Headers(override);\n }\n\n if (override === undefined) {\n return new Headers(base);\n }\n\n const merged = new Headers(base);\n for (const [key, value] of new Headers(override).entries()) {\n merged.set(key, value);\n }\n return merged;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,gBACX,MACA,aACwB;CACxB,IAAI,SAAS,KAAA,KAAa,aAAa,KAAA,GACrC;CAGF,IAAI,SAAS,KAAA,GACX,OAAO,IAAI,QAAQ,QAAQ;CAG7B,IAAI,aAAa,KAAA,GACf,OAAO,IAAI,QAAQ,IAAI;CAGzB,MAAM,SAAS,IAAI,QAAQ,IAAI;CAC/B,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,QAAQ,QAAQ,CAAC,CAAC,QAAQ,GACvD,OAAO,IAAI,KAAK,KAAK;CAEvB,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuEsB,OAAO,gBAAgB,kBAC3C,OAAO,YACP,QAAQ,SACR,SAAS;EAAwB;IAChC,QAAQ,iBAAiB,OAAO,iBAAiB,YAAY;iBAE1C,OAAO,gBAAgB,kBAC3C,OAAO,YACP,QAAQ,SACR,UAAU;EAAwB;IACjC,QAAQ,iBAAiB,YAAY;iBAElB,OACpB,OAAO,YACP,UAAU,sBACT,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;cAqCE,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmCL,cACX,iBAAgB,QAAQ;EAExB,QAAQ;EACR,KAAK"}
package/dist/index.mjs DELETED
@@ -1,103 +0,0 @@
1
- import { GLOBAL_DEFAULTS } from "./constants.mjs";
2
- import { fetchInternal } from "./internal.mjs";
3
- import { createMethod } from "./methods.mjs";
4
- import { isStandardSchema } from "@zap-studio/validation";
5
- //#region src/index.ts
6
- /**
7
- * Public entrypoint for the fetch package.
8
- *
9
- * Exposes:
10
- * - `$fetch` low-level typed fetch function
11
- * - `api` method shortcuts
12
- * - `createFetch` instance factory
13
- *
14
- * @module @zap-studio/fetch
15
- */
16
- async function $fetch(input, schemaOrOptions, optionsOrUndefined) {
17
- const [schema, options] = isStandardSchema(schemaOrOptions) ? [schemaOrOptions, optionsOrUndefined] : [void 0, schemaOrOptions];
18
- return await fetchInternal(input, schema, options, GLOBAL_DEFAULTS);
19
- }
20
- /**
21
- * Convenience methods for common HTTP verbs.
22
- *
23
- * These methods always require a schema for validation.
24
- * For raw responses without validation, use `$fetch` directly.
25
- *
26
- * Each method has the same throw behavior as {@link $fetch}.
27
- *
28
- * @example
29
- * import { z } from "zod";
30
- * import { api } from "@zap-studio/fetch";
31
- *
32
- * const PostSchema = z.object({
33
- * id: z.number(),
34
- * title: z.string(),
35
- * content: z.string(),
36
- * });
37
- *
38
- * async function fetchPost(postId: number) {
39
- * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);
40
- * return post; // post is typed as { id: number; title: string; content: string; }
41
- * }
42
- */
43
- const api = {
44
- delete: createMethod($fetch, "DELETE"),
45
- get: createMethod($fetch, "GET"),
46
- patch: createMethod($fetch, "PATCH"),
47
- post: createMethod($fetch, "POST"),
48
- put: createMethod($fetch, "PUT")
49
- };
50
- /**
51
- * Creates a custom fetch instance with pre-configured defaults.
52
- *
53
- * Use this factory to create API clients with a base URL, default headers,
54
- * and other shared configuration. Each instance is independent.
55
- *
56
- * The returned `$fetch` and `api` methods have the same throw behavior as the
57
- * top-level {@link $fetch} export.
58
- *
59
- * @example
60
- * import { z } from "zod";
61
- * import { createFetch } from "@zap-studio/fetch";
62
- *
63
- * // Create a configured instance
64
- * const { $fetch, api } = createFetch({
65
- * baseURL: "https://api.example.com",
66
- * headers: { "Authorization": "Bearer token" },
67
- * });
68
- *
69
- * const UserSchema = z.object({ id: z.number(), name: z.string() });
70
- *
71
- * // Now use relative paths - baseURL is prepended automatically
72
- * const user = await api.get("/users/1", UserSchema);
73
- *
74
- * // Or use $fetch directly
75
- * const response = await $fetch("/users", UserSchema, { method: "POST", json: { name: "John" } });
76
- */
77
- const createFetch = (factoryOptions = {}) => {
78
- const defaults = {
79
- ...GLOBAL_DEFAULTS,
80
- ...factoryOptions,
81
- baseURL: factoryOptions.baseURL ?? GLOBAL_DEFAULTS.baseURL,
82
- throwOnFetchError: factoryOptions.throwOnFetchError ?? GLOBAL_DEFAULTS.throwOnFetchError,
83
- throwOnValidationError: factoryOptions.throwOnValidationError ?? GLOBAL_DEFAULTS.throwOnValidationError
84
- };
85
- async function customFetch(input, schemaOrOptions, optionsOrUndefined) {
86
- const [schema, options] = isStandardSchema(schemaOrOptions) ? [schemaOrOptions, optionsOrUndefined] : [void 0, schemaOrOptions];
87
- return await fetchInternal(input, schema, options, defaults);
88
- }
89
- return {
90
- $fetch: customFetch,
91
- api: {
92
- delete: createMethod(customFetch, "DELETE"),
93
- get: createMethod(customFetch, "GET"),
94
- patch: createMethod(customFetch, "PATCH"),
95
- post: createMethod(customFetch, "POST"),
96
- put: createMethod(customFetch, "PUT")
97
- }
98
- };
99
- };
100
- //#endregion
101
- export { $fetch, api, createFetch };
102
-
103
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Public entrypoint for the fetch package.\n *\n * Exposes:\n * - `$fetch` low-level typed fetch function\n * - `api` method shortcuts\n * - `createFetch` instance factory\n *\n * @module @zap-studio/fetch\n */\n\nimport { isStandardSchema } from \"@zap-studio/validation\";\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\n\nimport { GLOBAL_DEFAULTS } from \"./constants.js\";\nimport { fetchInternal } from \"./internal.js\";\nimport { createMethod } from \"./methods.js\";\nimport type {\n $Fetch,\n ApiMethods,\n ExtendedRequestInit,\n FetchDefaults,\n FetchInput,\n} from \"./types.js\";\n\n/**\n * Type-safe fetch wrapper with Standard Schema validation.\n *\n * - When `throwOnValidationError: true`: validated data of type `TSchema`\n * - When `throwOnValidationError: false`: Standard Schema Result object `{ value?, issues? }`\n * - When `throwOnFetchError: true`: throws `FetchError` on non-ok responses\n *\n * If no schema is provided, returns the raw `Response` object.\n *\n * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.\n * @throws {ValidationError} When a schema is provided, validation returns issues, and\n * `throwOnValidationError` is `true`.\n * @throws {TypeError} When both `body` and `json` are provided, when JSON request\n * serialization fails, when request construction fails, when headers/search params are\n * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`\n * implementation rejects network-level failures as `TypeError`.\n * @throws {DOMException} When the runtime rejects an aborted request or response body read\n * as an `AbortError` DOMException.\n * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the\n * response body.\n * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.\n *\n * @example\n * import { z } from \"zod\";\n * import { $fetch } from \"@zap-studio/fetch\";\n *\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n *\n * // Basic usage (schema validation)\n * const user = await $fetch(\"/api/users/1\", UserSchema, { headers: { \"Authorization\": \"Bearer token\" } });\n * console.log(\"Validated user:\", user);\n *\n * // Raw usage (no schema validation and typed Response object)\n * const result = await $fetch(\"/api/data\", { method: \"POST\", body: JSON.stringify({ key: \"value\" }) });\n * const json = await result.json() as ResultType;\n * console.log(\"Raw response data:\", json);\n *\n * // Usage with validation errors returned instead of thrown\n * const result = await $fetch(\"/api/users/1\", UserSchema, { throwOnValidationError: false });\n *\n * if (result.issues) {\n * console.error(\"Validation errors:\", result.issues);\n * } else {\n * console.log(\"Validated user:\", result.value);\n * }\n */\nexport async function $fetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & { throwOnValidationError: false }\n): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\nexport async function $fetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & { throwOnValidationError?: true }\n): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\nexport async function $fetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n): Promise<Response>;\n\nexport async function $fetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n): Promise<unknown> {\n const [schema, options] = isStandardSchema(schemaOrOptions)\n ? [schemaOrOptions, optionsOrUndefined]\n : [undefined, schemaOrOptions];\n\n return await fetchInternal(input, schema, options, GLOBAL_DEFAULTS);\n}\n\n/**\n * Convenience methods for common HTTP verbs.\n *\n * These methods always require a schema for validation.\n * For raw responses without validation, use `$fetch` directly.\n *\n * Each method has the same throw behavior as {@link $fetch}.\n *\n * @example\n * import { z } from \"zod\";\n * import { api } from \"@zap-studio/fetch\";\n *\n * const PostSchema = z.object({\n * id: z.number(),\n * title: z.string(),\n * content: z.string(),\n * });\n *\n * async function fetchPost(postId: number) {\n * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);\n * return post; // post is typed as { id: number; title: string; content: string; }\n * }\n */\nexport const api: ApiMethods = {\n delete: createMethod($fetch, \"DELETE\"),\n get: createMethod($fetch, \"GET\"),\n patch: createMethod($fetch, \"PATCH\"),\n post: createMethod($fetch, \"POST\"),\n put: createMethod($fetch, \"PUT\"),\n};\n\n/**\n * Creates a custom fetch instance with pre-configured defaults.\n *\n * Use this factory to create API clients with a base URL, default headers,\n * and other shared configuration. Each instance is independent.\n *\n * The returned `$fetch` and `api` methods have the same throw behavior as the\n * top-level {@link $fetch} export.\n *\n * @example\n * import { z } from \"zod\";\n * import { createFetch } from \"@zap-studio/fetch\";\n *\n * // Create a configured instance\n * const { $fetch, api } = createFetch({\n * baseURL: \"https://api.example.com\",\n * headers: { \"Authorization\": \"Bearer token\" },\n * });\n *\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n *\n * // Now use relative paths - baseURL is prepended automatically\n * const user = await api.get(\"/users/1\", UserSchema);\n *\n * // Or use $fetch directly\n * const response = await $fetch(\"/users\", UserSchema, { method: \"POST\", json: { name: \"John\" } });\n */\nexport const createFetch = (\n factoryOptions: Partial<FetchDefaults> = {}\n): {\n $fetch: $Fetch;\n api: ApiMethods;\n} => {\n const defaults: FetchDefaults = {\n ...GLOBAL_DEFAULTS,\n ...factoryOptions,\n baseURL: factoryOptions.baseURL ?? GLOBAL_DEFAULTS.baseURL,\n throwOnFetchError:\n factoryOptions.throwOnFetchError ?? GLOBAL_DEFAULTS.throwOnFetchError,\n throwOnValidationError:\n factoryOptions.throwOnValidationError ??\n GLOBAL_DEFAULTS.throwOnValidationError,\n };\n\n async function customFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & { throwOnValidationError: false }\n ): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\n async function customFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & {\n throwOnValidationError?: true;\n }\n ): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\n async function customFetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n ): Promise<Response>;\n\n async function customFetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n ): Promise<unknown> {\n const [schema, options] = isStandardSchema(schemaOrOptions)\n ? [schemaOrOptions, optionsOrUndefined]\n : [undefined, schemaOrOptions];\n\n return await fetchInternal(input, schema, options, defaults);\n }\n\n const customApi = {\n delete: createMethod(customFetch, \"DELETE\"),\n get: createMethod(customFetch, \"GET\"),\n patch: createMethod(customFetch, \"PATCH\"),\n post: createMethod(customFetch, \"POST\"),\n put: createMethod(customFetch, \"PUT\"),\n };\n\n return {\n $fetch: customFetch,\n api: customApi,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;AAwFA,eAAsB,OACpB,OACA,iBACA,oBACkB;CAClB,MAAM,CAAC,QAAQ,WAAW,iBAAiB,eAAe,IACtD,CAAC,iBAAiB,kBAAkB,IACpC,CAAC,KAAA,GAAW,eAAe;CAE/B,OAAO,MAAM,cAAc,OAAO,QAAQ,SAAS,eAAe;AACpE;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,MAAkB;CAC7B,QAAQ,aAAa,QAAQ,QAAQ;CACrC,KAAK,aAAa,QAAQ,KAAK;CAC/B,OAAO,aAAa,QAAQ,OAAO;CACnC,MAAM,aAAa,QAAQ,MAAM;CACjC,KAAK,aAAa,QAAQ,KAAK;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,eACX,iBAAyC,CAAC,MAIvC;CACH,MAAM,WAA0B;EAC9B,GAAG;EACH,GAAG;EACH,SAAS,eAAe,WAAW,gBAAgB;EACnD,mBACE,eAAe,qBAAqB,gBAAgB;EACtD,wBACE,eAAe,0BACf,gBAAgB;CACpB;CAqBA,eAAe,YACb,OACA,iBACA,oBACkB;EAClB,MAAM,CAAC,QAAQ,WAAW,iBAAiB,eAAe,IACtD,CAAC,iBAAiB,kBAAkB,IACpC,CAAC,KAAA,GAAW,eAAe;EAE/B,OAAO,MAAM,cAAc,OAAO,QAAQ,SAAS,QAAQ;CAC7D;CAUA,OAAO;EACL,QAAQ;EACR,KAAK;GATL,QAAQ,aAAa,aAAa,QAAQ;GAC1C,KAAK,aAAa,aAAa,KAAK;GACpC,OAAO,aAAa,aAAa,OAAO;GACxC,MAAM,aAAa,aAAa,MAAM;GACtC,KAAK,aAAa,aAAa,KAAK;EAKvB;CACf;AACF"}
@@ -1,32 +0,0 @@
1
- import { ExtendedRequestInit, FetchDefaults, FetchInput } from "./types.mjs";
2
- import { StandardSchemaV1 } from "@zap-studio/validation";
3
- //#region src/internal.d.ts
4
- /**
5
- * Internal fetch implementation used by both $fetch and createFetch.
6
- *
7
- * This function normalizes request input, resolves final URL + query params,
8
- * executes `fetch`, optionally throws `FetchError`, and optionally validates
9
- * JSON response payloads using Standard Schema.
10
- *
11
- * @param input - Request URL, path, or Request object.
12
- * @param schema - Optional Standard Schema for response validation.
13
- * @param options - Optional request options and package-specific flags.
14
- * @param defaults - Effective client defaults.
15
- * @returns Raw `Response` when no schema is provided; otherwise validated output.
16
- * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
17
- * @throws {ValidationError} When a schema is provided, validation returns issues, and
18
- * `throwOnValidationError` is `true`.
19
- * @throws {TypeError} When both `body` and `json` are provided, when JSON request
20
- * serialization fails, when request construction fails, when headers/search params are
21
- * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`
22
- * implementation rejects network-level failures as `TypeError`.
23
- * @throws {DOMException} When the runtime rejects an aborted request or response body read
24
- * as an `AbortError` DOMException.
25
- * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the
26
- * response body.
27
- * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.
28
- */
29
- declare const fetchInternal: (input: FetchInput, schema: StandardSchemaV1 | undefined, options: ExtendedRequestInit | undefined, defaults: FetchDefaults) => Promise<unknown>;
30
- //#endregion
31
- export { fetchInternal };
32
- //# sourceMappingURL=internal.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"internal.d.mts","names":[],"sources":["../src/internal.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoGa,gBACX,OAAO,YACP,QAAQ,8BACR,SAAS,iCACT,UAAU,kBACT"}
package/dist/internal.mjs DELETED
@@ -1,75 +0,0 @@
1
- import { FetchError } from "./errors.mjs";
2
- import { mergeHeaders } from "./headers.mjs";
3
- import { normalizeRequest } from "./request.mjs";
4
- import { resolveRequestUrl } from "./url.mjs";
5
- import { standardValidate } from "@zap-studio/validation";
6
- //#region src/internal.ts
7
- /**
8
- * Normalizes request-level options into a final RequestInit payload and runtime flags.
9
- *
10
- * @param options - Request-level options.
11
- * @param defaults - Client-level defaults.
12
- * @returns Fully merged request init payload and effective runtime flags.
13
- */
14
- const prepareRequestInit = (options, defaults) => {
15
- const { headers, json, searchParams, throwOnFetchError = defaults.throwOnFetchError, throwOnValidationError = defaults.throwOnValidationError, ...rest } = options;
16
- const init = { ...rest };
17
- const mergedHeaders = mergeHeaders(defaults.headers, headers);
18
- if (mergedHeaders !== void 0) init.headers = mergedHeaders;
19
- if (json !== void 0) {
20
- if (init.body !== void 0 && init.body !== null) throw new TypeError("Cannot provide both `body` and `json`.");
21
- init.body = JSON.stringify(json);
22
- if (init.headers === void 0) init.headers = new Headers({ "Content-Type": "application/json" });
23
- else {
24
- const requestHeaders = new Headers(init.headers);
25
- if (!requestHeaders.has("Content-Type")) requestHeaders.set("Content-Type", "application/json");
26
- init.headers = requestHeaders;
27
- }
28
- }
29
- return {
30
- init,
31
- searchParams,
32
- throwOnFetchError,
33
- throwOnValidationError
34
- };
35
- };
36
- /**
37
- * Internal fetch implementation used by both $fetch and createFetch.
38
- *
39
- * This function normalizes request input, resolves final URL + query params,
40
- * executes `fetch`, optionally throws `FetchError`, and optionally validates
41
- * JSON response payloads using Standard Schema.
42
- *
43
- * @param input - Request URL, path, or Request object.
44
- * @param schema - Optional Standard Schema for response validation.
45
- * @param options - Optional request options and package-specific flags.
46
- * @param defaults - Effective client defaults.
47
- * @returns Raw `Response` when no schema is provided; otherwise validated output.
48
- * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
49
- * @throws {ValidationError} When a schema is provided, validation returns issues, and
50
- * `throwOnValidationError` is `true`.
51
- * @throws {TypeError} When both `body` and `json` are provided, when JSON request
52
- * serialization fails, when request construction fails, when headers/search params are
53
- * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`
54
- * implementation rejects network-level failures as `TypeError`.
55
- * @throws {DOMException} When the runtime rejects an aborted request or response body read
56
- * as an `AbortError` DOMException.
57
- * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the
58
- * response body.
59
- * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.
60
- */
61
- const fetchInternal = async (input, schema, options, defaults) => {
62
- const request = normalizeRequest(input, options);
63
- const { init, searchParams, throwOnFetchError, throwOnValidationError } = prepareRequestInit(request.options, defaults);
64
- const url = resolveRequestUrl(request.url, defaults, searchParams);
65
- const response = request.request ? await fetch(new Request(url, request.request), init) : await fetch(url, init);
66
- if (throwOnFetchError && !response.ok) throw new FetchError(`HTTP ${response.status}: ${response.statusText}`, response);
67
- if (schema === void 0) return response;
68
- const raw = await response.json();
69
- if (throwOnValidationError) return await standardValidate(schema, raw, { throwOnError: true });
70
- return await standardValidate(schema, raw, { throwOnError: false });
71
- };
72
- //#endregion
73
- export { fetchInternal };
74
-
75
- //# sourceMappingURL=internal.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"internal.mjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/**\n * Internal request execution and option preparation utilities.\n *\n * @module @zap-studio/fetch/internal\n */\n\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\nimport { standardValidate } from \"@zap-studio/validation\";\n\nimport { FetchError } from \"./errors.js\";\nimport { mergeHeaders } from \"./headers.js\";\nimport { normalizeRequest } from \"./request.js\";\nimport type {\n ExtendedRequestInit,\n FetchDefaults,\n FetchInput,\n} from \"./types.js\";\nimport { resolveRequestUrl } from \"./url.js\";\n\n/**\n * Normalizes request-level options into a final RequestInit payload and runtime flags.\n *\n * @param options - Request-level options.\n * @param defaults - Client-level defaults.\n * @returns Fully merged request init payload and effective runtime flags.\n */\nconst prepareRequestInit = (\n options: ExtendedRequestInit,\n defaults: FetchDefaults\n): {\n init: RequestInit;\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined;\n throwOnFetchError: boolean;\n throwOnValidationError: boolean;\n} => {\n const {\n headers,\n json,\n searchParams,\n throwOnFetchError = defaults.throwOnFetchError,\n throwOnValidationError = defaults.throwOnValidationError,\n ...rest\n } = options;\n\n const init: RequestInit = { ...rest };\n const mergedHeaders = mergeHeaders(defaults.headers, headers);\n if (mergedHeaders !== undefined) {\n init.headers = mergedHeaders;\n }\n\n if (json !== undefined) {\n if (init.body !== undefined && init.body !== null) {\n throw new TypeError(\"Cannot provide both `body` and `json`.\");\n }\n\n init.body = JSON.stringify(json);\n if (init.headers === undefined) {\n init.headers = new Headers({ \"Content-Type\": \"application/json\" });\n } else {\n const requestHeaders = new Headers(init.headers);\n if (!requestHeaders.has(\"Content-Type\")) {\n requestHeaders.set(\"Content-Type\", \"application/json\");\n }\n init.headers = requestHeaders;\n }\n }\n\n return {\n init,\n searchParams,\n throwOnFetchError,\n throwOnValidationError,\n };\n};\n\n/**\n * Internal fetch implementation used by both $fetch and createFetch.\n *\n * This function normalizes request input, resolves final URL + query params,\n * executes `fetch`, optionally throws `FetchError`, and optionally validates\n * JSON response payloads using Standard Schema.\n *\n * @param input - Request URL, path, or Request object.\n * @param schema - Optional Standard Schema for response validation.\n * @param options - Optional request options and package-specific flags.\n * @param defaults - Effective client defaults.\n * @returns Raw `Response` when no schema is provided; otherwise validated output.\n * @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.\n * @throws {ValidationError} When a schema is provided, validation returns issues, and\n * `throwOnValidationError` is `true`.\n * @throws {TypeError} When both `body` and `json` are provided, when JSON request\n * serialization fails, when request construction fails, when headers/search params are\n * invalid, when `response.json()` cannot read the body, or when the runtime `fetch`\n * implementation rejects network-level failures as `TypeError`.\n * @throws {DOMException} When the runtime rejects an aborted request or response body read\n * as an `AbortError` DOMException.\n * @throws {SyntaxError} When a schema is provided and `response.json()` cannot parse the\n * response body.\n * @throws {unknown} Any error thrown or rejected by the provided Standard Schema validator.\n */\nexport const fetchInternal = async (\n input: FetchInput,\n schema: StandardSchemaV1 | undefined,\n options: ExtendedRequestInit | undefined,\n defaults: FetchDefaults\n): Promise<unknown> => {\n const request = normalizeRequest(input, options);\n const { init, searchParams, throwOnFetchError, throwOnValidationError } =\n prepareRequestInit(request.options, defaults);\n const url = resolveRequestUrl(request.url, defaults, searchParams);\n const response = request.request\n ? await fetch(new Request(url, request.request), init)\n : await fetch(url, init);\n\n if (throwOnFetchError && !response.ok) {\n throw new FetchError(\n `HTTP ${response.status}: ${response.statusText}`,\n response\n );\n }\n\n if (schema === undefined) {\n return response;\n }\n\n const raw: unknown = await response.json();\n if (throwOnValidationError) {\n return await standardValidate(schema, raw, { throwOnError: true });\n }\n return await standardValidate(schema, raw, { throwOnError: false });\n};\n"],"mappings":";;;;;;;;;;;;;AA0BA,MAAM,sBACJ,SACA,aAMG;CACH,MAAM,EACJ,SACA,MACA,cACA,oBAAoB,SAAS,mBAC7B,yBAAyB,SAAS,wBAClC,GAAG,SACD;CAEJ,MAAM,OAAoB,EAAE,GAAG,KAAK;CACpC,MAAM,gBAAgB,aAAa,SAAS,SAAS,OAAO;CAC5D,IAAI,kBAAkB,KAAA,GACpB,KAAK,UAAU;CAGjB,IAAI,SAAS,KAAA,GAAW;EACtB,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,SAAS,MAC3C,MAAM,IAAI,UAAU,wCAAwC;EAG9D,KAAK,OAAO,KAAK,UAAU,IAAI;EAC/B,IAAI,KAAK,YAAY,KAAA,GACnB,KAAK,UAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;OAC5D;GACL,MAAM,iBAAiB,IAAI,QAAQ,KAAK,OAAO;GAC/C,IAAI,CAAC,eAAe,IAAI,cAAc,GACpC,eAAe,IAAI,gBAAgB,kBAAkB;GAEvD,KAAK,UAAU;EACjB;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,gBAAgB,OAC3B,OACA,QACA,SACA,aACqB;CACrB,MAAM,UAAU,iBAAiB,OAAO,OAAO;CAC/C,MAAM,EAAE,MAAM,cAAc,mBAAmB,2BAC7C,mBAAmB,QAAQ,SAAS,QAAQ;CAC9C,MAAM,MAAM,kBAAkB,QAAQ,KAAK,UAAU,YAAY;CACjE,MAAM,WAAW,QAAQ,UACrB,MAAM,MAAM,IAAI,QAAQ,KAAK,QAAQ,OAAO,GAAG,IAAI,IACnD,MAAM,MAAM,KAAK,IAAI;CAEzB,IAAI,qBAAqB,CAAC,SAAS,IACjC,MAAM,IAAI,WACR,QAAQ,SAAS,OAAO,IAAI,SAAS,cACrC,QACF;CAGF,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,MAAM,MAAe,MAAM,SAAS,KAAK;CACzC,IAAI,wBACF,OAAO,MAAM,iBAAiB,QAAQ,KAAK,EAAE,cAAc,KAAK,CAAC;CAEnE,OAAO,MAAM,iBAAiB,QAAQ,KAAK,EAAE,cAAc,MAAM,CAAC;AACpE"}
@@ -1,22 +0,0 @@
1
- import { $Fetch } from "./types.mjs";
2
- //#region src/methods.d.ts
3
- /**
4
- * Creates an HTTP method helper bound to a fetch function.
5
- *
6
- * The returned function mirrors `$Fetch` overloads but forces the provided
7
- * HTTP method (`GET`, `POST`, etc.) into request options.
8
- *
9
- * @param fetchFn - Fetch function to wrap.
10
- * @param method - HTTP method to enforce.
11
- * @returns Method-bound fetch function.
12
- * @throws {unknown} Any error thrown or rejected by `fetchFn` when the returned method-bound
13
- * fetch function is called.
14
- *
15
- * @example
16
- * const get = createMethod($fetch, "GET");
17
- * const user = await get("/users/1", UserSchema);
18
- */
19
- declare const createMethod: (fetchFn: $Fetch, method: string) => $Fetch;
20
- //#endregion
21
- export { createMethod };
22
- //# sourceMappingURL=methods.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"methods.d.mts","names":[],"sources":["../src/methods.ts"],"mappings":";;;;;;;;;;;;;;;;;;cA2Ba,eAAgB,SAAS,QAAQ,mBAAiB"}
package/dist/methods.mjs DELETED
@@ -1,58 +0,0 @@
1
- import { isStandardSchema } from "@zap-studio/validation";
2
- //#region src/methods.ts
3
- /**
4
- * Method helper factories used to build verb-specific fetch functions.
5
- *
6
- * @module @zap-studio/fetch/methods
7
- */
8
- /**
9
- * Creates an HTTP method helper bound to a fetch function.
10
- *
11
- * The returned function mirrors `$Fetch` overloads but forces the provided
12
- * HTTP method (`GET`, `POST`, etc.) into request options.
13
- *
14
- * @param fetchFn - Fetch function to wrap.
15
- * @param method - HTTP method to enforce.
16
- * @returns Method-bound fetch function.
17
- * @throws {unknown} Any error thrown or rejected by `fetchFn` when the returned method-bound
18
- * fetch function is called.
19
- *
20
- * @example
21
- * const get = createMethod($fetch, "GET");
22
- * const user = await get("/users/1", UserSchema);
23
- */
24
- const createMethod = (fetchFn, method) => {
25
- /**
26
- * Method-bound `$Fetch` implementation.
27
- *
28
- * Resolves schema/option overloads and injects the configured HTTP method.
29
- */
30
- async function methodFetch(input, schemaOrOptions, optionsOrUndefined) {
31
- if (isStandardSchema(schemaOrOptions)) {
32
- if (optionsOrUndefined?.throwOnValidationError === false) return await fetchFn(input, schemaOrOptions, {
33
- ...optionsOrUndefined,
34
- method,
35
- throwOnValidationError: false
36
- });
37
- const { throwOnValidationError, ...restOptions } = optionsOrUndefined ?? {};
38
- if (throwOnValidationError === true) return await fetchFn(input, schemaOrOptions, {
39
- ...restOptions,
40
- method,
41
- throwOnValidationError: true
42
- });
43
- return await fetchFn(input, schemaOrOptions, {
44
- ...restOptions,
45
- method
46
- });
47
- }
48
- return await fetchFn(input, {
49
- ...schemaOrOptions,
50
- method
51
- });
52
- }
53
- return methodFetch;
54
- };
55
- //#endregion
56
- export { createMethod };
57
-
58
- //# sourceMappingURL=methods.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"methods.mjs","names":[],"sources":["../src/methods.ts"],"sourcesContent":["/**\n * Method helper factories used to build verb-specific fetch functions.\n *\n * @module @zap-studio/fetch/methods\n */\n\nimport { isStandardSchema } from \"@zap-studio/validation\";\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\n\nimport type { $Fetch, ExtendedRequestInit, FetchInput } from \"./types.js\";\n\n/**\n * Creates an HTTP method helper bound to a fetch function.\n *\n * The returned function mirrors `$Fetch` overloads but forces the provided\n * HTTP method (`GET`, `POST`, etc.) into request options.\n *\n * @param fetchFn - Fetch function to wrap.\n * @param method - HTTP method to enforce.\n * @returns Method-bound fetch function.\n * @throws {unknown} Any error thrown or rejected by `fetchFn` when the returned method-bound\n * fetch function is called.\n *\n * @example\n * const get = createMethod($fetch, \"GET\");\n * const user = await get(\"/users/1\", UserSchema);\n */\nexport const createMethod = (fetchFn: $Fetch, method: string): $Fetch => {\n function methodFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options: ExtendedRequestInit & {\n throwOnValidationError: false;\n }\n ): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;\n\n function methodFetch<TSchema extends StandardSchemaV1>(\n input: FetchInput,\n schema: TSchema,\n options?: ExtendedRequestInit & {\n throwOnValidationError?: true;\n }\n ): Promise<StandardSchemaV1.InferOutput<TSchema>>;\n\n function methodFetch(\n input: FetchInput,\n options?: ExtendedRequestInit\n ): Promise<Response>;\n\n /**\n * Method-bound `$Fetch` implementation.\n *\n * Resolves schema/option overloads and injects the configured HTTP method.\n */\n async function methodFetch(\n input: FetchInput,\n schemaOrOptions?: StandardSchemaV1 | ExtendedRequestInit,\n optionsOrUndefined?: ExtendedRequestInit\n ): Promise<unknown> {\n if (isStandardSchema(schemaOrOptions)) {\n if (optionsOrUndefined?.throwOnValidationError === false) {\n return await fetchFn(input, schemaOrOptions, {\n ...optionsOrUndefined,\n method,\n throwOnValidationError: false,\n });\n }\n\n const { throwOnValidationError, ...restOptions } =\n optionsOrUndefined ?? {};\n\n if (throwOnValidationError === true) {\n return await fetchFn(input, schemaOrOptions, {\n ...restOptions,\n method,\n throwOnValidationError: true,\n });\n }\n\n return await fetchFn(input, schemaOrOptions, {\n ...restOptions,\n method,\n });\n }\n\n return await fetchFn(input, {\n ...schemaOrOptions,\n method,\n });\n }\n\n return methodFetch;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,gBAAgB,SAAiB,WAA2B;;;;;;CA2BvE,eAAe,YACb,OACA,iBACA,oBACkB;EAClB,IAAI,iBAAiB,eAAe,GAAG;GACrC,IAAI,oBAAoB,2BAA2B,OACjD,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;IACA,wBAAwB;GAC1B,CAAC;GAGH,MAAM,EAAE,wBAAwB,GAAG,gBACjC,sBAAsB,CAAC;GAEzB,IAAI,2BAA2B,MAC7B,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;IACA,wBAAwB;GAC1B,CAAC;GAGH,OAAO,MAAM,QAAQ,OAAO,iBAAiB;IAC3C,GAAG;IACH;GACF,CAAC;EACH;EAEA,OAAO,MAAM,QAAQ,OAAO;GAC1B,GAAG;GACH;EACF,CAAC;CACH;CAEA,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"request.d.mts","names":[],"sources":["../src/request.ts"],"mappings":";;;;;;;;;UAkBiB;EACf;EACA,UAAU;EACV,SAAS;;;;;;;;;;;;;;cAeE,mBACX,OAAO,YACP,UAAU,wBACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"request.mjs","names":[],"sources":["../src/request.ts"],"sourcesContent":["/**\n * Request normalization helpers for fetch `input` values.\n *\n * @module @zap-studio/fetch/request\n */\n\nimport { mergeHeaders } from \"./headers.js\";\nimport type { ExtendedRequestInit, FetchInput } from \"./types.js\";\n\nconst EMPTY_OPTIONS = {} as ExtendedRequestInit;\n\n/**\n * Normalized representation used by internal request execution.\n *\n * - `url`: resolved string URL from the input (string or `URL`; `Request` uses `request.url`)\n * - `request`: original Request clone when input is a Request\n * - `options`: normalized request options merged with Request headers\n */\nexport interface NormalizedRequest {\n url: string;\n request?: Request;\n options: ExtendedRequestInit;\n}\n\n/**\n * Normalizes fetch `input` and request-level options into a consistent internal shape.\n *\n * @param input - Request URL/path or Request instance.\n * @param options - Optional request options.\n * @returns A normalized request structure for internal processing.\n * @throws {TypeError} When cloning a `Request` fails or the merged headers are invalid.\n *\n * @example\n * const normalized = normalizeRequest(\"/users\", { method: \"GET\" });\n * console.log(normalized.url); // \"/users\"\n */\nexport const normalizeRequest = (\n input: FetchInput,\n options?: ExtendedRequestInit\n): NormalizedRequest => {\n if (!(input instanceof Request)) {\n const url = input instanceof URL ? input.href : input;\n return {\n options: options ?? EMPTY_OPTIONS,\n url,\n };\n }\n\n const request = new Request(input);\n const { headers, ...rest } = options ?? {};\n const mergedHeaders = mergeHeaders(request.headers, headers);\n const normalizedOptions = { ...rest } as ExtendedRequestInit;\n\n if (mergedHeaders !== undefined) {\n normalizedOptions.headers = mergedHeaders;\n }\n\n return {\n options: normalizedOptions,\n request,\n url: request.url,\n };\n};\n"],"mappings":";;;;;;;AASA,MAAM,gBAAgB,CAAC;;;;;;;;;;;;;AA2BvB,MAAa,oBACX,OACA,YACsB;CACtB,IAAI,EAAE,iBAAiB,UAAU;EAC/B,MAAM,MAAM,iBAAiB,MAAM,MAAM,OAAO;EAChD,OAAO;GACL,SAAS,WAAW;GACpB;EACF;CACF;CAEA,MAAM,UAAU,IAAI,QAAQ,KAAK;CACjC,MAAM,EAAE,SAAS,GAAG,SAAS,WAAW,CAAC;CACzC,MAAM,gBAAgB,aAAa,QAAQ,SAAS,OAAO;CAC3D,MAAM,oBAAoB,EAAE,GAAG,KAAK;CAEpC,IAAI,kBAAkB,KAAA,GACpB,kBAAkB,UAAU;CAG9B,OAAO;EACL,SAAS;EACT;EACA,KAAK,QAAQ;CACf;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;KAWY,aAAa,kBAAkB;KAEtC,uBAAuB,6BAA6B;KAEpD,kBAAkB;EACrB;;KAGG,eAAe,KAAK;;;;;EAKvB;EACA;;UAGQ;;;;;EAKR,eAAe;;;;;EAKf;;;;;EAKA;;;;;;;;;;;;KAaU,uBAAuB,kBAAkB,gBACnD;;;;;;;;;;;UAYe;;;;;EAKf;;;;;EAKA,UAAU;;;;;EAKV,eAAe;;;;;EAKf;;;;;EAKA;;;;;UAMe;;;;;;;;;;;;;;;;GAgBd,gBAAgB,kBACf,OAAO,YACP,QAAQ,SACR,SAAS;IAAwB;MAChC,QAAQ,iBAAiB,OAAO,iBAAiB,YAAY;;;;;;;;;;;;;;;;;GAkB/D,gBAAgB,kBACf,OAAO,YACP,QAAQ,SACR,UAAU;IACR;MAED,QAAQ,iBAAiB,YAAY;;;;;;;;;;;;GAavC,OAAO,YAAY,UAAU,sBAAsB,QAAQ;;;;;;;;UAS7C;;;;EAIf,QAAQ;;;;EAIR,KAAK;;;;EAIL,OAAO;;;;EAIP,MAAM;;;;EAIN,KAAK"}
package/dist/types.mjs DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"url.d.mts","names":[],"sources":["../src/url.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;cAmFa,oBACX,qBACA,UAAU,eACV,eAAe"}
package/dist/url.mjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"url.mjs","names":[],"sources":["../src/url.ts"],"sourcesContent":["/**\n * URL resolution and query merging utilities.\n *\n * @module @zap-studio/fetch/url\n */\n\nimport type { ExtendedRequestInit, FetchDefaults } from \"./types.js\";\n\n/**\n * Copies search params into target, overriding duplicate keys.\n */\nconst mergeSearchParams = (\n target: URLSearchParams,\n source: ExtendedRequestInit[\"searchParams\"] | undefined\n): void => {\n for (const [key, value] of new URLSearchParams(source)) {\n target.set(key, value);\n }\n};\n\n/**\n * Ensures a URL has a trailing slash for relative URL resolution.\n */\nconst ensureTrailingSlash = (url: string): string =>\n url.endsWith(\"/\") ? url : `${url}/`;\n\n/**\n * Resolves search params by applying default params, URL params, then request params.\n */\nconst resolveSearchParams = (\n url: string,\n defaultSearchParams: FetchDefaults[\"searchParams\"] | undefined,\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined\n): string => {\n if (defaultSearchParams === undefined && searchParams === undefined) {\n return url;\n }\n\n const hashIndex = url.indexOf(\"#\");\n const hasFragment = hashIndex !== -1;\n const urlWithoutHash = hasFragment ? url.slice(0, hashIndex) : url;\n const hash = hasFragment ? url.slice(hashIndex + 1) : \"\";\n const queryIndex = urlWithoutHash.indexOf(\"?\");\n const pathname =\n queryIndex === -1 ? urlWithoutHash : urlWithoutHash.slice(0, queryIndex);\n const urlSearchParams =\n queryIndex === -1 ? undefined : urlWithoutHash.slice(queryIndex + 1);\n const resolvedSearchParams = new URLSearchParams();\n\n mergeSearchParams(resolvedSearchParams, defaultSearchParams);\n mergeSearchParams(resolvedSearchParams, urlSearchParams);\n mergeSearchParams(resolvedSearchParams, searchParams);\n\n const resolvedSearch = resolvedSearchParams.toString();\n const fragmentSuffix = hasFragment ? `#${hash}` : \"\";\n\n if (resolvedSearch.length === 0) {\n return `${pathname}${fragmentSuffix}`;\n }\n\n return `${pathname}?${resolvedSearch}${fragmentSuffix}`;\n};\n\n/**\n * Resolves final request URL by applying baseURL and layered search params.\n *\n * Search param precedence:\n * 1. `defaults.searchParams`\n * 2. search params already present in `resourceUrl`\n * 3. per-request `searchParams`\n *\n * @example\n * const finalUrl = resolveRequestUrl(\n * \"/users?page=2\",\n * { ...defaults, baseURL: \"https://api.example.com\", searchParams: { locale: \"en\" } },\n * { page: \"3\" },\n * );\n * // https://api.example.com/users?locale=en&page=3\n *\n * @throws {TypeError} When `baseURL` and `resourceUrl` cannot be resolved by\n * `URL`, or when default/per-request search params cannot be converted by\n * `URLSearchParams`.\n */\nexport const resolveRequestUrl = (\n resourceUrl: string,\n defaults: FetchDefaults,\n searchParams?: ExtendedRequestInit[\"searchParams\"]\n): string => {\n const url = defaults.baseURL\n ? new URL(resourceUrl, ensureTrailingSlash(defaults.baseURL)).toString()\n : resourceUrl;\n\n return resolveSearchParams(url, defaults.searchParams, searchParams);\n};\n"],"mappings":";;;;AAWA,MAAM,qBACJ,QACA,WACS;CACT,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,gBAAgB,MAAM,GACnD,OAAO,IAAI,KAAK,KAAK;AAEzB;;;;AAKA,MAAM,uBAAuB,QAC3B,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,IAAI;;;;AAKnC,MAAM,uBACJ,KACA,qBACA,iBACW;CACX,IAAI,wBAAwB,KAAA,KAAa,iBAAiB,KAAA,GACxD,OAAO;CAGT,MAAM,YAAY,IAAI,QAAQ,GAAG;CACjC,MAAM,cAAc,cAAc;CAClC,MAAM,iBAAiB,cAAc,IAAI,MAAM,GAAG,SAAS,IAAI;CAC/D,MAAM,OAAO,cAAc,IAAI,MAAM,YAAY,CAAC,IAAI;CACtD,MAAM,aAAa,eAAe,QAAQ,GAAG;CAC7C,MAAM,WACJ,eAAe,KAAK,iBAAiB,eAAe,MAAM,GAAG,UAAU;CACzE,MAAM,kBACJ,eAAe,KAAK,KAAA,IAAY,eAAe,MAAM,aAAa,CAAC;CACrE,MAAM,uBAAuB,IAAI,gBAAgB;CAEjD,kBAAkB,sBAAsB,mBAAmB;CAC3D,kBAAkB,sBAAsB,eAAe;CACvD,kBAAkB,sBAAsB,YAAY;CAEpD,MAAM,iBAAiB,qBAAqB,SAAS;CACrD,MAAM,iBAAiB,cAAc,IAAI,SAAS;CAElD,IAAI,eAAe,WAAW,GAC5B,OAAO,GAAG,WAAW;CAGvB,OAAO,GAAG,SAAS,GAAG,iBAAiB;AACzC;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,qBACX,aACA,UACA,iBACW;CACX,MAAM,MAAM,SAAS,UACjB,IAAI,IAAI,aAAa,oBAAoB,SAAS,OAAO,CAAC,CAAC,CAAC,SAAS,IACrE;CAEJ,OAAO,oBAAoB,KAAK,SAAS,cAAc,YAAY;AACrE"}