@algolia/requester-fetch 5.8.1 → 5.9.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.
@@ -1,8 +1,61 @@
1
- import { Requester } from '@algolia/client-common';
1
+ // src/createFetchRequester.ts
2
+ function isAbortError(error) {
3
+ return error instanceof Error && error.name === "AbortError";
4
+ }
5
+ function getErrorMessage(error, abortContent) {
6
+ if (isAbortError(error)) {
7
+ return abortContent;
8
+ }
9
+ return error instanceof Error ? error.message : "Network request failed";
10
+ }
11
+ function createFetchRequester({ requesterOptions = {} } = {}) {
12
+ async function send(request) {
13
+ const abortController = new AbortController();
14
+ const signal = abortController.signal;
15
+ const createTimeout = (timeout) => {
16
+ return setTimeout(() => {
17
+ abortController.abort();
18
+ }, timeout);
19
+ };
20
+ const connectTimeout = createTimeout(request.connectTimeout);
21
+ let fetchRes;
22
+ try {
23
+ fetchRes = await fetch(request.url, {
24
+ method: request.method,
25
+ body: request.data || null,
26
+ redirect: "manual",
27
+ signal,
28
+ ...requesterOptions,
29
+ headers: {
30
+ ...requesterOptions.headers,
31
+ ...request.headers
32
+ }
33
+ });
34
+ } catch (error) {
35
+ return {
36
+ status: 0,
37
+ content: getErrorMessage(error, "Connection timeout"),
38
+ isTimedOut: isAbortError(error)
39
+ };
40
+ }
41
+ clearTimeout(connectTimeout);
42
+ createTimeout(request.responseTimeout);
43
+ try {
44
+ const content = await fetchRes.text();
45
+ return {
46
+ content,
47
+ isTimedOut: false,
48
+ status: fetchRes.status
49
+ };
50
+ } catch (error) {
51
+ return {
52
+ status: 0,
53
+ content: getErrorMessage(error, "Socket timeout"),
54
+ isTimedOut: isAbortError(error)
55
+ };
56
+ }
57
+ }
58
+ return { send };
59
+ }
2
60
 
3
- type FetchRequesterOptions = {
4
- readonly requesterOptions?: RequestInit;
5
- };
6
- declare function createFetchRequester({ requesterOptions }?: FetchRequesterOptions): Requester;
7
-
8
- export { type FetchRequesterOptions, createFetchRequester };
61
+ export { createFetchRequester };
@@ -1,8 +1,61 @@
1
- import { Requester } from '@algolia/client-common';
1
+ // src/createFetchRequester.ts
2
+ function isAbortError(error) {
3
+ return error instanceof Error && error.name === "AbortError";
4
+ }
5
+ function getErrorMessage(error, abortContent) {
6
+ if (isAbortError(error)) {
7
+ return abortContent;
8
+ }
9
+ return error instanceof Error ? error.message : "Network request failed";
10
+ }
11
+ function createFetchRequester({ requesterOptions = {} } = {}) {
12
+ async function send(request) {
13
+ const abortController = new AbortController();
14
+ const signal = abortController.signal;
15
+ const createTimeout = (timeout) => {
16
+ return setTimeout(() => {
17
+ abortController.abort();
18
+ }, timeout);
19
+ };
20
+ const connectTimeout = createTimeout(request.connectTimeout);
21
+ let fetchRes;
22
+ try {
23
+ fetchRes = await fetch(request.url, {
24
+ method: request.method,
25
+ body: request.data || null,
26
+ redirect: "manual",
27
+ signal,
28
+ ...requesterOptions,
29
+ headers: {
30
+ ...requesterOptions.headers,
31
+ ...request.headers
32
+ }
33
+ });
34
+ } catch (error) {
35
+ return {
36
+ status: 0,
37
+ content: getErrorMessage(error, "Connection timeout"),
38
+ isTimedOut: isAbortError(error)
39
+ };
40
+ }
41
+ clearTimeout(connectTimeout);
42
+ createTimeout(request.responseTimeout);
43
+ try {
44
+ const content = await fetchRes.text();
45
+ return {
46
+ content,
47
+ isTimedOut: false,
48
+ status: fetchRes.status
49
+ };
50
+ } catch (error) {
51
+ return {
52
+ status: 0,
53
+ content: getErrorMessage(error, "Socket timeout"),
54
+ isTimedOut: isAbortError(error)
55
+ };
56
+ }
57
+ }
58
+ return { send };
59
+ }
2
60
 
