@internetarchive/fetch-handler 1.0.1 → 1.1.0-webdev-7731.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.
Files changed (87) hide show
  1. package/.github/workflows/ci.yml +5 -0
  2. package/.github/workflows/gh-pages-main.yml +4 -0
  3. package/.github/workflows/pr-preview.yml +4 -0
  4. package/README.md +5 -5
  5. package/demo/app-root.ts +1 -1
  6. package/dist/demo/app-root.d.ts +1 -1
  7. package/dist/demo/app-root.js +1 -1
  8. package/dist/demo/app-root.js.map +1 -1
  9. package/dist/index.d.ts +6 -1
  10. package/dist/index.js +4 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/src/fetch-handler-interface.d.ts +16 -3
  13. package/dist/src/fetch-handler-interface.js.map +1 -1
  14. package/dist/src/{ia-fetch-handler.d.ts → fetch-handler.d.ts} +10 -2
  15. package/dist/src/{ia-fetch-handler.js → fetch-handler.js} +13 -16
  16. package/dist/src/fetch-handler.js.map +1 -0
  17. package/dist/src/fetch-options.d.ts +5 -0
  18. package/dist/src/fetch-options.js +2 -0
  19. package/dist/src/fetch-options.js.map +1 -0
  20. package/dist/src/fetch-retry/configuration/default-retry-configuration.d.ts +10 -0
  21. package/dist/src/fetch-retry/configuration/default-retry-configuration.js +20 -0
  22. package/dist/src/fetch-retry/configuration/default-retry-configuration.js.map +1 -0
  23. package/dist/src/fetch-retry/configuration/milliseconds.d.ts +1 -0
  24. package/dist/src/fetch-retry/configuration/milliseconds.js +2 -0
  25. package/dist/src/fetch-retry/configuration/milliseconds.js.map +1 -0
  26. package/dist/src/fetch-retry/configuration/no-retry-configuration.d.ts +6 -0
  27. package/dist/src/fetch-retry/configuration/no-retry-configuration.js +9 -0
  28. package/dist/src/fetch-retry/configuration/no-retry-configuration.js.map +1 -0
  29. package/dist/src/fetch-retry/configuration/retry-configuring.d.ts +5 -0
  30. package/dist/src/fetch-retry/configuration/retry-configuring.js +2 -0
  31. package/dist/src/fetch-retry/configuration/retry-configuring.js.map +1 -0
  32. package/dist/src/{utils → fetch-retry}/fetch-retrier.d.ts +11 -13
  33. package/dist/src/fetch-retry/fetch-retrier.js +97 -0
  34. package/dist/src/fetch-retry/fetch-retrier.js.map +1 -0
  35. package/dist/src/fetch-retry/legacy-args.d.ts +2 -0
  36. package/dist/src/fetch-retry/legacy-args.js +11 -0
  37. package/dist/src/fetch-retry/legacy-args.js.map +1 -0
  38. package/dist/test/default-retry-config.test.js +30 -0
  39. package/dist/test/default-retry-config.test.js.map +1 -0
  40. package/dist/test/fetch-handler.test.d.ts +1 -0
  41. package/dist/test/fetch-handler.test.js +87 -0
  42. package/dist/test/fetch-handler.test.js.map +1 -0
  43. package/dist/test/fetch-retrier.test.js +76 -42
  44. package/dist/test/fetch-retrier.test.js.map +1 -1
  45. package/dist/test/legacy-args.test.d.ts +1 -0
  46. package/dist/test/legacy-args.test.js +21 -0
  47. package/dist/test/legacy-args.test.js.map +1 -0
  48. package/dist/test/mocks/mock-fetch-retrier.d.ts +10 -0
  49. package/dist/test/mocks/mock-fetch-retrier.js +11 -0
  50. package/dist/test/mocks/mock-fetch-retrier.js.map +1 -0
  51. package/dist/test/mocks/mock-retry-config.d.ts +7 -0
  52. package/dist/test/mocks/mock-retry-config.js +13 -0
  53. package/dist/test/mocks/mock-retry-config.js.map +1 -0
  54. package/dist/test/no-retry-config.test.d.ts +1 -0
  55. package/dist/test/no-retry-config.test.js +13 -0
  56. package/dist/test/no-retry-config.test.js.map +1 -0
  57. package/dist/test/retrier-legacy-args.test.d.ts +1 -0
  58. package/dist/test/retrier-legacy-args.test.js +27 -0
  59. package/dist/test/retrier-legacy-args.test.js.map +1 -0
  60. package/index.ts +9 -1
  61. package/package.json +5 -5
  62. package/src/fetch-handler-interface.ts +23 -4
  63. package/src/{ia-fetch-handler.ts → fetch-handler.ts} +27 -15
  64. package/src/fetch-options.ts +6 -0
  65. package/src/fetch-retry/configuration/default-retry-configuration.ts +23 -0
  66. package/src/fetch-retry/configuration/milliseconds.ts +1 -0
  67. package/src/fetch-retry/configuration/no-retry-configuration.ts +12 -0
  68. package/src/fetch-retry/configuration/retry-configuring.ts +11 -0
  69. package/src/fetch-retry/fetch-retrier.ts +146 -0
  70. package/src/fetch-retry/legacy-args.ts +13 -0
  71. package/test/default-retry-config.test.ts +34 -0
  72. package/test/fetch-handler.test.ts +99 -0
  73. package/test/fetch-retrier.test.ts +87 -46
  74. package/test/legacy-args.test.ts +24 -0
  75. package/test/mocks/mock-fetch-retrier.ts +22 -0
  76. package/test/mocks/mock-retry-config.ts +19 -0
  77. package/test/no-retry-config.test.ts +14 -0
  78. package/test/retrier-legacy-args.test.ts +28 -0
  79. package/web-test-runner.config.mjs +5 -3
  80. package/dist/src/ia-fetch-handler.js.map +0 -1
  81. package/dist/src/utils/fetch-retrier.js +0 -94
  82. package/dist/src/utils/fetch-retrier.js.map +0 -1
  83. package/dist/test/ia-fetch-handler.test.js +0 -50
  84. package/dist/test/ia-fetch-handler.test.js.map +0 -1
  85. package/src/utils/fetch-retrier.ts +0 -141
  86. package/test/ia-fetch-handler.test.ts +0 -66
  87. /package/dist/test/{ia-fetch-handler.test.d.ts → default-retry-config.test.d.ts} +0 -0
