@zap-studio/fetch 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/{LICENSE.md → LICENSE} +1 -1
- package/README.md +276 -32
- package/dist/constants.d.mts +8 -1
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +9 -2
- package/dist/constants.mjs.map +1 -1
- package/dist/errors.d.mts +29 -0
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +46 -2
- package/dist/errors.mjs.map +1 -0
- package/dist/headers.d.mts +26 -0
- package/dist/headers.d.mts.map +1 -0
- package/dist/headers.mjs +35 -0
- package/dist/headers.mjs.map +1 -0
- package/dist/index.d.mts +28 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +23 -8
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +33 -0
- package/dist/internal.d.mts.map +1 -0
- package/dist/internal.mjs +73 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/methods.d.mts +23 -0
- package/dist/methods.d.mts.map +1 -0
- package/dist/methods.mjs +53 -0
- package/dist/methods.mjs.map +1 -0
- package/dist/request.d.mts +31 -0
- package/dist/request.d.mts.map +1 -0
- package/dist/request.mjs +39 -0
- package/dist/request.mjs.map +1 -0
- package/dist/types.d.mts +73 -47
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +1 -1
- package/dist/url.d.mts +27 -0
- package/dist/url.d.mts.map +1 -0
- package/dist/url.mjs +56 -0
- package/dist/url.mjs.map +1 -0
- package/package.json +47 -48
- package/dist/errors-DQfwnwmz.mjs +0 -18
- package/dist/errors-DQfwnwmz.mjs.map +0 -1
- package/dist/utils.d.mts +0 -33
- package/dist/utils.d.mts.map +0 -1
- package/dist/utils.mjs +0 -180
- package/dist/utils.mjs.map +0 -1
package/dist/types.d.mts
CHANGED
|
@@ -1,55 +1,56 @@
|
|
|
1
|
-
import { StandardSchemaV1 } from "@
|
|
1
|
+
import { StandardSchemaV1 } from "@zap-studio/validation";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* Can be a URLSearchParams object, a record of string pairs,
|
|
9
|
-
* a query string, or an array of tuples.
|
|
10
|
-
*/
|
|
11
|
-
type SearchParams = URLSearchParams | Record<string, string> | string | [string, string][];
|
|
12
|
-
/**
|
|
13
|
-
* JSON-serializable value used for request bodies.
|
|
14
|
-
*/
|
|
15
|
-
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
16
|
-
[key: string]: JsonValue;
|
|
4
|
+
type FetchInput = Parameters<typeof fetch>[0];
|
|
5
|
+
type URLSearchParamsInput = ConstructorParameters<typeof URLSearchParams>[0];
|
|
6
|
+
type RequestBodyInit = RequestInit & {
|
|
7
|
+
json?: never;
|
|
17
8
|
};
|
|
18
|
-
|
|
19
|
-
* Request body type accepted by the fetch wrapper.
|
|
20
|
-
*/
|
|
21
|
-
type RequestBody = BodyInit | JsonValue;
|
|
22
|
-
/**
|
|
23
|
-
* Base extended request options without throwOnValidationError
|
|
24
|
-
*/
|
|
25
|
-
type BaseExtendedRequestInit = Omit<RequestInit, "body"> & {
|
|
9
|
+
type JsonBodyInit = Omit<RequestInit, "body"> & {
|
|
26
10
|
/**
|
|
27
|
-
*
|
|
11
|
+
* JSON body convenience. When provided, this is JSON-stringified into `body`.
|
|
12
|
+
* @default undefined
|
|
28
13
|
*/
|
|
29
|
-
|
|
14
|
+
json: unknown;
|
|
15
|
+
body?: never;
|
|
16
|
+
};
|
|
17
|
+
type CustomRequestInit = {
|
|
30
18
|
/**
|
|
31
19
|
* Per-request query/search params
|
|
32
20
|
* @default undefined
|
|
33
21
|
*/
|
|
34
|
-
searchParams?:
|
|
22
|
+
searchParams?: URLSearchParamsInput | undefined;
|
|
35
23
|
/**
|
|
36
24
|
* Whether to throw a FetchError on HTTP errors (non-2xx responses)
|
|
37
25
|
* @default true
|
|
38
26
|
*/
|
|
39
|
-
throwOnFetchError?: boolean;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Extended RequestInit type to include custom fetch options
|
|
43
|
-
*/
|
|
44
|
-
type ExtendedRequestInit<TThrowOnValidationError extends boolean | undefined = boolean | undefined> = BaseExtendedRequestInit & {
|
|
27
|
+
throwOnFetchError?: boolean | undefined;
|
|
45
28
|
/**
|
|
46
29
|
* Whether to throw a ValidationError on validation errors
|
|
47
30
|
* @default true
|
|
48
31
|
*/
|
|
49
|
-
throwOnValidationError?:
|
|
32
|
+
throwOnValidationError?: boolean | undefined;
|
|
50
33
|
};
|
|
51
34
|
/**
|
|
35
|
+
* Extended RequestInit type to include custom fetch options
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const options: ExtendedRequestInit = {
|
|
39
|
+
* method: "POST",
|
|
40
|
+
* json: { name: "Ada" },
|
|
41
|
+
* throwOnFetchError: true,
|
|
42
|
+
* };
|
|
43
|
+
*/
|
|
44
|
+
type ExtendedRequestInit = (RequestBodyInit | JsonBodyInit) & CustomRequestInit;
|
|
45
|
+
/**
|
|
52
46
|
* Internal defaults used by fetchInternal
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const defaults: FetchDefaults = {
|
|
50
|
+
* baseURL: "https://api.example.com",
|
|
51
|
+
* throwOnFetchError: true,
|
|
52
|
+
* throwOnValidationError: true,
|
|
53
|
+
* };
|
|
53
54
|
*/
|
|
54
55
|
interface FetchDefaults {
|
|
55
56
|
/**
|
|
@@ -61,12 +62,12 @@ interface FetchDefaults {
|
|
|
61
62
|
* Default headers to include in all requests (can be overridden per request)
|
|
62
63
|
* @default undefined
|
|
63
64
|
*/
|
|
64
|
-
headers?: HeadersInit;
|
|
65
|
+
headers?: HeadersInit | undefined;
|
|
65
66
|
/**
|
|
66
67
|
* Default query/search params applied to every request (can be overridden per request)
|
|
67
68
|
* @default undefined
|
|
68
69
|
*/
|
|
69
|
-
searchParams?:
|
|
70
|
+
searchParams?: URLSearchParamsInput | undefined;
|
|
70
71
|
/**
|
|
71
72
|
* Whether to throw a `FetchError` on HTTP errors (non-2xx responses)
|
|
72
73
|
* @default true
|
|
@@ -79,39 +80,64 @@ interface FetchDefaults {
|
|
|
79
80
|
throwOnValidationError: boolean;
|
|
80
81
|
}
|
|
81
82
|
/**
|
|
82
|
-
* Options for creating a custom fetch instance with `createFetch`
|
|
83
|
-
*/
|
|
84
|
-
type CreateFetchOptions = Partial<FetchDefaults>;
|
|
85
|
-
/**
|
|
86
83
|
* Type-safe fetch function with Standard Schema validation support
|
|
87
84
|
*/
|
|
88
85
|
interface $Fetch {
|
|
89
86
|
/**
|
|
90
87
|
* Fetch with schema validation and throwOnValidationError: false
|
|
91
|
-
* @param
|
|
88
|
+
* @param input - URL or path to fetch
|
|
92
89
|
* @param schema - Standard Schema for response validation
|
|
93
90
|
* @param options - Extended request options with throwOnValidationError: false
|
|
94
91
|
* @returns Standard Schema Result object with value or issues
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
* @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
|
|
93
|
+
* @throws {TypeError} When request construction, JSON request serialization, headers,
|
|
94
|
+
* search params, native `fetch`, or `response.json()` body reading fail with a
|
|
95
|
+
* `TypeError`.
|
|
96
|
+
* @throws {DOMException} When native `fetch` or `response.json()` rejects an aborted
|
|
97
|
+
* request/body read as an `AbortError` DOMException.
|
|
98
|
+
* @throws {SyntaxError} When `response.json()` cannot parse the response body.
|
|
99
|
+
* @throws Any error thrown or rejected by the provided Standard Schema validator.
|
|
100
|
+
*/
|
|
101
|
+
<TSchema extends StandardSchemaV1>(input: FetchInput, schema: TSchema, options: ExtendedRequestInit & {
|
|
102
|
+
throwOnValidationError: false;
|
|
103
|
+
}): Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;
|
|
97
104
|
/**
|
|
98
105
|
* Fetch with schema validation and throwOnValidationError: true or undefined (default)
|
|
99
|
-
* @param
|
|
106
|
+
* @param input - URL or path to fetch
|
|
100
107
|
* @param schema - Standard Schema for response validation
|
|
101
108
|
* @param options - Extended request options
|
|
102
109
|
* @returns Validated data of type TSchema
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
* @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
|
|
111
|
+
* @throws {ValidationError} When validation returns issues.
|
|
112
|
+
* @throws {TypeError} When request construction, JSON request serialization, headers,
|
|
113
|
+
* search params, native `fetch`, or `response.json()` body reading fail with a
|
|
114
|
+
* `TypeError`.
|
|
115
|
+
* @throws {DOMException} When native `fetch` or `response.json()` rejects an aborted
|
|
116
|
+
* request/body read as an `AbortError` DOMException.
|
|
117
|
+
* @throws {SyntaxError} When `response.json()` cannot parse the response body.
|
|
118
|
+
* @throws Any error thrown or rejected by the provided Standard Schema validator.
|
|
119
|
+
*/
|
|
120
|
+
<TSchema extends StandardSchemaV1>(input: FetchInput, schema: TSchema, options?: ExtendedRequestInit & {
|
|
121
|
+
throwOnValidationError?: true | undefined;
|
|
122
|
+
}): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
105
123
|
/**
|
|
106
124
|
* Fetch without schema validation
|
|
107
|
-
* @param
|
|
125
|
+
* @param input - URL or path to fetch
|
|
108
126
|
* @param options - Extended request options
|
|
109
127
|
* @returns Raw Response object
|
|
128
|
+
* @throws {FetchError} When `throwOnFetchError` is `true` and the response is not ok.
|
|
129
|
+
* @throws {TypeError} When request construction, JSON request serialization, headers,
|
|
130
|
+
* search params, or native `fetch` fail with a `TypeError`.
|
|
131
|
+
* @throws {DOMException} When native `fetch` rejects an aborted request as an
|
|
132
|
+
* `AbortError` DOMException.
|
|
110
133
|
*/
|
|
111
|
-
(
|
|
134
|
+
(input: FetchInput, options?: ExtendedRequestInit): Promise<Response>;
|
|
112
135
|
}
|
|
113
136
|
/**
|
|
114
137
|
* API HTTP method-specific fetch functions
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const user = await api.get("/users/1", UserSchema);
|
|
115
141
|
*/
|
|
116
142
|
interface ApiMethods {
|
|
117
143
|
/**
|
|
@@ -136,5 +162,5 @@ interface ApiMethods {
|
|
|
136
162
|
put: $Fetch;
|
|
137
163
|
}
|
|
138
164
|
//#endregion
|
|
139
|
-
export { $Fetch, ApiMethods,
|
|
165
|
+
export { $Fetch, ApiMethods, ExtendedRequestInit, FetchDefaults, FetchInput };
|
|
140
166
|
//# sourceMappingURL=types.d.mts.map
|
package/dist/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;KAQY,UAAA,GAAa,UAAA,QAAkB,KAAA;AAAA,KAEtC,oBAAA,GAAuB,qBAAA,QAA6B,eAAA;AAAA,KAEpD,eAAA,GAAkB,WAAA;EACrB,IAAA;AAAA;AAAA,KAGG,YAAA,GAAe,IAAA,CAAK,WAAA;EANgC;AAAA;;;EAWvD,IAAA;EACA,IAAA;AAAA;AAAA,KAGG,iBAAA;;;;;EAKH,YAAA,GAAe,oBAAA;;;;AARf;EAaA,iBAAA;;;;;EAKA,sBAAA;AAAA;;;AAaF;;;;;;;;KAAY,mBAAA,IAAuB,eAAA,GAAkB,YAAA,IAAgB,iBAAA;;;;;AAYrE;;;;;;UAAiB,aAAA;;;;;EAKf,OAAA;EAoBA;AAMF;;;EArBE,OAAA,GAAU,WAAA;;;;;EAKV,YAAA,GAAe,oBAAA;;;;;EAKf,iBAAA;;;;;EAKA,sBAAA;AAAA;;;;UAMe,MAAA;;;;;;;;;;;;;;;;mBAgBE,gBAAA,EACf,KAAA,EAAO,UAAA,EACP,MAAA,EAAQ,OAAA,EACR,OAAA,EAAS,mBAAA;IAAwB,sBAAA;EAAA,IAChC,OAAA,CAAQ,gBAAA,CAAiB,MAAA,CAAO,gBAAA,CAAiB,WAAA,CAAY,OAAA;;;;;;;;;;;;;;;;;mBAkB/C,gBAAA,EACf,KAAA,EAAO,UAAA,EACP,MAAA,EAAQ,OAAA,EACR,OAAA,GAAU,mBAAA;IACR,sBAAA;EAAA,IAED,OAAA,CAAQ,gBAAA,CAAiB,WAAA,CAAY,OAAA;EAsBzB;;;;;;;;;;;EAAA,CATd,KAAA,EAAO,UAAA,EAAY,OAAA,GAAU,mBAAA,GAAsB,OAAA,CAAQ,QAAA;AAAA;;;;;;;UAS7C,UAAA;EAoBV;;;EAhBL,MAAA,EAAQ,MAAA;;;;EAIR,GAAA,EAAK,MAAA;;;;EAIL,KAAA,EAAO,MAAA;;;;EAIP,IAAA,EAAM,MAAA;;;;EAIN,GAAA,EAAK,MAAA;AAAA"}
|
package/dist/types.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/dist/url.d.mts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ExtendedRequestInit, FetchDefaults } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/url.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolves final request URL by applying baseURL and layered search params.
|
|
6
|
+
*
|
|
7
|
+
* Search param precedence:
|
|
8
|
+
* 1. `defaults.searchParams`
|
|
9
|
+
* 2. search params already present in `resourceUrl`
|
|
10
|
+
* 3. per-request `searchParams`
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const finalUrl = resolveRequestUrl(
|
|
14
|
+
* "/users?page=2",
|
|
15
|
+
* { ...defaults, baseURL: "https://api.example.com", searchParams: { locale: "en" } },
|
|
16
|
+
* { page: "3" },
|
|
17
|
+
* );
|
|
18
|
+
* // https://api.example.com/users?locale=en&page=3
|
|
19
|
+
*
|
|
20
|
+
* @throws {TypeError} When `baseURL` and `resourceUrl` cannot be resolved by
|
|
21
|
+
* `URL`, or when default/per-request search params cannot be converted by
|
|
22
|
+
* `URLSearchParams`.
|
|
23
|
+
*/
|
|
24
|
+
declare function resolveRequestUrl(resourceUrl: string, defaults: FetchDefaults, searchParams: ExtendedRequestInit["searchParams"] | undefined): string;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { resolveRequestUrl };
|
|
27
|
+
//# sourceMappingURL=url.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.mts","names":[],"sources":["../src/url.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;iBA4BgB,iBAAA,CACd,WAAA,UACA,QAAA,EAAU,aAAA,EACV,YAAA,EAAc,mBAAA"}
|
package/dist/url.mjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/url.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolves final request URL by applying baseURL and layered search params.
|
|
4
|
+
*
|
|
5
|
+
* Search param precedence:
|
|
6
|
+
* 1. `defaults.searchParams`
|
|
7
|
+
* 2. search params already present in `resourceUrl`
|
|
8
|
+
* 3. per-request `searchParams`
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const finalUrl = resolveRequestUrl(
|
|
12
|
+
* "/users?page=2",
|
|
13
|
+
* { ...defaults, baseURL: "https://api.example.com", searchParams: { locale: "en" } },
|
|
14
|
+
* { page: "3" },
|
|
15
|
+
* );
|
|
16
|
+
* // https://api.example.com/users?locale=en&page=3
|
|
17
|
+
*
|
|
18
|
+
* @throws {TypeError} When `baseURL` and `resourceUrl` cannot be resolved by
|
|
19
|
+
* `URL`, or when default/per-request search params cannot be converted by
|
|
20
|
+
* `URLSearchParams`.
|
|
21
|
+
*/
|
|
22
|
+
function resolveRequestUrl(resourceUrl, defaults, searchParams) {
|
|
23
|
+
return resolveSearchParams(defaults.baseURL ? new URL(resourceUrl, ensureTrailingSlash(defaults.baseURL)).toString() : resourceUrl, defaults.searchParams, searchParams);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolves search params by applying default params, URL params, then request params.
|
|
27
|
+
*/
|
|
28
|
+
function resolveSearchParams(url, defaultSearchParams, searchParams) {
|
|
29
|
+
const hasFragment = url.includes("#");
|
|
30
|
+
const [urlWithoutHash = "", hash = ""] = url.split("#", 2);
|
|
31
|
+
const [pathname = "", urlSearchParams] = urlWithoutHash.split("?", 2);
|
|
32
|
+
const resolvedSearchParams = new URLSearchParams();
|
|
33
|
+
mergeSearchParams(resolvedSearchParams, defaultSearchParams);
|
|
34
|
+
mergeSearchParams(resolvedSearchParams, urlSearchParams);
|
|
35
|
+
mergeSearchParams(resolvedSearchParams, searchParams);
|
|
36
|
+
const resolvedSearch = resolvedSearchParams.toString();
|
|
37
|
+
const fragmentSuffix = hasFragment ? `#${hash}` : "";
|
|
38
|
+
if (!resolvedSearch) return `${pathname}${fragmentSuffix}`;
|
|
39
|
+
return `${pathname}?${resolvedSearch}${fragmentSuffix}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Ensures a URL has a trailing slash for relative URL resolution.
|
|
43
|
+
*/
|
|
44
|
+
function ensureTrailingSlash(url) {
|
|
45
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Copies search params into target, overriding duplicate keys.
|
|
49
|
+
*/
|
|
50
|
+
function mergeSearchParams(target, source) {
|
|
51
|
+
for (const [key, value] of new URLSearchParams(source)) target.set(key, value);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { resolveRequestUrl };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=url.mjs.map
|
package/dist/url.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.mjs","names":[],"sources":["../src/url.ts"],"sourcesContent":["/**\n * URL resolution and query merging utilities.\n *\n * @module\n */\n\nimport type { ExtendedRequestInit, FetchDefaults } from \"./types.js\";\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 function resolveRequestUrl(\n resourceUrl: string,\n defaults: FetchDefaults,\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined,\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\n/**\n * Resolves search params by applying default params, URL params, then request params.\n */\nfunction resolveSearchParams(\n url: string,\n defaultSearchParams: FetchDefaults[\"searchParams\"] | undefined,\n searchParams: ExtendedRequestInit[\"searchParams\"] | undefined,\n): string {\n const hasFragment = url.includes(\"#\");\n const [urlWithoutHash = \"\", hash = \"\"] = url.split(\"#\", 2);\n const [pathname = \"\", urlSearchParams] = urlWithoutHash.split(\"?\", 2);\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) {\n return `${pathname}${fragmentSuffix}`;\n }\n\n return `${pathname}?${resolvedSearch}${fragmentSuffix}`;\n}\n\n/**\n * Ensures a URL has a trailing slash for relative URL resolution.\n */\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith(\"/\") ? url : `${url}/`;\n}\n\n/**\n * Copies search params into target, overriding duplicate keys.\n */\nfunction 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"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,kBACd,aACA,UACA,cACQ;AAKR,QAAO,oBAJK,SAAS,UACjB,IAAI,IAAI,aAAa,oBAAoB,SAAS,QAAQ,CAAC,CAAC,UAAU,GACtE,aAE4B,SAAS,cAAc,aAAa;;;;;AAMtE,SAAS,oBACP,KACA,qBACA,cACQ;CACR,MAAM,cAAc,IAAI,SAAS,IAAI;CACrC,MAAM,CAAC,iBAAiB,IAAI,OAAO,MAAM,IAAI,MAAM,KAAK,EAAE;CAC1D,MAAM,CAAC,WAAW,IAAI,mBAAmB,eAAe,MAAM,KAAK,EAAE;CACrE,MAAM,uBAAuB,IAAI,iBAAiB;AAElD,mBAAkB,sBAAsB,oBAAoB;AAC5D,mBAAkB,sBAAsB,gBAAgB;AACxD,mBAAkB,sBAAsB,aAAa;CAErD,MAAM,iBAAiB,qBAAqB,UAAU;CACtD,MAAM,iBAAiB,cAAc,IAAI,SAAS;AAElD,KAAI,CAAC,eACH,QAAO,GAAG,WAAW;AAGvB,QAAO,GAAG,SAAS,GAAG,iBAAiB;;;;;AAMzC,SAAS,oBAAoB,KAAqB;AAChD,QAAO,IAAI,SAAS,IAAI,GAAG,MAAM,GAAG,IAAI;;;;;AAM1C,SAAS,kBACP,QACA,QACM;AACN,MAAK,MAAM,CAAC,KAAK,UAAU,IAAI,gBAAgB,OAAO,CACpD,QAAO,IAAI,KAAK,MAAM"}
|
package/package.json
CHANGED
|
@@ -1,74 +1,73 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zap-studio/fetch",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"license": "MIT",
|
|
3
|
+
"version": "0.5.0",
|
|
6
4
|
"private": false,
|
|
7
|
-
"homepage": "https://www.zapstudio.dev/packages/fetch",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "https://github.com/zap-studio/monorepo.git",
|
|
11
|
-
"directory": "packages/fetch"
|
|
12
|
-
},
|
|
13
5
|
"description": "A type-safe fetch wrapper for HTTP requests with runtime schema validation.",
|
|
14
6
|
"keywords": [
|
|
15
|
-
"fetch",
|
|
16
|
-
"type-safe",
|
|
17
|
-
"validation",
|
|
18
|
-
"schema",
|
|
19
|
-
"typescript",
|
|
20
|
-
"http",
|
|
21
7
|
"api",
|
|
22
|
-
"zod",
|
|
23
|
-
"valibot",
|
|
24
8
|
"arktype",
|
|
25
|
-
"runtime validation",
|
|
26
9
|
"error handling",
|
|
27
|
-
"
|
|
10
|
+
"fetch",
|
|
11
|
+
"http",
|
|
28
12
|
"request",
|
|
29
|
-
"response"
|
|
13
|
+
"response",
|
|
14
|
+
"runtime validation",
|
|
15
|
+
"schema",
|
|
16
|
+
"standard schema",
|
|
17
|
+
"type-safe",
|
|
18
|
+
"typescript",
|
|
19
|
+
"valibot",
|
|
20
|
+
"validation",
|
|
21
|
+
"zod"
|
|
30
22
|
],
|
|
31
|
-
"
|
|
32
|
-
|
|
23
|
+
"homepage": "https://www.zapstudio.dev/packages/fetch",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/zap-studio/monorepo.git",
|
|
28
|
+
"directory": "packages/fetch"
|
|
33
29
|
},
|
|
34
30
|
"files": [
|
|
35
31
|
"dist",
|
|
36
32
|
"CHANGELOG.md",
|
|
37
|
-
"LICENSE
|
|
33
|
+
"LICENSE",
|
|
38
34
|
"README.md"
|
|
39
35
|
],
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/node": "^25.0.2",
|
|
46
|
-
"@vitest/coverage-v8": "^4.0.15",
|
|
47
|
-
"arktype": "^2.1.29",
|
|
48
|
-
"tsdown": "^0.18.0",
|
|
49
|
-
"typescript": "^5.9.3",
|
|
50
|
-
"valibot": "^1.2.0",
|
|
51
|
-
"vitest": "^4.0.18",
|
|
52
|
-
"zod": "^4.2.0",
|
|
53
|
-
"@zap-studio/vitest-config": "0.0.0",
|
|
54
|
-
"@zap-studio/typescript-config": "0.0.0",
|
|
55
|
-
"@zap-studio/tsdown-config": "0.0.0"
|
|
56
|
-
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"types": "./dist/index.d.mts",
|
|
57
39
|
"exports": {
|
|
58
40
|
".": "./dist/index.mjs",
|
|
59
41
|
"./constants": "./dist/constants.mjs",
|
|
60
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",
|
|
61
47
|
"./types": "./dist/types.mjs",
|
|
62
|
-
"./
|
|
48
|
+
"./url": "./dist/url.mjs",
|
|
63
49
|
"./package.json": "./package.json"
|
|
64
50
|
},
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@zap-studio/validation": "0.3.2"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"arktype": "^2.2.0",
|
|
59
|
+
"typescript": "^6.0.3",
|
|
60
|
+
"valibot": "^1.3.1",
|
|
61
|
+
"vite-plus": "^0.1.19",
|
|
62
|
+
"zod": "^4.3.6",
|
|
63
|
+
"@zap-studio/typescript": "0.0.0"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
67
|
+
},
|
|
68
68
|
"scripts": {
|
|
69
|
-
"build": "
|
|
70
|
-
"
|
|
71
|
-
"test": "
|
|
72
|
-
"test:watch": "vitest --watch"
|
|
69
|
+
"build": "vp pack",
|
|
70
|
+
"test": "vp test run",
|
|
71
|
+
"test:watch": "vp test watch"
|
|
73
72
|
}
|
|
74
73
|
}
|
package/dist/errors-DQfwnwmz.mjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//#region src/errors.ts
|
|
2
|
-
/**
|
|
3
|
-
* Error thrown for HTTP errors (non-2xx responses)
|
|
4
|
-
*/
|
|
5
|
-
var FetchError = class extends Error {
|
|
6
|
-
status;
|
|
7
|
-
response;
|
|
8
|
-
constructor(message, response) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = "FetchError";
|
|
11
|
-
this.status = response.status;
|
|
12
|
-
this.response = response;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
//#endregion
|
|
17
|
-
export { FetchError as t };
|
|
18
|
-
//# sourceMappingURL=errors-DQfwnwmz.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-DQfwnwmz.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Error thrown for HTTP errors (non-2xx responses)\n */\nexport class FetchError extends Error {\n status: Response[\"status\"];\n response: 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":";;;;AAGA,IAAa,aAAb,cAAgC,MAAM;CACpC;CACA;CAEA,YAAY,SAAiB,UAAoB;AAC/C,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS,SAAS;AACvB,OAAK,WAAW"}
|
package/dist/utils.d.mts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { $Fetch, ExtendedRequestInit, FetchDefaults } from "./types.mjs";
|
|
2
|
-
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
-
|
|
4
|
-
//#region src/utils.d.ts
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Merges two HeadersInit objects, with the second one taking precedence
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const baseHeaders = { "Authorization": "Bearer token", "Content-Type": "application/json" };
|
|
11
|
-
* const overrideHeaders = { "Content-Type": "application/xml", "X-Custom-Header": "value" };
|
|
12
|
-
*
|
|
13
|
-
* const merged = mergeHeaders(baseHeaders, overrideHeaders);
|
|
14
|
-
*
|
|
15
|
-
* // Resulting headers:
|
|
16
|
-
* // {
|
|
17
|
-
* // "Authorization": "Bearer token",
|
|
18
|
-
* // "Content-Type": "application/xml",
|
|
19
|
-
* // "X-Custom-Header": "value"
|
|
20
|
-
* // }
|
|
21
|
-
*/
|
|
22
|
-
declare function mergeHeaders(base: HeadersInit | undefined, override: HeadersInit | undefined): Headers | undefined;
|
|
23
|
-
/**
|
|
24
|
-
* Internal fetch implementation used by both $fetch and createFetch
|
|
25
|
-
*/
|
|
26
|
-
declare function fetchInternal(resource: string, schema: StandardSchemaV1 | undefined, options: ExtendedRequestInit | undefined, defaults: FetchDefaults): Promise<unknown>;
|
|
27
|
-
/**
|
|
28
|
-
* Creates an HTTP method helper bound to a fetch function
|
|
29
|
-
*/
|
|
30
|
-
declare function createMethod<TFetch extends $Fetch>(fetchFn: TFetch, method: string): $Fetch;
|
|
31
|
-
//#endregion
|
|
32
|
-
export { createMethod, fetchInternal, mergeHeaders };
|
|
33
|
-
//# sourceMappingURL=utils.d.mts.map
|
package/dist/utils.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.mts","names":[],"sources":["../src/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;AA0BA;;;;;AAyLA;;;;;;AAiFA;;;AAGG,iBA7Qa,YAAA,CA6Qb,IAAA,EA5QK,WA4QL,GAAA,SAAA,EAAA,QAAA,EA3QS,WA2QT,GAAA,SAAA,CAAA,EA1QA,OA0QA,GAAA,SAAA;;;;iBApFmB,aAAA,2BAEZ,uCACC,2CACC,gBACT;;;;iBA4Ea,4BAA4B,iBACjC,yBAER"}
|
package/dist/utils.mjs
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { t as FetchError } from "./errors-DQfwnwmz.mjs";
|
|
2
|
-
import { isStandardSchema, standardValidate } from "@zap-studio/validation";
|
|
3
|
-
|
|
4
|
-
//#region src/utils.ts
|
|
5
|
-
/**
|
|
6
|
-
* Merges two HeadersInit objects, with the second one taking precedence
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* const baseHeaders = { "Authorization": "Bearer token", "Content-Type": "application/json" };
|
|
10
|
-
* const overrideHeaders = { "Content-Type": "application/xml", "X-Custom-Header": "value" };
|
|
11
|
-
*
|
|
12
|
-
* const merged = mergeHeaders(baseHeaders, overrideHeaders);
|
|
13
|
-
*
|
|
14
|
-
* // Resulting headers:
|
|
15
|
-
* // {
|
|
16
|
-
* // "Authorization": "Bearer token",
|
|
17
|
-
* // "Content-Type": "application/xml",
|
|
18
|
-
* // "X-Custom-Header": "value"
|
|
19
|
-
* // }
|
|
20
|
-
*/
|
|
21
|
-
function mergeHeaders(base, override) {
|
|
22
|
-
if (!(base || override)) return;
|
|
23
|
-
const merged = new Headers(base);
|
|
24
|
-
if (override) {
|
|
25
|
-
const overrideHeaders = new Headers(override);
|
|
26
|
-
for (const [key, value] of overrideHeaders.entries()) merged.set(key, value);
|
|
27
|
-
}
|
|
28
|
-
return merged;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Removes trailing slashes from a string
|
|
32
|
-
*/
|
|
33
|
-
function trimTrailingSlashes(str) {
|
|
34
|
-
let end = str.length;
|
|
35
|
-
while (end > 0 && str[end - 1] === "/") end -= 1;
|
|
36
|
-
return str.slice(0, end);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Removes leading slashes from a string
|
|
40
|
-
*/
|
|
41
|
-
function trimLeadingSlashes(str) {
|
|
42
|
-
let start = 0;
|
|
43
|
-
while (start < str.length && str[start] === "/") start += 1;
|
|
44
|
-
return str.slice(start);
|
|
45
|
-
}
|
|
46
|
-
const ABSOLUTE_URL_PATTERN = /^(?:https?:)?\/\//i;
|
|
47
|
-
/**
|
|
48
|
-
* Checks if a URL is absolute (starts with http://, https://, or //)
|
|
49
|
-
*/
|
|
50
|
-
function isAbsoluteURL(url) {
|
|
51
|
-
return ABSOLUTE_URL_PATTERN.test(url);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Normalizes search parameters into a URLSearchParams object.
|
|
55
|
-
*/
|
|
56
|
-
function normalizeSearchParams(input) {
|
|
57
|
-
if (input === void 0 || input === null) return new URLSearchParams();
|
|
58
|
-
if (input instanceof URLSearchParams) return new URLSearchParams(input);
|
|
59
|
-
if (typeof input === "string") return new URLSearchParams(input);
|
|
60
|
-
if (Array.isArray(input)) return new URLSearchParams(input);
|
|
61
|
-
const params = new URLSearchParams();
|
|
62
|
-
for (const [k, v] of Object.entries(input)) params.set(k, v);
|
|
63
|
-
return params;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Parses a URL string into its path, query, and hash components.
|
|
67
|
-
* Handles both absolute URLs (using URL constructor) and relative paths.
|
|
68
|
-
*/
|
|
69
|
-
function parseUrlComponents(url) {
|
|
70
|
-
if (isAbsoluteURL(url)) try {
|
|
71
|
-
const parsed = new URL(url);
|
|
72
|
-
const pathOnly = `${parsed.origin}${parsed.pathname}`;
|
|
73
|
-
const existingQuery = parsed.search.startsWith("?") ? parsed.search.slice(1) : parsed.search;
|
|
74
|
-
let hash$1 = parsed.hash;
|
|
75
|
-
if (hash$1 === "" && url.endsWith("#")) hash$1 = "#";
|
|
76
|
-
return {
|
|
77
|
-
pathOnly,
|
|
78
|
-
existingQuery,
|
|
79
|
-
hash: hash$1
|
|
80
|
-
};
|
|
81
|
-
} catch {}
|
|
82
|
-
let hash = "";
|
|
83
|
-
let urlWithoutHash = url;
|
|
84
|
-
const hashIndex = url.indexOf("#");
|
|
85
|
-
if (hashIndex !== -1) {
|
|
86
|
-
hash = url.slice(hashIndex);
|
|
87
|
-
urlWithoutHash = url.slice(0, hashIndex);
|
|
88
|
-
}
|
|
89
|
-
const queryIndex = urlWithoutHash.indexOf("?");
|
|
90
|
-
if (queryIndex === -1) return {
|
|
91
|
-
pathOnly: urlWithoutHash,
|
|
92
|
-
existingQuery: "",
|
|
93
|
-
hash
|
|
94
|
-
};
|
|
95
|
-
return {
|
|
96
|
-
pathOnly: urlWithoutHash.slice(0, queryIndex),
|
|
97
|
-
existingQuery: urlWithoutHash.slice(queryIndex + 1),
|
|
98
|
-
hash
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Builds a URL with merged search parameters from factory defaults,
|
|
103
|
-
* request options, and existing query parameters.
|
|
104
|
-
*
|
|
105
|
-
* This function takes a base URL, factory search parameters,
|
|
106
|
-
* and request search parameters, and combines them into a single URL.
|
|
107
|
-
*
|
|
108
|
-
* Priority (highest to lowest):
|
|
109
|
-
* 1. Request search parameters (highest priority, overwrites all others)
|
|
110
|
-
* 2. Existing query parameters from the URL (overwrites factory defaults)
|
|
111
|
-
* 3. Factory search parameters (lowest priority, overwritten by all others)
|
|
112
|
-
*
|
|
113
|
-
* If no search parameters are provided, the URL will not have a query string.
|
|
114
|
-
* Any trailing hash/fragment is preserved.
|
|
115
|
-
*/
|
|
116
|
-
function buildUrlWithMergedSearchParams(url, factorySearch, requestSearch) {
|
|
117
|
-
const { pathOnly, existingQuery, hash } = parseUrlComponents(url);
|
|
118
|
-
if (!(factorySearch ?? requestSearch ?? existingQuery)) return url;
|
|
119
|
-
const mergedParams = new URLSearchParams();
|
|
120
|
-
const resourceParams = new URLSearchParams(existingQuery);
|
|
121
|
-
const factoryParams = normalizeSearchParams(factorySearch);
|
|
122
|
-
const reqParams = normalizeSearchParams(requestSearch);
|
|
123
|
-
for (const [k, v] of factoryParams.entries()) mergedParams.set(k, v);
|
|
124
|
-
for (const [k, v] of resourceParams.entries()) mergedParams.set(k, v);
|
|
125
|
-
for (const [k, v] of reqParams.entries()) mergedParams.set(k, v);
|
|
126
|
-
const queryString = mergedParams.toString();
|
|
127
|
-
if (queryString) return `${pathOnly}?${queryString}${hash}`;
|
|
128
|
-
return `${pathOnly}${hash}`;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Internal fetch implementation used by both $fetch and createFetch
|
|
132
|
-
*/
|
|
133
|
-
async function fetchInternal(resource, schema, options, defaults) {
|
|
134
|
-
const { throwOnValidationError = defaults.throwOnValidationError, throwOnFetchError = defaults.throwOnFetchError, headers: requestHeaders, searchParams: requestSearchParams, ...rest } = options || {};
|
|
135
|
-
const mergedHeaders = mergeHeaders(defaults.headers, requestHeaders);
|
|
136
|
-
const init = {
|
|
137
|
-
...rest,
|
|
138
|
-
headers: mergedHeaders
|
|
139
|
-
};
|
|
140
|
-
const isBodyInit = typeof init.body === "string" || init.body instanceof FormData || init.body instanceof URLSearchParams || init.body instanceof Blob || init.body instanceof ArrayBuffer || ArrayBuffer.isView(init.body) || init.body instanceof ReadableStream;
|
|
141
|
-
if (init.body != null && !isBodyInit) {
|
|
142
|
-
init.body = JSON.stringify(init.body);
|
|
143
|
-
const existingHeaders = new Headers(init.headers);
|
|
144
|
-
if (!existingHeaders.has("Content-Type")) existingHeaders.set("Content-Type", "application/json");
|
|
145
|
-
init.headers = existingHeaders;
|
|
146
|
-
}
|
|
147
|
-
let url;
|
|
148
|
-
if (isAbsoluteURL(resource)) url = resource;
|
|
149
|
-
else {
|
|
150
|
-
const base = trimTrailingSlashes(defaults.baseURL);
|
|
151
|
-
const path = trimLeadingSlashes(resource);
|
|
152
|
-
url = base ? `${base}/${path}` : resource;
|
|
153
|
-
}
|
|
154
|
-
url = buildUrlWithMergedSearchParams(url, defaults.searchParams, requestSearchParams);
|
|
155
|
-
const response = await fetch(url, init);
|
|
156
|
-
if (throwOnFetchError && !response.ok) throw new FetchError(`HTTP ${response.status}: ${response.statusText}`, response);
|
|
157
|
-
if (schema) {
|
|
158
|
-
const raw = await response.json();
|
|
159
|
-
if (throwOnValidationError) return standardValidate(schema, raw, { throwOnError: true });
|
|
160
|
-
return standardValidate(schema, raw, { throwOnError: false });
|
|
161
|
-
}
|
|
162
|
-
return response;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Creates an HTTP method helper bound to a fetch function
|
|
166
|
-
*/
|
|
167
|
-
function createMethod(fetchFn, method) {
|
|
168
|
-
function methodFetch(resource, schemaOrOptions, optionsOrUndefined) {
|
|
169
|
-
const [schema, options] = isStandardSchema(schemaOrOptions) ? [schemaOrOptions, optionsOrUndefined] : [void 0, schemaOrOptions];
|
|
170
|
-
return fetchFn(resource, schema, {
|
|
171
|
-
...options,
|
|
172
|
-
method
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
return methodFetch;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
//#endregion
|
|
179
|
-
export { createMethod, fetchInternal, mergeHeaders };
|
|
180
|
-
//# sourceMappingURL=utils.mjs.map
|