Skip to content

Commit 3c58a9e

Browse files
Replace unmaintained useragent dependency with my-ua-parser (#68)
* Replace unmaintained useragent dependency * Update test to support Node 24 * Replace useragent with my-ua-parser * Update docs * Unify version parsing * Remove Semver * Update tests * Add decorator and types * Unpin dependency
1 parent 5879211 commit 3c58a9e

6 files changed

Lines changed: 400 additions & 11 deletions

File tree

API.md

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2-
Scooter uses the [useragent] package to provide user-agent information. For
3-
more details of what information scooter provides, please see the [useragent](https://www.npmjs.org/package/useragent).
2+
Scooter uses the [my-ua-parser](https://www.npmjs.com/package/my-ua-parser) package to provide user-agent information. For
3+
more details of what information scooter provides, please see the [my-ua-parser](https://www.npmjs.com/package/my-ua-parser) documentation.
44

55
# Usage
66

@@ -17,7 +17,7 @@ const start = async () => {
1717
path: '/user-agent',
1818
handler: (request, h) => {
1919

20-
return request.plugins.scooter;
20+
return request.userAgent();
2121
}
2222
});
2323

@@ -28,3 +28,39 @@ const start = async () => {
2828

2929
start();
3030
```
31+
32+
## `request.userAgent()`
33+
34+
A convenience decoration added to every request. Returns the same parsed user-agent object as `request.plugins.scooter`.
35+
36+
```javascript
37+
// Both are equivalent
38+
request.userAgent()
39+
request.plugins.scooter
40+
```
41+
42+
The returned object has the following shape:
43+
44+
```
45+
{
46+
family: string, // browser name, e.g. 'Chrome'
47+
major: string, // major version, e.g. '91'
48+
minor: string, // minor version, e.g. '0'
49+
patch: string, // patch version, e.g. '4472'
50+
source: string, // original user-agent header value
51+
os: {
52+
family: string, // OS name, e.g. 'Windows'
53+
major: string,
54+
minor: string,
55+
patch: string
56+
},
57+
device: {
58+
family: string, // device model or type, e.g. 'iPhone'
59+
brand: string, // device vendor, e.g. 'Apple'
60+
model: string // device model, e.g. 'iPhone'
61+
}
62+
}
63+
```
64+
65+
Unknown values default to `'Other'` for family fields and `'0'` for version fields.
66+

lib/index.d.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Plugin } from '@hapi/hapi';
2+
3+
export interface ScooterVersionInfo {
4+
family: string;
5+
major: string;
6+
minor: string;
7+
patch: string;
8+
}
9+
10+
export interface DeviceInfo {
11+
family: string;
12+
brand: string | undefined;
13+
model: string | undefined;
14+
}
15+
16+
export interface ScooterResult {
17+
family: string;
18+
major: string;
19+
minor: string;
20+
patch: string;
21+
source: string | undefined;
22+
os: ScooterVersionInfo;
23+
device: DeviceInfo;
24+
}
25+
26+
declare module '@hapi/hapi' {
27+
28+
interface PluginsStates {
29+
scooter: ScooterResult;
30+
}
31+
32+
interface Request {
33+
userAgent(): ScooterResult;
34+
}
35+
}
36+
37+
export const plugin: Plugin<void>;

lib/index.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,44 @@
11
'use strict';
22

3-
const Useragent = require('useragent');
3+
const Useragent = require('my-ua-parser');
44

5-
// Requires semver be installed
6-
require('useragent/features'); // Enhances Useragent
5+
const internals = {};
76

87

9-
const internals = {};
8+
internals.parseVersion = function (version, index) {
9+
10+
if (!version) {
11+
return '0';
12+
}
13+
14+
const parts = version.split('.');
15+
return parts[index] || '0';
16+
};
17+
18+
19+
internals.parseUserAgent = function (userAgentString) {
20+
21+
const result = Useragent(userAgentString || '');
22+
23+
return {
24+
family: result.browser.name || 'Other',
25+
major: internals.parseVersion(result.browser.version, 0),
26+
minor: internals.parseVersion(result.browser.version, 1),
27+
patch: internals.parseVersion(result.browser.version, 2),
28+
source: userAgentString,
29+
os: {
30+
family: result.os.name || 'Other',
31+
major: internals.parseVersion(result.os.version, 0),
32+
minor: internals.parseVersion(result.os.version, 1),
33+
patch: internals.parseVersion(result.os.version, 2)
34+
},
35+
device: {
36+
family: result.device.model || result.device.type || 'Other',
37+
brand: result.device.vendor,
38+
model: result.device.model
39+
}
40+
};
41+
};
1042

1143

1244
exports.plugin = {
@@ -18,12 +50,17 @@ exports.plugin = {
1850
register: function (server, options) {
1951

2052
server.ext('onRequest', internals.onRequest);
53+
54+
server.decorate('request', 'userAgent', function () {
55+
56+
return this.plugins.scooter;
57+
});
2158
}
2259
};
2360

2461

2562
internals.onRequest = function (request, h) {
2663

27-
request.plugins.scooter = Useragent.lookup(request.headers['user-agent']);
64+
request.plugins.scooter = internals.parseUserAgent(request.headers['user-agent']);
2865
return h.continue;
2966
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"version": "7.0.0",
55
"repository": "git://github.com/hapijs/scooter",
66
"main": "lib/index.js",
7+
"types": "lib/index.d.ts",
78
"files": [
89
"lib"
910
],
@@ -18,8 +19,7 @@
1819
]
1920
},
2021
"dependencies": {
21-
"semver": "^7.3.8",
22-
"useragent": "^2.3.0"
22+
"my-ua-parser": "^2.0.4"
2323
},
2424
"devDependencies": {
2525
"@hapi/code": "^9.0.0",

test/esm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('import()', () => {
1919

2020
it('exposes all methods and classes as named imports', () => {
2121

22-
expect(Object.keys(Scooter)).to.equal([
22+
expect(Object.keys(Scooter)).to.contain([
2323
'default',
2424
'plugin'
2525
]);

0 commit comments

Comments
 (0)