create-request 1.4.3-rc.0 → 1.4.3-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -85,7 +85,7 @@ exports.ReferrerPolicy = void 0;
85
85
  /**
86
86
  * Cache modes for fetch requests
87
87
  */
88
- var CacheMode;
88
+ exports.CacheMode = void 0;
89
89
  (function (CacheMode) {
90
90
  CacheMode["RELOAD"] = "reload";
91
91
  CacheMode["DEFAULT"] = "default";
@@ -93,7 +93,7 @@ var CacheMode;
93
93
  CacheMode["NO_STORE"] = "no-store";
94
94
  CacheMode["FORCE_CACHE"] = "force-cache";
95
95
  CacheMode["ONLY_IF_CACHED"] = "only-if-cached";
96
- })(CacheMode || (CacheMode = {}));
96
+ })(exports.CacheMode || (exports.CacheMode = {}));
97
97
 
98
98
  class RequestError extends Error {
99
99
  status;
@@ -103,7 +103,7 @@ class RequestError extends Error {
103
103
  isTimeout;
104
104
  isAborted;
105
105
  constructor(message, url, method, options = {}) {
106
- super(message);
106
+ super(message, { cause: options.cause });
107
107
  this.name = "RequestError";
108
108
  this.url = url;
109
109
  this.method = method;
@@ -122,7 +122,7 @@ class RequestError extends Error {
122
122
  * Static methods for creating specific types of RequestError
123
123
  */
124
124
  static timeout(url, method, timeoutMs) {
125
- return new RequestError(`Timeout: ${timeoutMs}ms`, url, method, {
125
+ return new RequestError(`Timeout ${timeoutMs}ms`, url, method, {
126
126
  isTimeout: true,
127
127
  });
128
128
  }
@@ -164,16 +164,16 @@ class RequestError extends Error {
164
164
  // Check for connection errors (but not timeout errors)
165
165
  const isConnectionError = !isTimeoutError && (errorCode === "ECONNREFUSED" || errorCode === "ECONNRESET" || stack.includes("ECONNREFUSED") || stack.includes("connect"));
166
166
  if (isTimeoutError) {
167
- message = `Network error: Request timeout for ${url}`;
167
+ message = `Timeout ${url}`;
168
168
  }
169
169
  else if (isDnsError) {
170
- message = `Network error: Unable to resolve hostname for ${url}`;
170
+ message = `DNS error ${url}`;
171
171
  }
172
172
  else if (isConnectionError) {
173
- message = `Network error: Connection refused for ${url}`;
173
+ message = `Connection refused ${url}`;
174
174
  }
175
175
  else {
176
- message = `Network error: Failed to fetch ${url}`;
176
+ message = `Network error ${url}`;
177
177
  }
178
178
  }
179
179
  const error = new RequestError(message, url, method, {
@@ -200,11 +200,15 @@ class RequestError extends Error {
200
200
  * Wrapper for HTTP responses with methods to transform the response data
201
201
  */
202
202
  class ResponseWrapper {
203
- response;
204
203
  url;
205
204
  method;
206
- // GraphQL-specific options
205
+ response;
207
206
  graphQLOptions;
207
+ // Cache the body as the last used method
208
+ cachedBlob;
209
+ cachedText;
210
+ cachedJson;
211
+ cachedArrayBuffer;
208
212
  constructor(response, url, method, graphQLOptions) {
209
213
  this.response = response;
210
214
  this.url = url;
@@ -231,6 +235,18 @@ class ResponseWrapper {
231
235
  get raw() {
232
236
  return this.response;
233
237
  }
238
+ /**
239
+ * Check if the response body has already been consumed and throw an error if so
240
+ * @throws RequestError if the body has already been consumed
241
+ */
242
+ checkBodyNotConsumed() {
243
+ if (this.response.bodyUsed) {
244
+ throw new RequestError("Body already consumed", this.url || "", this.method || "", {
245
+ status: this.response.status,
246
+ response: this.response,
247
+ });
248
+ }
249
+ }
234
250
  /**
235
251
  * Check for GraphQL errors and throw if throwOnError is enabled
236
252
  * @param data - The parsed JSON data
@@ -245,18 +261,17 @@ class ResponseWrapper {
245
261
  const errors = responseData.errors;
246
262
  const errorMessages = errors.map(x => typeof x === "string" ? x : x && typeof x === "object" && "message" in x ? String(x.message || "Unknown error") : String(x));
247
263
  const errorMessage = errorMessages.join(", ");
248
- throw new RequestError(`GraphQL request failed with errors: ${errorMessage}`, this.url || "", this.method || "", {
264
+ throw new RequestError(`GraphQL errors: ${errorMessage}`, this.url || "", this.method || "", {
249
265
  status: this.response.status,
250
266
  response: this.response,
251
267
  });
252
268
  }
253
269
  /**
254
270
  * Parse the response body as JSON
255
- * Note: This consumes the response body and can only be called once.
256
271
  * If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
257
272
  *
258
273
  * @returns The parsed JSON data
259
- * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
274
+ * @throws {RequestError} When the request fails, JSON parsing fails, or GraphQL errors occur (if throwOnError enabled).
260
275
  *
261
276
  * @example
262
277
  * const data = await response.getJson();
@@ -273,19 +288,16 @@ class ResponseWrapper {
273
288
  * }
274
289
  */
275
290
  async getJson() {
276
- if (this.response.bodyUsed) {
277
- throw new RequestError("Body already consumed", this.url || "", this.method || "", {
278
- status: this.response.status,
279
- response: this.response,
280
- });
281
- }
291
+ if (this.cachedJson !== undefined)
292
+ return this.cachedJson;
293
+ this.checkBodyNotConsumed();
282
294
  try {
283
295
  const parsed = await this.response.json();
296
+ this.cachedJson = parsed;
284
297
  this.checkGraphQLErrors(parsed);
285
298
  return parsed;
286
299
  }
287
300
  catch (error) {
288
- // Re-throw RequestErrors from checkGraphQLErrors without wrapping
289
301
  if (error instanceof RequestError) {
290
302
  throw error;
291
303
  }
@@ -298,23 +310,21 @@ class ResponseWrapper {
298
310
  }
299
311
  /**
300
312
  * Get the response body as text
301
- * Note: This consumes the response body and can only be called once.
302
313
  *
303
314
  * @returns The response text
304
- * @throws {RequestError} When reading fails or the response has already been consumed
315
+ * @throws {RequestError} When reading fails
305
316
  *
306
317
  * @example
307
318
  * const text = await response.getText();
308
319
  */
309
320
  async getText() {
310
- if (this.response.bodyUsed) {
311
- throw new RequestError("Body already consumed", this.url || "", this.method || "", {
312
- status: this.response.status,
313
- response: this.response,
314
- });
315
- }
321
+ if (this.cachedText !== undefined)
322
+ return this.cachedText;
323
+ this.checkBodyNotConsumed();
316
324
  try {
317
- return await this.response.text();
325
+ const text = await this.response.text();
326
+ this.cachedText = text;
327
+ return text;
318
328
  }
319
329
  catch (e) {
320
330
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -326,24 +336,22 @@ class ResponseWrapper {
326
336
  }
327
337
  /**
328
338
  * Get the response body as a Blob
329
- * Note: This consumes the response body and can only be called once.
330
339
  *
331
340
  * @returns The response as a Blob
332
- * @throws {RequestError} When reading fails or the response has already been consumed
341
+ * @throws {RequestError} When reading fails
333
342
  *
334
343
  * @example
335
344
  * const blob = await response.getBlob();
336
345
  * const url = URL.createObjectURL(blob);
337
346
  */
338
347
  async getBlob() {
339
- if (this.response.bodyUsed) {
340
- throw new RequestError("Body already consumed", this.url || "", this.method || "", {
341
- status: this.response.status,
342
- response: this.response,
343
- });
344
- }
348
+ if (this.cachedBlob !== undefined)
349
+ return this.cachedBlob;
350
+ this.checkBodyNotConsumed();
345
351
  try {
346
- return await this.response.blob();
352
+ const blob = await this.response.blob();
353
+ this.cachedBlob = blob;
354
+ return blob;
347
355
  }
348
356
  catch (e) {
349
357
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -355,24 +363,23 @@ class ResponseWrapper {
355
363
  }
356
364
  /**
357
365
  * Get the response body as an ArrayBuffer
358
- * Note: This consumes the response body and can only be called once.
359
366
  *
360
367
  * @returns The response as an ArrayBuffer
361
- * @throws {RequestError} When reading fails or the response has already been consumed
368
+ * @throws {RequestError} When reading fails
362
369
  *
363
370
  * @example
364
371
  * const buffer = await response.getArrayBuffer();
365
372
  * const uint8Array = new Uint8Array(buffer);
366
373
  */
367
374
  async getArrayBuffer() {
368
- if (this.response.bodyUsed) {
369
- throw new RequestError("Body already consumed", this.url || "", this.method || "", {
370
- status: this.response.status,
371
- response: this.response,
372
- });
375
+ if (this.cachedArrayBuffer !== undefined) {
376
+ return this.cachedArrayBuffer;
373
377
  }
378
+ this.checkBodyNotConsumed();
374
379
  try {
375
- return await this.response.arrayBuffer();
380
+ const arrayBuffer = await this.response.arrayBuffer();
381
+ this.cachedArrayBuffer = arrayBuffer;
382
+ return arrayBuffer;
376
383
  }
377
384
  catch (e) {
378
385
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -385,6 +392,7 @@ class ResponseWrapper {
385
392
  /**
386
393
  * Get the raw response body as a ReadableStream
387
394
  * Note: This consumes the response body and should only be called once.
395
+ * Unlike other methods, streams cannot be cached, so this will throw if the body is already consumed.
388
396
  *
389
397
  * @returns The response body as a ReadableStream or null
390
398
  * @throws {RequestError} When the response body has already been consumed
@@ -397,12 +405,7 @@ class ResponseWrapper {
397
405
  * }
398
406
  */
399
407
  getBody() {
400
- if (this.response.bodyUsed) {
401
- throw new RequestError("Body already consumed", this.url || "", this.method || "", {
402
- status: this.response.status,
403
- response: this.response,
404
- });
405
- }
408
+ this.checkBodyNotConsumed();
406
409
  return this.response.body;
407
410
  }
408
411
  /**
@@ -894,9 +897,9 @@ class BaseRequest {
894
897
  }
895
898
  validateUrl(url) {
896
899
  if (!url?.trim())
897
- throw new RequestError("URL cannot be empty", url, this.method);
900
+ throw new RequestError("Invalid URL", url, this.method);
898
901
  if (url.includes("\0") || url.includes("\r") || url.includes("\n")) {
899
- throw new RequestError("Invalid URL (control chars)", url, this.method);
902
+ throw new RequestError("Invalid URL", url, this.method);
900
903
  }
901
904
  const trimmed = url.trim();
902
905
  if (/^https?:\/\//.test(trimmed)) {
@@ -904,7 +907,7 @@ class BaseRequest {
904
907
  new URL(trimmed);
905
908
  }
906
909
  catch {
907
- throw new RequestError(`Invalid URL: ${trimmed}`, trimmed, this.method);
910
+ throw new RequestError("Invalid URL", trimmed, this.method);
908
911
  }
909
912
  }
910
913
  }
@@ -945,11 +948,7 @@ class BaseRequest {
945
948
  * request.withHeader('Accept', 'application/json');
946
949
  */
947
950
  withHeader(key, value) {
948
- // Ignore null and undefined values
949
- if (value !== null && value !== undefined) {
950
- return this.withHeaders({ [key]: value });
951
- }
952
- return this;
951
+ return this.withHeaders({ [key]: value });
953
952
  }
954
953
  /**
955
954
  * Set a timeout for the request
@@ -964,7 +963,7 @@ class BaseRequest {
964
963
  */
965
964
  withTimeout(timeout) {
966
965
  if (!Number.isFinite(timeout) || timeout <= 0)
967
- throw new RequestError("Timeout must be a positive number", this.url, this.method);
966
+ throw new RequestError("Invalid timeout", this.url, this.method);
968
967
  this.requestOptions.timeout = timeout;
969
968
  return this;
970
969
  }
@@ -1348,13 +1347,7 @@ class BaseRequest {
1348
1347
  * @returns The instance for chaining
1349
1348
  */
1350
1349
  withQueryParam(key, value) {
1351
- if (value === null || value === undefined)
1352
- return this;
1353
- if (Array.isArray(value))
1354
- value.forEach(v => this.queryParams.append(key, String(v)));
1355
- else
1356
- this.queryParams.append(key, String(value));
1357
- return this;
1350
+ return this.withQueryParams({ [key]: value });
1358
1351
  }
1359
1352
  /**
1360
1353
  * Sets the request mode, which determines the CORS (Cross-Origin Resource Sharing) behavior
@@ -1398,9 +1391,7 @@ class BaseRequest {
1398
1391
  * @returns The instance for chaining
1399
1392
  */
1400
1393
  withContentType(contentType) {
1401
- return this.withHeaders({
1402
- "Content-Type": contentType,
1403
- });
1394
+ return this.withHeader("Content-Type", contentType);
1404
1395
  }
1405
1396
  /**
1406
1397
  * Shorthand for setting the Authorization header
@@ -1408,9 +1399,7 @@ class BaseRequest {
1408
1399
  * @returns The instance for chaining
1409
1400
  */
1410
1401
  withAuthorization(authValue) {
1411
- return this.withHeaders({
1412
- Authorization: authValue,
1413
- });
1402
+ return this.withHeader("Authorization", authValue);
1414
1403
  }
1415
1404
  /**
1416
1405
  * Shorthand for setting up Basic authentication
@@ -1440,7 +1429,7 @@ class BaseRequest {
1440
1429
  if (typeof Buffer !== "undefined")
1441
1430
  return Buffer.from(str).toString("base64");
1442
1431
  // Fallback (should never happen in modern environments)
1443
- throw new RequestError("Base64 encoding is not supported", this.url, this.method);
1432
+ throw new RequestError("Encoding not supported", this.url, this.method);
1444
1433
  }
1445
1434
  /**
1446
1435
  * Sets the bearer token for authentication
@@ -1525,9 +1514,7 @@ class BaseRequest {
1525
1514
  * @returns The instance for chaining
1526
1515
  */
1527
1516
  withCsrfToken(token, headerName = "X-CSRF-Token") {
1528
- return this.withHeaders({
1529
- [headerName]: token,
1530
- });
1517
+ return this.withHeader(headerName, token);
1531
1518
  }
1532
1519
  /**
1533
1520
  * Disables automatic anti-CSRF protection.
@@ -1543,9 +1530,7 @@ class BaseRequest {
1543
1530
  * @returns The instance for chaining
1544
1531
  */
1545
1532
  withAntiCsrfHeaders() {
1546
- return this.withHeaders({
1547
- "X-Requested-With": "XMLHttpRequest",
1548
- });
1533
+ return this.withHeader("X-Requested-With", "XMLHttpRequest");
1549
1534
  }
1550
1535
  /**
1551
1536
  * Add a request interceptor for this specific request
@@ -1714,9 +1699,7 @@ class BaseRequest {
1714
1699
  const config = Config.getInstance();
1715
1700
  // Apply anti-CSRF headers if enabled
1716
1701
  if (config.isAntiCsrfEnabled()) {
1717
- this.withHeaders({
1718
- "X-Requested-With": "XMLHttpRequest",
1719
- });
1702
+ this.withAntiCsrfHeaders();
1720
1703
  }
1721
1704
  // Apply global CSRF token if set
1722
1705
  const globalToken = config.getCsrfToken();
@@ -1724,9 +1707,7 @@ class BaseRequest {
1724
1707
  const csrfHeaderName = config.getCsrfHeaderName();
1725
1708
  const hasLocalToken = this.hasHeader("X-CSRF-Token") || this.hasHeader(csrfHeaderName);
1726
1709
  if (!hasLocalToken) {
1727
- this.withHeaders({
1728
- [csrfHeaderName]: globalToken,
1729
- });
1710
+ this.withHeader(csrfHeaderName, globalToken);
1730
1711
  }
1731
1712
  }
1732
1713
  // Check for XSRF token in cookies and send it as a header
@@ -1736,9 +1717,7 @@ class BaseRequest {
1736
1717
  const xsrfHeaderName = config.getXsrfHeaderName();
1737
1718
  const hasLocalToken = this.hasHeader("X-XSRF-TOKEN") || this.hasHeader(xsrfHeaderName);
1738
1719
  if (!hasLocalToken) {
1739
- this.withHeaders({
1740
- [xsrfHeaderName]: xsrfToken,
1741
- });
1720
+ this.withHeader(xsrfHeaderName, xsrfToken);
1742
1721
  }
1743
1722
  }
1744
1723
  }
@@ -1797,7 +1776,7 @@ class BaseRequest {
1797
1776
  const delay = typeof retriesConfig.delay === "function" ? retriesConfig.delay({ attempt: attempt + 1, error: requestError }) : retriesConfig.delay;
1798
1777
  // Validate delay result
1799
1778
  if (typeof delay !== "number" || !Number.isFinite(delay) || delay < 0) {
1800
- throw new RequestError(`Invalid retry delay: ${delay}`, url, method);
1779
+ throw new RequestError(`Invalid delay: ${delay}`, url, method);
1801
1780
  }
1802
1781
  // Wait for the delay
1803
1782
  if (delay > 0) {
@@ -1807,7 +1786,7 @@ class BaseRequest {
1807
1786
  }
1808
1787
  }
1809
1788
  // This should never happen but is needed for type safety
1810
- throw new RequestError(`Max retries reached`, url, method);
1789
+ throw new RequestError(`EO`, url, method);
1811
1790
  }
1812
1791
  /**
1813
1792
  * Run request interceptors in order: global interceptors first, then per-request
@@ -1829,7 +1808,7 @@ class BaseRequest {
1829
1808
  }
1830
1809
  catch (error) {
1831
1810
  const errorMessage = error instanceof Error ? error.message : String(error);
1832
- throw new RequestError(`Request interceptor failed: ${errorMessage}`, currentConfig.url, currentConfig.method);
1811
+ throw new RequestError(`Req Interceptor failed: ${errorMessage}`, currentConfig.url, currentConfig.method);
1833
1812
  }
1834
1813
  }
1835
1814
  return currentConfig;
@@ -1853,7 +1832,7 @@ class BaseRequest {
1853
1832
  const errorMessage = error instanceof Error ? error.message : String(error);
1854
1833
  const url = currentResponse.url || "";
1855
1834
  const method = currentResponse.method || "";
1856
- throw new RequestError(`Response interceptor failed: ${errorMessage}`, url, method);
1835
+ throw new RequestError(`Res Interceptor failed: ${errorMessage}`, url, method);
1857
1836
  }
1858
1837
  }
1859
1838
  return currentResponse;
@@ -1887,22 +1866,18 @@ class BaseRequest {
1887
1866
  const errorMessage = interceptorError instanceof Error ? interceptorError.message : String(interceptorError);
1888
1867
  // Always wrap in RequestError when we have context
1889
1868
  if (currentError instanceof RequestError) {
1890
- currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method, {
1869
+ currentError = new RequestError(`Err Interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method, {
1891
1870
  status: currentError.status,
1892
1871
  response: currentError.response,
1893
1872
  });
1894
1873
  }
1895
- else if (currentError instanceof ResponseWrapper && currentError.url && currentError.method) {
1896
- // If it's a ResponseWrapper, we can still create a proper RequestError from its context
1897
- currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method, {
1898
- status: currentError.status,
1899
- });
1900
- }
1901
1874
  else {
1875
+ /* c8 ignore start */
1902
1876
  // Last resort: if we have no context at all, use the original error's context
1903
1877
  // This shouldn't happen in practice, but handle it gracefully
1904
1878
  const errorObj = interceptorError instanceof Error ? interceptorError : new Error(String(interceptorError));
1905
1879
  currentError = RequestError.networkError(error.url, error.method, errorObj);
1880
+ /* c8 ignore end */
1906
1881
  }
1907
1882
  }
1908
1883
  }
@@ -2137,7 +2112,7 @@ class BodyRequest extends BaseRequest {
2137
2112
  }
2138
2113
  catch (error) {
2139
2114
  const errorMessage = error instanceof Error ? error.message : String(error);
2140
- throw new RequestError(`JSON stringify failed: ${errorMessage}`, this.url, this.method);
2115
+ throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method);
2141
2116
  }
2142
2117
  }
2143
2118
  else {
@@ -2169,21 +2144,21 @@ class BodyRequest extends BaseRequest {
2169
2144
  */
2170
2145
  withGraphQL(query, variables, options) {
2171
2146
  if (typeof query !== "string" || query.length === 0) {
2172
- throw new RequestError("Invalid GraphQL query", this.url, this.method);
2147
+ throw new RequestError("Invalid query", this.url, this.method);
2173
2148
  }
2174
2149
  const graphQLBody = {
2175
2150
  query: query,
2176
2151
  };
2177
2152
  if (variables !== undefined) {
2178
2153
  if (typeof variables !== "object" || variables === null || Array.isArray(variables)) {
2179
- throw new RequestError("Invalid GraphQL variables", this.url, this.method);
2154
+ throw new RequestError("Invalid vars", this.url, this.method);
2180
2155
  }
2181
2156
  graphQLBody.variables = variables;
2182
2157
  }
2183
2158
  // Store GraphQL options if provided
2184
2159
  if (options !== undefined) {
2185
2160
  if (typeof options !== "object" || options === null || Array.isArray(options)) {
2186
- throw new RequestError("Invalid GraphQL options", this.url, this.method);
2161
+ throw new RequestError("Invalid options", this.url, this.method);
2187
2162
  }
2188
2163
  // Store only the known GraphQL options properties
2189
2164
  const opts = options;
@@ -2197,7 +2172,7 @@ class BodyRequest extends BaseRequest {
2197
2172
  }
2198
2173
  catch (error) {
2199
2174
  const errorMessage = error instanceof Error ? error.message : String(error);
2200
- throw new RequestError(`JSON stringify failed: ${errorMessage}`, this.url, this.method);
2175
+ throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method);
2201
2176
  }
2202
2177
  this.body = graphQLBody;
2203
2178
  this.bodyType = BodyType.JSON;
@@ -2680,7 +2655,7 @@ class ApiBuilder {
2680
2655
  return propertyValue;
2681
2656
  }
2682
2657
  if (typeof prop === "string" && prop.startsWith("with") && prop !== "withBaseURL") {
2683
- if (prop === "withAbortController" || prop === "withBody" || prop === "withGraphQL")
2658
+ if (prop === "withAbortController" || prop === "withBody" || prop === "withGraphQL" || prop === "withQueryParam" || prop === "withQueryParams")
2684
2659
  return undefined;
2685
2660
  const prototypeMethod = BaseRequest.prototype[prop];
2686
2661
  if (typeof prototypeMethod === "function") {
@@ -2746,5 +2721,13 @@ exports.PostRequest = PostRequest;
2746
2721
  exports.PutRequest = PutRequest;
2747
2722
  exports.RequestError = RequestError;
2748
2723
  exports.ResponseWrapper = ResponseWrapper;
2724
+ exports.api = api;
2749
2725
  exports["default"] = create;
2726
+ exports.del = del;
2727
+ exports.get = get;
2728
+ exports.head = head;
2729
+ exports.options = options;
2730
+ exports.patch = patch;
2731
+ exports.post = post;
2732
+ exports.put = put;
2750
2733
  //# sourceMappingURL=index.cjs.map