3
- type FetchRequesterOptions = {
4
- readonly requesterOptions?: RequestInit;
5
- };
6
- declare function createFetchRequester({ requesterOptions }?: FetchRequesterOptions): Requester;
7
-
8
- export { type FetchRequesterOptions, createFetchRequester };
61
+ export { createFetchRequester };
@@ -1,8 +1,61 @@
1
- import { Requester } from '@algolia/client-common';
1
+ // src/createFetchRequester.ts
2
+ function isAbortError(error) {
3
+ return error instanceof Error && error.name === "AbortError";
4
+ }
5
+ function getErrorMessage(error, abortContent) {
6
+ if (isAbortError(error)) {
7
+ return abortContent;
8
+ }
9
+ return error instanceof Error ? error.message : "Network request failed";
10
+ }
11
+ function createFetchRequester({ requesterOptions = {} } = {}) {
12
+ async function send(request) {
13
+ const abortController = new AbortController();
14
+ const signal = abortController.signal;
15
+ const createTimeout = (timeout) => {
16
+ return setTimeout(() => {
17
+ abortController.abort();
18
+ }, timeout);
19
+ };
20
+ const connectTimeout = createTimeout(request.connectTimeout);
21
+ let fetchRes;
22
+ try {
23
+ fetchRes = await fetch(request.url, {
24
+ method: request.method,
25
+ body: request.data || null,
26
+ redirect: "manual",
27
+ signal,
28
+ ...requesterOptions,
29
+ headers: {
30
+ ...requesterOptions.headers,
31
+ ...request.headers
32
+ }
33
+ });
34
+ } catch (error) {
35
+ return {
36
+ status: 0,
37
+ content: getErrorMessage(error, "Connection timeout"),
38
+ isTimedOut: isAbortError(error)
39
+ };
40
+ }
41
+ clearTimeout(connectTimeout);
42
+ createTimeout(request.responseTimeout);
43
+ try {
44
+ const content = await fetchRes.text();
45
+ return {
46
+ content,
47
+ isTimedOut: false,
48
+ status: fetchRes.status
49
+ };
50
+ } catch (error) {
51
+ return {
52
+ status: 0,
53
+ content: getErrorMessage(error, "Socket timeout"),
54
+ isTimedOut: isAbortError(error)
55
+ };
56
+ }
57
+ }
58
+ return { send };
59
+ }
2
60
 
3
- type FetchRequesterOptions = {
4
- readonly requesterOptions?: RequestInit;
5
- };
6
- declare function createFetchRequester({ requesterOptions }?: FetchRequesterOptions): Requester;
7
-
8
- export { type FetchRequesterOptions, createFetchRequester };
61
+ export { createFetchRequester };
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/requester.fetch.node';
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/requester.fetch.node.cjs');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/requester-fetch",
3
- "version": "5.8.1",
3
+ "version": "5.9.0",
4
4
  "description": "Promise-based request library using Fetch.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,8 +37,8 @@
37
37
  "react-native": "./dist/requester.fetch.browser.js",
38
38
  "files": [
39
39
  "dist",
40
- "src",
41
- "index.ts"
40
+ "index.d.ts",
41
+ "index.js"
42
42
  ],
43
43
  "scripts": {
44
44
  "build": "yarn clean && yarn tsup",
@@ -47,16 +47,16 @@
47
47
  "test:bundle": "publint . && attw --pack ."
48
48
  },
