balena-request 13.3.3 → 13.3.4

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,3 +1,15 @@
1
+ - commits:
2
+ - subject: "Tests: Convert to typescript"
3
+ hash: 2697961c510380f62735cf0a3e9a2c2224e70545
4
+ body: ""
5
+ footer:
6
+ Change-type: patch
7
+ change-type: patch
8
+ author: Pagan Gazzard
9
+ nested: []
10
+ version: 13.3.4
11
+ title: ""
12
+ date: 2024-12-04T16:02:06.212Z
1
13
  - commits:
2
14
  - subject: "Tests: add missing @types/temp dependency"
3
15
  hash: 2698e0be2a764268831bf2388c82fabeeac542a4
@@ -33,7 +45,7 @@
33
45
  nested: []
34
46
  version: 13.3.3
35
47
  title: ""
36
- date: 2024-12-03T15:16:48.769Z
48
+ date: 2024-12-03T15:23:02.553Z
37
49
  - commits:
38
50
  - subject: Fix always following redirects when followRedirect = false
39
51
  hash: 34db7e138eabfcfcfd822133b391c738d01de323
package/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file
4
4
  automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
5
5
  This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ ## 13.3.4 - 2024-12-04
8
+
9
+ * Tests: Convert to typescript [Pagan Gazzard]
10
+
7
11
  ## 13.3.3 - 2024-12-03
8
12
 
9
13
  * Tests: add missing @types/temp dependency [Pagan Gazzard]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "balena-request",
3
- "version": "13.3.3",
3
+ "version": "13.3.4",
4
4
  "description": "Balena HTTP client",
5
5
  "main": "build/request.js",
6
6
  "types": "build/request.d.ts",
@@ -21,12 +21,12 @@
21
21
  "node": ">=18.0.0"
22
22
  },
23
23
  "scripts": {
24
- "lint": "balena-lint -e js -e ts lib tests",
24
+ "lint": "balena-lint -e js -e ts lib tests && tsc --noEmit --project tsconfig.dev.json",
25
25
  "lint-fix": "balena-lint -e js -e ts --fix lib tests",
26
26
  "pretest": "npm run build",
27
27
  "test": "npm run test-node && npm run test-browser",
28
28
  "posttest": "npm run lint",
29
- "test-node": "mocha -r ts-node/register/transpile-only --reporter spec tests/**/*.spec.js",
29
+ "test-node": "mocha -r ts-node/register/transpile-only --reporter spec tests/**/*.spec.ts",
30
30
  "test-browser": "mockttp -c karma start",
31
31
  "build": "npx tsc",
32
32
  "prepare": "npm run build",
@@ -82,6 +82,6 @@
82
82
  "balena-auth": "^6.0.1"
83
83
  },
84
84
  "versionist": {
85
- "publishedAt": "2024-12-03T15:16:48.906Z"
85
+ "publishedAt": "2024-12-04T16:02:06.338Z"
86
86
  }
87
87
  }
@@ -4,6 +4,7 @@ import * as sinon from 'sinon';
4
4
  import * as mockhttp from 'mockttp';
5
5
  import * as tokens from './tokens.json';
6
6
  import * as utils from '../build/utils';
7
+ import type { BalenaRequestPassThroughStream } from '../build/request';
7
8
 
8
9
  const johnDoeFixture = tokens.johndoe;
9
10
  const mockServer = mockhttp.getLocal();
@@ -89,9 +90,10 @@ describe('Request (api key):', function () {
89
90
  apiKey: '123456789',
90
91
  })
91
92
  .then(function (stream) {
92
- expect(stream.response.request.uri.query).to.equal(
93
- 'apikey=123456789',
94
- );
93
+ expect(
94
+ (stream as BalenaRequestPassThroughStream).response.request
95
+ .uri.query,
96
+ ).to.equal('apikey=123456789');
95
97
  return utils.getStreamContents(stream);
96
98
  })));
