@zap-studio/fetch 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @zap-studio/fetch
2
2
 
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 69c2b21: Change `safeFetch` to `$fetch` syntax and make sure `safeFetch` can also be used for legacy
8
+
9
+ ## 0.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 5f1812b: Change files field in package.json to distribute only necessary artifacts
14
+
3
15
  ## 0.1.0
4
16
 
5
17
  ### Minor Changes
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # @zap-studio/fetch
2
+
3
+ A type-safe fetch wrapper with Zod validation for TypeScript.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Type-safe requests** with automatic type inference
8
+ - 🛡️ **Runtime validation** using Zod schemas
9
+ - 🔄 **Automatic content-type** detection and handling
10
+ - ⚡️ **Convenient API methods** (GET, POST, PUT, PATCH, DELETE)
11
+ - 📦 **Multiple response types** (JSON, text, blob, arrayBuffer)
12
+ - 🚨 **Custom error handling** with FetchError class
13
+ - 📘 **Full TypeScript support** with zero configuration
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @zap-studio/fetch
19
+ #or
20
+ npm install @zap-studio/fetch
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { z } from "zod";
27
+ import { api } from "@zap-studio/fetch";
28
+
29
+ // Define your schema
30
+ const UserSchema = z.object({
31
+ id: z.number(),
32
+ name: z.string(),
33
+ email: z.email(),
34
+ });
35
+
36
+ // Make a type-safe request
37
+ const user = await api.get(
38
+ "https://api.example.com/users/1",
39
+ UserSchema
40
+ );
41
+
42
+ // user is fully typed and validated! ✨
43
+ console.log(user.name); // TypeScript knows this is a string
44
+ ```
45
+
46
+ ## API
47
+
48
+ ### `api.get(url, schema, options?)`
49
+
50
+ ```typescript
51
+ const user = await api.get("/api/users/1", UserSchema);
52
+ ```
53
+
54
+ ### `api.post(url, schema, body?, options?)`
55
+
56
+ ```typescript
57
+ const newUser = await api.post("/api/users", UserSchema, {
58
+ name: "John Doe",
59
+ email: "john@example.com",
60
+ });
61
+ ```
62
+
63
+ ### `api.put(url, schema, body?, options?)`
64
+
65
+ ```typescript
66
+ const updated = await api.put("/api/users/1", UserSchema, {
67
+ name: "Jane Doe",
68
+ });
69
+ ```
70
+
71
+ ### `api.patch(url, schema, body?, options?)`
72
+
73
+ ```typescript
74
+ const patched = await api.patch("/api/users/1", UserSchema, {
75
+ email: "newemail@example.com",
76
+ });
77
+ ```
78
+
79
+ ### `api.delete(url, schema, options?)`
80
+
81
+ ```typescript
82
+ const deleted = await api.delete("/api/users/1", UserSchema);
83
+ ```
84
+
85
+ ## Advanced Usage
86
+
87
+ ### Using `$fetch` directly
88
+
89
+ ```typescript
90
+ import { $fetch } from "@zap-studio/fetch";
91
+
92
+ const result = await $fetch(
93
+ "https://api.example.com/users/1",
94
+ UserSchema,
95
+ {
96
+ method: "GET",
97
+ headers: {
98
+ "Authorization": "Bearer token",
99
+ },
100
+ }
101
+ );
102
+ ```
103
+
104
+ ### Custom response types
105
+
106
+ ```typescript
107
+ // Get text response
108
+ const text = await $fetch(
109
+ "/api/data",
110
+ z.string(),
111
+ { responseType: "text" }
112
+ );
113
+
114
+ // Get blob response
115
+ const blob = await $fetch(
116
+ "/api/file",
117
+ z.instanceof(Blob),
118
+ { responseType: "blob" }
119
+ );
120
+ ```
121
+
122
+ ### Error handling
123
+
124
+ ```typescript
125
+ import { FetchError } from "@zap-studio/fetch/errors";
126
+
127
+ try {
128
+ const user = await api.get("/api/users/1", UserSchema);
129
+ } catch (error) {
130
+ if (error instanceof FetchError) {
131
+ console.error(`HTTP ${error.status}: ${error.statusText}`);
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### Flexible validation
137
+
138
+ ```typescript
139
+ // Throw on validation error (default)
140
+ const user = await $fetch(url, UserSchema, {
141
+ throwOnValidationError: true,
142
+ });
143
+
144
+ // Return validation result without throwing
145
+ const result = await $fetch(url, UserSchema, {
146
+ throwOnValidationError: false,
147
+ });
148
+
149
+ if (result.success) {
150
+ console.log(result.data);
151
+ } else {
152
+ console.error(result.error);
153
+ }
154
+ ```
155
+
156
+ ## Why @zap-studio/fetch?
157
+
158
+ **Before:**
159
+ ```typescript
160
+ const response = await fetch("/api/users/1");
161
+ const data = await response.json();
162
+ const user = data as User; // 😱 Unsafe type assertion
163
+ ```
164
+
165
+ **After:**
166
+ ```typescript
167
+ const user = await api.get("/api/users/1", UserSchema);
168
+ // ✨ Typed, validated, and safe!
169
+ ```
@@ -0,0 +1,10 @@
1
+ //#region src/errors/index.d.ts
2
+ declare class FetchError extends Error {
3
+ status: number;
4
+ statusText: string;
5
+ response: Response;
6
+ constructor(message: string, status: number, statusText: string, response: Response);
7
+ }
8
+ //#endregion
9
+ export { FetchError };
10
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/errors/index.ts"],"sourcesContent":[],"mappings":";cAAa,UAAA,SAAmB,KAAA;EAAnB,MAAA,EAAA,MAAW;EAGZ,UAAA,EAAA,MAAA;EAME,QAAA,EANF,QAME;EATkB,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EASlB,QATkB"}
@@ -0,0 +1,3 @@
1
+ import { t as FetchError } from "../errors-BCMOymYz.mjs";
2
+
3
+ export { FetchError };
@@ -0,0 +1,17 @@
1
+ //#region src/errors/index.ts
2
+ var FetchError = class extends Error {
3
+ status;
4
+ statusText;
5
+ response;
6
+ constructor(message, status, statusText, response) {
7
+ super(message);
8
+ this.name = "FetchError";
9
+ this.status = status;
10
+ this.statusText = statusText;
11
+ this.response = response;
12
+ }
13
+ };
14
+
15
+ //#endregion
16
+ export { FetchError as t };
17
+ //# sourceMappingURL=errors-BCMOymYz.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-BCMOymYz.mjs","names":[],"sources":["../src/errors/index.ts"],"sourcesContent":["export class FetchError extends Error {\n status: number;\n statusText: string;\n response: Response;\n\n constructor(\n message: string,\n status: number,\n statusText: string,\n response: Response\n ) {\n super(message);\n this.name = \"FetchError\";\n this.status = status;\n this.statusText = statusText;\n this.response = response;\n }\n}\n"],"mappings":";AAAA,IAAa,aAAb,cAAgC,MAAM;CACpC;CACA;CACA;CAEA,YACE,SACA,QACA,YACA,UACA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,aAAa;AAClB,OAAK,WAAW"}
@@ -0,0 +1,18 @@
1
+ //#region src/types/index.d.ts
2
+ type FetchConfig<TBody> = Omit<RequestInit, "body"> & {
3
+ body?: TBody;
4
+ throwOnValidationError?: boolean;
5
+ };
6
+ type ResponseType = "arrayBuffer" | "blob" | "bytes" | "clone" | "formData" | "json" | "text";
7
+ type ResponseTypeMap = {
8
+ arrayBuffer: ArrayBuffer;
9
+ blob: Blob;
10
+ bytes: Uint8Array;
11
+ clone: Response;
12
+ formData: FormData;
13
+ json: unknown;
14
+ text: string;
15
+ };
16
+ //#endregion
17
+ export { ResponseType as n, ResponseTypeMap as r, FetchConfig as t };
18
+ //# sourceMappingURL=index-Dz58_HD6.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-Dz58_HD6.d.mts","names":[],"sources":["../src/types/index.ts"],"sourcesContent":[],"mappings":";KAAY,qBAAqB,KAAK;EAA1B,IAAA,CAAA,EACH,KADG;EAA0B,sBAAA,CAAA,EAAA,OAAA;CAAL;AACxB,KAIG,YAAA,GAJH,aAAA,GAAA,MAAA,GAAA,OAAA,GAAA,OAAA,GAAA,UAAA,GAAA,MAAA,GAAA,MAAA;AAAK,KAaF,eAAA,GAbE;EAIF,WAAA,EAUG,WAVS;EASZ,IAAA,EAEJ,IAFI;EACG,KAAA,EAEN,UAFM;EACP,KAAA,EAEC,QAFD;EACC,QAAA,EAEG,QAFH;EACA,IAAA,EAAA,OAAA;EACG,IAAA,EAAA,MAAA;CAAQ"}
@@ -0,0 +1,60 @@
1
+ import { n as ResponseType, t as FetchConfig } from "./index-Dz58_HD6.mjs";
2
+ import { z } from "zod";
3
+
4
+ //#region src/index.d.ts
5
+
6
+ /**
7
+ * Type-safe fetch wrapper with Zod validation
8
+ *
9
+ * @example
10
+ * import { z } from "zod";
11
+ * import { $fetch } from "@zap-studio/fetch";
12
+ *
13
+ * const UserSchema = z.object({
14
+ * id: z.number(),
15
+ * name: z.string(),
16
+ * email: z.string().email(),
17
+ * });
18
+ *
19
+ * async function getUser(userId: number) {
20
+ * const user = await $fetch(
21
+ * `https://api.example.com/users/${userId}`,
22
+ * UserSchema,
23
+ * { method: "GET" }
24
+ * );
25
+ * return user; // user is typed as { id: number; name: string; email: string; }
26
+ * }
27
+ */
28
+ declare function $fetch<TResponse, TBody = unknown, TResponseType extends ResponseType = "json">(resource: string, responseSchema: z.ZodType<TResponse>, config?: FetchConfig<TBody> & {
29
+ throwOnValidationError?: boolean;
30
+ responseType?: TResponseType;
31
+ }): Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
32
+ declare const safeFetch: typeof $fetch;
33
+ /**
34
+ * Convenience methods for common HTTP verbs
35
+ *
36
+ * @example
37
+ * import { z } from "zod";
38
+ * import { api } from "@zap-studio/fetch";
39
+ *
40
+ * const PostSchema = z.object({
41
+ * id: z.number(),
42
+ * title: z.string(),
43
+ * content: z.string(),
44
+ * });
45
+ *
46
+ * async function fetchPost(postId: number) {
47
+ * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);
48
+ * return post; // post is typed as { id: number; title: string; content: string; }
49
+ * }
50
+ */
51
+ declare const api: {
52
+ get: <TResponse>(resource: string, schema: z.ZodType<TResponse>, options?: Omit<RequestInit, "method" | "body">) => Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
53
+ post: <TResponse, TBody = unknown>(resource: string, schema: z.ZodType<TResponse>, body?: TBody, options?: Omit<RequestInit, "method" | "body">) => Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
54
+ put: <TResponse, TBody = unknown>(resource: string, schema: z.ZodType<TResponse>, body?: TBody, options?: Omit<RequestInit, "method" | "body">) => Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
55
+ patch: <TResponse, TBody = unknown>(resource: string, schema: z.ZodType<TResponse>, body?: TBody, options?: Omit<RequestInit, "method" | "body">) => Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
56
+ delete: <TResponse>(resource: string, schema: z.ZodType<TResponse>, options?: Omit<RequestInit, "method" | "body">) => Promise<TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>>;
57
+ };
58
+ //#endregion
59
+ export { $fetch, api, safeFetch };
60
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AA2BA;;;;;;;;;;;;;;AA+CA;AAoBA;;;;;AAIkD,iBAvE5B,MAuE4B,CAAA,SAAA,EAAA,QAAA,OAAA,EAAA,sBApE1B,YAoE0B,GAAA,MAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,cAAA,EAjEhC,CAAA,CAAE,OAiE8B,CAjEtB,SAiEsB,CAAA,EAAA,MAAA,CAAA,EAhEvC,WAgEuC,CAhE3B,KAgE2B,CAAA,GAAA;EAAA,sBAAA,CAAA,EAAA,OAAA;EAAA,YAAA,CAAA,EA9D/B,aA8D+B;CAAA,CAAA,EA5D/C,OA4D+C,CA3DhD,SA2DgD,GA3DpC,CAAA,CAAE,mBA2DkC,CA3Dd,SA2Dc,CAAA,GA3DD,CAAA,CAAE,iBA2DD,CA3DmB,SA2DnB,CAAA,CAAA;AAAA,cAxBrC,SAwBqC,EAAA,OAxB5B,MAwB4B;;;;;;;;;;;;;;;;;;;AAcA,cAlBrC,GAkBqC,EAAA;EAAA,GAAA,EAAA,CAAA,SAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAftC,CAAA,CAAE,OAeoC,CAf5B,SAe4B,CAAA,EAAA,OAAA,CAAA,EAdpC,IAcoC,CAd/B,WAc+B,EAAA,QAAA,GAAA,MAAA,CAAA,EAAA,GAdA,OAcA,CAdA,SAcA,GAdA,CAAA,CAAA,mBAcA,CAdA,SAcA,CAAA,GAdA,CAAA,CAAA,iBAcA,CAdA,SAcA,CAAA,CAAA;EAAA,IAAA,EAAA,CAAA,SAAA,EAAA,QAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EATtC,CAAA,CAAE,OASoC,CAT5B,SAS4B,CAAA,EAAA,IAAA,CAAA,EARvC,KAQuC,EAAA,OAAA,CAAA,EAPpC,IAOoC,CAP/B,WAO+B,EAAA,QAAA,GAAA,MAAA,CAAA,EAAA,GAPA,OAOA,CAPA,SAOA,GAPA,CAAA,CAAA,mBAOA,CAPA,SAOA,CAAA,GAPA,CAAA,CAAA,iBAOA,CAPA,SAOA,CAAA,CAAA;EAAA,GAAA,EAAA,CAAA,SAAA,EAAA,QAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAFtC,CAAA,CAAE,OAEoC,CAF5B,SAE4B,CAAA,EAAA,IAAA,CAAA,EADvC,KACuC,EAAA,OAAA,CAAA,EAApC,IAAoC,CAA/B,WAA+B,EAAA,QAAA,GAAA,MAAA,CAAA,EAAA,GAAA,OAAA,CAAA,SAAA,GAAA,CAAA,CAAA,mBAAA,CAAA,SAAA,CAAA,GAAA,CAAA,CAAA,iBAAA,CAAA,SAAA,CAAA,CAAA;EAAA,KAAA,EAAA,CAAA,SAAA,EAAA,QAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAKtC,CAAA,CAAE,OALoC,CAK5B,SAL4B,CAAA,EAAA,IAAA,CAAA,EAMvC,KANuC,EAAA,OAAA,CAAA,EAOpC,IAPoC,CAO/B,WAP+B,EAAA,QAAA,GAAA,MAAA,CAAA,EAAA,GAOA,OAPA,CAOA,SAPA,GAOA,CAAA,CAAA,mBAPA,CAOA,SAPA,CAAA,GAOA,CAAA,CAAA,iBAPA,CAOA,SAPA,CAAA,CAAA;EAK5B,MAAA,EAAA,CAAA,SAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAOV,CAAA,CAAE,OAPQ,CAOA,SAPA,CAAA,EAAA,OAAA,CAAA,EAQR,IARQ,CAQH,WARG,EAAA,QAAA,GAAA,MAAA,CAAA,EAAA,GAQ4B,OAR5B,CAQ4B,SAR5B,GAQ4B,CAAA,CAAA,mBAR5B,CAQ4B,SAR5B,CAAA,GAQ4B,CAAA,CAAA,iBAR5B,CAQ4B,SAR5B,CAAA,CAAA;CAAV"}
package/dist/index.mjs ADDED
@@ -0,0 +1,86 @@
1
+ import { t as FetchError } from "./errors-BCMOymYz.mjs";
2
+ import { n as prepareHeadersAndBody, t as parseResponse } from "./utils-CZ-1Z2-1.mjs";
3
+
4
+ //#region src/index.ts
5
+ /**
6
+ * Type-safe fetch wrapper with Zod validation
7
+ *
8
+ * @example
9
+ * import { z } from "zod";
10
+ * import { $fetch } from "@zap-studio/fetch";
11
+ *
12
+ * const UserSchema = z.object({
13
+ * id: z.number(),
14
+ * name: z.string(),
15
+ * email: z.string().email(),
16
+ * });
17
+ *
18
+ * async function getUser(userId: number) {
19
+ * const user = await $fetch(
20
+ * `https://api.example.com/users/${userId}`,
21
+ * UserSchema,
22
+ * { method: "GET" }
23
+ * );
24
+ * return user; // user is typed as { id: number; name: string; email: string; }
25
+ * }
26
+ */
27
+ async function $fetch(resource, responseSchema, config) {
28
+ const { body, headers, throwOnValidationError = true, responseType = "json", ...rest } = config || {};
29
+ const { body: preparedBody, headers: preparedHeaders } = prepareHeadersAndBody(body, headers);
30
+ const response = await fetch(resource, {
31
+ ...rest,
32
+ body: preparedBody,
33
+ headers: preparedHeaders
34
+ });
35
+ if (!response.ok) throw new FetchError(`HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, response);
36
+ const data = await parseResponse(response, responseType);
37
+ return throwOnValidationError ? responseSchema.parse(data) : responseSchema.safeParse(data);
38
+ }
39
+ const safeFetch = $fetch;
40
+ /**
41
+ * Convenience methods for common HTTP verbs
42
+ *
43
+ * @example
44
+ * import { z } from "zod";
45
+ * import { api } from "@zap-studio/fetch";
46
+ *
47
+ * const PostSchema = z.object({
48
+ * id: z.number(),
49
+ * title: z.string(),
50
+ * content: z.string(),
51
+ * });
52
+ *
53
+ * async function fetchPost(postId: number) {
54
+ * const post = await api.get(`https://api.example.com/posts/${postId}`, PostSchema);
55
+ * return post; // post is typed as { id: number; title: string; content: string; }
56
+ * }
57
+ */
58
+ const api = {
59
+ get: (resource, schema, options) => $fetch(resource, schema, {
60
+ ...options,
61
+ method: "GET"
62
+ }),
63
+ post: (resource, schema, body, options) => $fetch(resource, schema, {
64
+ ...options,
65
+ method: "POST",
66
+ body
67
+ }),
68
+ put: (resource, schema, body, options) => $fetch(resource, schema, {
69
+ ...options,
70
+ method: "PUT",
71
+ body
72
+ }),
73
+ patch: (resource, schema, body, options) => $fetch(resource, schema, {
74
+ ...options,
75
+ method: "PATCH",
76
+ body
77
+ }),
78
+ delete: (resource, schema, options) => $fetch(resource, schema, {
79
+ ...options,
80
+ method: "DELETE"
81
+ })
82
+ };
83
+
84
+ //#endregion
85
+ export { $fetch, api, safeFetch };
86
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { z } from \"zod\";\nimport { FetchError } from \"./errors\";\nimport type { FetchConfig, ResponseType } from \"./types\";\nimport { parseResponse, prepareHeadersAndBody } from \"./utils\";\n\n/**\n * Type-safe fetch wrapper with Zod validation\n *\n * @example\n * import { z } from \"zod\";\n * import { $fetch } from \"@zap-studio/fetch\";\n *\n * const UserSchema = z.object({\n * id: z.number(),\n * name: z.string(),\n * email: z.string().email(),\n * });\n *\n * async function getUser(userId: number) {\n * const user = await $fetch(\n * `https://api.example.com/users/${userId}`,\n * UserSchema,\n * { method: \"GET\" }\n * );\n * return user; // user is typed as { id: number; name: string; email: string; }\n * }\n */\nexport async function $fetch<\n TResponse,\n TBody = unknown,\n TResponseType extends ResponseType = \"json\",\n>(\n resource: string,\n responseSchema: z.ZodType<TResponse>,\n config?: FetchConfig<TBody> & {\n throwOnValidationError?: boolean;\n responseType?: TResponseType;\n }\n): Promise<\n TResponse | z.ZodSafeParseSuccess<TResponse> | z.ZodSafeParseError<TResponse>\n> {\n const {\n body,\n headers,\n throwOnValidationError = true,\n responseType = \"json\" as TResponseType,\n ...rest\n } = config || {};\n\n const { body: preparedBody, headers: preparedHeaders } =\n prepareHeadersAndBody(body, headers);\n\n const response = await fetch(resource, {\n ...rest,\n body: preparedBody,\n headers: preparedHeaders,\n });\n\n if (!response.ok) {\n throw new FetchError(\n `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n response.statusText,\n response\n );\n }\n\n const data = await parseResponse(response, responseType);\n\n return throwOnValidationError\n ? responseSchema.parse(data)\n : responseSchema.safeParse(data);\n}\n\nexport const safeFetch = $fetch;\n\n/**\n * Convenience methods for common HTTP verbs\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 = {\n get: <TResponse>(\n resource: string,\n schema: z.ZodType<TResponse>,\n options?: Omit<RequestInit, \"method\" | \"body\">\n ) => $fetch(resource, schema, { ...options, method: \"GET\" }),\n\n post: <TResponse, TBody = unknown>(\n resource: string,\n schema: z.ZodType<TResponse>,\n body?: TBody,\n options?: Omit<RequestInit, \"method\" | \"body\">\n ) => $fetch(resource, schema, { ...options, method: \"POST\", body }),\n\n put: <TResponse, TBody = unknown>(\n resource: string,\n schema: z.ZodType<TResponse>,\n body?: TBody,\n options?: Omit<RequestInit, \"method\" | \"body\">\n ) => $fetch(resource, schema, { ...options, method: \"PUT\", body }),\n\n patch: <TResponse, TBody = unknown>(\n resource: string,\n schema: z.ZodType<TResponse>,\n body?: TBody,\n options?: Omit<RequestInit, \"method\" | \"body\">\n ) => $fetch(resource, schema, { ...options, method: \"PATCH\", body }),\n\n delete: <TResponse>(\n resource: string,\n schema: z.ZodType<TResponse>,\n options?: Omit<RequestInit, \"method\" | \"body\">\n ) => $fetch(resource, schema, { ...options, method: \"DELETE\" }),\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,eAAsB,OAKpB,UACA,gBACA,QAMA;CACA,MAAM,EACJ,MACA,SACA,yBAAyB,MACzB,eAAe,QACf,GAAG,SACD,UAAU,EAAE;CAEhB,MAAM,EAAE,MAAM,cAAc,SAAS,oBACnC,sBAAsB,MAAM,QAAQ;CAEtC,MAAM,WAAW,MAAM,MAAM,UAAU;EACrC,GAAG;EACH,MAAM;EACN,SAAS;EACV,CAAC;AAEF,KAAI,CAAC,SAAS,GACZ,OAAM,IAAI,WACR,QAAQ,SAAS,OAAO,IAAI,SAAS,cACrC,SAAS,QACT,SAAS,YACT,SACD;CAGH,MAAM,OAAO,MAAM,cAAc,UAAU,aAAa;AAExD,QAAO,yBACH,eAAe,MAAM,KAAK,GAC1B,eAAe,UAAU,KAAK;;AAGpC,MAAa,YAAY;;;;;;;;;;;;;;;;;;;AAoBzB,MAAa,MAAM;CACjB,MACE,UACA,QACA,YACG,OAAO,UAAU,QAAQ;EAAE,GAAG;EAAS,QAAQ;EAAO,CAAC;CAE5D,OACE,UACA,QACA,MACA,YACG,OAAO,UAAU,QAAQ;EAAE,GAAG;EAAS,QAAQ;EAAQ;EAAM,CAAC;CAEnE,MACE,UACA,QACA,MACA,YACG,OAAO,UAAU,QAAQ;EAAE,GAAG;EAAS,QAAQ;EAAO;EAAM,CAAC;CAElE,QACE,UACA,QACA,MACA,YACG,OAAO,UAAU,QAAQ;EAAE,GAAG;EAAS,QAAQ;EAAS;EAAM,CAAC;CAEpE,SACE,UACA,QACA,YACG,OAAO,UAAU,QAAQ;EAAE,GAAG;EAAS,QAAQ;EAAU,CAAC;CAChE"}
@@ -0,0 +1,2 @@
1
+ import { n as ResponseType, r as ResponseTypeMap, t as FetchConfig } from "../index-Dz58_HD6.mjs";
2
+ export { FetchConfig, ResponseType, ResponseTypeMap };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,11 @@
1
+ import { n as ResponseType, r as ResponseTypeMap } from "../index-Dz58_HD6.mjs";
2
+
3
+ //#region src/utils/index.d.ts
4
+ declare function parseResponse<TResponseType extends ResponseType>(response: Response, responseType: TResponseType): Promise<ResponseTypeMap[TResponseType]>;
5
+ declare function prepareHeadersAndBody<TBody = unknown>(body: TBody, headers: HeadersInit | undefined): {
6
+ body: BodyInit | null;
7
+ headers: HeadersInit | undefined;
8
+ };
9
+ //#endregion
10
+ export { parseResponse, prepareHeadersAndBody };
11
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":[],"mappings":";;;iBAGgB,oCAAoC,wBACxC,wBACI,gBACb,QAAQ,gBAAgB;iBAmEX,6CACR,gBACG;EAxEK,IAAA,EAyEL,QAzEK,GAAa,IAAA;EAAuB,OAAA,EAyEf,WAzEe,GAAA,SAAA;CACxC"}
@@ -0,0 +1,4 @@
1
+ import "../errors-BCMOymYz.mjs";
2
+ import { n as prepareHeadersAndBody, t as parseResponse } from "../utils-CZ-1Z2-1.mjs";
3
+
4
+ export { parseResponse, prepareHeadersAndBody };
@@ -0,0 +1,53 @@
1
+ import { t as FetchError } from "./errors-BCMOymYz.mjs";
2
+
3
+ //#region src/utils/index.ts
4
+ function parseResponse(response, responseType) {
5
+ const contentType = response.headers.get("content-type");
6
+ const parser = {
7
+ json: async () => {
8
+ if (!contentType?.includes("application/json")) throw new FetchError(contentType ? `Expected JSON response but received content type: ${contentType}` : "Expected JSON response but received no content type", response.status, response.statusText, response);
9
+ return await response.json();
10
+ },
11
+ arrayBuffer: async () => response.arrayBuffer(),
12
+ blob: async () => response.blob(),
13
+ bytes: async () => {
14
+ const buffer = await response.arrayBuffer();
15
+ return new Uint8Array(buffer);
16
+ },
17
+ clone: async () => response.clone(),
18
+ formData: async () => {
19
+ if (!contentType?.includes("multipart/form-data")) throw new FetchError("Expected FormData response but received different content type", response.status, response.statusText, response);
20
+ return await response.formData();
21
+ },
22
+ text: async () => {
23
+ if (!contentType?.includes("text/")) throw new FetchError("Expected text response but received different content type", response.status, response.statusText, response);
24
+ return await response.text();
25
+ }
26
+ }[responseType];
27
+ if (!parser) return Promise.reject(new FetchError(`Unsupported response type: ${responseType}`, response.status, response.statusText, response));
28
+ return parser();
29
+ }
30
+ function prepareHeadersAndBody(body, headers) {
31
+ let preparedBody = null;
32
+ let preparedHeaders = headers;
33
+ if (body === null || body === void 0) return {
34
+ body: null,
35
+ headers: preparedHeaders
36
+ };
37
+ if (body instanceof FormData || body instanceof URLSearchParams || body instanceof Blob || body instanceof ArrayBuffer || body instanceof ReadableStream || typeof body === "string") preparedBody = body;
38
+ else if (typeof body === "object") {
39
+ preparedBody = JSON.stringify(body);
40
+ preparedHeaders = {
41
+ "Content-Type": "application/json",
42
+ ...headers
43
+ };
44
+ } else preparedBody = String(body);
45
+ return {
46
+ body: preparedBody,
47
+ headers: preparedHeaders
48
+ };
49
+ }
50
+
51
+ //#endregion
52
+ export { prepareHeadersAndBody as n, parseResponse as t };
53
+ //# sourceMappingURL=utils-CZ-1Z2-1.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-CZ-1Z2-1.mjs","names":["preparedBody: BodyInit | null"],"sources":["../src/utils/index.ts"],"sourcesContent":["import { FetchError } from \"../errors\";\nimport type { ResponseType, ResponseTypeMap } from \"../types\";\n\nexport function parseResponse<TResponseType extends ResponseType>(\n response: Response,\n responseType: TResponseType\n): Promise<ResponseTypeMap[TResponseType]> {\n const contentType = response.headers.get(\"content-type\");\n\n const parsers: Record<\n ResponseType,\n () => Promise<ResponseTypeMap[ResponseType]>\n > = {\n json: async () => {\n if (!contentType?.includes(\"application/json\")) {\n const errorMessage = contentType\n ? `Expected JSON response but received content type: ${contentType}`\n : \"Expected JSON response but received no content type\";\n throw new FetchError(\n errorMessage,\n response.status,\n response.statusText,\n response\n );\n }\n return await response.json();\n },\n arrayBuffer: async () => response.arrayBuffer(),\n blob: async () => response.blob(),\n bytes: async () => {\n const buffer = await response.arrayBuffer();\n return new Uint8Array(buffer);\n },\n clone: async () => response.clone(),\n formData: async () => {\n if (!contentType?.includes(\"multipart/form-data\")) {\n throw new FetchError(\n \"Expected FormData response but received different content type\",\n response.status,\n response.statusText,\n response\n );\n }\n return await response.formData();\n },\n text: async () => {\n if (!contentType?.includes(\"text/\")) {\n throw new FetchError(\n \"Expected text response but received different content type\",\n response.status,\n response.statusText,\n response\n );\n }\n return await response.text();\n },\n };\n\n const parser = parsers[responseType];\n if (!parser) {\n return Promise.reject(\n new FetchError(\n `Unsupported response type: ${responseType}`,\n response.status,\n response.statusText,\n response\n )\n ) as Promise<ResponseTypeMap[TResponseType]>;\n }\n\n return parser() as Promise<ResponseTypeMap[TResponseType]>;\n}\n\nexport function prepareHeadersAndBody<TBody = unknown>(\n body: TBody,\n headers: HeadersInit | undefined\n): { body: BodyInit | null; headers: HeadersInit | undefined } {\n let preparedBody: BodyInit | null = null;\n let preparedHeaders = headers;\n\n // Handle null/undefined body\n if (body === null || body === undefined) {\n return { body: null, headers: preparedHeaders };\n }\n\n // Check if body is already a valid BodyInit type\n if (\n body instanceof FormData ||\n body instanceof URLSearchParams ||\n body instanceof Blob ||\n body instanceof ArrayBuffer ||\n body instanceof ReadableStream ||\n typeof body === \"string\"\n ) {\n preparedBody = body as BodyInit;\n } else if (typeof body === \"object\") {\n // If body is a plain object, stringify it and set Content-Type\n preparedBody = JSON.stringify(body);\n preparedHeaders = {\n \"Content-Type\": \"application/json\",\n ...headers,\n };\n } else {\n // Handle other primitive types by converting to string\n preparedBody = String(body);\n }\n\n return { body: preparedBody, headers: preparedHeaders };\n}\n"],"mappings":";;;AAGA,SAAgB,cACd,UACA,cACyC;CACzC,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;CAmDxD,MAAM,SA9CF;EACF,MAAM,YAAY;AAChB,OAAI,CAAC,aAAa,SAAS,mBAAmB,CAI5C,OAAM,IAAI,WAHW,cACjB,qDAAqD,gBACrD,uDAGF,SAAS,QACT,SAAS,YACT,SACD;AAEH,UAAO,MAAM,SAAS,MAAM;;EAE9B,aAAa,YAAY,SAAS,aAAa;EAC/C,MAAM,YAAY,SAAS,MAAM;EACjC,OAAO,YAAY;GACjB,MAAM,SAAS,MAAM,SAAS,aAAa;AAC3C,UAAO,IAAI,WAAW,OAAO;;EAE/B,OAAO,YAAY,SAAS,OAAO;EACnC,UAAU,YAAY;AACpB,OAAI,CAAC,aAAa,SAAS,sBAAsB,CAC/C,OAAM,IAAI,WACR,kEACA,SAAS,QACT,SAAS,YACT,SACD;AAEH,UAAO,MAAM,SAAS,UAAU;;EAElC,MAAM,YAAY;AAChB,OAAI,CAAC,aAAa,SAAS,QAAQ,CACjC,OAAM,IAAI,WACR,8DACA,SAAS,QACT,SAAS,YACT,SACD;AAEH,UAAO,MAAM,SAAS,MAAM;;EAE/B,CAEsB;AACvB,KAAI,CAAC,OACH,QAAO,QAAQ,OACb,IAAI,WACF,8BAA8B,gBAC9B,SAAS,QACT,SAAS,YACT,SACD,CACF;AAGH,QAAO,QAAQ;;AAGjB,SAAgB,sBACd,MACA,SAC6D;CAC7D,IAAIA,eAAgC;CACpC,IAAI,kBAAkB;AAGtB,KAAI,SAAS,QAAQ,SAAS,OAC5B,QAAO;EAAE,MAAM;EAAM,SAAS;EAAiB;AAIjD,KACE,gBAAgB,YAChB,gBAAgB,mBAChB,gBAAgB,QAChB,gBAAgB,eAChB,gBAAgB,kBAChB,OAAO,SAAS,SAEhB,gBAAe;UACN,OAAO,SAAS,UAAU;AAEnC,iBAAe,KAAK,UAAU,KAAK;AACnC,oBAAkB;GAChB,gBAAgB;GAChB,GAAG;GACJ;OAGD,gBAAe,OAAO,KAAK;AAG7B,QAAO;EAAE,MAAM;EAAc,SAAS;EAAiB"}
package/package.json CHANGED
@@ -1,33 +1,40 @@
1
1
  {
2
2
  "name": "@zap-studio/fetch",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "CHANGELOG.md"
13
+ ],
9
14
  "dependencies": {
10
15
  "zod": "^4.1.12"
11
16
  },
12
17
  "devDependencies": {
13
18
  "@types/node": "latest",
19
+ "@vitest/coverage-v8": "^4.0.13",
14
20
  "jsdom": "latest",
15
21
  "tsdown": "latest",
16
22
  "typescript": "latest",
17
23
  "vitest": "latest",
24
+ "@zap-studio/tsdown-config": "0.0.0",
18
25
  "@zap-studio/typescript-config": "0.0.0",
19
- "@zap-studio/vitest-config": "0.0.0",
20
- "@zap-studio/tsdown-config": "0.0.0"
26
+ "@zap-studio/vitest-config": "0.0.0"
21
27
  },
22
28
  "exports": {
23
- ".": "./dist/index.js",
24
- "./errors": "./dist/errors/index.js",
25
- "./types": "./dist/types/index.js",
29
+ ".": "./dist/index.mjs",
30
+ "./errors": "./dist/errors/index.mjs",
31
+ "./types": "./dist/types/index.mjs",
32
+ "./utils": "./dist/utils/index.mjs",
26
33
  "./package.json": "./package.json"
27
34
  },
28
- "main": "./dist/index.js",
29
- "module": "./dist/index.js",
30
- "types": "./dist/index.d.ts",
35
+ "main": "./dist/index.mjs",
36
+ "module": "./dist/index.mjs",
37
+ "types": "./dist/index.d.mts",
31
38
  "scripts": {
32
39
  "build": "tsdown --config tsdown.config.ts",
33
40
  "check-types": "tsc --noEmit",
@@ -1,11 +0,0 @@
1
- export class FetchError extends Error {
2
- constructor(
3
- message: string,
4
- public status: number,
5
- public statusText: string,
6
- public response: Response,
7
- ) {
8
- super(message);
9
- this.name = "FetchError";
10
- }
11
- }