49
49
  "dependencies": {
50
- "@algolia/client-common": "5.8.1"
50
+ "@algolia/client-common": "5.9.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@arethetypeswrong/cli": "0.16.4",
54
- "@types/node": "22.7.4",
54
+ "@types/node": "22.7.5",
55
55
  "cross-fetch": "4.0.0",
56
56
  "nock": "13.5.5",
57
57
  "publint": "0.2.11",
58
58
  "tsup": "8.3.0",
59
- "typescript": "5.6.2",
59
+ "typescript": "5.6.3",
60
60
  "vitest": "2.1.2"
61
61
  },
62
62
  "engines": {
package/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './src/createFetchRequester';
@@ -1,278 +0,0 @@
1
- import crossFetch from 'cross-fetch';
2
- import type http from 'http';
3
- import nock from 'nock';
4
- import { Readable } from 'stream';
5
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest';
6
-
7
- import type { EndRequest } from '@algolia/client-common';
8
-
9
- import { createFetchRequester } from '../..';
10
- import {
11
- createTestServer,
12
- getStringifiedBody,
13
- headers,
14
- requestStub,
15
- testQueryBaseUrl,
16
- testQueryHeader,
17
- timeoutRequest,
18
- } from '../../../../tests/utils';
19
-
20
- const originalFetch = window.fetch;
21
-
22
- beforeEach(() => {
23
- window.fetch = crossFetch;
24
- });
25
-
26
- afterEach(() => {
27
- window.fetch = originalFetch;
28
- });
29
-
30
- const requester = createFetchRequester();
31
-
32
- describe('status code handling', () => {
33
- test('sends requests', async () => {
34
- const body = getStringifiedBody();
35
-
36
- nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, body);
37
-
38
- const response = await requester.send(requestStub);
39
-
40
- expect(response.content).toEqual(body);
41
- });
42
-
43
- test('resolves status 200', async () => {
44
- const body = getStringifiedBody();
45
-
46
- nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, body);
47
-
48
- const response = await requester.send(requestStub);
49
-
50
- expect(response.status).toBe(200);
51
- expect(response.content).toBe(body);
52
- expect(response.isTimedOut).toBe(false);
53
- });
54
-
55
- test('resolves status 300', async () => {
56
- const reason = 'Multiple Choices';
57
-
58
- nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(300, reason);
59
-
60
- const response = await requester.send(requestStub);
61
-
62
- expect(response.status).toBe(300);
63
- expect(response.content).toBe(reason);
64
- expect(response.isTimedOut).toBe(false);
65
- });
66
-
67
- test('resolves status 400', async () => {
68
- const body = getStringifiedBody({
69
- message: 'Invalid Application-Id or API-Key',
70
- });
71
-
72
- nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(400, body);
73
-
74
- const response = await requester.send(requestStub);
75
-
76
- expect(response.status).toBe(400);
77
- expect(response.content).toBe(body);
78
- expect(response.isTimedOut).toBe(false);
79
- });
80
-
81
- test('handles chunked responses inside unicode character boundaries', async () => {
82
- const data = Buffer.from('äöü');
83
-
84
- // create a test response stream that is chunked inside a unicode character
85
- function* generate() {
86
- yield data.subarray(0, 3);
87
- yield data.subarray(3);
88
- }
89
-
90
- const testStream = Readable.from(generate());
91
-
92
- nock(testQueryBaseUrl, { reqheaders: headers }).post('/foo').query(testQueryHeader).reply(200, testStream);
93
-
94
- const response = await requester.send(requestStub);
95
-
96
- expect(response.content).toEqual(data.toString());
97
- });
98
- });
99
-
100
- describe('timeout handling', () => {
101
- let server: http.Server;
102
- // setup http server to test timeout
103
- beforeAll(() => {
104
- server = createTestServer();
105
-
106
- server.listen('1113');
107
- });
108
-
109
- afterAll(
110
- () =>
111
- new Promise((done) => {
112
- done();
113
- }),
114
- );
115
-
116
- test('timeouts with the given 1 seconds connection timeout', async () => {
117
- const before = Date.now();
118
- const response = await requester.send({
119
- ...timeoutRequest,
120
- connectTimeout: 1000,
121
- url: 'http://localhost:1113/connection_timeout',
122
- });
123
-
124
- const now = Date.now();
125
-
126
- expect(response.content).toBe('Connection timeout');
127
- expect(now - before).toBeGreaterThanOrEqual(999);
128
- expect(now - before).toBeLessThanOrEqual(1200);
129
- });
130
-
131
- test('connection timeouts with the given 2 seconds connection timeout', async () => {
132
- const before = Date.now();
133
- const response = await requester.send({
134
- ...timeoutRequest,
135
- connectTimeout: 2000,
136
- url: 'http://localhost:1113/connection_timeout',
137
- });
138
-
139
- const now = Date.now();
140
-
141
- expect(response.content).toBe('Connection timeout');
142
- expect(now - before).toBeGreaterThanOrEqual(1999);
143
- expect(now - before).toBeLessThanOrEqual(2200);
144
- });
145
-
146
- test("socket timeouts if response don't appears before the timeout with 2 seconds timeout", async () => {
147
- const before = Date.now();
148
-
149
- const response = await requester.send({
150
- ...timeoutRequest,
151
- responseTimeout: 2000,
152
- url: 'http://localhost:1113',
153
- });
154
-
155
- const now = Date.now();
156
-
157
- expect(response.content).toBe('Socket timeout');
158
- expect(now - before).toBeGreaterThanOrEqual(1999);
159
- expect(now - before).toBeLessThanOrEqual(2200);
160
- });
161
-
162
- test("socket timeouts if response don't appears before the timeout with 3 seconds timeout", async () => {
163
- const before = Date.now();
164
- const response = await requester.send({
165
- ...timeoutRequest,
166
- responseTimeout: 3000,
167
- url: 'http://localhost:1113',
168
- });
169
-
170
- const now = Date.now();
171
-
172
- expect(response.content).toBe('Socket timeout');
173
- expect(now - before).toBeGreaterThanOrEqual(2999);
174
- expect(now - before).toBeLessThanOrEqual(3200);
175
- });
176
-
177
- test('do not timeouts if response appears before the timeout', async () => {
178
- const before = Date.now();
179
- const response = await requester.send({
180
- ...requestStub,
181
- url: 'http://localhost:1113',
182
- responseTimeout: 6000,
183
- });
184
-
185
- const now = Date.now();
186
-
187
- expect(response.isTimedOut).toBe(false);
188
- expect(response.status).toBe(200);
189
- expect(response.content).toBe('{"foo": "bar"}');
190
- expect(now - before).toBeGreaterThanOrEqual(4999);
191
- expect(now - before).toBeLessThanOrEqual(5200);
192
- }, 10000); // This is a long-running test, default server timeout is set to 5000ms
193
- });
194
-
195
- describe('error handling', (): void => {
196
- test('resolves dns not found', async () => {
197
- const request: EndRequest = {
198
- url: 'https://this-dont-exist.algolia.com',
199
- method: 'POST',
200
- headers,
201
- data: getStringifiedBody(),
202
- responseTimeout: 2000,
203
- connectTimeout: 1000,
204
- };
205
-
206
- const response = await requester.send(request);
207
-
208
- expect(response.status).toBe(0);
209
- expect(response.content).toContain('');
210
- expect(response.isTimedOut).toBe(false);
211
- });
212
-
213
- test('resolves general network errors', async () => {
214
- nock(testQueryBaseUrl, { reqheaders: headers })
215
- .post('/foo')
216
- .query(testQueryHeader)
217
- .replyWithError('This is a general error');
218
-
219
- const response = await requester.send(requestStub);
220
-
221
- expect(response.status).toBe(0);
222
- expect(response.content).toBe(
223
- 'request to https://algolia-dns.net/foo?x-algolia-header=bar failed, reason: This is a general error',
224
- );
225
- expect(response.isTimedOut).toBe(false);
226
- });
227
- });
228
-
229
- describe('requesterOptions', () => {
230
- test('allows to pass requesterOptions', async () => {
231
- const body = getStringifiedBody();
232
- const requesterTmp = createFetchRequester({
233
- requesterOptions: {
234
- headers: testQueryHeader,
235
- },
236
- });
237
-
238
- nock(testQueryBaseUrl, {
239
- reqheaders: {
240
- ...headers,
241
- ...testQueryHeader,
242
- },
243
- })
244
- .post('/foo')
245
- .query(testQueryHeader)
246
- .reply(200, body);
247
-
248
- const response = await requesterTmp.send(requestStub);
249
-
250
- expect(response.status).toBe(200);
251
- expect(response.content).toBe(body);
252
- });
253
-
254
- test('allows overriding default requesterOptions', async () => {
255
- const body = getStringifiedBody();
256
- const requesterTmp = createFetchRequester({
257
- requesterOptions: {
258
- headers: testQueryHeader,
259
- mode: 'no-cors',
260
- },
261
- });
262
-
263
- nock(testQueryBaseUrl, {
264
- reqheaders: {
265
- ...headers,
266
- ...testQueryHeader,
267
- },
268
- })
269
- .post('/foo')
270
- .query(testQueryHeader)
271
- .reply(200, body);
272
-
273
- const response = await requesterTmp.send(requestStub);
274
-
275
- expect(response.status).toBe(200);
276
- expect(response.content).toBe(body);
277
- });
278
- });
@@ -1,74 +0,0 @@
1
- import type { Response as AlgoliaResponse, EndRequest, Requester } from '@algolia/client-common';
2
-
3
- function isAbortError(error: unknown): boolean {
4
- return error instanceof Error && error.name === 'AbortError';
5
- }
6
-
7
- function getErrorMessage(error: unknown, abortContent: string): string {
8
- if (isAbortError(error)) {
9
- return abortContent;
10
- }
11
- return error instanceof Error ? error.message : 'Network request failed';
12
- }
13
-
14
- export type FetchRequesterOptions = {
15
- readonly requesterOptions?: RequestInit;
16
- };
17
-
18
- export function createFetchRequester({ requesterOptions = {} }: FetchRequesterOptions = {}): Requester {
19
- async function send(request: EndRequest): Promise<AlgoliaResponse> {
20
- const abortController = new AbortController();
21
- const signal = abortController.signal;
22
-
23
- const createTimeout = (timeout: number): NodeJS.Timeout => {
24
- return setTimeout(() => {
25
- abortController.abort();
26
- }, timeout);
27
- };
28
-
29
- const connectTimeout = createTimeout(request.connectTimeout);
30
-
31
- let fetchRes: Response;
32
- try {
33
- fetchRes = await fetch(request.url, {
34
- method: request.method,
35
- body: request.data || null,
36
- redirect: 'manual',
37
- signal,
38
- ...requesterOptions,
39
- headers: {
40
- ...requesterOptions.headers,
41
- ...request.headers,
42
- },
43
- });
44
- } catch (error) {
45
- return {
46
- status: 0,
47
- content: getErrorMessage(error, 'Connection timeout'),
48
- isTimedOut: isAbortError(error),
49
- };
50
- }
51
-
52
- clearTimeout(connectTimeout);
53
-
54
- createTimeout(request.responseTimeout);
55
-
56
- try {
57
- const content = await fetchRes.text();
58
-
59
- return {
60
- content,
61
- isTimedOut: false,
62
- status: fetchRes.status,
63
- };
64
- } catch (error) {
65
- return {
66
- status: 0,
67
- content: getErrorMessage(error, 'Socket timeout'),
68
- isTimedOut: isAbortError(error),
69
- };
70
- }
71
- }
72
-
73
- return { send };
74
- }