97
99
  });
@@ -120,7 +122,7 @@ describe('Request (api key):', function () {
120
122
  url: '/foo',
121
123
  apiKey: '123456789',
122
124
  })
123
- .then((v) => v.request.headers.Authorization);
125
+ .then((v) => v.request.headers?.Authorization);
124
126
  return expect(promise).to.eventually.equal(
125
127
  `Bearer ${johnDoeFixture.token}`,
126
128
  );
@@ -137,9 +139,10 @@ describe('Request (api key):', function () {
137
139
  apiKey: '123456789',
138
140
  })
139
141
  .then(function (stream) {
140
- expect(stream.response.request.uri.query).to.equal(
141
- 'apikey=123456789',
142
- );
142
+ expect(
143
+ (stream as BalenaRequestPassThroughStream).response.request
144
+ .uri.query,
145
+ ).to.equal('apikey=123456789');
143
146
  return utils.getStreamContents(stream);
144
147
  }));
145
148
 
@@ -152,8 +155,9 @@ describe('Request (api key):', function () {
152
155
  apiKey: '123456789',
153
156
  })
154
157
  .then(function (stream) {
155
- const { headers } = stream.response.request;
156
- expect(headers.Authorization).to.equal(
158
+ const { headers } = (stream as BalenaRequestPassThroughStream)
159
+ .response.request;
160
+ expect(headers?.Authorization).to.equal(
157
161
  `Bearer ${johnDoeFixture.token}`,
158
162
  );
159
163
  return utils.getStreamContents(stream);
@@ -189,7 +193,10 @@ describe('Request (api key):', function () {
189
193
  apiKey: '',
190
194
  })
191
195
  .then(function (stream) {
192
- expect(stream.response.request.uri.query).to.not.exist;
196
+ expect(
197
+ (stream as BalenaRequestPassThroughStream).response.request
198
+ .uri.query,
199
+ ).to.not.exist;
193
200
  return utils.getStreamContents(stream);
194
201
  })));
195
202
  }));
