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