@ma-dev/api-client 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -12,6 +12,11 @@ Shared HTTP client infrastructure for frontend projects.
12
12
  | `ApiResponse<T>` | Generic response envelope matching the NWC API contract |
13
13
  | `HttpClient` | Inferred type of the object returned by `createHttpClient` |
14
14
  | `HttpClientConfig` | Config interface for `createHttpClient` |
15
+ | `RequestOptions` | Per-request options (headers, params, signal) |
16
+ | `RetryConfig` | Retry strategy configuration |
17
+ | `QueryParams` | Query parameter map type |
18
+ | `ResponseInterceptor` | Callback type for response interceptors |
19
+ | `ErrorInterceptor` | Callback type for error interceptors |
15
20
 
16
21
  ## Installation
17
22
 
@@ -34,6 +39,8 @@ import { createHttpClient, tokenStore } from "@ma-dev/api-client";
34
39
  export const httpClient = createHttpClient({
35
40
  baseUrl: import.meta.env.VITE_API_URL,
36
41
  getToken: tokenStore.getToken,
42
+ timeoutMs: 10_000,
43
+ retry: { attempts: 2 },
37
44
  });
38
45
  ```
39
46
 
@@ -77,6 +84,142 @@ try {
77
84
  }
78
85
  ```
79
86
 
87
+ ---
88
+
89
+ ## Features
90
+
91
+ ### Query parameters
92
+
93
+ Pass a `params` object in `RequestOptions` instead of building query strings manually. Arrays are serialised as repeated keys.
94
+
95
+ ```ts
96
+ // GET /users?page=2&role=admin&role=editor
97
+ const users = await httpClient.get<UserListResponse>("/users", {
98
+ params: { page: 2, role: ["admin", "editor"] },
99
+ });
100
+ ```
101
+
102
+ `null` and `undefined` values are automatically omitted.
103
+
104
+ ### Request timeouts
105
+
106
+ Set a global deadline for every request:
107
+
108
+ ```ts
109
+ const httpClient = createHttpClient({
110
+ baseUrl: "...",
111
+ timeoutMs: 8_000, // 8 s global timeout
112
+ });
113
+ ```
114
+
115
+ Or override per-request using a standard `AbortSignal`:
116
+
117
+ ```ts
118
+ const result = await httpClient.get("/slow-endpoint", {
119
+ signal: AbortSignal.timeout(3_000),
120
+ });
121
+ ```
122
+
123
+ ### Retry with exponential back-off
124
+
125
+ ```ts
126
+ const httpClient = createHttpClient({
127
+ baseUrl: "...",
128
+ retry: {
129
+ attempts: 3, // up to 3 retries (4 total attempts)
130
+ baseDelayMs: 300, // 300 ms → 600 ms → 1 200 ms
131
+ retryOn: [429, 500, 502, 503, 504], // default if omitted
132
+ },
133
+ });
134
+ ```
135
+
136
+ Retries are never triggered for `AbortError` (user/timeout cancellations).
137
+
138
+ ### Interceptors
139
+
140
+ **Response interceptors** — called after every response, useful for logging or analytics:
141
+
142
+ ```ts
143
+ const httpClient = createHttpClient({
144
+ baseUrl: "...",
145
+ responseInterceptors: [
146
+ (res, req) => {
147
+ console.log(`[${req.method}] ${req.path} → ${res.status}`);
148
+ },
149
+ ],
150
+ });
151
+ ```
152
+
153
+ **Error interceptors** — called before `ApiError` is thrown. Return `true` to suppress the error:
154
+
155
+ ```ts
156
+ import { useNavigate } from "react-router-dom";
157
+
158
+ const httpClient = createHttpClient({
159
+ baseUrl: "...",
160
+ errorInterceptors: [
161
+ (error) => {
162
+ if (error.status === 401) {
163
+ tokenStore.setToken(null);
164
+ window.location.href = "/login";
165
+ return true; // suppress the throw
166
+ }
167
+ },
168
+ ],
169
+ });
170
+ ```
171
+
172
+ ### File uploads (`multipart`)
173
+
174
+ ```ts
175
+ const form = new FormData();
176
+ form.append("avatar", fileInput.files[0]);
177
+
178
+ const result = await httpClient.multipart<UploadResponse>("/profile/avatar", form);
179
+ ```
180
+
181
+ `Content-Type` is omitted so the browser automatically supplies the correct `multipart/form-data; boundary=…` header.
182
+
183
+ ### HEAD requests
184
+
185
+ ```ts
186
+ await httpClient.head("/healthz");
187
+ ```
188
+
189
+ ---
190
+
191
+ ## API reference
192
+
193
+ ### `createHttpClient(config)`
194
+
195
+ | Option | Type | Description |
196
+ |---|---|---|
197
+ | `baseUrl` | `string` | Prepended to every path. No trailing slash. |
198
+ | `getToken` | `() => string \| null` | Returns the current bearer token. Read on every request. |
199
+ | `defaultHeaders` | `Record<string, string>` | Static headers merged into every request. |
200
+ | `timeoutMs` | `number` | Global request deadline in ms. Uses `AbortSignal.timeout`. |
201
+ | `retry` | `RetryConfig` | Retry strategy. Default: no retries. |
202
+ | `responseInterceptors` | `ResponseInterceptor[]` | Called after every response. |
203
+ | `errorInterceptors` | `ErrorInterceptor[]` | Called before an `ApiError` is thrown. |
204
+
205
+ ### `RequestOptions`
206
+
207
+ | Option | Type | Description |
208
+ |---|---|---|
209
+ | `headers` | `HeadersInit` | Extra headers for this request only. |
210
+ | `params` | `QueryParams` | Query-string parameters. Arrays → repeated keys. |
211
+ | `signal` | `AbortSignal` | Cancellation / per-request timeout. |
212
+
213
+ ### `RetryConfig`
214
+
215
+ | Option | Type | Default | Description |
216
+ |---|---|---|---|
217
+ | `attempts` | `number` | — | Max retries after the initial failure. |
218
+ | `baseDelayMs` | `number` | `300` | Base delay in ms for exponential back-off. |
219
+ | `retryOn` | `number[]` | `[408, 429, 500, 502, 503, 504]` | Status codes that trigger a retry. |
220
+
221
+ ---
222
+
80
223
  ## Building the package