@@ -1,141 +0,0 @@
1
- import type { AnalyticsHandlerInterface } from '@internetarchive/analytics-manager';
2
- import { AnalyticsHandler } from '@internetarchive/analytics-manager';
3
- import { promisedSleep } from './promised-sleep';
4
-
5
- /**
6
- * A class that retries a fetch request.
7
- */
8
- export interface FetchRetrierInterface {
9
- /**
10
- * Execute a fetch with retry.
11
- *
12
- * @param requestInfo RequestInfo
13
- * @param init Optional RequestInit
14
- * @param retries Optional number of retries to attempt
15
- * @returns Promise<Response>
16
- */
17
- fetchRetry(
18
- requestInfo: RequestInfo,
19
- init?: RequestInit,
20
- retries?: number,
21
- ): Promise<Response>;
22
- }
23
-
24
- /** @inheritdoc */
25
- export class FetchRetrier implements FetchRetrierInterface {
26
- private analyticsHandler = new AnalyticsHandler({ enableAnalytics: true });
27
- private sleep = promisedSleep; // default sleep function
28
-
29
- private retryCount = 2;
30
-
31
- private retryDelay = 1000;
32
-
33
- constructor(options?: {
34
- analyticsHandler?: AnalyticsHandlerInterface;
35
- retryCount?: number;
36
- retryDelay?: number;
37
- sleepFn?: (ms: number) => Promise<void>; // <-- add this!
38
- }) {
39
- if (options?.analyticsHandler)
40
- this.analyticsHandler = options.analyticsHandler;
41
- if (options?.retryCount) this.retryCount = options.retryCount;
42
- if (options?.retryDelay) this.retryDelay = options.retryDelay;
43
- if (options?.sleepFn) this.sleep = options.sleepFn; // override if provided
44
- }
45
-
46
- /** @inheritdoc */
47
- public async fetchRetry(
48
- requestInfo: RequestInfo,
49
- init?: RequestInit,
50
- retries: number = this.retryCount,
51
- ): Promise<Response> {
52
- const urlString =
53
- typeof requestInfo === 'string' ? requestInfo : requestInfo.url;
54
- const retryNumber = this.retryCount - retries + 1;
55
-
56
- try {
57
- const response = await fetch(requestInfo, init);
58
- if (response.ok) return response;
59
- // don't retry on a 404 since this will never succeed
60
- if (response.status === 404) {
61
- this.log404Event(urlString);
62
- return response;
63
- }
64
- if (retries > 0) {
65
- await this.sleep(this.retryDelay);
66
- this.logRetryEvent(
67
- urlString,
68
- retryNumber,
69
- response.statusText,
70
- response.status,
71
- );
72
- return this.fetchRetry(requestInfo, init, retries - 1);
73
- }
74
- this.logFailureEvent(urlString, response.status);
75
- return response;
76
- } catch (error) {
77
- // if a content blocker is detected, log it and don't retry
78
- if (this.isContentBlockerError(error)) {
79
- this.logContentBlockingEvent(urlString, error);
80
- throw error;
81
- }
82
-
83
- if (retries > 0) {
84
- await this.sleep(this.retryDelay);
85
- // intentionally duplicating the error message here since we want something
86
- // in the status code even though we won't have an actual code
87
- this.logRetryEvent(urlString, retryNumber, error, error);
88
- return this.fetchRetry(requestInfo, init, retries - 1);
89
- }
90
-
91
- this.logFailureEvent(urlString, error);
92
- throw error;
93
- }
94
- }
95
-
96
- private isContentBlockerError(error: unknown): boolean {
97
- // all of the content blocker errors are `TypeError`
98
- if (!(error instanceof TypeError)) return false;
99
- const message = error.message.toLowerCase();
100
- return message.includes('content blocker');
101
- }
102
-
103
- private readonly eventCategory = 'offshootFetchRetry';
104
-
105
- private logRetryEvent(
106
- urlString: string,
107
- retryNumber: number,
108
- status: unknown,
109
- code: unknown,
110
- ) {
111
- this.analyticsHandler.sendEvent({
112
- category: this.eventCategory,
113
- action: 'retryingFetch',
114
- label: `retryNumber: ${retryNumber} / ${this.retryCount}, code: ${code}, status: ${status}, url: ${urlString}`,
115
- });
116
- }
117
-
118
- private logFailureEvent(urlString: string, error: unknown) {
119
- this.analyticsHandler.sendEvent({
120
- category: this.eventCategory,
121
- action: 'fetchFailed',
122
- label: `error: ${error}, url: ${urlString}`,
123
- });
124
- }
125
-
126
- private log404Event(urlString: string) {
127
- this.analyticsHandler.sendEvent({
128
- category: this.eventCategory,
129
- action: 'status404NotRetrying',
130
- label: `url: ${urlString}`,
131
- });
132
- }
133
-
134
- private logContentBlockingEvent(urlString: string, error: unknown) {
135
- this.analyticsHandler.sendEvent({
136
- category: this.eventCategory,
137
- action: 'contentBlockerDetectedNotRetrying',
138
- label: `error: ${error}, url: ${urlString}`,
139
- });
140
- }
141
- }
@@ -1,66 +0,0 @@
1
- import { expect } from '@open-wc/testing';
2
- import { IaFetchHandler } from '../src/ia-fetch-handler';
3
- import { FetchRetrierInterface } from '../src/utils/fetch-retrier';
4
-
5
- class MockFetchRetrier implements FetchRetrierInterface {
6
- requestInfo?: RequestInfo;
7
- init?: RequestInit;
8
- retries?: number;
9
-
10
- async fetchRetry(
11
- requestInfo: RequestInfo,
12
- init?: RequestInit,
13
- retries?: number,
14
- ): Promise<Response> {
15
- this.init = init;
16
- this.requestInfo = requestInfo;
17
- this.retries = retries;
18
- return new Response(JSON.stringify({ boop: 'snoot' }), { status: 200 });
19
- }
20
- }
21
-
22
- describe('Fetch Handler', () => {
23
- describe('fetch', () => {
24
- it('adds reCache=1 if it is in the current url', async () => {
25
- const fetchRetrier = new MockFetchRetrier();
26
- const fetchHandler = new IaFetchHandler({
27
- fetchRetrier: fetchRetrier,
28
- searchParams: '?reCache=1',
29
- });
30
- await fetchHandler.fetch('https://foo.org/api/v1/snoot');
31
- expect(fetchRetrier.requestInfo).to.equal(
32
- 'https://foo.org/api/v1/snoot?reCache=1',
33
- );
34
- });
35
- });
36
-
37
- describe('fetchIAApiResponse', () => {
38
- it('prepends the IA basehost to the url when making a request', async () => {
39
- const endpoint = '/foo/service/endpoint.php';
40
- const fetchRetrier = new MockFetchRetrier();
41
- const fetchHandler = new IaFetchHandler({
42
- iaApiBaseUrl: 'www.example.com',
43
- fetchRetrier: fetchRetrier,
44
- });
45
- await fetchHandler.fetchIAApiResponse(endpoint);
46
- expect(fetchRetrier.requestInfo).to.equal(
47
- 'www.example.com/foo/service/endpoint.php',
48
- );
49
- });
50
- });
51
-
52
- describe('fetchApiResponse', () => {
53
- it('adds credentials: include if requested', async () => {
54
- const endpoint = '/foo/service/endpoint.php';
55
- const fetchRetrier = new MockFetchRetrier();
56
- const fetchHandler = new IaFetchHandler({
57
- iaApiBaseUrl: 'www.example.com',
58
- fetchRetrier: fetchRetrier,
59
- });
60
- await fetchHandler.fetchApiResponse(endpoint, {
61
- includeCredentials: true,
62
- });
63
- expect(fetchRetrier.init).to.deep.equal({ credentials: 'include' });
64
- });
65
- });
66
- });