@@ -0,0 +1,3 @@
1
+ declare module 'browserify-zlib' {
2
+ export * from 'zlib';
3
+ }
@@ -111,15 +111,16 @@ describe('An interceptor', function () {
111
111
 
112
112
  describe('with a requestError hook', function () {
113
113
  it('should not call requestError if there are no errors', function () {
114
+ const requestErrors = [sinon.mock(), sinon.mock()];
114
115
  request.interceptors[0] = {
115
116
  request(req) {
116
117
  return Object.assign({}, req, {
117
118
  url: mockServer.urlFor('/changed'),
118
119
  });
119
120
  },
120
- requestError: sinon.mock(),
121
+ requestError: requestErrors[0],
121
122
  };
122
- request.interceptors[1] = { requestError: sinon.mock() };
123
+ request.interceptors[1] = { requestError: requestErrors[1] };
123
124
 
124
125
  return request
125
126
  .send({
@@ -127,8 +128,8 @@ describe('An interceptor', function () {
127
128
  })
128
129
  .then(function ({ body }) {
129
130
  expect(body).to.deep.equal({ requested: 'changed' });
130
- request.interceptors.forEach((interceptor) =>
131
- expect(interceptor.requestError.called).to.equal(
131
+ requestErrors.forEach((requestError) =>
132
+ expect(requestError.called).to.equal(
132
133
  false,
133
134
  'requestError should not have been called',
134
135
  ),
@@ -137,22 +138,23 @@ describe('An interceptor', function () {
137
138
  });
138
139
 
139
140
  it('should call requestError only in the subsequent hook, if a previous hook fails', function () {
141
+ const requestErrors = [sinon.mock(), sinon.mock()];
140
142
  request.interceptors[0] = {
141
143
  request: sinon.mock().throws(new Error('blocked')),
142
- requestError: sinon.mock(),
144
+ requestError: requestErrors[0],
143
145
  };
144
146
  request.interceptors[1] = {
145
- requestError: sinon.mock().throws(new Error('error overridden')),
147
+ requestError: requestErrors[1].throws(new Error('error overridden')),
146
148
  };
147
149
 
148
150
  return expect(request.send({ url: mockServer.url })).to.be.rejected.then(
149
151
  function (err) {
150
152
  expect(err.message).to.deep.equal('error overridden');
151
- expect(request.interceptors[0].requestError.called).to.equal(
153
+ expect(requestErrors[0].called).to.equal(
152
154
  false,
153
155
  'Preceeding requestError hooks should not be called',
154
156
  );
155
- expect(request.interceptors[1].requestError.called).to.equal(
157
+ expect(requestErrors[1].called).to.equal(
156
158
  true,
157
159
  'Subsequent requestError hook should be called',
158
160
  );
@@ -164,7 +166,7 @@ describe('An interceptor', function () {
164
166
  beforeEach(function () {
165
167
  return (this.utilsShouldUpdateToken = sinon
166
168
  .stub(utils, 'shouldRefreshKey')
167
- .returns(true));
169
+ .resolves(true));
168
170
  });
169
171
 
170
172
  afterEach(function () {
@@ -293,7 +295,7 @@ describe('An interceptor', function () {
293
295
 
294
296
  it('should be able to change a stream response before it is returned', function () {
295
297
  request.interceptors[0] = {
296
- response() {
298
+ async response() {
297
299
  return stringToStream('replacement stream');
298
300
  },
299
301
  };
@@ -309,7 +311,8 @@ describe('An interceptor', function () {
309
311
 
310
312
  describe('with a responseError hook', function () {
311
313
  it('should not call responseError if there are no errors', function () {
312
- request.interceptors[0] = { responseError: sinon.mock() };
314
+ const responseError = sinon.mock();
315
+ request.interceptors[0] = { responseError };
313
316
 
314
317
  return request
315
318
  .send({
@@ -317,7 +320,7 @@ describe('An interceptor', function () {
317
320
  })
318
321
  .then(function ({ body }) {
319
322
  expect(body).to.deep.equal({ requested: '/' });
320
- return expect(request.interceptors[0].responseError.called).to.equal(
323
+ return expect(responseError.called).to.equal(
321
324
  false,
322
325
  'responseError should not have been called',
323
326
  );
@@ -394,6 +397,7 @@ describe('An interceptor', function () {
394
397
  return expect(
395
398
  request.send({
396
399
  url: targetUrl,
400
+ // @ts-expect-error We're intentionally testing invalid input
397
401
  anotherExtraOption: true,
398
402
  }),
399
403
  ).to.be.rejected.then(function (err) {
@@ -418,6 +422,7 @@ describe('An interceptor', function () {
418
422
  return expect(
419
423
  request.send({
420
424
  url: targetUrl,
425
+ // @ts-expect-error We're intentionally testing invalid input
421
426
  anotherExtraOption: true,
422
427
  }),
423
428
  ).to.be.rejected.then(function (err) {
@@ -9,12 +9,23 @@ const mockServer = mockhttp.getLocal();
9
9
  const { auth, request, getCustomRequest, IS_BROWSER, delay } = setup();
10
10
 
11
11
  class TestFile extends Blob {
12
- constructor(blobParts, name, type) {
12
+ constructor(
13
+ blobParts: BlobPart[],
14
+ public name: string,
15
+ type?: string,
16
+ ) {
13
17
  super(blobParts, { type });
14
- this.name = name;
15
18
  }
16
19
  }
17
20
 
21
+ const writeMethods = [
22
+ ['DELETE', 'Delete'],
23
+ ['PATCH', 'Patch'],
24
+ ['PUT', 'Put'],
25
+ ['POST', 'Post'],
26
+ ] as const;
27
+ const methods = [['GET', 'Get'], ...writeMethods] as const;
28
+
18
29
  describe('Request:', function () {
19
30
  this.timeout(10000);
20
31
 
@@ -53,11 +64,9 @@ describe('Request:', function () {
53
64
  describe('given multiple endpoints', function () {
54
65
  beforeEach(() =>
55
66
  Promise.all(
56
- ['get', 'post', 'put', 'patch', 'delete'].map((method) =>
57
- mockServer[`for${method[0].toUpperCase() + method.slice(1)}`](
58
- '/foo',
59
- ).thenJson(200, {
60
- method: method.toUpperCase(),
67
+ methods.map(([upperMethod, camelMethod]) =>
68
+ mockServer[`for${camelMethod}`]('/foo').thenJson(200, {
69
+ method: upperMethod,
61
70
  }),
62
71
  ),
63
72
  ),
@@ -276,7 +285,7 @@ describe('Request:', function () {
276
285
  });
277
286
 
278
287
  describe('when the http server specifies the Access-Control-Expose-Headers=Retry-After in the OPTIONS response headers', function () {
279
- let mockServer2;
288
+ let mockServer2: mockhttp.Mockttp;
280
289
 
281
290
  beforeEach(async () => {
282
291
  await mockServer.stop();
@@ -356,10 +365,10 @@ describe('Request:', function () {
356
365
  });
357
366
 
358
367
  describe('given simple endpoints that handle a request body', () =>
359
- ['delete', 'patch', 'put', 'post'].forEach((method) =>
360
- describe(`given a ${method.toUpperCase()} endpoint that matches the request body`, function () {
368
+ writeMethods.forEach(([upperMethod, camelMethod]) =>
369
+ describe(`given a ${upperMethod} endpoint that matches the request body`, function () {
361
370
  beforeEach(() =>
362
- mockServer[`for${method[0].toUpperCase() + method.slice(1)}`]('/')
371
+ mockServer[`for${camelMethod}`]('/')
363
372
  .withBody(JSON.stringify({ foo: 'bar' }))
364
373
  .thenJson(200, { matched: true }),
365
374
  );
@@ -367,7 +376,7 @@ describe('Request:', function () {
367
376
  it('should eventually return the body', function () {
368
377
  const promise = request
369
378
  .send({
370
- method: method.toUpperCase(),
379
+ method: upperMethod,
371
380
  baseUrl: mockServer.url,
372
381
  url: '/',
373
382
  body: {
@@ -80,7 +80,7 @@ describe('responseFormat:', function () {
80
80
  return reader.readAsText(body);
81
81
  });
82
82
  });
83
- return expect(promise).to.eventually.satisfy(function (body) {
83
+ return expect(promise).to.eventually.satisfy(function (body: any) {
84
84
  const s = JSON.stringify(RESPONSE_BODY);
85
85
  if (IS_BROWSER) {
86
86
  return body === s;
@@ -97,6 +97,7 @@ describe('responseFormat:', function () {
97
97
  method: 'GET',
98
98
  baseUrl: mockServer.url,
99
99
  url: '/',
100
+ // @ts-expect-error We're intentionally testing invalid input
100
101
  responseFormat: 'uzabzabza',
101
102
  });
102
103
  return expect(promise).to.be.rejectedWith(
@@ -1,6 +1,6 @@
1
1
  const IS_BROWSER = typeof window !== 'undefined' && window !== null;
2
2
 
3
- let dataDirectoryPath = null;
3
+ let dataDirectoryPath: string | undefined;
4
4
  if (!IS_BROWSER) {
5
5
  // eslint-disable-next-line @typescript-eslint/no-var-requires
6
6
  const temp = require('temp').track();
@@ -22,15 +22,15 @@ import * as chai from 'chai';
22
22
  import * as chaiAsPromised from 'chai-as-promised';
23
23
  chai.use(chaiAsPromised);
24
24
 
25
- const getCustomRequest = function (opts) {
26
- opts = Object.assign({}, { auth, debug: false, isBrowser: IS_BROWSER }, opts);
25
+ const getCustomRequest = function (opts = {}) {
26
+ opts = { auth, debug: false, isBrowser: IS_BROWSER, ...opts };
27
27
  return getRequest(opts);
28
28
  };
29
29
 
30
30
  // Grab setTimeout before we replace it with a fake later, so
31
31
  // we can still do real waiting in the tests themselves
32
32
  const unstubbedSetTimeout = setTimeout;
33
- const delay = (delayMs) =>
33
+ const delay = (delayMs: number) =>
34
34
  new Promise((resolve) => unstubbedSetTimeout(resolve, delayMs));
35
35
 
36
36
  export default () => ({
@@ -1,3 +1,5 @@
1
+ import './browserify-zlib';
2
+
1
3
  import { PassThrough } from 'stream';
2
4
  import { expect } from 'chai';
3
5
  import setup from './setup';
@@ -8,8 +10,8 @@ import * as utils from '../build/utils';
8
10
  const mockServer = mockhttp.getLocal();
9
11
 
10
12
  const { auth, request, delay } = setup();
11
- const gzip = (contents) =>
12
- new Promise((resolve, reject) =>
13
+ const gzip = (contents: string) =>
14
+ new Promise<Buffer>((resolve, reject) =>
13
15
  zlib.gzip(contents, (err, res) => {
14
16
  if (err) {
15
17
  reject(err);
@@ -19,6 +21,14 @@ const gzip = (contents) =>
19
21
  }),
20
22
  );
21
23
 
24
+ const writeMethods = [
25
+ ['DELETE', 'Delete'],
26
+ ['PATCH', 'Patch'],
27
+ ['PUT', 'Put'],
28
+ ['POST', 'Post'],
29
+ ] as const;
30
+ const methods = [['GET', 'Get'], ...writeMethods] as const;
31
+
22
32
  describe('Request (stream):', function () {
23
33
  beforeEach(() => Promise.all([auth.removeKey(), mockServer.start()]));
24
34
 
@@ -86,10 +96,8 @@ describe('Request (stream):', function () {
86
96
 
87
97
  describe('given multiple endpoints', function () {
88
98
  beforeEach(() =>
89
- ['get', 'post', 'put', 'patch', 'delete'].forEach((method) =>
90
- mockServer[`for${method[0].toUpperCase() + method.slice(1)}`](
91
- '/foo',
92
- ).thenReply(200, method.toUpperCase()),
99
+ methods.forEach(([upperMethod, camelMethod]) =>
100
+ mockServer[`for${camelMethod}`]('/foo').thenReply(200, upperMethod),
93
101
  ),
94
102
  );
95
103
 
@@ -134,7 +142,10 @@ describe('Request (stream):', function () {
134
142
  baseUrl: mockServer.url,
135
143
  url: '/foo',
136
144
  })
137
- .then((stream) => expect(stream.length).to.be.undefined));
145
+ .then((stream) => {
146
+ // @ts-expect-error We're intentionally testing invalid property
147
+ expect(stream.length).to.be.undefined;
148
+ }));
138
149
  });
139
150
 
140
151
  describe('given an gzip endpoint with a content-length header', function () {
@@ -5,6 +5,7 @@ import * as errors from 'balena-errors';
5
5
  import * as mockhttp from 'mockttp';
6
6
  import * as tokens from './tokens.json';
7
7
  import * as utils from '../build/utils';
8
+ import type { BalenaRequestPassThroughStream } from '../build/request';
8
9
 
9
10
  const johnDoeFixture = tokens.johndoe;
10
11
  const janeDoeFixture = tokens.janedoe;
@@ -44,7 +45,7 @@ describe('Request (token):', function () {
44
45
  baseUrl: mockServer.url,
45
46
  url: '/foo',
46
47
  })
47
- .then((v) => v.request.headers.Authorization);
48
+ .then((v) => v.request.headers?.Authorization);
48
49
  return expect(promise).to.eventually.equal(
49
50
  `Bearer ${johnDoeFixture.token}`,
50
51
  );
@@ -58,7 +59,7 @@ describe('Request (token):', function () {
58
59
  url: '/foo',
59
60
  sendToken: false,
60
61
  })
61
- .then((v) => v.request.headers.Authorization);
62
+ .then((v) => v.request.headers?.Authorization);
62
63
  return expect(promise).to.eventually.equal(undefined);
63
64
  });
64
65
  });
@@ -73,7 +74,7 @@ describe('Request (token):', function () {
73
74
  baseUrl: mockServer.url,
74
75
  url: '/foo',
75
76
  })
76
- .then((v) => v.request.headers.Authorization);
77
+ .then((v) => v.request.headers?.Authorization);
77
78
  return expect(promise).to.eventually.not.exist;
78
79
  });
79
80
  });
@@ -156,7 +157,7 @@ describe('Request (token):', function () {
156
157
  })
157
158
  .then(function (response) {
158
159
  const authorizationHeader =
159
- response.request.headers.Authorization;
160
+ response.request.headers?.Authorization;
160
161
  return expect(authorizationHeader).to.equal(
161
162
  `Bearer ${janeDoeFixture.token}`,
162
163
  );
@@ -302,8 +303,9 @@ describe('Request (token):', function () {
302
303
  url: '/foo',
303
304
  })
304
305
  .then(function (stream) {
305
- const { headers } = stream.response.request;
306
- expect(headers.Authorization).to.equal(
306
+ const { headers } = (stream as BalenaRequestPassThroughStream)
307
+ .response.request;
308
+ expect(headers?.Authorization).to.equal(
307
309
  `Bearer ${johnDoeFixture.token}`,
308
310
  );
309
311
  return utils.getStreamContents(stream);
@@ -321,8 +323,9 @@ describe('Request (token):', function () {
321
323
  url: '/foo',
322
324
  })
323
325
  .then(function (stream) {
324
- const { headers } = stream.response.request;
325
- expect(headers.Authorization).to.not.exist;
326
+ const { headers } = (stream as BalenaRequestPassThroughStream)
327
+ .response.request;
328
+ expect(headers?.Authorization).to.not.exist;
326
329
  return utils.getStreamContents(stream);
327
330
  }));
328
331
  });
@@ -5,6 +5,7 @@ import * as fetchPonyfill from 'fetch-ponyfill';
5
5
  import * as sinon from 'sinon';
6
6
  import * as tokens from './tokens.json';
7
7
  import * as utils from '../build/utils';
8
+ import type { BalenaRequestResponse } from '../build/request';
8
9
 
9
10
  const { Headers } = fetchPonyfill({ Promise });
10
11
  const { auth } = setup();
@@ -237,13 +238,15 @@ describe('Utils:', function () {
237
238
  headers: new Headers({
238
239
  'content-encoding': 'gzip',
239
240
  }),
240
- };
241
+ } as BalenaRequestResponse<any>;
241
242
 
242
243
  return expect(utils.isResponseCompressed(response)).to.be.true;
243
244
  });
244
245
 
245
246
  it('should return false if Content-Encoding is not set', function () {
246
- const response = { headers: new Headers({}) };
247
+ const response = {
248
+ headers: new Headers({}),
249
+ } as BalenaRequestResponse<any>;
247
250
 
248
251
  return expect(utils.isResponseCompressed(response)).to.be.false;
249
252
  });
package/tsconfig.json CHANGED
@@ -11,8 +11,6 @@
11
11
  "noUnusedParameters": true,
12
12
  "noUnusedLocals": true,
13
13
  "moduleResolution": "node",
14
- "allowJs": true,
15
- "checkJs": true,
16
14
  "skipLibCheck": true,
17
15
  "importHelpers": true
18
16
  },