Skip to content

Commit 1c99efa

Browse files
authored
Update error messages and throw on bad parse input (#107)
1 parent 5cf4cb9 commit 1c99efa

3 files changed

Lines changed: 28 additions & 48 deletions

File tree

‎src/format.spec.ts‎

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ describe('format(credentials)', function () {
55
describe('arguments', function () {
66
describe('credentials', function () {
77
it('should be required', function () {
8-
assert.throws(
9-
() => (format as any)(),
10-
/argument credentials is required/,
11-
);
8+
assert.throws(() => (format as any)(), /Expected an object/);
129
});
1310

1411
it('should accept credentials', function () {
@@ -17,65 +14,56 @@ describe('format(credentials)', function () {
1714
});
1815

1916
it('should reject null', function () {
20-
assert.throws(
21-
format.bind(null, null as any),
22-
/argument credentials is required/,
23-
);
17+
assert.throws(format.bind(null, null as any), /Expected an object/);
2418
});
2519

2620
it('should reject a number', function () {
27-
assert.throws(
28-
format.bind(null, 42 as any),
29-
/argument credentials is required/,
30-
);
21+
assert.throws(format.bind(null, 42 as any), /Expected an object/);
3122
});
3223

3324
it('should reject a string', function () {
34-
assert.throws(
35-
format.bind(null, '' as any),
36-
/argument credentials is required/,
37-
);
25+
assert.throws(format.bind(null, '' as any), /Expected an object/);
3826
});
3927

4028
it('should reject an object without name', function () {
4129
assert.throws(
4230
format.bind(null, { pass: 'bar' } as any),
43-
/argument credentials is required to have name and pass properties/,
31+
/Object must have string properties "name" and "pass"/,
4432
);
4533
});
4634

4735
it('should reject an object without pass', function () {
4836
assert.throws(
4937
format.bind(null, { name: 'foo' } as any),
50-
/argument credentials is required to have name and pass properties/,
38+
/Object must have string properties "name" and "pass"/,
5139
);
5240
});
5341

5442
it('should reject an object with non-string name', function () {
5543
assert.throws(
5644
format.bind(null, { name: 42, pass: 'bar' } as any),
57-
/argument credentials is required to have name and pass properties/,
45+
/Object must have string properties "name" and "pass"/,
5846
);
5947
});
6048

6149
it('should reject an object with non-string pass', function () {
6250
assert.throws(
6351
format.bind(null, { name: 'foo', pass: 42 } as any),
64-
/argument credentials is required to have name and pass properties/,
52+
/Object must have string properties "name" and "pass"/,
6553
);
6654
});
6755

6856
it('should reject userid containing colon', function () {
6957
assert.throws(
7058
format.bind(null, { name: 'foo:bar', pass: 'baz' }),
71-
/must not contain a colon or control characters/,
59+
/must not contain a colon/,
7260
);
7361
});
7462

7563
it('should reject control chars in userid', function () {
7664
assert.throws(
7765
format.bind(null, { name: 'foo\u0000bar', pass: 'baz' }),
78-
/must not contain a colon or control characters/,
66+
/must not contain control characters/,
7967
);
8068
});
8169

@@ -96,21 +84,21 @@ describe('format(credentials)', function () {
9684
});
9785

9886
describe('with empty password', function () {
99-
it('should throw', function () {
87+
it('should return header', function () {
10088
const header = format({ name: 'foo', pass: '' });
10189
assert.strictEqual(header, 'Basic Zm9vOg==');
10290
});
10391
});
10492

10593
describe('with empty userid', function () {
106-
it('should throw', function () {
94+
it('should return header', function () {
10795
const header = format({ name: '', pass: 'pass' });
10896
assert.strictEqual(header, 'Basic OnBhc3M=');
10997
});
11098
});
11199

112100
describe('with empty userid and pass', function () {
113-
it('should throw', function () {
101+
it('should return header', function () {
114102
const header = format({ name: '', pass: '' });
115103
assert.strictEqual(header, 'Basic Og==');
116104
});

‎src/index.ts‎

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface Credentials {
2626

2727
export function parse(string: string): Credentials | undefined {
2828
if (typeof string !== 'string') {
29-
return undefined;
29+
throw new TypeError('Expected a string');
3030
}
3131

3232
// parse header
@@ -52,39 +52,31 @@ export function parse(string: string): Credentials | undefined {
5252
* @public
5353
*/
5454
export function format(credentials: Credentials): string {
55-
if (!credentials) {
56-
throw new TypeError('argument credentials is required');
57-
}
58-
59-
if (typeof credentials !== 'object') {
60-
throw new TypeError('argument credentials is required to be an object');
55+
if (typeof credentials !== 'object' || credentials === null) {
56+
throw new TypeError('Expected an object');
6157
}
6258

6359
if (
6460
typeof credentials.name !== 'string' ||
6561
typeof credentials.pass !== 'string'
6662
) {
67-
throw new TypeError(
68-
'argument credentials is required to have name and pass properties',
69-
);
63+
throw new TypeError('Object must have string properties "name" and "pass"');
7064
}
7165

72-
if (
73-
credentials.name.includes(':') || // RFC 7617 disallows colon in username
74-
CONTROL_CHARS_REGEXP.test(credentials.name)
75-
) {
76-
throw new TypeError(
77-
'argument credentials.name must not contain a colon or control characters',
78-
);
66+
// RFC 7617 disallows colon in username
67+
if (credentials.name.includes(':')) {
68+
throw new TypeError('Object "name" must not contain a colon');
7969
}
8070

81-
if (CONTROL_CHARS_REGEXP.test(credentials.pass)) {
71+
const str = credentials.name + ':' + credentials.pass;
72+
73+
if (CONTROL_CHARS_REGEXP.test(str)) {
8274
throw new TypeError(
83-
'argument credentials.pass must not contain control characters',
75+
'Object "name" and "pass" must not contain control characters',
8476
);
8577
}
8678

87-
return 'Basic ' + base64.encode(credentials.name + ':' + credentials.pass);
79+
return 'Basic ' + base64.encode(str);
8880
}
8981

9082
/**

‎src/parse.spec.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { describe, it, assert } from 'vitest';
22
import { parse } from './index.js';
33

44
describe('parse(string)', function () {
5-
describe('with undefined string', function () {
6-
it('should return undefined', function () {
7-
assert.strictEqual((parse as any)(), undefined);
5+
describe('with non string', function () {
6+
it('should throw', function () {
7+
assert.throws(() => parse(undefined as any), /Expected a string/);
88
});
99
});
1010

0 commit comments

Comments
 (0)