@graphql-tools/url-loader 7.7.1 → 7.8.0-alpha-8b59ce96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare type SyncImportFn = (moduleName: string) => any;
14
14
  declare type HeadersConfig = Record<string, string>;
15
15
  interface ExecutionExtensions {
16
16
  headers?: HeadersConfig;
17
+ endpoint?: string;
17
18
  }
18
19
  export declare enum SubscriptionProtocol {
19
20
  WS = "WS",
@@ -80,6 +81,18 @@ export interface LoadFromUrlOptions extends BaseLoaderOptions, Partial<Introspec
80
81
  * Additional options to pass to the graphql-sse client.
81
82
  */
82
83
  graphqlSseOptions?: Omit<GraphQLSSEClientOptions, 'url' | 'headers' | 'fetchFn' | 'abortControllerImpl'>;
84
+ /**
85
+ * Retry attempts
86
+ */
87
+ retry?: number;
88
+ /**
89
+ * Timeout in milliseconds
90
+ */
91
+ timeout?: number;
92
+ /**
93
+ * Request Credentials
94
+ */
95
+ credentials?: RequestCredentials;
83
96
  }
84
97
  /**
85
98
  * This loader loads a schema from a URL. The loaded schema is a fully-executable,
package/index.js CHANGED
@@ -406,14 +406,14 @@ class UrlLoader {
406
406
  const finalUrl = urlObj.toString().replace(dummyHostname, '');
407
407
  return finalUrl;
408
408
  }
409
- buildHTTPExecutor(endpoint, fetch, options) {
409
+ buildHTTPExecutor(initialEndpoint, fetch, options) {
410
410
  const defaultMethod = this.getDefaultMethodFromOptions(options === null || options === void 0 ? void 0 : options.method, 'POST');
411
- const HTTP_URL = switchProtocols(endpoint, {
411
+ const HTTP_URL = switchProtocols(initialEndpoint, {
412
412
  wss: 'https',
413
413
  ws: 'http',
414
414
  });
415
415
  const executor = (request) => {
416
- var _a;
416
+ var _a, _b;
417
417
  const controller = new crossUndiciFetch.AbortController();
418
418
  let method = defaultMethod;
419
419
  if (options === null || options === void 0 ? void 0 : options.useGETForQueries) {
@@ -423,7 +423,8 @@ class UrlLoader {
423
423
  method = 'GET';
424
424
  }
425
425
  }
426
- const headers = Object.assign({}, options === null || options === void 0 ? void 0 : options.headers, ((_a = request.extensions) === null || _a === void 0 ? void 0 : _a.headers) || {});
426
+ const endpoint = ((_a = request.extensions) === null || _a === void 0 ? void 0 : _a.endpoint) || initialEndpoint;
427
+ const headers = Object.assign({}, options === null || options === void 0 ? void 0 : options.headers, ((_b = request.extensions) === null || _b === void 0 ? void 0 : _b.headers) || {});
427
428
  const acceptedProtocols = [`application/json`];
428
429
  if (method === 'GET' && (options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === exports.SubscriptionProtocol.SSE) {
429
430
  acceptedProtocols.push('text/event-stream');
@@ -439,6 +440,15 @@ class UrlLoader {
439
440
  operationName: request.operationName,
440
441
  extensions: request.extensions,
441
442
  };
443
+ let timeoutId;
444
+ if (options === null || options === void 0 ? void 0 : options.timeout) {
445
+ timeoutId = setTimeout(() => {
446
+ if (!controller.signal.aborted) {
447
+ controller.abort();
448
+ }
449
+ }, options.timeout);
450
+ }
451
+ const credentials = (options === null || options === void 0 ? void 0 : options.credentials) || 'same-origin';
442
452
  return new valueOrPromise.ValueOrPromise(() => {
443
453
  switch (method) {
444
454
  case 'GET':
@@ -448,7 +458,7 @@ class UrlLoader {
448
458
  });
449
459
  return fetch(finalUrl, {
450
460
  method: 'GET',
451
- credentials: 'include',
461
+ credentials,
452
462
  headers: {
453
463
  accept,
454
464
  ...headers,
@@ -460,7 +470,7 @@ class UrlLoader {
460
470
  return new valueOrPromise.ValueOrPromise(() => this.createFormDataFromVariables(requestBody))
461
471
  .then(form => fetch(HTTP_URL, {
462
472
  method: 'POST',
463
- credentials: 'include',
473
+ credentials,
464
474
  body: form,
465
475
  headers: {
466
476
  accept,
@@ -473,7 +483,7 @@ class UrlLoader {
473
483
  else {
474
484
  return fetch(HTTP_URL, {
475
485
  method: 'POST',
476
- credentials: 'include',
486
+ credentials,
477
487
  body: JSON.stringify(requestBody),
478
488
  headers: {
479
489
  accept,
@@ -486,6 +496,13 @@ class UrlLoader {
486
496
  }
487
497
  })
488
498
  .then((fetchResult) => {
499
+ if (timeoutId != null) {
500
+ clearTimeout(timeoutId);
501
+ }
502
+ // Retry should respect HTTP Errors
503
+ if ((options === null || options === void 0 ? void 0 : options.retry) != null && !fetchResult.status.toString().startsWith('2')) {
504
+ throw new Error(fetchResult.statusText || `HTTP Error: ${fetchResult.status}`);
505
+ }
489
506
  const contentType = fetchResult.headers.get('content-type');
490
507
  if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/event-stream')) {
491
508
  return handleEventStreamResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
@@ -497,6 +514,40 @@ class UrlLoader {
497
514
  })
498
515
  .resolve();
499
516
  };
517
+ if ((options === null || options === void 0 ? void 0 : options.retry) != null) {
518
+ return function retryExecutor(request) {
519
+ let result;
520
+ let error;
521
+ let attempt = 0;
522
+ function retryAttempt() {
523
+ attempt++;
524
+ if (attempt > options.retry) {
525
+ if (result != null) {
526
+ return result;
527
+ }
528
+ if (error != null) {
529
+ throw error;
530
+ }
531
+ throw new Error('No result');
532
+ }
533
+ return new valueOrPromise.ValueOrPromise(() => executor(request))
534
+ .then(res => {
535
+ var _a;
536
+ result = res;
537
+ if ((_a = result === null || result === void 0 ? void 0 : result.errors) === null || _a === void 0 ? void 0 : _a.length) {
538
+ return retryAttempt();
539
+ }
540
+ return result;
541
+ })
542
+ .catch((e) => {
543
+ error = e;
544
+ return retryAttempt();
545
+ })
546
+ .resolve();
547
+ }
548
+ return retryAttempt();
549
+ };
550
+ }
500
551
  return executor;
501
552
  }
502
553
  buildWSExecutor(subscriptionsEndpoint, webSocketImpl, connectionParams) {
package/index.mjs CHANGED
@@ -382,14 +382,14 @@ class UrlLoader {
382
382
  const finalUrl = urlObj.toString().replace(dummyHostname, '');
383
383
  return finalUrl;
384
384
  }
385
- buildHTTPExecutor(endpoint, fetch, options) {
385
+ buildHTTPExecutor(initialEndpoint, fetch, options) {
386
386
  const defaultMethod = this.getDefaultMethodFromOptions(options === null || options === void 0 ? void 0 : options.method, 'POST');
387
- const HTTP_URL = switchProtocols(endpoint, {
387
+ const HTTP_URL = switchProtocols(initialEndpoint, {
388
388
  wss: 'https',
389
389
  ws: 'http',
390
390
  });
391
391
  const executor = (request) => {
392
- var _a;
392
+ var _a, _b;
393
393
  const controller = new AbortController();
394
394
  let method = defaultMethod;
395
395
  if (options === null || options === void 0 ? void 0 : options.useGETForQueries) {
@@ -399,7 +399,8 @@ class UrlLoader {
399
399
  method = 'GET';
400
400
  }
401
401
  }
402
- const headers = Object.assign({}, options === null || options === void 0 ? void 0 : options.headers, ((_a = request.extensions) === null || _a === void 0 ? void 0 : _a.headers) || {});
402
+ const endpoint = ((_a = request.extensions) === null || _a === void 0 ? void 0 : _a.endpoint) || initialEndpoint;
403
+ const headers = Object.assign({}, options === null || options === void 0 ? void 0 : options.headers, ((_b = request.extensions) === null || _b === void 0 ? void 0 : _b.headers) || {});
403
404
  const acceptedProtocols = [`application/json`];
404
405
  if (method === 'GET' && (options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.SSE) {
405
406
  acceptedProtocols.push('text/event-stream');
@@ -415,6 +416,15 @@ class UrlLoader {
415
416
  operationName: request.operationName,
416
417
  extensions: request.extensions,
417
418
  };
419
+ let timeoutId;
420
+ if (options === null || options === void 0 ? void 0 : options.timeout) {
421
+ timeoutId = setTimeout(() => {
422
+ if (!controller.signal.aborted) {
423
+ controller.abort();
424
+ }
425
+ }, options.timeout);
426
+ }
427
+ const credentials = (options === null || options === void 0 ? void 0 : options.credentials) || 'same-origin';
418
428
  return new ValueOrPromise(() => {
419
429
  switch (method) {
420
430
  case 'GET':
@@ -424,7 +434,7 @@ class UrlLoader {
424
434
  });
425
435
  return fetch(finalUrl, {
426
436
  method: 'GET',
427
- credentials: 'include',
437
+ credentials,
428
438
  headers: {
429
439
  accept,
430
440
  ...headers,
@@ -436,7 +446,7 @@ class UrlLoader {
436
446
  return new ValueOrPromise(() => this.createFormDataFromVariables(requestBody))
437
447
  .then(form => fetch(HTTP_URL, {
438
448
  method: 'POST',
439
- credentials: 'include',
449
+ credentials,
440
450
  body: form,
441
451
  headers: {
442
452
  accept,
@@ -449,7 +459,7 @@ class UrlLoader {
449
459
  else {
450
460
  return fetch(HTTP_URL, {
451
461
  method: 'POST',
452
- credentials: 'include',
462
+ credentials,
453
463
  body: JSON.stringify(requestBody),
454
464
  headers: {
455
465
  accept,
@@ -462,6 +472,13 @@ class UrlLoader {
462
472
  }
463
473
  })
464
474
  .then((fetchResult) => {
475
+ if (timeoutId != null) {
476
+ clearTimeout(timeoutId);
477
+ }
478
+ // Retry should respect HTTP Errors
479
+ if ((options === null || options === void 0 ? void 0 : options.retry) != null && !fetchResult.status.toString().startsWith('2')) {
480
+ throw new Error(fetchResult.statusText || `HTTP Error: ${fetchResult.status}`);
481
+ }
465
482
  const contentType = fetchResult.headers.get('content-type');
466
483
  if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/event-stream')) {
467
484
  return handleEventStreamResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
@@ -473,6 +490,40 @@ class UrlLoader {
473
490
  })
474
491
  .resolve();
475
492
  };
493
+ if ((options === null || options === void 0 ? void 0 : options.retry) != null) {
494
+ return function retryExecutor(request) {
495
+ let result;
496
+ let error;
497
+ let attempt = 0;
498
+ function retryAttempt() {
499
+ attempt++;
500
+ if (attempt > options.retry) {
501
+ if (result != null) {
502
+ return result;
503
+ }
504
+ if (error != null) {
505
+ throw error;
506
+ }
507
+ throw new Error('No result');
508
+ }
509
+ return new ValueOrPromise(() => executor(request))
510
+ .then(res => {
511
+ var _a;
512
+ result = res;
513
+ if ((_a = result === null || result === void 0 ? void 0 : result.errors) === null || _a === void 0 ? void 0 : _a.length) {
514
+ return retryAttempt();
515
+ }
516
+ return result;
517
+ })
518
+ .catch((e) => {
519
+ error = e;
520
+ return retryAttempt();
521
+ })
522
+ .resolve();
523
+ }
524
+ return retryAttempt();
525
+ };
526
+ }
476
527
  return executor;
477
528
  }
478
529
  buildWSExecutor(subscriptionsEndpoint, webSocketImpl, connectionParams) {
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@graphql-tools/url-loader",
3
- "version": "7.7.1",
3
+ "version": "7.8.0-alpha-8b59ce96.0",
4
4
  "description": "A set of utils for faster development of GraphQL tools",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
7
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
8
8
  },
9
9
  "dependencies": {
10
- "@graphql-tools/delegate": "^8.4.1",
11
- "@graphql-tools/utils": "^8.5.1",
12
- "@graphql-tools/wrap": "^8.3.1",
10
+ "@graphql-tools/delegate": "^8.5.1",
11
+ "@graphql-tools/utils": "^8.6.2",
12
+ "@graphql-tools/wrap": "^8.4.2",
13
13
  "@n1ru4l/graphql-live-query": "^0.9.0",
14
14
  "@types/websocket": "^1.0.4",
15
15
  "@types/ws": "^8.0.0",