dexix-request 1.0.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 ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-07-12
9
+
10
+ ### Added
11
+
12
+ - Initial public release of DexiX Request.
13
+ - Callable instance API: `dx(url)`, `dx.get`, `dx.post`, `dx.put`, `dx.patch`, `dx.delete`, `dx.head`, `dx.options`.
14
+ - `dx.create()` for scoped instances with `baseURL`, default `headers`, and other defaults.
15
+ - Automatic JSON parsing of response bodies — no more `response.json()`.
16
+ - Automatic JSON serialization of plain object and array request bodies.
17
+ - Native support for `FormData`, `Blob`, `URLSearchParams`, `ArrayBuffer`, typed arrays, and `ReadableStream` bodies.
18
+ - Built-in request `timeout`, composable with a user-supplied `AbortSignal`.
19
+ - Custom error classes: `NetworkError`, `TimeoutError`, `HTTPError`, `ParseError`, all extending a shared `DexiXError` base with `status`, `headers`, `url`, `request`, and `response`.
20
+ - Full TypeScript generics: `dx.get<T>()`, `dx.post<T>()`, etc., with zero use of `any` in the public API.
21
+ - Zero runtime dependencies. ESM and CommonJS builds, tree-shakeable, under 5KB gzipped.
22
+ - `onRequest` / `onResponse` hooks for logging, auth injection, and observability.
23
+ - Full test suite covering GET, POST, PUT, DELETE, error handling, timeouts, and JSON parsing.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DexiX Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,328 @@
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/dexix-dev/dexix-request/main/.github/logo-placeholder.svg" width="88" height="88" alt="DexiX Request logo" />
4
+
5
+ # DexiX Request
6
+
7
+ **If Axios were redesigned from scratch in 2026.**
8
+
9
+ A tiny, modern, zero-dependency HTTP client for JavaScript and TypeScript — for the browser, Node.js, and React.
10
+
11
+ [![npm version](https://img.shields.io/npm/v/dexix-request.svg?color=blue)](https://www.npmjs.com/package/dexix-request)
12
+ [![npm downloads](https://img.shields.io/npm/dm/dexix-request.svg)](https://www.npmjs.com/package/dexix-request)
13
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/dexix-request)](https://bundlephobia.com/package/dexix-request)
14
+ [![CI](https://github.com/dexix-dev/dexix-request/actions/workflows/ci.yml/badge.svg)](https://github.com/dexix-dev/dexix-request/actions/workflows/ci.yml)
15
+ [![types](https://img.shields.io/npm/types/dexix-request.svg)](https://www.npmjs.com/package/dexix-request)
16
+ [![license](https://img.shields.io/npm/l/dexix-request.svg)](./LICENSE)
17
+ [![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](./package.json)
18
+
19
+ [Quick Start](#quick-start) · [Features](#features) · [API Reference](#api-reference) · [Why DexiX](#why-dexix-request) · [Comparison](#comparison-with-axios) · [FAQ](#faq)
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install dexix-request
29
+ ```
30
+
31
+ ```bash
32
+ pnpm add dexix-request
33
+ ```
34
+
35
+ ```bash
36
+ yarn add dexix-request
37
+ ```
38
+
39
+ No peer dependencies. No polyfills required in any environment that has `fetch` (all evergreen browsers, and Node.js 20+).
40
+
41
+ ## Quick Start
42
+
43
+ ```ts
44
+ import dx from "dexix-request";
45
+
46
+ // Calling the instance directly performs a GET request.
47
+ const users = await dx("/users");
48
+
49
+ // Method helpers for everything else.
50
+ const user = await dx.get<User>("/users/1");
51
+
52
+ await dx.post("/posts", {
53
+ title: "Hello DexiX",
54
+ });
55
+
56
+ // Scoped instances for talking to a specific API.
57
+ const api = dx.create({
58
+ baseURL: "https://api.example.com",
59
+ headers: { Authorization: "Bearer token" },
60
+ });
61
+
62
+ const profile = await api("/me");
63
+ ```
64
+
65
+ That's it. No `response.json()`. No `axios.create()` boilerplate. No config objects three levels deep.
66
+
67
+ ## Features
68
+
69
+ - ✅ **Extremely simple API** — call the instance directly, or use `.get` / `.post` / `.put` / `.patch` / `.delete` / `.head` / `.options`
70
+ - ✅ **Automatic JSON** — request bodies are serialized and response bodies are parsed for you
71
+ - ✅ **Zero dependencies** — nothing to audit, nothing to update, nothing to break
72
+ - ✅ **Tiny** — ~2KB gzipped, tree-shakeable, side-effect free
73
+ - ✅ **Modern TypeScript** — full generics, strict types, no `any` in the public API
74
+ - ✅ **Built on the platform** — `fetch`, `AbortController`, `URLSearchParams`, `ReadableStream`, `FormData` — nothing reinvented
75
+ - ✅ **Composable timeouts** — `timeout` and a caller-supplied `AbortSignal` work together, not against each other
76
+ - ✅ **Rich errors** — `NetworkError`, `TimeoutError`, `HTTPError`, and `ParseError`, each carrying the request and response
77
+ - ✅ **Works everywhere** — browser, Node.js 20+, React, Next.js, Vite, ESM, and CommonJS
78
+
79
+ ## Examples
80
+
81
+ ### JavaScript
82
+
83
+ ```js
84
+ import dx from "dexix-request";
85
+
86
+ const todos = await dx.get("https://api.example.com/todos", {
87
+ query: { userId: 1 },
88
+ });
89
+
90
+ const created = await dx.post("https://api.example.com/todos", {
91
+ title: "Ship DexiX Request",
92
+ completed: false,
93
+ });
94
+ ```
95
+
96
+ ### TypeScript
97
+
98
+ ```ts
99
+ import dx from "dexix-request";
100
+
101
+ interface Todo {
102
+ id: number;
103
+ title: string;
104
+ completed: boolean;
105
+ }
106
+
107
+ const todos = await dx.get<Todo[]>("https://api.example.com/todos");
108
+ const todo = await dx.post<Todo>("https://api.example.com/todos", {
109
+ title: "Ship DexiX Request",
110
+ completed: false,
111
+ });
112
+ ```
113
+
114
+ ### React
115
+
116
+ ```tsx
117
+ import { useEffect, useState } from "react";
118
+ import dx, { HTTPError } from "dexix-request";
119
+
120
+ function useUser(id: number) {
121
+ const [user, setUser] = useState<User | null>(null);
122
+ const [error, setError] = useState<string | null>(null);
123
+
124
+ useEffect(() => {
125
+ const controller = new AbortController();
126
+
127
+ dx.get<User>(`/users/${id}`, { signal: controller.signal })
128
+ .then(setUser)
129
+ .catch((err) => {
130
+ if (err instanceof HTTPError && err.status === 404) {
131
+ setError("User not found");
132
+ }
133
+ });
134
+
135
+ return () => controller.abort();
136
+ }, [id]);
137
+
138
+ return { user, error };
139
+ }
140
+ ```
141
+
142
+ More complete examples live in [`examples/`](./examples): [Node.js](./examples/node.mjs), [browser](./examples/browser.js), [React](./examples/react-example.tsx), and [TypeScript](./examples/typescript-example.ts).
143
+
144
+ ## API Reference
145
+
146
+ ### `dx(url, config?)`
147
+
148
+ Performs a `GET` request and resolves with the parsed response body. Equivalent to `dx.get(url, config)`.
149
+
150
+ ### `dx.get<T>(url, config?)`
151
+
152
+ ### `dx.post<T>(url, body?, config?)`
153
+
154
+ ### `dx.put<T>(url, body?, config?)`
155
+
156
+ ### `dx.patch<T>(url, body?, config?)`
157
+
158
+ ### `dx.delete<T>(url, config?)`
159
+
160
+ ### `dx.head(url, config?)`
161
+
162
+ ### `dx.options<T>(url, config?)`
163
+
164
+ Every method returns a `Promise<T>` that resolves with the already-parsed response body, or rejects with a [DexiX error](#error-handling).
165
+
166
+ ### `dx.create(config?)`
167
+
168
+ Creates a new, independent instance with its own defaults. Config passed to individual requests is merged on top of (and overrides) the instance's defaults.
169
+
170
+ ```ts
171
+ const api = dx.create({
172
+ baseURL: "https://api.example.com",
173
+ headers: { "X-App": "my-app" },
174
+ timeout: 10_000,
175
+ });
176
+ ```
177
+
178
+ ### `RequestConfig`
179
+
180
+ | Option | Type | Description |
181
+ | --------------- | ---------------------------------------- | ----------------------------------------------------------------|
182
+ | `baseURL` | `string` | Prepended to relative URLs. |
183
+ | `headers` | `HeadersInit \| Record<string, string>` | Merged with instance defaults; per-request wins. |
184
+ | `query` | `Record<string, ...>` | Serialized into the URL's query string. Arrays become repeated keys. |
185
+ | `body` | `BodyInit \| object \| unknown[]` | Plain objects/arrays are JSON-serialized automatically. |
186
+ | `timeout` | `number` | Milliseconds before the request is aborted with `TimeoutError`. |
187
+ | `signal` | `AbortSignal` | Composed with the internal timeout signal. |
188
+ | `responseType` | `"json" \| "text" \| "blob" \| "arrayBuffer" \| "stream"` | Overrides automatic response parsing. |
189
+ | `credentials`, `cache`, `redirect`, `referrerPolicy`, `mode` | — | Forwarded straight to `fetch`. |
190
+ | `onRequest` | `(request: Request) => void` | Called with the final `Request` before it's sent. |
191
+ | `onResponse` | `(response: Response) => void` | Called with the raw `Response` before the body is parsed. |
192
+
193
+ ## Error Handling
194
+
195
+ DexiX Request never throws a bare `Error`. Every failure is one of four typed classes, all extending `DexiXError`:
196
+
197
+ ```ts
198
+ import dx, { NetworkError, TimeoutError, HTTPError, ParseError } from "dexix-request";
199
+
200
+ try {
201
+ await dx.get("/users/1");
202
+ } catch (error) {
203
+ if (error instanceof HTTPError) {
204
+ // Server responded with a non-2xx status.
205
+ console.error(error.status, error.response?.data);
206
+ } else if (error instanceof TimeoutError) {
207
+ // Request exceeded `timeout`.
208
+ console.error(`Timed out after ${error.timeout}ms`);
209
+ } else if (error instanceof NetworkError) {
210
+ // The request never reached a server (offline, DNS, CORS, etc).
211
+ console.error(error.message);
212
+ } else if (error instanceof ParseError) {
213
+ // A response body couldn't be parsed as JSON/text/etc.
214
+ console.error(error.message);
215
+ }
216
+ }
217
+ ```
218
+
219
+ Every DexiX error carries:
220
+
221
+ - `message` — human-readable description
222
+ - `status` — HTTP status code, when a response was received
223
+ - `headers` — response `Headers`, when a response was received
224
+ - `url` — the fully-resolved request URL
225
+ - `request` — `{ url, method, headers, body }` describing what was sent
226
+ - `response` — `{ data, status, statusText, headers, url }`, when available
227
+
228
+ ## Why DexiX Request
229
+
230
+ Most HTTP clients fall into one of two camps: a thin `fetch` wrapper that still makes you write `response.json()` everywhere, or a heavyweight library carrying years of legacy API surface and a dependency tree of its own.
231
+
232
+ DexiX Request is neither. It's built directly on the platform primitives that already ship in every modern runtime — `fetch`, `AbortController`, `Headers`, `URLSearchParams`, `FormData`, `ReadableStream` — and adds exactly one thing on top: an API that gets out of your way.
233
+
234
+ - No dependencies to audit or update.
235
+ - No custom body-parsing logic to trust — you get real `Headers`, real `Request`, real `Response` under the hood.
236
+ - No configuration required to get sensible behavior; every option is there for when you need to override it, not because you have to set it.
237
+
238
+ ## Comparison with Axios
239
+
240
+ | | DexiX Request | Axios |
241
+ | --- | --- | --- |
242
+ | Dependencies | 0 | 0 (as of v1, but larger internal surface) |
243
+ | Bundle size (gzip) | ~2KB | ~13KB |
244
+ | Transport | `fetch` | `XMLHttpRequest` (browser) / `http` (Node) |
245
+ | TypeScript | Written in TS, strict, no `any` | Typed via bundled `.d.ts`, some `any` |
246
+ | Interceptors | `onRequest` / `onResponse` hooks | Interceptor chain |
247
+ | Cancellation | Native `AbortSignal`, composable with `timeout` | Native `AbortSignal` (v1+) or legacy `CancelToken` |
248
+ | Default body parsing | Automatic JSON | Automatic JSON |
249
+ | Node.js support | 20+ (uses global `fetch`) | Broad, including legacy Node |
250
+ | Instance creation | `dx.create(config)` | `axios.create(config)` |
251
+
252
+ DexiX Request intentionally supports fewer legacy environments in exchange for a smaller, simpler, more predictable core. If you need to support Node.js < 18 or Internet Explorer, Axios remains the better fit.
253
+
254
+ ## Roadmap
255
+
256
+ - [ ] Request/response interceptor chains (beyond `onRequest`/`onResponse`)
257
+ - [ ] Built-in retry with configurable backoff
258
+ - [ ] Upload/download progress events
259
+ - [ ] Automatic request deduplication
260
+ - [ ] Optional plugin for schema validation (Zod/Valibot) on responses
261
+
262
+ Have an idea? [Open an issue](https://github.com/dexix-dev/dexix-request/issues).
263
+
264
+ ## Browser Support
265
+
266
+ All browsers with `fetch`, `AbortController`, and `URLSearchParams` support — effectively every browser released since 2017 (Chrome 66+, Firefox 60+, Safari 12+, Edge 79+). DexiX Request does not include or require any polyfills.
267
+
268
+ ## Performance
269
+
270
+ - **Bundle size:** ~2KB gzipped, well under the 5KB target
271
+ - **Zero dependencies:** nothing extra ships in your `node_modules` or your bundle
272
+ - **Tree-shakeable:** unused exports (individual error classes, `createInstance`) are dropped by any modern bundler
273
+ - **Minimal allocations:** a single `Headers` merge and a single `Request` construction per call — no intermediate wrapper objects
274
+
275
+ ## Developer Experience
276
+
277
+ - Every public function is documented with JSDoc, so your editor shows real descriptions, not just type signatures.
278
+ - Errors are specific and typed, so `catch` blocks can branch with `instanceof` instead of parsing error strings.
279
+ - `dx.create()` returns a fully independent instance — no shared mutable state, no surprises across modules.
280
+
281
+ ## Contributing
282
+
283
+ Contributions are welcome. To get started:
284
+
285
+ ```bash
286
+ git clone https://github.com/dexix-dev/dexix-request.git
287
+ cd dexix-request/packages/request
288
+ npm install
289
+ npm run test
290
+ ```
291
+
292
+ Please open an issue before starting work on a large change, and make sure `npm run typecheck` and `npm run test` both pass before opening a pull request.
293
+
294
+ ## FAQ
295
+
296
+ **Does DexiX Request work in Node.js?**
297
+ Yes — Node.js 20+ ships a global `fetch` implementation, so DexiX Request works with no extra setup.
298
+
299
+ **Does it support CommonJS?**
300
+ Yes. The published package includes both ESM (`dist/index.js`) and CommonJS (`dist/index.cjs`) builds, selected automatically via `package.json#exports`.
301
+
302
+ **Can I use it with Next.js Server Components / Route Handlers?**
303
+ Yes — DexiX Request is just `fetch` under the hood, so it works anywhere `fetch` is available, including the Edge Runtime.
304
+
305
+ **How do I send `multipart/form-data`?**
306
+ Pass a `FormData` instance as the body — DexiX Request passes it through untouched so the platform can set the correct multipart boundary.
307
+
308
+ ```ts
309
+ const form = new FormData();
310
+ form.append("file", file);
311
+ await dx.post("/upload", form);
312
+ ```
313
+
314
+ **How do I cancel a request?**
315
+ Pass an `AbortSignal` via `signal`, or set a `timeout` — both work together.
316
+
317
+ ```ts
318
+ const controller = new AbortController();
319
+ dx.get("/slow", { signal: controller.signal });
320
+ controller.abort();
321
+ ```
322
+
323
+ **Does DexiX Request retry failed requests automatically?**
324
+ Not yet — automatic retry with backoff is on the [roadmap](#roadmap). Today, wrap a call in your own retry logic if needed.
325
+
326
+ ## License
327
+
328
+ [MIT](./LICENSE) © DexiX Contributors
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var i=class extends Error{request;response;status;headers;url;constructor(t,r,n){super(t),this.name="DexiXError",this.request=r,this.response=n,this.status=n?.status,this.headers=n?.headers,this.url=r.url,Object.setPrototypeOf(this,new.target.prototype);}};var c=class extends i{constructor(t,r){super(`Request failed with status ${r.status} (${r.statusText}) for ${t.url}`,t,r),this.name="HTTPError";}};var d=class extends i{constructor(t,r,n){super(t||"Network request failed",r),this.name="NetworkError",n!==void 0&&(this.cause=n);}};var m=class extends i{timeout;constructor(t,r){super(`Request to ${t.url} timed out after ${r}ms`,t),this.name="TimeoutError",this.timeout=r;}};function C(e){return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function k(e,t){if(!e||C(t))return t;let r=e.endsWith("/")?e.slice(0,-1):e,n=t.startsWith("/")?t:`/${t}`;return `${r}${n}`}function H(e,t){if(!t||Object.keys(t).length===0)return e;let[r,n=""]=e.split("?"),o=new URLSearchParams(n);for(let[f,u]of Object.entries(t))if(u!=null)if(Array.isArray(u))for(let b of u)o.append(f,String(b));else o.append(f,String(u));let s=o.toString();return s?`${r}?${s}`:r}function q(e,t,r){let n=k(e??"",t);return H(n,r)}function l(...e){let t=new Headers;for(let r of e)if(r){if(r instanceof Headers){r.forEach((n,o)=>{t.set(o,n);});continue}if(Array.isArray(r)){for(let[n,o]of r)t.set(n,o);continue}for(let[n,o]of Object.entries(r))o===void 0?t.delete(n):t.set(n,o);}return t}function R(e,t){return {...e,...t,headers:l(e.headers,t.headers),query:{...e.query,...t.query}}}function E(e){if(typeof e!="object"||e===null)return false;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function S(e){return typeof e=="string"||e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||e instanceof ReadableStream}function w(e){return e==null?{body:void 0}:S(e)?e instanceof FormData?{body:e}:e instanceof URLSearchParams?{body:e,contentType:"application/x-www-form-urlencoded;charset=UTF-8"}:typeof e=="string"?{body:e,contentType:"text/plain;charset=UTF-8"}:{body:e}:E(e)||Array.isArray(e)?{body:JSON.stringify(e),contentType:"application/json;charset=UTF-8"}:{body:JSON.stringify(e),contentType:"application/json;charset=UTF-8"}}var x=class extends i{constructor(t,r,n){super(`Failed to parse response body from ${t.url}`,t,r),this.name="ParseError",n!==void 0&&(this.cause=n);}};var O=new Set([204,205,304]);function A(e){let t=e.headers.get("content-type")??"";return t.includes("application/json")||t.includes("+json")?"json":t.startsWith("text/")?"text":"json"}async function h(e,t,r){if(t.method==="HEAD"||O.has(e.status))return;let n=r??A(e);try{switch(n){case "json":{let o=await e.text();return o.length===0?void 0:JSON.parse(o)}case "text":return await e.text();case "blob":return await e.blob();case "arrayBuffer":return await e.arrayBuffer();case "stream":return e.body;default:return await e.text()}}catch(o){let s={data:void 0,status:e.status,statusText:e.statusText,headers:e.headers,url:e.url||t.url};throw new x(t,s,o)}}function D(e,t){if(!t||t<=0)return {signal:e,cancel:()=>{},didTimeout:()=>false};let r=new AbortController,n=false,o=setTimeout(()=>{n=true,r.abort();},t),s=()=>r.abort();e?.addEventListener("abort",s,{once:true});let f=()=>{clearTimeout(o),e?.removeEventListener("abort",s);};return {signal:r.signal,cancel:f,didTimeout:()=>n}}async function j(e){let{body:t,contentType:r}=w(e.body),n=l(r?{"content-type":r}:void 0,e.headers),o=q(e.baseURL,e.url,e.query),s={url:o,method:e.method,headers:n,body:e.body},{signal:f,cancel:u,didTimeout:b}=D(e.signal,e.timeout),I={method:e.method,headers:n,body:e.method==="GET"||e.method==="HEAD"?void 0:t,signal:f,credentials:e.credentials,cache:e.cache,redirect:e.redirect,referrerPolicy:e.referrerPolicy,mode:e.mode},X=new Request(o,I);e.onRequest&&await e.onRequest(X);let a;try{a=await fetch(X);}catch(p){throw u(),p instanceof DOMException&&p.name==="AbortError"?b()?new m(s,e.timeout??0):p:new d(p instanceof Error?p.message:"Network request failed",s,p)}if(u(),e.onResponse&&await e.onResponse(a),!a.ok){let P={data:await h(a,s,e.responseType).catch(()=>{}),status:a.status,statusText:a.statusText,headers:a.headers,url:a.url||o};throw new c(s,P)}return h(a,s,e.responseType)}function y(e,t,r,n){let o={...R(e,n??{}),method:t,url:r};return j(o)}function g(e,t,r,n,o){return y(e,t,r,{...o,body:n??o?.body})}function T(e={}){let t=((r,n)=>y(e,"GET",r,n));return t.get=(r,n)=>y(e,"GET",r,n),t.post=(r,n,o)=>g(e,"POST",r,n,o),t.put=(r,n,o)=>g(e,"PUT",r,n,o),t.patch=(r,n,o)=>g(e,"PATCH",r,n,o),t.delete=(r,n)=>y(e,"DELETE",r,n),t.head=(r,n)=>y(e,"HEAD",r,n),t.options=(r,n)=>y(e,"OPTIONS",r,n),Object.defineProperty(t,"defaults",{value:e,writable:false,enumerable:true}),t.create=r=>T(R(e,r??{})),t}var B=T();var Ee=B;
2
+ exports.DexiXError=i;exports.HTTPError=c;exports.NetworkError=d;exports.ParseError=x;exports.TimeoutError=m;exports.createInstance=T;exports.default=Ee;//# sourceMappingURL=index.cjs.map
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/DexiXError.ts","../src/errors/HTTPError.ts","../src/errors/NetworkError.ts","../src/errors/TimeoutError.ts","../src/utils/buildURL.ts","../src/utils/mergeHeaders.ts","../src/utils/mergeConfig.ts","../src/utils/isPlainObject.ts","../src/utils/serializeBody.ts","../src/errors/ParseError.ts","../src/utils/parseResponse.ts","../src/utils/combineSignals.ts","../src/core/dispatch.ts","../src/core/instance.ts","../src/index.ts"],"names":["DexiXError","message","request","response","HTTPError","NetworkError","cause","TimeoutError","timeout","isAbsoluteURL","url","joinURL","baseURL","trimmedBase","trimmedPath","appendQuery","query","path","existingSearch","params","key","value","item","search","buildURL","joined","mergeHeaders","sources","result","source","mergeConfig","parent","child","isPlainObject","prototype","isNativeBodyType","serializeBody","body","ParseError","EMPTY_STATUS_CODES","inferResponseType","contentType","parseResponse","responseType","type","text","failedResponse","combineSignals","userSignal","controller","timedOut","timer","onUserAbort","cancel","dispatch","config","headers","requestInfo","signal","didTimeout","init","errorResponse","defaults","method","resolved","requestWithBody","createInstance","instance","dx","index_default"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Base class for every error thrown by DexiX Request.\n * All DexiX errors carry the originating request and, when available,\n * the response that was received.\n */\nexport class DexiXError extends Error {\n /** The request that caused this error. */\n public readonly request: DexiXRequestInfo;\n /** The response received, if any (absent for network/timeout errors). */\n public readonly response?: DexiXResponse;\n /** HTTP status code, if a response was received. */\n public readonly status?: number;\n /** Response headers, if a response was received. */\n public readonly headers?: Headers;\n /** The request URL. */\n public readonly url: string;\n\n public constructor(\n message: string,\n request: DexiXRequestInfo,\n response?: DexiXResponse,\n ) {\n super(message);\n this.name = \"DexiXError\";\n this.request = request;\n this.response = response;\n this.status = response?.status;\n this.headers = response?.headers;\n this.url = request.url;\n\n // Restore the correct prototype chain when compiled down to ES5-style\n // targets (not an issue at ES2022, but harmless and defensive).\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Thrown when the server responds with a non-2xx status code.\n * Always carries the full `response`, including any parsed body DexiX\n * Request was able to extract before raising the error.\n */\nexport class HTTPError extends DexiXError {\n public constructor(request: DexiXRequestInfo, response: DexiXResponse) {\n super(\n `Request failed with status ${response.status} (${response.statusText}) for ${request.url}`,\n request,\n response,\n );\n this.name = \"HTTPError\";\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo } from \"../types/index.js\";\n\n/**\n * Thrown when the request could not be completed because of a network\n * failure (DNS resolution failure, connection refused, offline, CORS\n * rejection, etc). No response was ever received.\n */\nexport class NetworkError extends DexiXError {\n public constructor(message: string, request: DexiXRequestInfo, cause?: unknown) {\n super(message || \"Network request failed\", request);\n this.name = \"NetworkError\";\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo } from \"../types/index.js\";\n\n/**\n * Thrown when a request exceeds its configured `timeout` before a\n * response is received.\n */\nexport class TimeoutError extends DexiXError {\n /** The timeout duration, in milliseconds, that was exceeded. */\n public readonly timeout: number;\n\n public constructor(request: DexiXRequestInfo, timeout: number) {\n super(`Request to ${request.url} timed out after ${timeout}ms`, request);\n this.name = \"TimeoutError\";\n this.timeout = timeout;\n }\n}\n","import type { QueryParams } from \"../types/index.js\";\n\n/**\n * Detects absolute URLs (has a scheme like `https://`, `http://`, or is\n * protocol-relative `//host`).\n */\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url);\n}\n\n/**\n * Joins a base URL and a relative path without producing double slashes\n * or dropping a needed one.\n */\nfunction joinURL(baseURL: string, url: string): string {\n if (!baseURL) return url;\n if (isAbsoluteURL(url)) return url;\n\n const trimmedBase = baseURL.endsWith(\"/\") ? baseURL.slice(0, -1) : baseURL;\n const trimmedPath = url.startsWith(\"/\") ? url : `/${url}`;\n return `${trimmedBase}${trimmedPath}`;\n}\n\n/**\n * Appends query parameters to a URL, preserving any existing search\n * string already present on the URL. Array values become repeated keys.\n * `null`/`undefined` values are omitted entirely.\n */\nfunction appendQuery(url: string, query?: QueryParams): string {\n if (!query || Object.keys(query).length === 0) {\n return url;\n }\n\n const [path, existingSearch = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(existingSearch);\n\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) {\n params.append(key, String(item));\n }\n } else {\n params.append(key, String(value));\n }\n }\n\n const search = params.toString();\n return search ? `${path}?${search}` : (path as string);\n}\n\n/**\n * Builds the final request URL from a base URL, a relative or absolute\n * URL, and an optional set of query parameters.\n */\nexport function buildURL(baseURL: string | undefined, url: string, query?: QueryParams): string {\n const joined = joinURL(baseURL ?? \"\", url);\n return appendQuery(joined, query);\n}\n","import type { HeadersInput } from \"../types/index.js\";\n\n/**\n * Merges any number of header sources into a single `Headers` instance.\n * Later sources override earlier ones for the same (case-insensitive) key.\n * A value of `undefined` in a plain-object source removes that header.\n */\nexport function mergeHeaders(...sources: Array<HeadersInput | undefined>): Headers {\n const result = new Headers();\n\n for (const source of sources) {\n if (!source) continue;\n\n if (source instanceof Headers) {\n source.forEach((value, key) => {\n result.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(source)) {\n for (const [key, value] of source) {\n result.set(key, value);\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n result.delete(key);\n } else {\n result.set(key, value);\n }\n }\n }\n\n return result;\n}\n","import { mergeHeaders } from \"./mergeHeaders.js\";\nimport type { RequestConfig } from \"../types/index.js\";\n\n/**\n * Merges a child `RequestConfig` on top of a parent one. Headers and\n * query params are merged (child wins per-key); every other field is\n * simply overridden by the child when present.\n */\nexport function mergeConfig(parent: RequestConfig, child: RequestConfig): RequestConfig {\n return {\n ...parent,\n ...child,\n headers: mergeHeaders(parent.headers, child.headers),\n query: { ...parent.query, ...child.query },\n };\n}\n","/**\n * Returns `true` for plain JavaScript objects (object literals, or objects\n * with `Object.prototype` / `null` as their prototype). Returns `false`\n * for arrays, class instances, `FormData`, `Blob`, `File`, `URLSearchParams`,\n * `ReadableStream`, `Date`, `Map`, `Set`, and primitives.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value) as unknown;\n return prototype === Object.prototype || prototype === null;\n}\n","import { isPlainObject } from \"./isPlainObject.js\";\nimport type { RequestBody } from \"../types/index.js\";\n\n/**\n * Result of serializing a request body: the value ready to hand to\n * `fetch`, and an optional `Content-Type` to apply when one wasn't\n * already set by the caller.\n */\nexport interface SerializedBody {\n body: BodyInit | null | undefined;\n contentType?: string;\n}\n\n/**\n * Determines whether a value is already a \"native\" body type that\n * `fetch` understands natively and should be passed through untouched.\n */\nfunction isNativeBodyType(value: unknown): value is BodyInit {\n return (\n typeof value === \"string\" ||\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n value instanceof ReadableStream\n );\n}\n\n/**\n * Serializes a DexiX request body. Plain objects and arrays are\n * JSON-serialized automatically; every other supported `BodyInit` type is\n * passed through untouched so the caller retains full control when\n * needed (e.g. uploading a `FormData` or streaming a `ReadableStream`).\n */\nexport function serializeBody(body: RequestBody): SerializedBody {\n if (body === null || body === undefined) {\n return { body: undefined };\n }\n\n if (isNativeBodyType(body)) {\n // FormData sets its own multipart boundary; let the platform handle it.\n if (body instanceof FormData) {\n return { body };\n }\n if (body instanceof URLSearchParams) {\n return { body, contentType: \"application/x-www-form-urlencoded;charset=UTF-8\" };\n }\n if (typeof body === \"string\") {\n return { body, contentType: \"text/plain;charset=UTF-8\" };\n }\n return { body };\n }\n\n if (isPlainObject(body) || Array.isArray(body)) {\n return {\n body: JSON.stringify(body),\n contentType: \"application/json;charset=UTF-8\",\n };\n }\n\n // Fallback: best-effort JSON serialization for other serializable values.\n return {\n body: JSON.stringify(body),\n contentType: \"application/json;charset=UTF-8\",\n };\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Thrown when a response body could not be parsed according to the\n * requested (or inferred) `responseType`, e.g. invalid JSON.\n */\nexport class ParseError extends DexiXError {\n public constructor(request: DexiXRequestInfo, response: DexiXResponse, cause?: unknown) {\n super(`Failed to parse response body from ${request.url}`, request, response);\n this.name = \"ParseError\";\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { ParseError } from \"../errors/ParseError.js\";\nimport type { DexiXRequestInfo, DexiXResponse, ResponseType } from \"../types/index.js\";\n\nconst EMPTY_STATUS_CODES = new Set([204, 205, 304]);\n\n/**\n * Infers the best response type from the `Content-Type` header when the\n * caller didn't explicitly request one.\n */\nfunction inferResponseType(response: Response): ResponseType {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\") || contentType.includes(\"+json\")) {\n return \"json\";\n }\n if (contentType.startsWith(\"text/\")) {\n return \"text\";\n }\n return \"json\";\n}\n\n/**\n * Parses a `Response` body according to `responseType` (or an inferred\n * type based on `Content-Type`), returning the parsed value. Throws\n * `ParseError` if parsing fails.\n */\nexport async function parseResponse<T>(\n response: Response,\n request: DexiXRequestInfo,\n responseType?: ResponseType,\n): Promise<T> {\n if (request.method === \"HEAD\" || EMPTY_STATUS_CODES.has(response.status)) {\n return undefined as T;\n }\n\n const type = responseType ?? inferResponseType(response);\n\n try {\n switch (type) {\n case \"json\": {\n const text = await response.text();\n if (text.length === 0) {\n return undefined as T;\n }\n return JSON.parse(text) as T;\n }\n case \"text\":\n return (await response.text()) as unknown as T;\n case \"blob\":\n return (await response.blob()) as unknown as T;\n case \"arrayBuffer\":\n return (await response.arrayBuffer()) as unknown as T;\n case \"stream\":\n return response.body as unknown as T;\n default:\n return (await response.text()) as unknown as T;\n }\n } catch (cause) {\n const failedResponse: DexiXResponse = {\n data: undefined,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n url: response.url || request.url,\n };\n throw new ParseError(request, failedResponse, cause);\n }\n}\n","/**\n * Combines an optional user-provided `AbortSignal` with an internal\n * timeout, producing a single signal that aborts when either source\n * aborts. Returns the combined signal along with a `cancel()` function\n * that must be called once the request settles to clear the timer, and\n * a `didTimeout()` predicate to distinguish a timeout abort from a\n * user-initiated abort.\n */\nexport function combineSignals(\n userSignal: AbortSignal | undefined,\n timeout: number | undefined,\n): {\n signal: AbortSignal | undefined;\n cancel: () => void;\n didTimeout: () => boolean;\n} {\n if (!timeout || timeout <= 0) {\n return {\n signal: userSignal,\n cancel: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeout);\n\n const onUserAbort = () => controller.abort();\n userSignal?.addEventListener(\"abort\", onUserAbort, { once: true });\n\n const cancel = () => {\n clearTimeout(timer);\n userSignal?.removeEventListener(\"abort\", onUserAbort);\n };\n\n return {\n signal: controller.signal,\n cancel,\n didTimeout: () => timedOut,\n };\n}\n","import { HTTPError } from \"../errors/HTTPError.js\";\nimport { NetworkError } from \"../errors/NetworkError.js\";\nimport { TimeoutError } from \"../errors/TimeoutError.js\";\nimport { buildURL, combineSignals, mergeHeaders, parseResponse, serializeBody } from \"../utils/index.js\";\nimport type { DexiXRequestInfo, DexiXResponse, ResolvedRequestConfig } from \"../types/index.js\";\n\n/**\n * Executes a single HTTP request described by a fully-resolved config.\n * This is the single point in the library that calls `fetch`.\n *\n * @internal\n */\nexport async function dispatch<T>(config: ResolvedRequestConfig): Promise<T> {\n const { body, contentType } = serializeBody(config.body);\n\n const headers = mergeHeaders(\n contentType ? { \"content-type\": contentType } : undefined,\n config.headers,\n );\n\n const url = buildURL(config.baseURL, config.url, config.query);\n\n const requestInfo: DexiXRequestInfo = {\n url,\n method: config.method,\n headers,\n body: config.body,\n };\n\n const { signal, cancel, didTimeout } = combineSignals(config.signal, config.timeout);\n\n const init: RequestInit = {\n method: config.method,\n headers,\n body: config.method === \"GET\" || config.method === \"HEAD\" ? undefined : body,\n signal,\n credentials: config.credentials,\n cache: config.cache,\n redirect: config.redirect,\n referrerPolicy: config.referrerPolicy,\n mode: config.mode,\n };\n\n const request = new Request(url, init);\n\n if (config.onRequest) {\n await config.onRequest(request);\n }\n\n let response: Response;\n\n try {\n response = await fetch(request);\n } catch (cause) {\n cancel();\n\n if (cause instanceof DOMException && cause.name === \"AbortError\") {\n if (didTimeout()) {\n throw new TimeoutError(requestInfo, config.timeout ?? 0);\n }\n throw cause;\n }\n\n throw new NetworkError(\n cause instanceof Error ? cause.message : \"Network request failed\",\n requestInfo,\n cause,\n );\n }\n\n cancel();\n\n if (config.onResponse) {\n await config.onResponse(response);\n }\n\n if (!response.ok) {\n const data = await parseResponse<unknown>(response, requestInfo, config.responseType).catch(\n () => undefined,\n );\n\n const errorResponse: DexiXResponse = {\n data,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n url: response.url || url,\n };\n\n throw new HTTPError(requestInfo, errorResponse);\n }\n\n return parseResponse<T>(response, requestInfo, config.responseType);\n}\n","import { dispatch } from \"./dispatch.js\";\nimport { mergeConfig } from \"../utils/index.js\";\nimport type {\n DexiXInstance,\n HttpMethod,\n RequestBody,\n RequestConfig,\n ResolvedRequestConfig,\n} from \"../types/index.js\";\n\nfunction request<T>(\n defaults: RequestConfig,\n method: HttpMethod,\n url: string,\n config?: RequestConfig,\n): Promise<T> {\n const resolved: ResolvedRequestConfig = {\n ...mergeConfig(defaults, config ?? {}),\n method,\n url,\n };\n\n return dispatch<T>(resolved);\n}\n\nfunction requestWithBody<T>(\n defaults: RequestConfig,\n method: HttpMethod,\n url: string,\n body?: RequestBody,\n config?: RequestConfig,\n): Promise<T> {\n return request<T>(defaults, method, url, { ...config, body: body ?? config?.body });\n}\n\n/**\n * Creates a new DexiX Request instance. Calling the instance directly\n * performs a GET request; method-specific helpers (`get`, `post`, `put`,\n * `patch`, `delete`, `head`, `options`) are attached for convenience.\n */\nexport function createInstance(defaults: RequestConfig = {}): DexiXInstance {\n const instance = (<T>(url: string, config?: RequestConfig): Promise<T> =>\n request<T>(defaults, \"GET\", url, config)) as DexiXInstance;\n\n instance.get = (url, config) => request(defaults, \"GET\", url, config);\n instance.post = (url, body, config) => requestWithBody(defaults, \"POST\", url, body, config);\n instance.put = (url, body, config) => requestWithBody(defaults, \"PUT\", url, body, config);\n instance.patch = (url, body, config) => requestWithBody(defaults, \"PATCH\", url, body, config);\n instance.delete = (url, config) => request(defaults, \"DELETE\", url, config);\n instance.head = (url, config) => request(defaults, \"HEAD\", url, config);\n instance.options = (url, config) => request(defaults, \"OPTIONS\", url, config);\n\n Object.defineProperty(instance, \"defaults\", {\n value: defaults,\n writable: false,\n enumerable: true,\n });\n\n instance.create = (config?: RequestConfig) => createInstance(mergeConfig(defaults, config ?? {}));\n\n return instance;\n}\n","import { createInstance } from \"./core/index.js\";\nimport type { DexiXInstance } from \"./types/index.js\";\n\nexport type {\n DexiXInstance,\n RequestConfig,\n HttpMethod,\n QueryParams,\n RequestBody,\n HeadersInput,\n ResponseType,\n DexiXResponse,\n DexiXRequestInfo,\n} from \"./types/index.js\";\n\nexport { DexiXError, NetworkError, TimeoutError, HTTPError, ParseError } from \"./errors/index.js\";\n\n/**\n * The default DexiX Request instance.\n *\n * @example\n * ```ts\n * import dx from \"dexix-request\";\n *\n * const users = await dx(\"/users\");\n * const user = await dx.get<User>(\"/users/1\");\n * await dx.post(\"/posts\", { title: \"Hello\" });\n * ```\n */\nconst dx: DexiXInstance = createInstance();\n\nexport { createInstance };\nexport default dx;\n"]}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * HTTP methods supported by DexiX Request.
3
+ */
4
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
5
+ /**
6
+ * Plain object of query parameters. Array values are serialized as
7
+ * repeated keys (e.g. `{ tag: ["a", "b"] }` -> `?tag=a&tag=b`).
8
+ */
9
+ type QueryParams = Record<string, string | number | boolean | null | undefined | Array<string | number | boolean>>;
10
+ /**
11
+ * Anything that can be sent as a request body.
12
+ * Plain objects and arrays are automatically JSON-serialized.
13
+ */
14
+ type RequestBody = BodyInit | Record<string, unknown> | unknown[] | null | undefined;
15
+ /**
16
+ * Headers accepted by DexiX Request. Accepts a plain object, a `Headers`
17
+ * instance, or an array of key/value pairs (same as the `HeadersInit` type).
18
+ */
19
+ type HeadersInput = HeadersInit | Record<string, string | undefined>;
20
+ /**
21
+ * Response type hint. DexiX Request defaults to `"json"` and falls back
22
+ * gracefully when the response has no JSON body.
23
+ */
24
+ type ResponseType = "json" | "text" | "blob" | "arrayBuffer" | "stream";
25
+ /**
26
+ * Configuration accepted by every request call, and by `dx.create()`.
27
+ */
28
+ interface RequestConfig {
29
+ /** Base URL prepended to every relative request URL. */
30
+ baseURL?: string;
31
+ /** Headers merged into every request. */
32
+ headers?: HeadersInput;
33
+ /** Query parameters appended to the URL's search string. */
34
+ query?: QueryParams;
35
+ /** Request body. Objects/arrays are JSON-serialized automatically. */
36
+ body?: RequestBody;
37
+ /** Timeout in milliseconds. `0` or `undefined` disables the timeout. */
38
+ timeout?: number;
39
+ /** An `AbortSignal` to cancel the request. Composable with `timeout`. */
40
+ signal?: AbortSignal;
41
+ /** How to parse the response body. Defaults to `"json"`. */
42
+ responseType?: ResponseType;
43
+ /** Credentials mode, forwarded to `fetch`. */
44
+ credentials?: RequestCredentials;
45
+ /** Cache mode, forwarded to `fetch`. */
46
+ cache?: RequestCache;
47
+ /** Redirect mode, forwarded to `fetch`. */
48
+ redirect?: RequestRedirect;
49
+ /** Referrer policy, forwarded to `fetch`. */
50
+ referrerPolicy?: ReferrerPolicy;
51
+ /** Mode, forwarded to `fetch`. */
52
+ mode?: RequestMode;
53
+ /**
54
+ * Called once per request with the fully-resolved `Request` before it is
55
+ * sent. Useful for logging or attaching auth tokens dynamically.
56
+ */
57
+ onRequest?: (request: Request) => void | Promise<void>;
58
+ /**
59
+ * Called once per request with the raw `Response`, before the body is
60
+ * parsed.
61
+ */
62
+ onResponse?: (response: Response) => void | Promise<void>;
63
+ }
64
+ /**
65
+ * Metadata attached to every DexiX error, describing the request that
66
+ * produced it.
67
+ */
68
+ interface DexiXRequestInfo {
69
+ url: string;
70
+ method: HttpMethod;
71
+ headers: Headers;
72
+ body?: RequestBody;
73
+ }
74
+ /**
75
+ * The shape of a successful DexiX Request response before unwrapping.
76
+ * Consumers normally never see this directly, since `dx()` resolves to
77
+ * the parsed body, but it's available via `HTTPError.response`.
78
+ */
79
+ interface DexiXResponse<T = unknown> {
80
+ data: T;
81
+ status: number;
82
+ statusText: string;
83
+ headers: Headers;
84
+ url: string;
85
+ }
86
+ /**
87
+ * A callable DexiX Request instance. Calling it directly performs a GET
88
+ * request. Method-specific helpers are attached for convenience.
89
+ */
90
+ interface DexiXInstance {
91
+ <T = unknown>(url: string, config?: RequestConfig): Promise<T>;
92
+ get<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
93
+ post<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
94
+ put<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
95
+ patch<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
96
+ delete<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
97
+ head(url: string, config?: RequestConfig): Promise<void>;
98
+ options<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
99
+ /** The base configuration this instance was created with. */
100
+ readonly defaults: RequestConfig;
101
+ /**
102
+ * Create a new, independent DexiX Request instance. Config is merged
103
+ * with the parent instance's defaults (child config wins).
104
+ */
105
+ create(config?: RequestConfig): DexiXInstance;
106
+ }
107
+
108
+ /**
109
+ * Creates a new DexiX Request instance. Calling the instance directly
110
+ * performs a GET request; method-specific helpers (`get`, `post`, `put`,
111
+ * `patch`, `delete`, `head`, `options`) are attached for convenience.
112
+ */
113
+ declare function createInstance(defaults?: RequestConfig): DexiXInstance;
114
+
115
+ /**
116
+ * Base class for every error thrown by DexiX Request.
117
+ * All DexiX errors carry the originating request and, when available,
118
+ * the response that was received.
119
+ */
120
+ declare class DexiXError extends Error {
121
+ /** The request that caused this error. */
122
+ readonly request: DexiXRequestInfo;
123
+ /** The response received, if any (absent for network/timeout errors). */
124
+ readonly response?: DexiXResponse;
125
+ /** HTTP status code, if a response was received. */
126
+ readonly status?: number;
127
+ /** Response headers, if a response was received. */
128
+ readonly headers?: Headers;
129
+ /** The request URL. */
130
+ readonly url: string;
131
+ constructor(message: string, request: DexiXRequestInfo, response?: DexiXResponse);
132
+ }
133
+
134
+ /**
135
+ * Thrown when the request could not be completed because of a network
136
+ * failure (DNS resolution failure, connection refused, offline, CORS
137
+ * rejection, etc). No response was ever received.
138
+ */
139
+ declare class NetworkError extends DexiXError {
140
+ constructor(message: string, request: DexiXRequestInfo, cause?: unknown);
141
+ }
142
+
143
+ /**
144
+ * Thrown when a request exceeds its configured `timeout` before a
145
+ * response is received.
146
+ */
147
+ declare class TimeoutError extends DexiXError {
148
+ /** The timeout duration, in milliseconds, that was exceeded. */
149
+ readonly timeout: number;
150
+ constructor(request: DexiXRequestInfo, timeout: number);
151
+ }
152
+
153
+ /**
154
+ * Thrown when the server responds with a non-2xx status code.
155
+ * Always carries the full `response`, including any parsed body DexiX
156
+ * Request was able to extract before raising the error.
157
+ */
158
+ declare class HTTPError extends DexiXError {
159
+ constructor(request: DexiXRequestInfo, response: DexiXResponse);
160
+ }
161
+
162
+ /**
163
+ * Thrown when a response body could not be parsed according to the
164
+ * requested (or inferred) `responseType`, e.g. invalid JSON.
165
+ */
166
+ declare class ParseError extends DexiXError {
167
+ constructor(request: DexiXRequestInfo, response: DexiXResponse, cause?: unknown);
168
+ }
169
+
170
+ /**
171
+ * The default DexiX Request instance.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import dx from "dexix-request";
176
+ *
177
+ * const users = await dx("/users");
178
+ * const user = await dx.get<User>("/users/1");
179
+ * await dx.post("/posts", { title: "Hello" });
180
+ * ```
181
+ */
182
+ declare const dx: DexiXInstance;
183
+
184
+ export { DexiXError, type DexiXInstance, type DexiXRequestInfo, type DexiXResponse, HTTPError, type HeadersInput, type HttpMethod, NetworkError, ParseError, type QueryParams, type RequestBody, type RequestConfig, type ResponseType, TimeoutError, createInstance, dx as default };
@@ -0,0 +1,184 @@
1
+ /**
2
+ * HTTP methods supported by DexiX Request.
3
+ */
4
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
5
+ /**
6
+ * Plain object of query parameters. Array values are serialized as
7
+ * repeated keys (e.g. `{ tag: ["a", "b"] }` -> `?tag=a&tag=b`).
8
+ */
9
+ type QueryParams = Record<string, string | number | boolean | null | undefined | Array<string | number | boolean>>;
10
+ /**
11
+ * Anything that can be sent as a request body.
12
+ * Plain objects and arrays are automatically JSON-serialized.
13
+ */
14
+ type RequestBody = BodyInit | Record<string, unknown> | unknown[] | null | undefined;
15
+ /**
16
+ * Headers accepted by DexiX Request. Accepts a plain object, a `Headers`
17
+ * instance, or an array of key/value pairs (same as the `HeadersInit` type).
18
+ */
19
+ type HeadersInput = HeadersInit | Record<string, string | undefined>;
20
+ /**
21
+ * Response type hint. DexiX Request defaults to `"json"` and falls back
22
+ * gracefully when the response has no JSON body.
23
+ */
24
+ type ResponseType = "json" | "text" | "blob" | "arrayBuffer" | "stream";
25
+ /**
26
+ * Configuration accepted by every request call, and by `dx.create()`.
27
+ */
28
+ interface RequestConfig {
29
+ /** Base URL prepended to every relative request URL. */
30
+ baseURL?: string;
31
+ /** Headers merged into every request. */
32
+ headers?: HeadersInput;
33
+ /** Query parameters appended to the URL's search string. */
34
+ query?: QueryParams;
35
+ /** Request body. Objects/arrays are JSON-serialized automatically. */
36
+ body?: RequestBody;
37
+ /** Timeout in milliseconds. `0` or `undefined` disables the timeout. */
38
+ timeout?: number;
39
+ /** An `AbortSignal` to cancel the request. Composable with `timeout`. */
40
+ signal?: AbortSignal;
41
+ /** How to parse the response body. Defaults to `"json"`. */
42
+ responseType?: ResponseType;
43
+ /** Credentials mode, forwarded to `fetch`. */
44
+ credentials?: RequestCredentials;
45
+ /** Cache mode, forwarded to `fetch`. */
46
+ cache?: RequestCache;
47
+ /** Redirect mode, forwarded to `fetch`. */
48
+ redirect?: RequestRedirect;
49
+ /** Referrer policy, forwarded to `fetch`. */
50
+ referrerPolicy?: ReferrerPolicy;
51
+ /** Mode, forwarded to `fetch`. */
52
+ mode?: RequestMode;
53
+ /**
54
+ * Called once per request with the fully-resolved `Request` before it is
55
+ * sent. Useful for logging or attaching auth tokens dynamically.
56
+ */
57
+ onRequest?: (request: Request) => void | Promise<void>;
58
+ /**
59
+ * Called once per request with the raw `Response`, before the body is
60
+ * parsed.
61
+ */
62
+ onResponse?: (response: Response) => void | Promise<void>;
63
+ }
64
+ /**
65
+ * Metadata attached to every DexiX error, describing the request that
66
+ * produced it.
67
+ */
68
+ interface DexiXRequestInfo {
69
+ url: string;
70
+ method: HttpMethod;
71
+ headers: Headers;
72
+ body?: RequestBody;
73
+ }
74
+ /**
75
+ * The shape of a successful DexiX Request response before unwrapping.
76
+ * Consumers normally never see this directly, since `dx()` resolves to
77
+ * the parsed body, but it's available via `HTTPError.response`.
78
+ */
79
+ interface DexiXResponse<T = unknown> {
80
+ data: T;
81
+ status: number;
82
+ statusText: string;
83
+ headers: Headers;
84
+ url: string;
85
+ }
86
+ /**
87
+ * A callable DexiX Request instance. Calling it directly performs a GET
88
+ * request. Method-specific helpers are attached for convenience.
89
+ */
90
+ interface DexiXInstance {
91
+ <T = unknown>(url: string, config?: RequestConfig): Promise<T>;
92
+ get<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
93
+ post<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
94
+ put<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
95
+ patch<T = unknown>(url: string, body?: RequestBody, config?: RequestConfig): Promise<T>;
96
+ delete<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
97
+ head(url: string, config?: RequestConfig): Promise<void>;
98
+ options<T = unknown>(url: string, config?: RequestConfig): Promise<T>;
99
+ /** The base configuration this instance was created with. */
100
+ readonly defaults: RequestConfig;
101
+ /**
102
+ * Create a new, independent DexiX Request instance. Config is merged
103
+ * with the parent instance's defaults (child config wins).
104
+ */
105
+ create(config?: RequestConfig): DexiXInstance;
106
+ }
107
+
108
+ /**
109
+ * Creates a new DexiX Request instance. Calling the instance directly
110
+ * performs a GET request; method-specific helpers (`get`, `post`, `put`,
111
+ * `patch`, `delete`, `head`, `options`) are attached for convenience.
112
+ */
113
+ declare function createInstance(defaults?: RequestConfig): DexiXInstance;
114
+
115
+ /**
116
+ * Base class for every error thrown by DexiX Request.
117
+ * All DexiX errors carry the originating request and, when available,
118
+ * the response that was received.
119
+ */
120
+ declare class DexiXError extends Error {
121
+ /** The request that caused this error. */
122
+ readonly request: DexiXRequestInfo;
123
+ /** The response received, if any (absent for network/timeout errors). */
124
+ readonly response?: DexiXResponse;
125
+ /** HTTP status code, if a response was received. */
126
+ readonly status?: number;
127
+ /** Response headers, if a response was received. */
128
+ readonly headers?: Headers;
129
+ /** The request URL. */
130
+ readonly url: string;
131
+ constructor(message: string, request: DexiXRequestInfo, response?: DexiXResponse);
132
+ }
133
+
134
+ /**
135
+ * Thrown when the request could not be completed because of a network
136
+ * failure (DNS resolution failure, connection refused, offline, CORS
137
+ * rejection, etc). No response was ever received.
138
+ */
139
+ declare class NetworkError extends DexiXError {
140
+ constructor(message: string, request: DexiXRequestInfo, cause?: unknown);
141
+ }
142
+
143
+ /**
144
+ * Thrown when a request exceeds its configured `timeout` before a
145
+ * response is received.
146
+ */
147
+ declare class TimeoutError extends DexiXError {
148
+ /** The timeout duration, in milliseconds, that was exceeded. */
149
+ readonly timeout: number;
150
+ constructor(request: DexiXRequestInfo, timeout: number);
151
+ }
152
+
153
+ /**
154
+ * Thrown when the server responds with a non-2xx status code.
155
+ * Always carries the full `response`, including any parsed body DexiX
156
+ * Request was able to extract before raising the error.
157
+ */
158
+ declare class HTTPError extends DexiXError {
159
+ constructor(request: DexiXRequestInfo, response: DexiXResponse);
160
+ }
161
+
162
+ /**
163
+ * Thrown when a response body could not be parsed according to the
164
+ * requested (or inferred) `responseType`, e.g. invalid JSON.
165
+ */
166
+ declare class ParseError extends DexiXError {
167
+ constructor(request: DexiXRequestInfo, response: DexiXResponse, cause?: unknown);
168
+ }
169
+
170
+ /**
171
+ * The default DexiX Request instance.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import dx from "dexix-request";
176
+ *
177
+ * const users = await dx("/users");
178
+ * const user = await dx.get<User>("/users/1");
179
+ * await dx.post("/posts", { title: "Hello" });
180
+ * ```
181
+ */
182
+ declare const dx: DexiXInstance;
183
+
184
+ export { DexiXError, type DexiXInstance, type DexiXRequestInfo, type DexiXResponse, HTTPError, type HeadersInput, type HttpMethod, NetworkError, ParseError, type QueryParams, type RequestBody, type RequestConfig, type ResponseType, TimeoutError, createInstance, dx as default };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ var i=class extends Error{request;response;status;headers;url;constructor(t,r,n){super(t),this.name="DexiXError",this.request=r,this.response=n,this.status=n?.status,this.headers=n?.headers,this.url=r.url,Object.setPrototypeOf(this,new.target.prototype);}};var c=class extends i{constructor(t,r){super(`Request failed with status ${r.status} (${r.statusText}) for ${t.url}`,t,r),this.name="HTTPError";}};var d=class extends i{constructor(t,r,n){super(t||"Network request failed",r),this.name="NetworkError",n!==void 0&&(this.cause=n);}};var m=class extends i{timeout;constructor(t,r){super(`Request to ${t.url} timed out after ${r}ms`,t),this.name="TimeoutError",this.timeout=r;}};function C(e){return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function k(e,t){if(!e||C(t))return t;let r=e.endsWith("/")?e.slice(0,-1):e,n=t.startsWith("/")?t:`/${t}`;return `${r}${n}`}function H(e,t){if(!t||Object.keys(t).length===0)return e;let[r,n=""]=e.split("?"),o=new URLSearchParams(n);for(let[f,u]of Object.entries(t))if(u!=null)if(Array.isArray(u))for(let b of u)o.append(f,String(b));else o.append(f,String(u));let s=o.toString();return s?`${r}?${s}`:r}function q(e,t,r){let n=k(e??"",t);return H(n,r)}function l(...e){let t=new Headers;for(let r of e)if(r){if(r instanceof Headers){r.forEach((n,o)=>{t.set(o,n);});continue}if(Array.isArray(r)){for(let[n,o]of r)t.set(n,o);continue}for(let[n,o]of Object.entries(r))o===void 0?t.delete(n):t.set(n,o);}return t}function R(e,t){return {...e,...t,headers:l(e.headers,t.headers),query:{...e.query,...t.query}}}function E(e){if(typeof e!="object"||e===null)return false;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function S(e){return typeof e=="string"||e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||e instanceof ReadableStream}function w(e){return e==null?{body:void 0}:S(e)?e instanceof FormData?{body:e}:e instanceof URLSearchParams?{body:e,contentType:"application/x-www-form-urlencoded;charset=UTF-8"}:typeof e=="string"?{body:e,contentType:"text/plain;charset=UTF-8"}:{body:e}:E(e)||Array.isArray(e)?{body:JSON.stringify(e),contentType:"application/json;charset=UTF-8"}:{body:JSON.stringify(e),contentType:"application/json;charset=UTF-8"}}var x=class extends i{constructor(t,r,n){super(`Failed to parse response body from ${t.url}`,t,r),this.name="ParseError",n!==void 0&&(this.cause=n);}};var O=new Set([204,205,304]);function A(e){let t=e.headers.get("content-type")??"";return t.includes("application/json")||t.includes("+json")?"json":t.startsWith("text/")?"text":"json"}async function h(e,t,r){if(t.method==="HEAD"||O.has(e.status))return;let n=r??A(e);try{switch(n){case "json":{let o=await e.text();return o.length===0?void 0:JSON.parse(o)}case "text":return await e.text();case "blob":return await e.blob();case "arrayBuffer":return await e.arrayBuffer();case "stream":return e.body;default:return await e.text()}}catch(o){let s={data:void 0,status:e.status,statusText:e.statusText,headers:e.headers,url:e.url||t.url};throw new x(t,s,o)}}function D(e,t){if(!t||t<=0)return {signal:e,cancel:()=>{},didTimeout:()=>false};let r=new AbortController,n=false,o=setTimeout(()=>{n=true,r.abort();},t),s=()=>r.abort();e?.addEventListener("abort",s,{once:true});let f=()=>{clearTimeout(o),e?.removeEventListener("abort",s);};return {signal:r.signal,cancel:f,didTimeout:()=>n}}async function j(e){let{body:t,contentType:r}=w(e.body),n=l(r?{"content-type":r}:void 0,e.headers),o=q(e.baseURL,e.url,e.query),s={url:o,method:e.method,headers:n,body:e.body},{signal:f,cancel:u,didTimeout:b}=D(e.signal,e.timeout),I={method:e.method,headers:n,body:e.method==="GET"||e.method==="HEAD"?void 0:t,signal:f,credentials:e.credentials,cache:e.cache,redirect:e.redirect,referrerPolicy:e.referrerPolicy,mode:e.mode},X=new Request(o,I);e.onRequest&&await e.onRequest(X);let a;try{a=await fetch(X);}catch(p){throw u(),p instanceof DOMException&&p.name==="AbortError"?b()?new m(s,e.timeout??0):p:new d(p instanceof Error?p.message:"Network request failed",s,p)}if(u(),e.onResponse&&await e.onResponse(a),!a.ok){let P={data:await h(a,s,e.responseType).catch(()=>{}),status:a.status,statusText:a.statusText,headers:a.headers,url:a.url||o};throw new c(s,P)}return h(a,s,e.responseType)}function y(e,t,r,n){let o={...R(e,n??{}),method:t,url:r};return j(o)}function g(e,t,r,n,o){return y(e,t,r,{...o,body:n??o?.body})}function T(e={}){let t=((r,n)=>y(e,"GET",r,n));return t.get=(r,n)=>y(e,"GET",r,n),t.post=(r,n,o)=>g(e,"POST",r,n,o),t.put=(r,n,o)=>g(e,"PUT",r,n,o),t.patch=(r,n,o)=>g(e,"PATCH",r,n,o),t.delete=(r,n)=>y(e,"DELETE",r,n),t.head=(r,n)=>y(e,"HEAD",r,n),t.options=(r,n)=>y(e,"OPTIONS",r,n),Object.defineProperty(t,"defaults",{value:e,writable:false,enumerable:true}),t.create=r=>T(R(e,r??{})),t}var B=T();var Ee=B;
2
+ export{i as DexiXError,c as HTTPError,d as NetworkError,x as ParseError,m as TimeoutError,T as createInstance,Ee as default};//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/DexiXError.ts","../src/errors/HTTPError.ts","../src/errors/NetworkError.ts","../src/errors/TimeoutError.ts","../src/utils/buildURL.ts","../src/utils/mergeHeaders.ts","../src/utils/mergeConfig.ts","../src/utils/isPlainObject.ts","../src/utils/serializeBody.ts","../src/errors/ParseError.ts","../src/utils/parseResponse.ts","../src/utils/combineSignals.ts","../src/core/dispatch.ts","../src/core/instance.ts","../src/index.ts"],"names":["DexiXError","message","request","response","HTTPError","NetworkError","cause","TimeoutError","timeout","isAbsoluteURL","url","joinURL","baseURL","trimmedBase","trimmedPath","appendQuery","query","path","existingSearch","params","key","value","item","search","buildURL","joined","mergeHeaders","sources","result","source","mergeConfig","parent","child","isPlainObject","prototype","isNativeBodyType","serializeBody","body","ParseError","EMPTY_STATUS_CODES","inferResponseType","contentType","parseResponse","responseType","type","text","failedResponse","combineSignals","userSignal","controller","timedOut","timer","onUserAbort","cancel","dispatch","config","headers","requestInfo","signal","didTimeout","init","errorResponse","defaults","method","resolved","requestWithBody","createInstance","instance","dx","index_default"],"mappings":"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","file":"index.js","sourcesContent":["import type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Base class for every error thrown by DexiX Request.\n * All DexiX errors carry the originating request and, when available,\n * the response that was received.\n */\nexport class DexiXError extends Error {\n /** The request that caused this error. */\n public readonly request: DexiXRequestInfo;\n /** The response received, if any (absent for network/timeout errors). */\n public readonly response?: DexiXResponse;\n /** HTTP status code, if a response was received. */\n public readonly status?: number;\n /** Response headers, if a response was received. */\n public readonly headers?: Headers;\n /** The request URL. */\n public readonly url: string;\n\n public constructor(\n message: string,\n request: DexiXRequestInfo,\n response?: DexiXResponse,\n ) {\n super(message);\n this.name = \"DexiXError\";\n this.request = request;\n this.response = response;\n this.status = response?.status;\n this.headers = response?.headers;\n this.url = request.url;\n\n // Restore the correct prototype chain when compiled down to ES5-style\n // targets (not an issue at ES2022, but harmless and defensive).\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Thrown when the server responds with a non-2xx status code.\n * Always carries the full `response`, including any parsed body DexiX\n * Request was able to extract before raising the error.\n */\nexport class HTTPError extends DexiXError {\n public constructor(request: DexiXRequestInfo, response: DexiXResponse) {\n super(\n `Request failed with status ${response.status} (${response.statusText}) for ${request.url}`,\n request,\n response,\n );\n this.name = \"HTTPError\";\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo } from \"../types/index.js\";\n\n/**\n * Thrown when the request could not be completed because of a network\n * failure (DNS resolution failure, connection refused, offline, CORS\n * rejection, etc). No response was ever received.\n */\nexport class NetworkError extends DexiXError {\n public constructor(message: string, request: DexiXRequestInfo, cause?: unknown) {\n super(message || \"Network request failed\", request);\n this.name = \"NetworkError\";\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo } from \"../types/index.js\";\n\n/**\n * Thrown when a request exceeds its configured `timeout` before a\n * response is received.\n */\nexport class TimeoutError extends DexiXError {\n /** The timeout duration, in milliseconds, that was exceeded. */\n public readonly timeout: number;\n\n public constructor(request: DexiXRequestInfo, timeout: number) {\n super(`Request to ${request.url} timed out after ${timeout}ms`, request);\n this.name = \"TimeoutError\";\n this.timeout = timeout;\n }\n}\n","import type { QueryParams } from \"../types/index.js\";\n\n/**\n * Detects absolute URLs (has a scheme like `https://`, `http://`, or is\n * protocol-relative `//host`).\n */\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url);\n}\n\n/**\n * Joins a base URL and a relative path without producing double slashes\n * or dropping a needed one.\n */\nfunction joinURL(baseURL: string, url: string): string {\n if (!baseURL) return url;\n if (isAbsoluteURL(url)) return url;\n\n const trimmedBase = baseURL.endsWith(\"/\") ? baseURL.slice(0, -1) : baseURL;\n const trimmedPath = url.startsWith(\"/\") ? url : `/${url}`;\n return `${trimmedBase}${trimmedPath}`;\n}\n\n/**\n * Appends query parameters to a URL, preserving any existing search\n * string already present on the URL. Array values become repeated keys.\n * `null`/`undefined` values are omitted entirely.\n */\nfunction appendQuery(url: string, query?: QueryParams): string {\n if (!query || Object.keys(query).length === 0) {\n return url;\n }\n\n const [path, existingSearch = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(existingSearch);\n\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) {\n params.append(key, String(item));\n }\n } else {\n params.append(key, String(value));\n }\n }\n\n const search = params.toString();\n return search ? `${path}?${search}` : (path as string);\n}\n\n/**\n * Builds the final request URL from a base URL, a relative or absolute\n * URL, and an optional set of query parameters.\n */\nexport function buildURL(baseURL: string | undefined, url: string, query?: QueryParams): string {\n const joined = joinURL(baseURL ?? \"\", url);\n return appendQuery(joined, query);\n}\n","import type { HeadersInput } from \"../types/index.js\";\n\n/**\n * Merges any number of header sources into a single `Headers` instance.\n * Later sources override earlier ones for the same (case-insensitive) key.\n * A value of `undefined` in a plain-object source removes that header.\n */\nexport function mergeHeaders(...sources: Array<HeadersInput | undefined>): Headers {\n const result = new Headers();\n\n for (const source of sources) {\n if (!source) continue;\n\n if (source instanceof Headers) {\n source.forEach((value, key) => {\n result.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(source)) {\n for (const [key, value] of source) {\n result.set(key, value);\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n result.delete(key);\n } else {\n result.set(key, value);\n }\n }\n }\n\n return result;\n}\n","import { mergeHeaders } from \"./mergeHeaders.js\";\nimport type { RequestConfig } from \"../types/index.js\";\n\n/**\n * Merges a child `RequestConfig` on top of a parent one. Headers and\n * query params are merged (child wins per-key); every other field is\n * simply overridden by the child when present.\n */\nexport function mergeConfig(parent: RequestConfig, child: RequestConfig): RequestConfig {\n return {\n ...parent,\n ...child,\n headers: mergeHeaders(parent.headers, child.headers),\n query: { ...parent.query, ...child.query },\n };\n}\n","/**\n * Returns `true` for plain JavaScript objects (object literals, or objects\n * with `Object.prototype` / `null` as their prototype). Returns `false`\n * for arrays, class instances, `FormData`, `Blob`, `File`, `URLSearchParams`,\n * `ReadableStream`, `Date`, `Map`, `Set`, and primitives.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value) as unknown;\n return prototype === Object.prototype || prototype === null;\n}\n","import { isPlainObject } from \"./isPlainObject.js\";\nimport type { RequestBody } from \"../types/index.js\";\n\n/**\n * Result of serializing a request body: the value ready to hand to\n * `fetch`, and an optional `Content-Type` to apply when one wasn't\n * already set by the caller.\n */\nexport interface SerializedBody {\n body: BodyInit | null | undefined;\n contentType?: string;\n}\n\n/**\n * Determines whether a value is already a \"native\" body type that\n * `fetch` understands natively and should be passed through untouched.\n */\nfunction isNativeBodyType(value: unknown): value is BodyInit {\n return (\n typeof value === \"string\" ||\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n value instanceof ReadableStream\n );\n}\n\n/**\n * Serializes a DexiX request body. Plain objects and arrays are\n * JSON-serialized automatically; every other supported `BodyInit` type is\n * passed through untouched so the caller retains full control when\n * needed (e.g. uploading a `FormData` or streaming a `ReadableStream`).\n */\nexport function serializeBody(body: RequestBody): SerializedBody {\n if (body === null || body === undefined) {\n return { body: undefined };\n }\n\n if (isNativeBodyType(body)) {\n // FormData sets its own multipart boundary; let the platform handle it.\n if (body instanceof FormData) {\n return { body };\n }\n if (body instanceof URLSearchParams) {\n return { body, contentType: \"application/x-www-form-urlencoded;charset=UTF-8\" };\n }\n if (typeof body === \"string\") {\n return { body, contentType: \"text/plain;charset=UTF-8\" };\n }\n return { body };\n }\n\n if (isPlainObject(body) || Array.isArray(body)) {\n return {\n body: JSON.stringify(body),\n contentType: \"application/json;charset=UTF-8\",\n };\n }\n\n // Fallback: best-effort JSON serialization for other serializable values.\n return {\n body: JSON.stringify(body),\n contentType: \"application/json;charset=UTF-8\",\n };\n}\n","import { DexiXError } from \"./DexiXError.js\";\nimport type { DexiXRequestInfo, DexiXResponse } from \"../types/index.js\";\n\n/**\n * Thrown when a response body could not be parsed according to the\n * requested (or inferred) `responseType`, e.g. invalid JSON.\n */\nexport class ParseError extends DexiXError {\n public constructor(request: DexiXRequestInfo, response: DexiXResponse, cause?: unknown) {\n super(`Failed to parse response body from ${request.url}`, request, response);\n this.name = \"ParseError\";\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n","import { ParseError } from \"../errors/ParseError.js\";\nimport type { DexiXRequestInfo, DexiXResponse, ResponseType } from \"../types/index.js\";\n\nconst EMPTY_STATUS_CODES = new Set([204, 205, 304]);\n\n/**\n * Infers the best response type from the `Content-Type` header when the\n * caller didn't explicitly request one.\n */\nfunction inferResponseType(response: Response): ResponseType {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\") || contentType.includes(\"+json\")) {\n return \"json\";\n }\n if (contentType.startsWith(\"text/\")) {\n return \"text\";\n }\n return \"json\";\n}\n\n/**\n * Parses a `Response` body according to `responseType` (or an inferred\n * type based on `Content-Type`), returning the parsed value. Throws\n * `ParseError` if parsing fails.\n */\nexport async function parseResponse<T>(\n response: Response,\n request: DexiXRequestInfo,\n responseType?: ResponseType,\n): Promise<T> {\n if (request.method === \"HEAD\" || EMPTY_STATUS_CODES.has(response.status)) {\n return undefined as T;\n }\n\n const type = responseType ?? inferResponseType(response);\n\n try {\n switch (type) {\n case \"json\": {\n const text = await response.text();\n if (text.length === 0) {\n return undefined as T;\n }\n return JSON.parse(text) as T;\n }\n case \"text\":\n return (await response.text()) as unknown as T;\n case \"blob\":\n return (await response.blob()) as unknown as T;\n case \"arrayBuffer\":\n return (await response.arrayBuffer()) as unknown as T;\n case \"stream\":\n return response.body as unknown as T;\n default:\n return (await response.text()) as unknown as T;\n }\n } catch (cause) {\n const failedResponse: DexiXResponse = {\n data: undefined,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n url: response.url || request.url,\n };\n throw new ParseError(request, failedResponse, cause);\n }\n}\n","/**\n * Combines an optional user-provided `AbortSignal` with an internal\n * timeout, producing a single signal that aborts when either source\n * aborts. Returns the combined signal along with a `cancel()` function\n * that must be called once the request settles to clear the timer, and\n * a `didTimeout()` predicate to distinguish a timeout abort from a\n * user-initiated abort.\n */\nexport function combineSignals(\n userSignal: AbortSignal | undefined,\n timeout: number | undefined,\n): {\n signal: AbortSignal | undefined;\n cancel: () => void;\n didTimeout: () => boolean;\n} {\n if (!timeout || timeout <= 0) {\n return {\n signal: userSignal,\n cancel: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeout);\n\n const onUserAbort = () => controller.abort();\n userSignal?.addEventListener(\"abort\", onUserAbort, { once: true });\n\n const cancel = () => {\n clearTimeout(timer);\n userSignal?.removeEventListener(\"abort\", onUserAbort);\n };\n\n return {\n signal: controller.signal,\n cancel,\n didTimeout: () => timedOut,\n };\n}\n","import { HTTPError } from \"../errors/HTTPError.js\";\nimport { NetworkError } from \"../errors/NetworkError.js\";\nimport { TimeoutError } from \"../errors/TimeoutError.js\";\nimport { buildURL, combineSignals, mergeHeaders, parseResponse, serializeBody } from \"../utils/index.js\";\nimport type { DexiXRequestInfo, DexiXResponse, ResolvedRequestConfig } from \"../types/index.js\";\n\n/**\n * Executes a single HTTP request described by a fully-resolved config.\n * This is the single point in the library that calls `fetch`.\n *\n * @internal\n */\nexport async function dispatch<T>(config: ResolvedRequestConfig): Promise<T> {\n const { body, contentType } = serializeBody(config.body);\n\n const headers = mergeHeaders(\n contentType ? { \"content-type\": contentType } : undefined,\n config.headers,\n );\n\n const url = buildURL(config.baseURL, config.url, config.query);\n\n const requestInfo: DexiXRequestInfo = {\n url,\n method: config.method,\n headers,\n body: config.body,\n };\n\n const { signal, cancel, didTimeout } = combineSignals(config.signal, config.timeout);\n\n const init: RequestInit = {\n method: config.method,\n headers,\n body: config.method === \"GET\" || config.method === \"HEAD\" ? undefined : body,\n signal,\n credentials: config.credentials,\n cache: config.cache,\n redirect: config.redirect,\n referrerPolicy: config.referrerPolicy,\n mode: config.mode,\n };\n\n const request = new Request(url, init);\n\n if (config.onRequest) {\n await config.onRequest(request);\n }\n\n let response: Response;\n\n try {\n response = await fetch(request);\n } catch (cause) {\n cancel();\n\n if (cause instanceof DOMException && cause.name === \"AbortError\") {\n if (didTimeout()) {\n throw new TimeoutError(requestInfo, config.timeout ?? 0);\n }\n throw cause;\n }\n\n throw new NetworkError(\n cause instanceof Error ? cause.message : \"Network request failed\",\n requestInfo,\n cause,\n );\n }\n\n cancel();\n\n if (config.onResponse) {\n await config.onResponse(response);\n }\n\n if (!response.ok) {\n const data = await parseResponse<unknown>(response, requestInfo, config.responseType).catch(\n () => undefined,\n );\n\n const errorResponse: DexiXResponse = {\n data,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n url: response.url || url,\n };\n\n throw new HTTPError(requestInfo, errorResponse);\n }\n\n return parseResponse<T>(response, requestInfo, config.responseType);\n}\n","import { dispatch } from \"./dispatch.js\";\nimport { mergeConfig } from \"../utils/index.js\";\nimport type {\n DexiXInstance,\n HttpMethod,\n RequestBody,\n RequestConfig,\n ResolvedRequestConfig,\n} from \"../types/index.js\";\n\nfunction request<T>(\n defaults: RequestConfig,\n method: HttpMethod,\n url: string,\n config?: RequestConfig,\n): Promise<T> {\n const resolved: ResolvedRequestConfig = {\n ...mergeConfig(defaults, config ?? {}),\n method,\n url,\n };\n\n return dispatch<T>(resolved);\n}\n\nfunction requestWithBody<T>(\n defaults: RequestConfig,\n method: HttpMethod,\n url: string,\n body?: RequestBody,\n config?: RequestConfig,\n): Promise<T> {\n return request<T>(defaults, method, url, { ...config, body: body ?? config?.body });\n}\n\n/**\n * Creates a new DexiX Request instance. Calling the instance directly\n * performs a GET request; method-specific helpers (`get`, `post`, `put`,\n * `patch`, `delete`, `head`, `options`) are attached for convenience.\n */\nexport function createInstance(defaults: RequestConfig = {}): DexiXInstance {\n const instance = (<T>(url: string, config?: RequestConfig): Promise<T> =>\n request<T>(defaults, \"GET\", url, config)) as DexiXInstance;\n\n instance.get = (url, config) => request(defaults, \"GET\", url, config);\n instance.post = (url, body, config) => requestWithBody(defaults, \"POST\", url, body, config);\n instance.put = (url, body, config) => requestWithBody(defaults, \"PUT\", url, body, config);\n instance.patch = (url, body, config) => requestWithBody(defaults, \"PATCH\", url, body, config);\n instance.delete = (url, config) => request(defaults, \"DELETE\", url, config);\n instance.head = (url, config) => request(defaults, \"HEAD\", url, config);\n instance.options = (url, config) => request(defaults, \"OPTIONS\", url, config);\n\n Object.defineProperty(instance, \"defaults\", {\n value: defaults,\n writable: false,\n enumerable: true,\n });\n\n instance.create = (config?: RequestConfig) => createInstance(mergeConfig(defaults, config ?? {}));\n\n return instance;\n}\n","import { createInstance } from \"./core/index.js\";\nimport type { DexiXInstance } from \"./types/index.js\";\n\nexport type {\n DexiXInstance,\n RequestConfig,\n HttpMethod,\n QueryParams,\n RequestBody,\n HeadersInput,\n ResponseType,\n DexiXResponse,\n DexiXRequestInfo,\n} from \"./types/index.js\";\n\nexport { DexiXError, NetworkError, TimeoutError, HTTPError, ParseError } from \"./errors/index.js\";\n\n/**\n * The default DexiX Request instance.\n *\n * @example\n * ```ts\n * import dx from \"dexix-request\";\n *\n * const users = await dx(\"/users\");\n * const user = await dx.get<User>(\"/users/1\");\n * await dx.post(\"/posts\", { title: \"Hello\" });\n * ```\n */\nconst dx: DexiXInstance = createInstance();\n\nexport { createInstance };\nexport default dx;\n"]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "dexix-request",
3
+ "version": "1.0.0",
4
+ "description": "A tiny, modern, zero-dependency HTTP client for JavaScript and TypeScript. Built for the browser, Node.js, and React.",
5
+ "keywords": [
6
+ "http",
7
+ "http-client",
8
+ "fetch",
9
+ "request",
10
+ "api",
11
+ "typescript",
12
+ "axios-alternative",
13
+ "react",
14
+ "node"
15
+ ],
16
+ "homepage": "https://github.com/AkbariCodes/DexiX-Request/#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/AkbariCodes/DexiX-Request/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/AkbariCodes/DexiX-Request.git"
23
+ },
24
+ "license": "MIT",
25
+ "author": "DexiX Contributors",
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "main": "./dist/index.cjs",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "require": "./dist/index.cjs"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE",
43
+ "CHANGELOG.md"
44
+ ],
45
+ "engines": {
46
+ "node": ">=20"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "test:coverage": "vitest run --coverage",
54
+ "typecheck": "tsc --noEmit",
55
+ "lint": "tsc --noEmit",
56
+ "prepublishOnly": "npm run build"
57
+ },
58
+ "devDependencies": {
59
+ "tsup": "^8.5.1",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^2.0.5"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }