create-request 1.0.0 → 1.2.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.
@@ -82,6 +82,18 @@ exports.ReferrerPolicy = void 0;
82
82
  ReferrerPolicy["NO_REFERRER_WHEN_DOWNGRADE"] = "no-referrer-when-downgrade";
83
83
  ReferrerPolicy["STRICT_ORIGIN_WHEN_CROSS_ORIGIN"] = "strict-origin-when-cross-origin";
84
84
  })(exports.ReferrerPolicy || (exports.ReferrerPolicy = {}));
85
+ /**
86
+ * Cache modes for fetch requests
87
+ */
88
+ var CacheMode;
89
+ (function (CacheMode) {
90
+ CacheMode["RELOAD"] = "reload";
91
+ CacheMode["DEFAULT"] = "default";
92
+ CacheMode["NO_CACHE"] = "no-cache";
93
+ CacheMode["NO_STORE"] = "no-store";
94
+ CacheMode["FORCE_CACHE"] = "force-cache";
95
+ CacheMode["ONLY_IF_CACHED"] = "only-if-cached";
96
+ })(CacheMode || (CacheMode = {}));
85
97
 
86
98
  class RequestError extends Error {
87
99
  constructor(message, url, method, options = {}) {
@@ -178,11 +190,16 @@ class RequestError extends Error {
178
190
  * Provides caching and conversion between different response formats.
179
191
  */
180
192
  class ResponseWrapper {
181
- constructor(response, url, method) {
193
+ constructor(response, url, method, graphQLOptions) {
182
194
  this.bodyUsed = false;
183
195
  this.response = response;
184
196
  this.url = url;
185
197
  this.method = method;
198
+ if (graphQLOptions) {
199
+ this.graphQLOptions = {
200
+ throwOnError: graphQLOptions.throwOnError,
201
+ };
202
+ }
186
203
  }
187
204
  // Wrapper properties
188
205
  get status() {
@@ -200,19 +217,51 @@ class ResponseWrapper {
200
217
  get raw() {
201
218
  return this.response;
202
219
  }
220
+ /**
221
+ * Check for GraphQL errors and throw if throwOnError is enabled
222
+ * @param data - The parsed JSON data
223
+ * @throws RequestError if GraphQL response contains errors and throwOnError is enabled
224
+ */
225
+ checkGraphQLErrors(data) {
226
+ var _a;
227
+ if (!((_a = this.graphQLOptions) === null || _a === void 0 ? void 0 : _a.throwOnError) || typeof data !== "object" || data === null)
228
+ return;
229
+ const responseData = data;
230
+ if (!Array.isArray(responseData.errors) || responseData.errors.length === 0)
231
+ return;
232
+ const errors = responseData.errors;
233
+ const errorMessages = errors.map(x => typeof x === "string" ? x : x && typeof x === "object" && "message" in x ? String(x.message || "Unknown error") : String(x));
234
+ const errorMessage = errorMessages.join(", ");
235
+ throw new RequestError(`GraphQL request failed with errors: ${errorMessage}`, this.url || "", this.method || "", {
236
+ status: this.response.status,
237
+ response: this.response,
238
+ });
239
+ }
203
240
  /**
204
241
  * Parse the response body as JSON
205
242
  * Caches the result for subsequent calls.
243
+ * If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
206
244
  *
207
245
  * @returns The parsed JSON data
208
- * @throws Error if the response has already been consumed in a different format
246
+ * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
209
247
  *
210
248
  * @example
211
249
  * const data = await response.getJson();
212
250
  * console.log(data.items);
251
+ *
252
+ * @example
253
+ * // Error handling - errors are always RequestError
254
+ * try {
255
+ * const data = await response.getJson();
256
+ * } catch (error) {
257
+ * if (error instanceof RequestError) {
258
+ * console.log(error.status, error.url, error.method);
259
+ * }
260
+ * }
213
261
  */
214
262
  async getJson() {
215
263
  if (this.jsonCache !== undefined) {
264
+ this.checkGraphQLErrors(this.jsonCache);
216
265
  return this.jsonCache;
217
266
  }
218
267
  if (this.bodyUsed) {
@@ -221,40 +270,43 @@ class ResponseWrapper {
221
270
  try {
222
271
  const parsed = JSON.parse(this.textCache);
223
272
  this.jsonCache = parsed;
273
+ this.checkGraphQLErrors(parsed);
224
274
  return parsed;
225
275
  }
226
- catch (e) {
227
- const errorMessage = e instanceof Error ? e.message : String(e);
228
- if (this.url && this.method) {
229
- throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method, {
230
- response: this.response,
231
- });
276
+ catch (error) {
277
+ // Re-throw RequestErrors from checkGraphQLErrors without wrapping
278
+ if (error instanceof RequestError) {
279
+ throw error;
232
280
  }
233
- throw new Error(`Invalid JSON: ${errorMessage}`);
281
+ const errorMessage = error instanceof Error ? error.message : String(error);
282
+ throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url || "", this.method || "", {
283
+ status: this.response.status,
284
+ response: this.response,
285
+ });
234
286
  }
235
287
  }
236
- if (this.url && this.method) {
237
- throw new RequestError("Body already consumed", this.url, this.method, {
238
- response: this.response,
239
- });
240
- }
241
- throw new Error("Body already consumed");
288
+ throw new RequestError("Body already consumed", this.url || "", this.method || "", {
289
+ status: this.response.status,
290
+ response: this.response,
291
+ });
242
292
  }
243
293
  this.bodyUsed = true;
244
294
  try {
245
295
  const parsed = await this.response.json();
246
296
  this.jsonCache = parsed;
297
+ this.checkGraphQLErrors(parsed);
247
298
  return parsed;
248
299
  }
249
- catch (e) {
250
- const errorMessage = e instanceof Error ? e.message : String(e);
251
- if (this.url && this.method) {
252
- throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method, {
253
- status: this.response.status,
254
- response: this.response,
255
- });
300
+ catch (error) {
301
+ // Re-throw RequestErrors from checkGraphQLErrors without wrapping
302
+ if (error instanceof RequestError) {
303
+ throw error;
256
304
  }
257
- throw e;
305
+ const errorMessage = error instanceof Error ? error.message : String(error);
306
+ throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url || "", this.method || "", {
307
+ status: this.response.status,
308
+ response: this.response,
309
+ });
258
310
  }
259
311
  }
260
312
  /**
@@ -263,7 +315,7 @@ class ResponseWrapper {
263
315
  * if the body has already been consumed in those formats.
264
316
  *
265
317
  * @returns The response text
266
- * @throws Error if the response has already been consumed in an incompatible format
318
+ * @throws {RequestError} When reading fails or the response has already been consumed in an incompatible format
267
319
  *
268
320
  * @example
269
321
  * const text = await response.getText();
@@ -286,20 +338,16 @@ class ResponseWrapper {
286
338
  }
287
339
  catch (e) {
288
340
  const errorMessage = e instanceof Error ? e.message : String(e);
289
- if (this.url && this.method) {
290
- throw new RequestError(`Cannot convert to text: ${errorMessage}`, this.url, this.method, {
291
- response: this.response,
292
- });
293
- }
294
- throw e;
341
+ throw new RequestError(`Cannot convert to text: ${errorMessage}`, this.url || "", this.method || "", {
342
+ status: this.response.status,
343
+ response: this.response,
344
+ });
295
345
  }
296
346
  }
297
- if (this.url && this.method) {
298
- throw new RequestError("Cannot convert to text", this.url, this.method, {
299
- response: this.response,
300
- });
301
- }
302
- throw new Error("Cannot convert to text");
347
+ throw new RequestError("Cannot convert to text", this.url || "", this.method || "", {
348
+ status: this.response.status,
349
+ response: this.response,
350
+ });
303
351
  }
304
352
  this.bodyUsed = true;
305
353
  try {
@@ -308,13 +356,10 @@ class ResponseWrapper {
308
356
  }
309
357
  catch (e) {
310
358
  const errorMessage = e instanceof Error ? e.message : String(e);
311
- if (this.url && this.method) {
312
- throw new RequestError(`Read failed: ${errorMessage}`, this.url, this.method, {
313
- status: this.response.status,
314
- response: this.response,
315
- });
316
- }
317
- throw e;
359
+ throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
360
+ status: this.response.status,
361
+ response: this.response,
362
+ });
318
363
  }
319
364
  }
320
365
  /**
@@ -323,7 +368,7 @@ class ResponseWrapper {
323
368
  * if the body has already been consumed as text.
324
369
  *
325
370
  * @returns The response as a Blob
326
- * @throws Error if the response has already been consumed in an incompatible format
371
+ * @throws {RequestError} When reading fails or the response has already been consumed in an incompatible format
327
372
  *
328
373
  * @example
329
374
  * const blob = await response.getBlob();
@@ -339,12 +384,10 @@ class ResponseWrapper {
339
384
  this.blobCache = new Blob([this.textCache], { type: "text/plain" });
340
385
  return this.blobCache;
341
386
  }
342
- if (this.url && this.method) {
343
- throw new RequestError("Cannot convert to Blob", this.url, this.method, {
344
- response: this.response,
345
- });
346
- }
347
- throw new Error("Cannot convert to Blob");
387
+ throw new RequestError("Cannot convert to Blob", this.url || "", this.method || "", {
388
+ status: this.response.status,
389
+ response: this.response,
390
+ });
348
391
  }
349
392
  this.bodyUsed = true;
350
393
  try {
@@ -353,13 +396,10 @@ class ResponseWrapper {
353
396
  }
354
397
  catch (e) {
355
398
  const errorMessage = e instanceof Error ? e.message : String(e);
356
- if (this.url && this.method) {
357
- throw new RequestError(`Read failed: ${errorMessage}`, this.url, this.method, {
358
- status: this.response.status,
359
- response: this.response,
360
- });
361
- }
362
- throw e;
399
+ throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
400
+ status: this.response.status,
401
+ response: this.response,
402
+ });
363
403
  }
364
404
  }
365
405
  /**
@@ -367,7 +407,7 @@ class ResponseWrapper {
367
407
  * This method consumes the body and should only be called once.
368
408
  *
369
409
  * @returns The response body as a ReadableStream or null
370
- * @throws Error if the response body has already been consumed
410
+ * @throws {RequestError} When the response body has already been consumed
371
411
  *
372
412
  * @example
373
413
  * const stream = response.getBody();
@@ -378,12 +418,10 @@ class ResponseWrapper {
378
418
  */
379
419
  getBody() {
380
420
  if (this.bodyUsed) {
381
- if (this.url && this.method) {
382
- throw new RequestError("Body already consumed", this.url, this.method, {
383
- response: this.response,
384
- });
385
- }
386
- throw new Error("Body already consumed");
421
+ throw new RequestError("Body already consumed", this.url || "", this.method || "", {
422
+ status: this.response.status,
423
+ response: this.response,
424
+ });
387
425
  }
388
426
  this.bodyUsed = true;
389
427
  return this.response.body;
@@ -394,6 +432,7 @@ class ResponseWrapper {
394
432
  *
395
433
  * @param selector - Optional function to extract and transform data
396
434
  * @returns A promise that resolves to the selected data
435
+ * @throws {RequestError} When the request fails, JSON parsing fails, or the selector throws an error
397
436
  *
398
437
  * @example
399
438
  * // Get full response
@@ -401,6 +440,16 @@ class ResponseWrapper {
401
440
  *
402
441
  * // Extract specific data
403
442
  * const users = await response.getData(data => data.results.users);
443
+ *
444
+ * @example
445
+ * // Error handling - errors are always RequestError
446
+ * try {
447
+ * const data = await response.getData();
448
+ * } catch (error) {
449
+ * if (error instanceof RequestError) {
450
+ * console.log(error.status, error.url, error.method);
451
+ * }
452
+ * }
404
453
  */
405
454
  async getData(selector) {
406
455
  try {
@@ -419,20 +468,15 @@ class ResponseWrapper {
419
468
  // Enhance selector errors with context
420
469
  if (selector) {
421
470
  const errorMessage = error instanceof Error ? error.message : String(error);
422
- if (this.url && this.method) {
423
- throw new RequestError(`Data selector failed: ${errorMessage}`, this.url, this.method, {
424
- response: this.response,
425
- });
426
- }
427
- throw new Error(`Data selector failed: ${errorMessage}`);
471
+ throw new RequestError(`Data selector failed: ${errorMessage}`, this.url || "", this.method || "", {
472
+ status: this.response.status,
473
+ response: this.response,
474
+ });
428
475
  }
429
476
  // If we get here and it's not a RequestError, wrap it
430
477
  // This should rarely happen as getJson() should throw RequestError
431
478
  const errorObj = error instanceof Error ? error : new Error(String(error));
432
- if (this.url && this.method) {
433
- throw RequestError.networkError(this.url, this.method, errorObj);
434
- }
435
- throw errorObj;
479
+ throw RequestError.networkError(this.url || "", this.method || "", errorObj);
436
480
  }
437
481
  }
438
482
  }
@@ -840,6 +884,13 @@ class BaseRequest {
840
884
  this.errorInterceptors = [];
841
885
  this.url = url;
842
886
  }
887
+ /**
888
+ * Get GraphQL options if set (only for BodyRequest subclasses)
889
+ * @returns GraphQL options or undefined
890
+ */
891
+ getGraphQLOptions() {
892
+ return undefined;
893
+ }
843
894
  /**
844
895
  * Creates a fluent API for setting enum-based options
845
896
  * Combines direct setter with convenience methods
@@ -925,14 +976,14 @@ class BaseRequest {
925
976
  *
926
977
  * @param timeout - The timeout in milliseconds
927
978
  * @returns The request instance for chaining
928
- * @throws Error if timeout is not a positive number
979
+ * @throws RequestError if timeout is not a positive number
929
980
  *
930
981
  * @example
931
982
  * request.withTimeout(5000); // 5 seconds timeout
932
983
  */
933
984
  withTimeout(timeout) {
934
985
  if (!Number.isFinite(timeout) || timeout <= 0)
935
- throw new Error("Timeout must be a positive number");
986
+ throw new RequestError("Timeout must be a positive number", this.url, this.method);
936
987
  this.requestOptions.timeout = timeout;
937
988
  return this;
938
989
  }
@@ -941,14 +992,14 @@ class BaseRequest {
941
992
  *
942
993
  * @param retries - Number of retry attempts before failing
943
994
  * @returns The request instance for chaining
944
- * @throws Error if retries is not a non-negative integer
995
+ * @throws RequestError if retries is not a non-negative integer
945
996
  *
946
997
  * @example
947
998
  * request.withRetries(3); // Retry up to 3 times
948
999
  */
949
1000
  withRetries(retries) {
950
1001
  if (!Number.isInteger(retries) || retries < 0)
951
- throw new Error("Retry count must be a non-negative integer");
1002
+ throw new RequestError("Retry count must be a non-negative integer", this.url, this.method);
952
1003
  this.requestOptions.retries = retries;
953
1004
  return this;
954
1005
  }
@@ -1057,6 +1108,35 @@ class BaseRequest {
1057
1108
  AUTO: exports.RequestPriority.AUTO,
1058
1109
  });
1059
1110
  }
1111
+ /**
1112
+ * Sets the integrity hash for subresource integrity verification
1113
+ * @param integrity - The integrity hash string (e.g., "sha256-...")
1114
+ * @returns The instance for chaining
1115
+ * @example
1116
+ * request.withIntegrity("sha256-abcdef1234567890...")
1117
+ */
1118
+ withIntegrity(integrity) {
1119
+ this.requestOptions.integrity = integrity;
1120
+ return this;
1121
+ }
1122
+ /**
1123
+ * Sets cache mode - supports both direct call and fluent API
1124
+ * @example
1125
+ * request.withCache("no-cache")
1126
+ * request.withCache(CacheMode.NO_CACHE)
1127
+ * request.withCache.NO_CACHE()
1128
+ */
1129
+ get withCache() {
1130
+ const cacheOptions = {
1131
+ DEFAULT: "default",
1132
+ NO_STORE: "no-store",
1133
+ RELOAD: "reload",
1134
+ NO_CACHE: "no-cache",
1135
+ FORCE_CACHE: "force-cache",
1136
+ ONLY_IF_CACHED: "only-if-cached",
1137
+ };
1138
+ return this.createFluentSetter("cache", cacheOptions);
1139
+ }
1060
1140
  /**
1061
1141
  * Adds query parameters to the request URL
1062
1142
  * @param params - An object containing the query parameters
@@ -1155,7 +1235,7 @@ class BaseRequest {
1155
1235
  if (typeof Buffer !== "undefined")
1156
1236
  return Buffer.from(str).toString("base64");
1157
1237
  // Fallback (should never happen in modern environments)
1158
- throw new Error("Base64 encoding is not supported");
1238
+ throw new RequestError("Base64 encoding is not supported", this.url, this.method);
1159
1239
  }
1160
1240
  /**
1161
1241
  * Sets the bearer token for authentication
@@ -1336,9 +1416,20 @@ class BaseRequest {
1336
1416
  * Execute the request and parse the response as JSON
1337
1417
  *
1338
1418
  * @returns A promise that resolves to the parsed JSON data
1419
+ * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
1339
1420
  *
1340
1421
  * @example
1341
1422
  * const users = await request.getJson<User[]>();
1423
+ *
1424
+ * @example
1425
+ * // Error handling - errors are always RequestError
1426
+ * try {
1427
+ * const data = await request.getJson();
1428
+ * } catch (error) {
1429
+ * if (error instanceof RequestError) {
1430
+ * console.log(error.status, error.url, error.method);
1431
+ * }
1432
+ * }
1342
1433
  */
1343
1434
  async getJson() {
1344
1435
  const response = await this.getResponse();
@@ -1386,6 +1477,7 @@ class BaseRequest {
1386
1477
  *
1387
1478
  * @param selector - Optional function to extract and transform data
1388
1479
  * @returns A promise that resolves to the selected data
1480
+ * @throws {RequestError} When the request fails, JSON parsing fails, or the selector throws an error
1389
1481
  *
1390
1482
  * @example
1391
1483
  * // Get full response
@@ -1393,6 +1485,16 @@ class BaseRequest {
1393
1485
  *
1394
1486
  * // Extract specific data
1395
1487
  * const users = await request.getData(data => data.results.users);
1488
+ *
1489
+ * @example
1490
+ * // Error handling - errors are always RequestError
1491
+ * try {
1492
+ * const data = await request.getData();
1493
+ * } catch (error) {
1494
+ * if (error instanceof RequestError) {
1495
+ * console.log(error.status, error.url, error.method);
1496
+ * }
1497
+ * }
1396
1498
  */
1397
1499
  async getData(selector) {
1398
1500
  const response = await this.getResponse();
@@ -1442,7 +1544,8 @@ class BaseRequest {
1442
1544
  * @returns The URL with query parameters appended
1443
1545
  */
1444
1546
  formatUrlWithQueryParams(url) {
1445
- if (!this.queryParams.toString()) {
1547
+ const queryString = this.queryParams.toString();
1548
+ if (!queryString) {
1446
1549
  return url;
1447
1550
  }
1448
1551
  try {
@@ -1458,7 +1561,7 @@ class BaseRequest {
1458
1561
  // Handle relative URLs
1459
1562
  const hasExistingParams = url.includes("?");
1460
1563
  const separator = hasExistingParams ? "&" : "?";
1461
- return `${url}${separator}${this.queryParams.toString()}`;
1564
+ return `${url}${separator}${queryString}`;
1462
1565
  }
1463
1566
  }
1464
1567
  /**
@@ -1507,8 +1610,7 @@ class BaseRequest {
1507
1610
  }
1508
1611
  catch (error) {
1509
1612
  const errorMessage = error instanceof Error ? error.message : String(error);
1510
- // Use RequestError when we have request context
1511
- throw new RequestError(`Interceptor failed: ${errorMessage}`, currentConfig.url, currentConfig.method);
1613
+ throw new RequestError(`Request interceptor failed: ${errorMessage}`, currentConfig.url, currentConfig.method);
1512
1614
  }
1513
1615
  }
1514
1616
  return currentConfig;
@@ -1530,12 +1632,9 @@ class BaseRequest {
1530
1632
  }
1531
1633
  catch (error) {
1532
1634
  const errorMessage = error instanceof Error ? error.message : String(error);
1533
- // Use RequestError when we have response context (url/method from ResponseWrapper)
1534
- if (currentResponse.url && currentResponse.method) {
1535
- throw new RequestError(`Interceptor failed: ${errorMessage}`, currentResponse.url, currentResponse.method);
1536
- }
1537
- // Fallback to generic Error if no context available (shouldn't happen in practice)
1538
- throw new Error(`Interceptor failed: ${errorMessage}`);
1635
+ const url = currentResponse.url || "";
1636
+ const method = currentResponse.method || "";
1637
+ throw new RequestError(`Response interceptor failed: ${errorMessage}`, url, method);
1539
1638
  }
1540
1639
  }
1541
1640
  return currentResponse;
@@ -1569,11 +1668,16 @@ class BaseRequest {
1569
1668
  const errorMessage = interceptorError instanceof Error ? interceptorError.message : String(interceptorError);
1570
1669
  // Always wrap in RequestError when we have context
1571
1670
  if (currentError instanceof RequestError) {
1572
- currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method);
1671
+ currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method, {
1672
+ status: currentError.status,
1673
+ response: currentError.response,
1674
+ });
1573
1675
  }
1574
1676
  else if (currentError instanceof ResponseWrapper && currentError.url && currentError.method) {
1575
1677
  // If it's a ResponseWrapper, we can still create a proper RequestError from its context
1576
- currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method);
1678
+ currentError = new RequestError(`Error interceptor ${i + 1} failed: ${errorMessage}`, currentError.url, currentError.method, {
1679
+ status: currentError.status,
1680
+ });
1577
1681
  }
1578
1682
  else {
1579
1683
  // Last resort: if we have no context at all, use the original error's context
@@ -1674,6 +1778,8 @@ class BaseRequest {
1674
1778
  referrerPolicy: fetchOptions.referrerPolicy,
1675
1779
  keepalive: fetchOptions.keepalive,
1676
1780
  priority: extendedOptions.priority,
1781
+ integrity: fetchOptions.integrity,
1782
+ cache: fetchOptions.cache,
1677
1783
  };
1678
1784
  }
1679
1785
  /**
@@ -1684,6 +1790,12 @@ class BaseRequest {
1684
1790
  if (config.body !== undefined) {
1685
1791
  fetchOptions.body = config.body;
1686
1792
  }
1793
+ if (config.integrity !== undefined) {
1794
+ fetchOptions.integrity = config.integrity;
1795
+ }
1796
+ if (config.cache !== undefined) {
1797
+ fetchOptions.cache = config.cache;
1798
+ }
1687
1799
  }
1688
1800
  async executeRequest(url, fetchOptions) {
1689
1801
  const method = typeof fetchOptions.method === "string" ? fetchOptions.method : "GET";
@@ -1695,7 +1807,8 @@ class BaseRequest {
1695
1807
  const interceptorResult = await this.runRequestInterceptors(requestConfig);
1696
1808
  // If interceptor returned a Response, short-circuit and wrap it
1697
1809
  if (interceptorResult instanceof Response) {
1698
- const wrappedResponse = new ResponseWrapper(interceptorResult, this.url, this.method);
1810
+ const graphQLOptions = this.getGraphQLOptions();
1811
+ const wrappedResponse = new ResponseWrapper(interceptorResult, this.url, this.method, graphQLOptions);
1699
1812
  return await this.runResponseInterceptors(wrappedResponse);
1700
1813
  }
1701
1814
  // Update fetchOptions with interceptor modifications
@@ -1731,7 +1844,8 @@ class BaseRequest {
1731
1844
  if (!response.ok) {
1732
1845
  throw RequestError.fromResponse(response, url, method);
1733
1846
  }
1734
- const wrappedResponse = new ResponseWrapper(response, url, method);
1847
+ const graphQLOptions = this.getGraphQLOptions();
1848
+ const wrappedResponse = new ResponseWrapper(response, url, method, graphQLOptions);
1735
1849
  return await this.runResponseInterceptors(wrappedResponse);
1736
1850
  }
1737
1851
  catch (error) {
@@ -1764,6 +1878,7 @@ class BaseRequest {
1764
1878
  class BodyRequest extends BaseRequest {
1765
1879
  constructor(url) {
1766
1880
  super(url);
1881
+ this.graphQLOptions = undefined;
1767
1882
  }
1768
1883
  /**
1769
1884
  * Sets the body of the request
@@ -1786,7 +1901,8 @@ class BodyRequest extends BaseRequest {
1786
1901
  JSON.stringify(body);
1787
1902
  }
1788
1903
  catch (error) {
1789
- throw new RequestError(`Stringify failed: ${String(error)}`, this.url, this.method);
1904
+ const errorMessage = error instanceof Error ? error.message : String(error);
1905
+ throw new RequestError(`JSON stringify failed: ${errorMessage}`, this.url, this.method);
1790
1906
  }
1791
1907
  }
1792
1908
  else {
@@ -1800,6 +1916,7 @@ class BodyRequest extends BaseRequest {
1800
1916
  *
1801
1917
  * @param query - The GraphQL query or mutation string
1802
1918
  * @param variables - Optional variables object to pass with the query
1919
+ * @param options - Optional GraphQL-specific options
1803
1920
  * @returns The request instance for chaining
1804
1921
  *
1805
1922
  * @example
@@ -1809,13 +1926,18 @@ class BodyRequest extends BaseRequest {
1809
1926
  * @example
1810
1927
  * const request = new PostRequest('/graphql')
1811
1928
  * .withGraphQL('mutation { createUser(name: $name) { id } }', { name: 'John' });
1929
+ *
1930
+ * @example
1931
+ * // Throw an error if the GraphQL response contains errors
1932
+ * const request = new PostRequest('/graphql')
1933
+ * .withGraphQL('query { user { id } }', undefined, { throwOnError: true });
1812
1934
  */
1813
- withGraphQL(query, variables) {
1814
- if (typeof query !== "string" || query.trim().length === 0) {
1935
+ withGraphQL(query, variables, options) {
1936
+ if (typeof query !== "string" || query.length === 0) {
1815
1937
  throw new RequestError("Invalid GraphQL query", this.url, this.method);
1816
1938
  }
1817
1939
  const graphQLBody = {
1818
- query: query.trim(),
1940
+ query: query,
1819
1941
  };
1820
1942
  if (variables !== undefined) {
1821
1943
  if (typeof variables !== "object" || variables === null || Array.isArray(variables)) {
@@ -1823,12 +1945,24 @@ class BodyRequest extends BaseRequest {
1823
1945
  }
1824
1946
  graphQLBody.variables = variables;
1825
1947
  }
1948
+ // Store GraphQL options if provided
1949
+ if (options !== undefined) {
1950
+ if (typeof options !== "object" || options === null || Array.isArray(options)) {
1951
+ throw new RequestError("Invalid GraphQL options", this.url, this.method);
1952
+ }
1953
+ // Store only the known GraphQL options properties
1954
+ const opts = options;
1955
+ this.graphQLOptions = {
1956
+ throwOnError: typeof opts.throwOnError === "boolean" ? opts.throwOnError : undefined,
1957
+ };
1958
+ }
1826
1959
  // Validate JSON is stringifiable early
1827
1960
  try {
1828
1961
  JSON.stringify(graphQLBody);
1829
1962
  }
1830
1963
  catch (error) {
1831
- throw new RequestError(`Stringify failed: ${String(error)}`, this.url, this.method);
1964
+ const errorMessage = error instanceof Error ? error.message : String(error);
1965
+ throw new RequestError(`JSON stringify failed: ${errorMessage}`, this.url, this.method);
1832
1966
  }
1833
1967
  this.body = graphQLBody;
1834
1968
  this.bodyType = BodyType.JSON;
@@ -1851,6 +1985,13 @@ class BodyRequest extends BaseRequest {
1851
1985
  this.withContentType(contentType);
1852
1986
  }
1853
1987
  }
1988
+ /**
1989
+ * Get the GraphQL options if set
1990
+ * @returns The GraphQL options or undefined
1991
+ */
1992
+ getGraphQLOptions() {
1993
+ return this.graphQLOptions;
1994
+ }
1854
1995
  /**
1855
1996
  * Execute the request and return the ResponseWrapper
1856
1997
  * Overrides the base implementation to add body handling