81
224
 
82
225
  ```bash
package/dist/http.d.ts CHANGED
@@ -26,6 +26,51 @@ export declare class ApiError extends Error {
26
26
  body?: unknown | undefined);
27
27
  }
28
28
  type TokenGetter = () => string | null;
29
+ /** Scalar query-param value types. Arrays are serialised as repeated keys. */
30
+ export type QueryParamValue = string | number | boolean | null | undefined;
31
+ export type QueryParams = Record<string, QueryParamValue | QueryParamValue[]>;
32
+ /** Called after every response — use for logging, analytics, etc. */
33
+ export type ResponseInterceptor = (response: Response, request: {
34
+ method: string;
35
+ path: string;
36
+ }) => void | Promise<void>;
37
+ /**
38
+ * Called when the server returns a non-2xx status **before** `ApiError` is
39
+ * thrown. Return `true` to suppress the error (e.g. redirect to login on 401).
40
+ */
41
+ export type ErrorInterceptor = (error: ApiError, request: {
42
+ method: string;
43
+ path: string;
44
+ }) => boolean | void | Promise<boolean | void>;
45
+ export interface RetryConfig {
46
+ /**
47
+ * Maximum number of retry attempts after the initial request fails.
48
+ * Default: `0` (no retries).
49
+ */
50
+ attempts: number;
51
+ /**
52
+ * Base delay in milliseconds between retries. Each subsequent attempt
53
+ * waits `baseDelayMs * 2^(attempt - 1)` (exponential back-off).
54
+ * Default: `300`.
55
+ */
56
+ baseDelayMs?: number;
57
+ /**
58
+ * HTTP status codes that should trigger a retry.
59
+ * Default: `[408, 429, 500, 502, 503, 504]`.
60
+ */
61
+ retryOn?: number[];
62
+ }
63
+ export interface RequestOptions {
64
+ /** Additional headers merged into this specific request only. */
65
+ headers?: HeadersInit;
66
+ /** Query string parameters appended to the URL. */
67
+ params?: QueryParams;
68
+ /**
69
+ * Abort signal for request cancellation.
70
+ * Tip: `AbortSignal.timeout(5_000)` for a 5 s deadline.
71
+ */
72
+ signal?: AbortSignal;
73
+ }
29
74
  export interface HttpClientConfig {
30
75
  /**
31
76
  * Base URL prepended to every request path.
@@ -39,15 +84,39 @@ export interface HttpClientConfig {
39
84
  getToken?: TokenGetter;
40
85
  /** Static headers merged into every request (e.g. custom API keys). */
41
86
  defaultHeaders?: Record<string, string>;
87
+ /**
88
+ * Global request timeout in milliseconds. Applied to every request via
89
+ * `AbortSignal.timeout()` unless the caller supplies their own `signal`.
90
+ * Default: no timeout.
91
+ */
92
+ timeoutMs?: number;
93
+ /** Retry strategy for transient failures. Default: no retries. */
94
+ retry?: RetryConfig;
95
+ /**
96
+ * Interceptors called after every response (2xx and non-2xx).
97
+ * Useful for logging or analytics.
98
+ */
99
+ responseInterceptors?: ResponseInterceptor[];
100
+ /**
101
+ * Interceptors called before an `ApiError` is thrown.
102
+ * Return `true` from any interceptor to suppress the error entirely
103
+ * (e.g. redirect to `/login` on 401 and swallow the throw).
104
+ */
105
+ errorInterceptors?: ErrorInterceptor[];
42
106
  }
43
107
  /**
44
108
  * Creates a thin, fully-typed `fetch` wrapper.
45
109
  *
46
110
  * Features:
47
- * - Prepends `baseUrl` to every path.
111
+ * - Prepends `baseUrl` to every request path.
48
112
  * - Injects `Authorization: Bearer <token>` when `getToken` returns a value.
49
113
  * - Throws `ApiError` on non-2xx responses with a structured message.
50
114
  * - Generic return types — callers get typed response bodies with zero casting.
115
+ * - Query param serialisation via `options.params`.
116
+ * - Per-request (or global) timeout via `AbortSignal.timeout`.
117
+ * - Configurable retry with exponential back-off.
118
+ * - Response & error interceptors for cross-cutting concerns.
119
+ * - `multipart` helper for `FormData` / file uploads.
51
120
  *
52
121
  * @example
53
122
  * import { createHttpClient, tokenStore } from "@ma-dev/api-client";
@@ -55,14 +124,27 @@ export interface HttpClientConfig {
55
124
  * export const httpClient = createHttpClient({
56
125
  * baseUrl: import.meta.env.VITE_API_URL,
57
126
  * getToken: tokenStore.getToken,
127
+ * timeoutMs: 10_000,
128
+ * retry: { attempts: 2 },
58
129
  * });
59
130
  */
60
- export declare function createHttpClient({ baseUrl, getToken, defaultHeaders, }: HttpClientConfig): {
61
- get: <T>(path: string, headers?: HeadersInit) => Promise<T>;
62
- post: <T>(path: string, body: unknown, headers?: HeadersInit) => Promise<T>;
63
- put: <T>(path: string, body: unknown, headers?: HeadersInit) => Promise<T>;
64
- patch: <T>(path: string, body: unknown, headers?: HeadersInit) => Promise<T>;
65
- delete: <T>(path: string, headers?: HeadersInit) => Promise<T>;
131
+ export declare function createHttpClient({ baseUrl, getToken, defaultHeaders, timeoutMs, retry, responseInterceptors, errorInterceptors, }: HttpClientConfig): {
132
+ get: <T>(path: string, options?: RequestOptions) => Promise<T>;
133
+ post: <T>(path: string, body: unknown, options?: RequestOptions) => Promise<T>;
134
+ put: <T>(path: string, body: unknown, options?: RequestOptions) => Promise<T>;
135
+ patch: <T>(path: string, body: unknown, options?: RequestOptions) => Promise<T>;
136
+ delete: <T>(path: string, options?: RequestOptions) => Promise<T>;
137
+ head: (path: string, options?: RequestOptions) => Promise<void>;
138
+ /**
139
+ * Upload `FormData` (e.g. files). Skips `Content-Type: application/json`
140
+ * so the browser can set the correct `multipart/form-data; boundary=…` header.
141
+ *
142
+ * @example
143
+ * const form = new FormData();
144
+ * form.append("file", fileInput.files[0]);
145
+ * await httpClient.multipart<UploadResponse>("/uploads", form);
146
+ */
147
+ multipart: <T>(path: string, form: FormData, options?: RequestOptions) => Promise<T>;
66
148
  };
67
149
  /** Inferred type of the object returned by `createHttpClient`. */
68
150
  export type HttpClient = ReturnType<typeof createHttpClient>;
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAE/B,6CAA6C;aAC7B,MAAM,EAAE,MAAM;IAG9B,6DAA6D;aAC7C,IAAI,CAAC,EAAE,OAAO;;IAL9B,6CAA6C;IAC7B,MAAM,EAAE,MAAM;IAC9B,mEAAmE;IACnE,OAAO,EAAE,MAAM;IACf,6DAA6D;IAC7C,IAAI,CAAC,EAAE,OAAO,YAAA;CAOjC;AAmCD,KAAK,WAAW,GAAG,MAAM,MAAM,GAAG,IAAI,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,cAAmB,GACpB,EAAE,gBAAgB;UA2CT,CAAC,QAAQ,MAAM,YAAY,WAAW;WAGrC,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,WAAW;UAGtD,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,WAAW;YAGnD,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,WAAW;aAGpD,CAAC,QAAQ,MAAM,YAAY,WAAW;EAGlD;AAED,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAE/B,6CAA6C;aAC7B,MAAM,EAAE,MAAM;IAG9B,6DAA6D;aAC7C,IAAI,CAAC,EAAE,OAAO;;IAL9B,6CAA6C;IAC7B,MAAM,EAAE,MAAM;IAC9B,mEAAmE;IACnE,OAAO,EAAE,MAAM;IACf,6DAA6D;IAC7C,IAAI,CAAC,EAAE,OAAO,YAAA;CAOjC;AAiED,KAAK,WAAW,GAAG,MAAM,MAAM,GAAG,IAAI,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAC3E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,EAAE,CAAC,CAAC;AAE9E,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAChC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACtC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACtC,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,mDAAmD;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACxC;AAQD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,cAAmB,EACnB,SAAS,EACT,KAAK,EACL,oBAAyB,EACzB,iBAAsB,GACvB,EAAE,gBAAgB;UA2HT,CAAC,QAAQ,MAAM,YAAY,cAAc;WAGxC,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,cAAc;UAGzD,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,cAAc;YAGtD,CAAC,QAAQ,MAAM,QAAQ,OAAO,YAAY,cAAc;aAGvD,CAAC,QAAQ,MAAM,YAAY,cAAc;iBAGrC,MAAM,YAAY,cAAc;IAG7C;;;;;;;;OAQG;gBACS,CAAC,QAAQ,MAAM,QAAQ,QAAQ,YAAY,cAAc;EAGxE;AAED,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
package/dist/http.js CHANGED
@@ -36,15 +36,23 @@ export class ApiError extends Error {
36
36
  // ---------------------------------------------------------------------------
37
37
  // Internal helpers
38
38
  // ---------------------------------------------------------------------------
39
+ /**
40
+ * BUG FIX: The original called `res.json()` even for `text/plain` responses,
41
+ * which throws a SyntaxError on non-JSON text bodies. Now we correctly use
42
+ * `res.text()` for plain text and only attempt JSON parsing for JSON content.
43
+ */
39
44
  async function parseBody(res) {
40
45
  const ct = res.headers.get("Content-Type") ?? "";
41
46
  try {
42
- if (ct.includes("application/json") || ct.includes("text/plain")) {
47
+ if (ct.includes("application/json")) {
43
48
  return await res.json();
44
49
  }
50
+ if (ct.includes("text/")) {
51
+ return await res.text();
52
+ }
45
53
  }
46
54
  catch {
47
- // Body may be empty or unparseable — treat as null
55
+ // Body may be empty or unparseable — treat as null.
48
56
  }
49
57
  return null;
50
58
  }
@@ -56,16 +64,42 @@ function extractErrorMessage(body, fallback) {
56
64
  (typeof b.detail === "string" && b.detail) ||
57
65
  fallback);
58
66
  }
67
+ if (typeof body === "string" && body.length > 0) {
68
+ return body;
69
+ }
59
70
  return fallback;
60
71
  }
72
+ function buildQueryString(params) {
73
+ const entries = Object.entries(params).filter(([, v]) => v !== undefined && v !== null);
74
+ if (entries.length === 0)
75
+ return "";
76
+ const qs = entries
77
+ .flatMap(([k, v]) => Array.isArray(v)
78
+ ? v.map((item) => `${encodeURIComponent(k)}=${encodeURIComponent(String(item))}`)
79
+ : [`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`])
80
+ .join("&");
81
+ return `?${qs}`;
82
+ }
83
+ function sleep(ms) {
84
+ return new Promise((resolve) => setTimeout(resolve, ms));
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // HTTP client factory
88
+ // ---------------------------------------------------------------------------
89
+ const DEFAULT_RETRY_ON = [408, 429, 500, 502, 503, 504];
61
90
  /**
62
91
  * Creates a thin, fully-typed `fetch` wrapper.
63
92
  *
64
93
  * Features:
65
- * - Prepends `baseUrl` to every path.
94
+ * - Prepends `baseUrl` to every request path.
66
95
  * - Injects `Authorization: Bearer <token>` when `getToken` returns a value.
67
96
  * - Throws `ApiError` on non-2xx responses with a structured message.
68
97
  * - Generic return types — callers get typed response bodies with zero casting.
98
+ * - Query param serialisation via `options.params`.
99
+ * - Per-request (or global) timeout via `AbortSignal.timeout`.
100
+ * - Configurable retry with exponential back-off.
101
+ * - Response & error interceptors for cross-cutting concerns.
102
+ * - `multipart` helper for `FormData` / file uploads.
69
103
  *
70
104
  * @example
71
105
  * import { createHttpClient, tokenStore } from "@ma-dev/api-client";
@@ -73,12 +107,18 @@ function extractErrorMessage(body, fallback) {
73
107
  * export const httpClient = createHttpClient({
74
108
  * baseUrl: import.meta.env.VITE_API_URL,
75
109
  * getToken: tokenStore.getToken,
110
+ * timeoutMs: 10_000,
111
+ * retry: { attempts: 2 },
76
112
  * });
77
113
  */
78
- export function createHttpClient({ baseUrl, getToken, defaultHeaders = {}, }) {
79
- function buildHeaders(extra = {}) {
114
+ export function createHttpClient({ baseUrl, getToken, defaultHeaders = {}, timeoutMs, retry, responseInterceptors = [], errorInterceptors = [], }) {
115
+ const retryAttempts = retry?.attempts ?? 0;
116
+ const retryBaseDelay = retry?.baseDelayMs ?? 300;
117
+ const retryOn = retry?.retryOn ?? DEFAULT_RETRY_ON;
118
+ function buildHeaders(extra = {}, isFormData = false) {
80
119
  const headers = new Headers({
81
- "Content-Type": "application/json",
120
+ // Omit Content-Type for FormData — the browser sets it with the boundary.
121
+ ...(!isFormData ? { "Content-Type": "application/json" } : {}),
82
122
  Accept: "application/json, text/plain",
83
123
  ...defaultHeaders,
84
124
  ...extra,
@@ -89,25 +129,103 @@ export function createHttpClient({ baseUrl, getToken, defaultHeaders = {}, }) {
89
129
  }
90
130
  return headers;
91
131
  }
92
- async function request(method, path, body, headers) {
93
- const res = await fetch(`${baseUrl}${path}`, {
94
- method,
95
- headers: buildHeaders(headers),
96
- body: body !== undefined ? JSON.stringify(body) : undefined,
97
- });
98
- const parsed = await parseBody(res);
99
- if (!res.ok) {
100
- const message = extractErrorMessage(parsed, `Request failed with status ${res.status}`);
101
- throw new ApiError(res.status, message, parsed);
132
+ function resolveSignal(callerSignal) {
133
+ if (callerSignal)
134
+ return callerSignal;
135
+ if (timeoutMs !== undefined)
136
+ return AbortSignal.timeout(timeoutMs);
137
+ return undefined;
138
+ }
139
+ async function request(method, path, body, options = {}, isFormData = false) {
140
+ const { headers: extraHeaders, params, signal: callerSignal } = options;
141
+ const qs = params ? buildQueryString(params) : "";
142
+ const url = `${baseUrl}${path}${qs}`;
143
+ const signal = resolveSignal(callerSignal);
144
+ let lastError;
145
+ for (let attempt = 0; attempt <= retryAttempts; attempt++) {
146
+ if (attempt > 0) {
147
+ await sleep(retryBaseDelay * 2 ** (attempt - 1));
148
+ }
149
+ let res;
150
+ try {
151
+ res = await fetch(url, {
152
+ method,
153
+ headers: buildHeaders(extraHeaders ?? {}, isFormData),
154
+ body: body instanceof FormData
155
+ ? body
156
+ : body !== undefined
157
+ ? JSON.stringify(body)
158
+ : undefined,
159
+ signal,
160
+ });
161
+ }
162
+ catch (networkError) {
163
+ // Network-level failure (offline, DNS, timeout, abort).
164
+ // Only retry if we haven't exhausted attempts and it's not an abort.
165
+ if (networkError instanceof DOMException &&
166
+ networkError.name === "AbortError") {
167
+ throw networkError;
168
+ }
169
+ lastError = networkError;
170
+ if (attempt < retryAttempts)
171
+ continue;
172
+ throw networkError;
173
+ }
174
+ // Run response interceptors (fire-and-forget; errors are swallowed).
175
+ for (const interceptor of responseInterceptors) {
176
+ try {
177
+ await interceptor(res.clone(), { method, path });
178
+ }
179
+ catch {
180
+ // Interceptors must not break the request pipeline.
181
+ }
182
+ }
183
+ const parsed = await parseBody(res);
184
+ if (!res.ok) {
185
+ const message = extractErrorMessage(parsed, `Request failed with status ${res.status}`);
186
+ const apiError = new ApiError(res.status, message, parsed);
187
+ // Run error interceptors.
188
+ let suppressed = false;
189
+ for (const interceptor of errorInterceptors) {
190
+ try {
191
+ const result = await interceptor(apiError, { method, path });
192
+ if (result === true)
193
+ suppressed = true;
194
+ }
195
+ catch {
196
+ // Interceptors must not break the request pipeline.
197
+ }
198
+ }
199
+ if (suppressed)
200
+ return undefined;
201
+ // Retry on configured status codes.
202
+ if (attempt < retryAttempts && retryOn.includes(res.status)) {
203
+ lastError = apiError;
204
+ continue;
205
+ }
206
+ throw apiError;
207
+ }
208
+ return parsed;
102
209
  }
103
- return parsed;
210
+ throw lastError;
104
211
  }
105
212
  return {
106
- get: (path, headers) => request("GET", path, undefined, headers),
107
- post: (path, body, headers) => request("POST", path, body, headers),
108
- put: (path, body, headers) => request("PUT", path, body, headers),
109
- patch: (path, body, headers) => request("PATCH", path, body, headers),
110
- delete: (path, headers) => request("DELETE", path, undefined, headers),
213
+ get: (path, options) => request("GET", path, undefined, options),
214
+ post: (path, body, options) => request("POST", path, body, options),
215
+ put: (path, body, options) => request("PUT", path, body, options),
216
+ patch: (path, body, options) => request("PATCH", path, body, options),
217
+ delete: (path, options) => request("DELETE", path, undefined, options),
218
+ head: (path, options) => request("HEAD", path, undefined, options),
219
+ /**
220
+ * Upload `FormData` (e.g. files). Skips `Content-Type: application/json`
221
+ * so the browser can set the correct `multipart/form-data; boundary=…` header.
222
+ *
223
+ * @example
224
+ * const form = new FormData();
225
+ * form.append("file", fileInput.files[0]);
226
+ * await httpClient.multipart<UploadResponse>("/uploads", form);
227
+ */
228
+ multipart: (path, form, options) => request("POST", path, form, options, true),
111
229
  };
112
230
  }
113
231
  //# sourceMappingURL=http.js.map
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,10 +6,19 @@
6
6
  *
7
7
  * @example
8
8
  * import { createHttpClient, tokenStore, ApiError } from "@ma-dev/api-client";
9
- * import type { ApiResponse, HttpClient, HttpClientConfig } from "@ma-dev/api-client";
9
+ * import type {
10
+ * ApiResponse,
11
+ * HttpClient,
12
+ * HttpClientConfig,
13
+ * RequestOptions,
14
+ * RetryConfig,
15
+ * QueryParams,
16
+ * ResponseInterceptor,
17
+ * ErrorInterceptor,
18
+ * } from "@ma-dev/api-client";
10
19
  */
11
- export { ApiError, createHttpClient, } from "./http";
12
- export type { HttpClient, HttpClientConfig, } from "./http";
20
+ export { ApiError, createHttpClient } from "./http";
21
+ export type { HttpClient, HttpClientConfig, RequestOptions, RetryConfig, QueryParams, QueryParamValue, ResponseInterceptor, ErrorInterceptor, } from "./http";
13
22
  export { tokenStore } from "./tokenStore";
14
23
  export type { ApiResponse } from "./types";
15
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,QAAQ,EACR,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAChB,YAAY,EACV,UAAU,EACV,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACpD,YAAY,EACV,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -6,10 +6,19 @@
6
6
  *
7
7
  * @example
8
8
  * import { createHttpClient, tokenStore, ApiError } from "@ma-dev/api-client";
9
- * import type { ApiResponse, HttpClient, HttpClientConfig } from "@ma-dev/api-client";
9
+ * import type {
10
+ * ApiResponse,
11
+ * HttpClient,
12
+ * HttpClientConfig,
13
+ * RequestOptions,
14
+ * RetryConfig,
15
+ * QueryParams,
16
+ * ResponseInterceptor,
17
+ * ErrorInterceptor,
18
+ * } from "@ma-dev/api-client";
10
19
  */
11
20
  // HTTP client factory + error class
12
- export { ApiError, createHttpClient, } from "./http";
21
+ export { ApiError, createHttpClient } from "./http";
13
22
  // Token store singleton
14
23
  export { tokenStore } from "./tokenStore";
15
24
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oCAAoC;AACpC,OAAO,EACL,QAAQ,EACR,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAMhB,wBAAwB;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,oCAAoC;AACpC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAYpD,wBAAwB;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ma-dev/api-client",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Shared HTTP client and token store for frontend projects.",
5
5
  "type": "module",
6
6
  "exports": {