user-agents 1.1.9 → 2.0.0-alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/user-agents.json +151323 -0
- package/package.json +49 -28
- package/src/{gunzip-data.js → gunzip-data.ts} +4 -5
- package/src/index.ts +4 -0
- package/src/{update-data.js → update-data.ts} +36 -36
- package/src/user-agent.ts +215 -0
- package/.babelrc +0 -31
- package/.circleci/config.yml +0 -207
- package/.clabot +0 -7
- package/.eslintrc +0 -15
- package/.github/pull_request_template.md +0 -7
- package/CLA.md +0 -126
- package/CONTRIBUTING.md +0 -8
- package/media/ycombinator.png +0 -0
- package/src/index.js +0 -4
- package/src/user-agent.js +0 -154
- package/src/user-agents.json.gz +0 -0
- package/test/test-user-agent.js +0 -113
- package/webpack.config.js +0 -49
package/.circleci/config.yml
DELETED
@@ -1,207 +0,0 @@
|
|
1
|
-
defaults: &defaults
|
2
|
-
working_directory: ~/user-agents
|
3
|
-
docker:
|
4
|
-
- image: circleci/node:fermium
|
5
|
-
|
6
|
-
whitelist: &whitelist
|
7
|
-
paths:
|
8
|
-
- .babelrc
|
9
|
-
- .clabot
|
10
|
-
- .circleci/*
|
11
|
-
- .eslintrc
|
12
|
-
- .git/*
|
13
|
-
- .github/*
|
14
|
-
- .gitignore
|
15
|
-
- dist/*
|
16
|
-
- CLA.md
|
17
|
-
- CONTRIBUTING.md
|
18
|
-
- LICENSE
|
19
|
-
- README.md
|
20
|
-
- media/*
|
21
|
-
- package.json
|
22
|
-
- src/*
|
23
|
-
- test/*
|
24
|
-
- webpack.config.js
|
25
|
-
- yarn.lock
|
26
|
-
|
27
|
-
version: 2
|
28
|
-
jobs:
|
29
|
-
checkout:
|
30
|
-
<<: *defaults
|
31
|
-
steps:
|
32
|
-
- checkout
|
33
|
-
- restore_cache:
|
34
|
-
key: dependency-cache-{{ checksum "yarn.lock" }}
|
35
|
-
- run:
|
36
|
-
name: Install Dependencies
|
37
|
-
command: yarn install
|
38
|
-
- save_cache:
|
39
|
-
key: dependency-cache-{{ checksum "yarn.lock" }}
|
40
|
-
paths:
|
41
|
-
- ./node_modules
|
42
|
-
- persist_to_workspace:
|
43
|
-
root: ~/user-agents
|
44
|
-
<<: *whitelist
|
45
|
-
|
46
|
-
build:
|
47
|
-
<<: *defaults
|
48
|
-
steps:
|
49
|
-
- attach_workspace:
|
50
|
-
at: ~/user-agents
|
51
|
-
- restore_cache:
|
52
|
-
key: dependency-cache-{{ checksum "yarn.lock" }}
|
53
|
-
- run:
|
54
|
-
name: Build
|
55
|
-
command: |
|
56
|
-
yarn gunzip-data
|
57
|
-
yarn build
|
58
|
-
- persist_to_workspace:
|
59
|
-
root: ~/user-agents
|
60
|
-
<<: *whitelist
|
61
|
-
|
62
|
-
test:
|
63
|
-
<<: *defaults
|
64
|
-
steps:
|
65
|
-
- attach_workspace:
|
66
|
-
at: ~/user-agents
|
67
|
-
- restore_cache:
|
68
|
-
key: dependency-cache-{{ checksum "yarn.lock" }}
|
69
|
-
- run:
|
70
|
-
name: Test
|
71
|
-
command: |
|
72
|
-
yarn test
|
73
|
-
|
74
|
-
deploy:
|
75
|
-
<<: *defaults
|
76
|
-
steps:
|
77
|
-
- attach_workspace:
|
78
|
-
at: ~/user-agents
|
79
|
-
- run:
|
80
|
-
name: Write NPM Token to ~/.npmrc
|
81
|
-
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
82
|
-
- run:
|
83
|
-
name: Install dot-json package
|
84
|
-
command: npm install --no-save dot-json
|
85
|
-
- run:
|
86
|
-
name: Write version to package.json
|
87
|
-
command: $(yarn bin)/dot-json package.json version ${CIRCLE_TAG:1}
|
88
|
-
- run:
|
89
|
-
name: Publish to NPM
|
90
|
-
command: npm publish --access=public
|
91
|
-
|
92
|
-
update:
|
93
|
-
<<: *defaults
|
94
|
-
steps:
|
95
|
-
- attach_workspace:
|
96
|
-
at: ~/user-agents
|
97
|
-
- restore_cache:
|
98
|
-
key: dependency-cache-{{ checksum "yarn.lock" }}
|
99
|
-
- run:
|
100
|
-
name: Update the user agents data
|
101
|
-
command: |
|
102
|
-
yarn update-data
|
103
|
-
- store_artifacts:
|
104
|
-
path: ~/user-agents/src/user-agents.json.gz
|
105
|
-
destination: user-agents.json.gz
|
106
|
-
- persist_to_workspace:
|
107
|
-
root: ~/user-agents
|
108
|
-
<<: *whitelist
|
109
|
-
|
110
|
-
publish-new-version:
|
111
|
-
<<: *defaults
|
112
|
-
steps:
|
113
|
-
- attach_workspace:
|
114
|
-
at: ~/user-agents
|
115
|
-
- run:
|
116
|
-
name: Commit the newly downloaded data
|
117
|
-
command: |
|
118
|
-
git add src/user-agents.json.gz
|
119
|
-
# Configure some identity details for the machine deployment account.
|
120
|
-
git config --global user.email "user-agents@intoli.com"
|
121
|
-
git config --global user.name "User Agents"
|
122
|
-
git config --global push.default "simple"
|
123
|
-
# Disable strict host checking.
|
124
|
-
mkdir -p ~/.ssh/
|
125
|
-
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
126
|
-
# The status code will be 1 if there are no changes,
|
127
|
-
# but we want to publish anyway to stay on a regular schedule.
|
128
|
-
git commit -m 'Regularly scheduled user agent data update.' || true
|
129
|
-
- run:
|
130
|
-
name: Bump the patch version and trigger a new release
|
131
|
-
command: npm version patch && git push && git push --tags
|
132
|
-
|
133
|
-
workflows:
|
134
|
-
version: 2
|
135
|
-
|
136
|
-
build:
|
137
|
-
jobs:
|
138
|
-
- checkout
|
139
|
-
- build:
|
140
|
-
filters:
|
141
|
-
tags:
|
142
|
-
ignore: /v[0-9]+(\.[0-9]+)*/
|
143
|
-
requires:
|
144
|
-
- checkout
|
145
|
-
- test:
|
146
|
-
filters:
|
147
|
-
tags:
|
148
|
-
ignore: /v[0-9]+(\.[0-9]+)*/
|
149
|
-
requires:
|
150
|
-
- build
|
151
|
-
|
152
|
-
release:
|
153
|
-
jobs:
|
154
|
-
- checkout:
|
155
|
-
filters:
|
156
|
-
tags:
|
157
|
-
only: /v[0-9]+(\.[0-9]+)*/
|
158
|
-
branches:
|
159
|
-
ignore: /.*/
|
160
|
-
- build:
|
161
|
-
filters:
|
162
|
-
tags:
|
163
|
-
only: /v[0-9]+(\.[0-9]+)*/
|
164
|
-
branches:
|
165
|
-
ignore: /.*/
|
166
|
-
requires:
|
167
|
-
- checkout
|
168
|
-
- test:
|
169
|
-
filters:
|
170
|
-
tags:
|
171
|
-
only: /v[0-9]+(\.[0-9]+)*/
|
172
|
-
branches:
|
173
|
-
ignore: /.*/
|
174
|
-
requires:
|
175
|
-
- build
|
176
|
-
- deploy:
|
177
|
-
filters:
|
178
|
-
tags:
|
179
|
-
only: /v[0-9]+(\.[0-9]+)*/
|
180
|
-
branches:
|
181
|
-
ignore: /.*/
|
182
|
-
requires:
|
183
|
-
- test
|
184
|
-
|
185
|
-
scheduled-release:
|
186
|
-
triggers:
|
187
|
-
- schedule:
|
188
|
-
cron: "00 06 * * *"
|
189
|
-
filters:
|
190
|
-
branches:
|
191
|
-
only:
|
192
|
-
- master
|
193
|
-
|
194
|
-
jobs:
|
195
|
-
- checkout
|
196
|
-
- update:
|
197
|
-
requires:
|
198
|
-
- checkout
|
199
|
-
- build:
|
200
|
-
requires:
|
201
|
-
- update
|
202
|
-
- test:
|
203
|
-
requires:
|
204
|
-
- build
|
205
|
-
- publish-new-version:
|
206
|
-
requires:
|
207
|
-
- test
|
package/.clabot
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"contributors": [
|
3
|
-
"sangaline",
|
4
|
-
"HugoPoi"
|
5
|
-
],
|
6
|
-
"message": "Thank you for making a contribution! We require new contributors to sign our [Contributor License Agreement (CLA)](https://github.com/intoli/user-agents/blob/master/CLA.md). Please review our [Contributing Guide](https://github.com/intoli/user-agents/blob/master/CONTRIBUTING.md) and make sure that you've followed the steps listed there."
|
7
|
-
}
|
package/.eslintrc
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"env": {
|
3
|
-
"es6": true,
|
4
|
-
"mocha": true
|
5
|
-
},
|
6
|
-
"extends": "airbnb-base",
|
7
|
-
"parser": "@babel/eslint-parser",
|
8
|
-
"rules": {
|
9
|
-
"no-await-in-loop": "off",
|
10
|
-
"object-curly-newline": ["error", {
|
11
|
-
"consistent": true,
|
12
|
-
"minProperties": 5
|
13
|
-
}]
|
14
|
-
}
|
15
|
-
}
|
@@ -1,7 +0,0 @@
|
|
1
|
-
*the pull request description goes here...*
|
2
|
-
|
3
|
-
|
4
|
-
**Please check the boxes below to confirm that you have completed these steps:**
|
5
|
-
|
6
|
-
* [ ] I have read and followed the [Contribution Agreement](https://github.com/intoli/user-agents/blob/master/CONTRIBUTING.md).
|
7
|
-
* [ ] I have signed the project [Contributor License Agreement](https://github.com/intoli/user-agents/blob/master/CONTRIBUTING.md).
|
package/CLA.md
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
Contributor Agreement
|
2
|
-
---------------------
|
3
|
-
|
4
|
-
Individual Contributor Exclusive License Agreement
|
5
|
-
--------------------------------------------------
|
6
|
-
|
7
|
-
(including the Traditional Patent License OPTION)
|
8
|
-
-------------------------------------------------
|
9
|
-
|
10
|
-
Thank you for your interest in contributing to Intoli, LLC's User-Agents ("We" or "Us").
|
11
|
-
|
12
|
-
The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us. To make this document effective, please follow the instructions at https://github.com/intoli/user-agents/blob/master/CONTRIBUTING.md.
|
13
|
-
|
14
|
-
### How to use this Contributor Agreement
|
15
|
-
|
16
|
-
If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document. If You do not own the Copyright in the entire work of authorship, any other author of the Contribution should also sign this – in any event, please contact Us at open-source@intoli.com
|
17
|
-
|
18
|
-
### 1\. Definitions
|
19
|
-
|
20
|
-
**"You"** means the individual Copyright owner who Submits a Contribution to Us.
|
21
|
-
|
22
|
-
**"Legal Entity"** means an entity that is not a natural person.
|
23
|
-
|
24
|
-
**"Affiliate"** means any other Legal Entity that controls, is controlled by, or under common control with that Legal Entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such Legal Entity, whether by contract or otherwise, (ii) ownership of fifty percent (50%) or more of the outstanding shares or securities that vote to elect the management or other persons who direct such Legal Entity or (iii) beneficial ownership of such entity.
|
25
|
-
|
26
|
-
**"Contribution"** means any original work of authorship, including any original modifications or additions to an existing work of authorship, Submitted by You to Us, in which You own the Copyright.
|
27
|
-
|
28
|
-
**"Copyright"** means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence.
|
29
|
-
|
30
|
-
**"Material"** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
|
31
|
-
|
32
|
-
**"Submit"** means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
33
|
-
|
34
|
-
**"Documentation"** means any non-software portion of a Contribution.
|
35
|
-
|
36
|
-
### 2\. License grant
|
37
|
-
|
38
|
-
#### 2.1 Copyright license to Us
|
39
|
-
|
40
|
-
Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, Exclusive, perpetual and irrevocable (except as stated in Section 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
|
41
|
-
|
42
|
-
* publish the Contribution,
|
43
|
-
* modify the Contribution,
|
44
|
-
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
|
45
|
-
* reproduce the Contribution in original or modified form,
|
46
|
-
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
|
47
|
-
|
48
|
-
#### 2.2 Moral rights
|
49
|
-
|
50
|
-
Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law. Notwithstanding, You may add your name to the attribution mechanism customary used in the Materials you Contribute to, such as the header of the source code files of Your Contribution, and We will respect this attribution when using Your Contribution.
|
51
|
-
|
52
|
-
#### 2.3 Copyright license back to You
|
53
|
-
|
54
|
-
Upon such grant of rights to Us, We immediately grant to You a worldwide, royalty-free, non-exclusive, perpetual and irrevocable license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
|
55
|
-
|
56
|
-
* publish the Contribution,
|
57
|
-
* modify the Contribution,
|
58
|
-
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
|
59
|
-
* reproduce the Contribution in original or modified form,
|
60
|
-
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
|
61
|
-
|
62
|
-
This license back is limited to the Contribution and does not provide any rights to the Material.
|
63
|
-
|
64
|
-
### 3\. Patents
|
65
|
-
|
66
|
-
#### 3.1 Patent license
|
67
|
-
|
68
|
-
Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material.
|
69
|
-
|
70
|
-
#### 3.2 Revocation of patent license
|
71
|
-
|
72
|
-
You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees.
|
73
|
-
|
74
|
-
### 4. Disclaimer
|
75
|
-
|
76
|
-
THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW.
|
77
|
-
|
78
|
-
### 5. Consequential damage waiver
|
79
|
-
|
80
|
-
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
|
81
|
-
|
82
|
-
### 6. Approximation of disclaimer and damage waiver
|
83
|
-
|
84
|
-
IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 4. AND SECTION 5. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION.
|
85
|
-
|
86
|
-
### 7. Term
|
87
|
-
|
88
|
-
7.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions.
|
89
|
-
|
90
|
-
7.3 In the event of a termination of this Agreement Sections 4, 5, 6, 7 and 8 shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, Free and Open Source Software (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement.
|
91
|
-
|
92
|
-
### 8 Miscellaneous
|
93
|
-
|
94
|
-
8.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of United States excluding its private international law provisions.
|
95
|
-
|
96
|
-
8.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
|
97
|
-
|
98
|
-
8.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person.
|
99
|
-
|
100
|
-
8.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
|
101
|
-
|
102
|
-
8.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect.
|
103
|
-
|
104
|
-
### You
|
105
|
-
|
106
|
-
Date:
|
107
|
-
|
108
|
-
Name:
|
109
|
-
|
110
|
-
Title:
|
111
|
-
|
112
|
-
Address:
|
113
|
-
|
114
|
-
### Us
|
115
|
-
|
116
|
-
Date:
|
117
|
-
|
118
|
-
Name:
|
119
|
-
|
120
|
-
Title:
|
121
|
-
|
122
|
-
Address:
|
123
|
-
|
124
|
-
#### Recreate this Contributor License Agreement
|
125
|
-
|
126
|
-
[https://contributoragreements.org/ca-cla-chooser/?beneficiary-name=Intoli%2C+LLC&project-name=User-Agents&project-website=https%3A%2F%2Fgithub.com%2Fintoli%2Fuser-agents&project-email=open-source%40intoli.com&process-url=https%3A%2F%2Fgithub.com%2Fintoli%2Fuser-agents%2Fblob%2Fmaster%2FCONTRIBUTING.md&project-jurisdiction=United+States&agreement-exclusivity=exclusive&fsfe-compliance=&fsfe-fla=&outbound-option=no-commitment&outboundlist=&outboundlist-custom=&medialist=\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_&patent-option=Traditional&your-date=&your-name=&your-title=&your-address=&your-patents=&pos=apply&action=](https://contributoragreements.org/ca-cla-chooser/?beneficiary-name=Intoli%2C+LLC&project-name=User-Agents&project-website=https%3A%2F%2Fgithub.com%2Fintoli%2Fuser-agents&project-email=open-source%40intoli.com&process-url=https%3A%2F%2Fgithub.com%2Fintoli%2Fuser-agents%2Fblob%2Fmaster%2FCONTRIBUTING.md&project-jurisdiction=United+States&agreement-exclusivity=exclusive&fsfe-compliance=&fsfe-fla=&outbound-option=no-commitment&outboundlist=&outboundlist-custom=&medialist=____________________&patent-option=Traditional&your-date=&your-name=&your-title=&your-address=&your-patents=&pos=apply&action=)
|
package/CONTRIBUTING.md
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
## Contributing
|
2
|
-
|
3
|
-
Contributions are welcome, but please follow these contributor guidelines:
|
4
|
-
|
5
|
-
- Create an issue on [the issue tracker](https://github.com/intoli/user-agents/issues/new) to discuss potential changes before submitting a pull request.
|
6
|
-
- Include at least one test to cover any new functionality or bug fixes.
|
7
|
-
- Make sure that all of your tests are passing and that there are no merge conflicts.
|
8
|
-
- Print, sign, and email the [Contributor License Agreement](https://github.com/intoli/user-agents/blob/master/CLA.md) to [open-source@intoli.com](mailto:open-source@intoli.com).
|
package/media/ycombinator.png
DELETED
Binary file
|
package/src/index.js
DELETED
package/src/user-agent.js
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
import cloneDeep from 'lodash.clonedeep';
|
2
|
-
|
3
|
-
import userAgents from './user-agents.json';
|
4
|
-
|
5
|
-
|
6
|
-
// Normalizes the total weight to 1 and constructs a cumulative distribution.
|
7
|
-
const makeCumulativeWeightIndexPairs = (weightIndexPairs) => {
|
8
|
-
const totalWeight = weightIndexPairs.reduce((sum, [weight]) => sum + weight, 0);
|
9
|
-
let sum = 0;
|
10
|
-
return weightIndexPairs.map(([weight, index]) => {
|
11
|
-
sum += weight / totalWeight;
|
12
|
-
return [sum, index];
|
13
|
-
});
|
14
|
-
};
|
15
|
-
|
16
|
-
// Precompute these so that we can quickly generate unfiltered user agents.
|
17
|
-
const defaultWeightIndexPairs = userAgents.map(({ weight }, index) => [weight, index]);
|
18
|
-
const defaultCumulativeWeightIndexPairs = makeCumulativeWeightIndexPairs(defaultWeightIndexPairs);
|
19
|
-
|
20
|
-
|
21
|
-
// Turn the various filter formats into a single filter function that acts on raw user agents.
|
22
|
-
const constructFilter = (filters, accessor = parentObject => parentObject) => {
|
23
|
-
let childFilters;
|
24
|
-
if (typeof filters === 'function') {
|
25
|
-
childFilters = [filters];
|
26
|
-
} else if (filters instanceof RegExp) {
|
27
|
-
childFilters = [
|
28
|
-
value => (
|
29
|
-
typeof value === 'object' && value && value.userAgent
|
30
|
-
? filters.test(value.userAgent)
|
31
|
-
: filters.test(value)
|
32
|
-
),
|
33
|
-
];
|
34
|
-
} else if (filters instanceof Array) {
|
35
|
-
childFilters = filters.map(childFilter => constructFilter(childFilter));
|
36
|
-
} else if (typeof filters === 'object') {
|
37
|
-
childFilters = Object.entries(filters).map(([key, valueFilter]) => (
|
38
|
-
constructFilter(valueFilter, parentObject => parentObject[key])
|
39
|
-
));
|
40
|
-
} else {
|
41
|
-
childFilters = [
|
42
|
-
value => (
|
43
|
-
typeof value === 'object' && value && value.userAgent
|
44
|
-
? filters === value.userAgent
|
45
|
-
: filters === value
|
46
|
-
),
|
47
|
-
];
|
48
|
-
}
|
49
|
-
|
50
|
-
return (parentObject) => {
|
51
|
-
try {
|
52
|
-
const value = accessor(parentObject);
|
53
|
-
return childFilters.every(childFilter => childFilter(value));
|
54
|
-
} catch (error) {
|
55
|
-
// This happens when a user-agent lacks a nested property.
|
56
|
-
return false;
|
57
|
-
}
|
58
|
-
};
|
59
|
-
};
|
60
|
-
|
61
|
-
|
62
|
-
// Construct normalized cumulative weight index pairs given the filters.
|
63
|
-
const constructCumulativeWeightIndexPairsFromFilters = (filters) => {
|
64
|
-
if (!filters) {
|
65
|
-
return defaultCumulativeWeightIndexPairs;
|
66
|
-
}
|
67
|
-
|
68
|
-
const filter = constructFilter(filters);
|
69
|
-
|
70
|
-
const weightIndexPairs = [];
|
71
|
-
userAgents.forEach((rawUserAgent, index) => {
|
72
|
-
if (filter(rawUserAgent)) {
|
73
|
-
weightIndexPairs.push([rawUserAgent.weight, index]);
|
74
|
-
}
|
75
|
-
});
|
76
|
-
return makeCumulativeWeightIndexPairs(weightIndexPairs);
|
77
|
-
};
|
78
|
-
|
79
|
-
|
80
|
-
const setCumulativeWeightIndexPairs = (userAgent, cumulativeWeightIndexPairs) => {
|
81
|
-
Object.defineProperty(userAgent, 'cumulativeWeightIndexPairs', {
|
82
|
-
configurable: true,
|
83
|
-
enumerable: false,
|
84
|
-
writable: false,
|
85
|
-
value: cumulativeWeightIndexPairs,
|
86
|
-
});
|
87
|
-
};
|
88
|
-
|
89
|
-
|
90
|
-
export default class UserAgent extends Function {
|
91
|
-
constructor(filters) {
|
92
|
-
super();
|
93
|
-
setCumulativeWeightIndexPairs(this, constructCumulativeWeightIndexPairsFromFilters(filters));
|
94
|
-
if (this.cumulativeWeightIndexPairs.length === 0) {
|
95
|
-
throw new Error('No user agents matched your filters.');
|
96
|
-
}
|
97
|
-
|
98
|
-
this.randomize();
|
99
|
-
|
100
|
-
return new Proxy(this, {
|
101
|
-
apply: () => this.random(),
|
102
|
-
get: (target, property, receiver) => {
|
103
|
-
const dataCandidate = target.data && typeof property === 'string'
|
104
|
-
&& Object.prototype.hasOwnProperty.call(target.data, property)
|
105
|
-
&& Object.prototype.propertyIsEnumerable.call(target.data, property);
|
106
|
-
if (dataCandidate) {
|
107
|
-
const value = target.data[property];
|
108
|
-
if (value !== undefined) {
|
109
|
-
return value;
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
return Reflect.get(target, property, receiver);
|
114
|
-
},
|
115
|
-
});
|
116
|
-
}
|
117
|
-
|
118
|
-
static random = (filters) => {
|
119
|
-
try {
|
120
|
-
return new UserAgent(filters);
|
121
|
-
} catch (error) {
|
122
|
-
return null;
|
123
|
-
}
|
124
|
-
};
|
125
|
-
|
126
|
-
//
|
127
|
-
// Standard Object Methods
|
128
|
-
//
|
129
|
-
|
130
|
-
[Symbol.toPrimitive] = () => (
|
131
|
-
this.data.userAgent
|
132
|
-
);
|
133
|
-
|
134
|
-
toString = () => (
|
135
|
-
this.data.userAgent
|
136
|
-
);
|
137
|
-
|
138
|
-
random = () => {
|
139
|
-
const userAgent = new UserAgent();
|
140
|
-
setCumulativeWeightIndexPairs(userAgent, this.cumulativeWeightIndexPairs);
|
141
|
-
userAgent.randomize();
|
142
|
-
return userAgent;
|
143
|
-
};
|
144
|
-
|
145
|
-
randomize = () => {
|
146
|
-
// Find a random raw random user agent.
|
147
|
-
const randomNumber = Math.random();
|
148
|
-
const [, index] = this.cumulativeWeightIndexPairs
|
149
|
-
.find(([cumulativeWeight]) => cumulativeWeight > randomNumber);
|
150
|
-
const rawUserAgent = userAgents[index];
|
151
|
-
|
152
|
-
this.data = cloneDeep(rawUserAgent);
|
153
|
-
}
|
154
|
-
}
|
package/src/user-agents.json.gz
DELETED
Binary file
|
package/test/test-user-agent.js
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
import assert from 'assert';
|
2
|
-
|
3
|
-
import UserAgent from '../src/user-agent';
|
4
|
-
|
5
|
-
|
6
|
-
// The randomization tests will be repeated once for each element in the range.
|
7
|
-
// We should add a more sophisticated RNG with seeding support for additional testing.
|
8
|
-
const range = Array(1000).fill();
|
9
|
-
|
10
|
-
|
11
|
-
describe('UserAgent', () => {
|
12
|
-
describe('filtering', () => {
|
13
|
-
it('support object properties', () => {
|
14
|
-
const userAgent = new UserAgent({ deviceCategory: 'tablet' });
|
15
|
-
range.forEach(() => {
|
16
|
-
assert(userAgent().deviceCategory === 'tablet');
|
17
|
-
});
|
18
|
-
});
|
19
|
-
|
20
|
-
it('support nested object properties', () => {
|
21
|
-
const userAgent = new UserAgent({ connection: { effectiveType: '4g' }});
|
22
|
-
range.forEach(() => {
|
23
|
-
assert(userAgent().connection.effectiveType === '4g');
|
24
|
-
});
|
25
|
-
});
|
26
|
-
|
27
|
-
it('support multiple object properties', () => {
|
28
|
-
const userAgent = new UserAgent({ deviceCategory: 'mobile', pluginsLength: 0 });
|
29
|
-
range.forEach(() => {
|
30
|
-
const { deviceCategory, pluginsLength } = userAgent();
|
31
|
-
assert(deviceCategory === 'mobile');
|
32
|
-
assert(pluginsLength === 0);
|
33
|
-
});
|
34
|
-
});
|
35
|
-
|
36
|
-
it('support top-level regular expressions', () => {
|
37
|
-
const userAgent = new UserAgent(/Safari/);
|
38
|
-
range.forEach(() => {
|
39
|
-
assert(/Safari/.test(userAgent()));
|
40
|
-
});
|
41
|
-
});
|
42
|
-
|
43
|
-
it('support object property regular expressions', () => {
|
44
|
-
const userAgent = new UserAgent({ userAgent: /Safari/ });
|
45
|
-
range.forEach(() => {
|
46
|
-
assert(/Safari/.test(userAgent()));
|
47
|
-
});
|
48
|
-
});
|
49
|
-
|
50
|
-
it('support top-level arrays', () => {
|
51
|
-
const userAgent = new UserAgent([/Android/, /Linux/]);
|
52
|
-
range.forEach(() => {
|
53
|
-
const randomUserAgent = userAgent();
|
54
|
-
assert(/Android/.test(randomUserAgent) && /Linux/.test(randomUserAgent));
|
55
|
-
});
|
56
|
-
});
|
57
|
-
|
58
|
-
it('support object property arrays', () => {
|
59
|
-
const userAgent = new UserAgent({ deviceCategory: [/(tablet|mobile)/, 'mobile'] });
|
60
|
-
range.forEach(() => {
|
61
|
-
const { deviceCategory } = userAgent();
|
62
|
-
assert(deviceCategory === 'mobile');
|
63
|
-
});
|
64
|
-
});
|
65
|
-
});
|
66
|
-
|
67
|
-
describe('constructor', () => {
|
68
|
-
it('throw an error when no filters match', () => {
|
69
|
-
let storedError;
|
70
|
-
try {
|
71
|
-
const userAgent = new UserAgent({ deviceCategory: 'fake-no-matches' });
|
72
|
-
} catch (error) {
|
73
|
-
storedError = error;
|
74
|
-
}
|
75
|
-
assert(storedError);
|
76
|
-
});
|
77
|
-
});
|
78
|
-
|
79
|
-
describe('static random()', () => {
|
80
|
-
it('return null when no filters match', () => {
|
81
|
-
const userAgent = UserAgent.random({ deviceCategory: 'fake-no-matches' });
|
82
|
-
assert(userAgent === null);
|
83
|
-
});
|
84
|
-
|
85
|
-
it('return a valid user agent when a filter matches', () => {
|
86
|
-
const userAgent = UserAgent.random({ userAgent: /Chrome/ });
|
87
|
-
assert(userAgent.toString().includes('Chrome'));
|
88
|
-
assert(/Chrome/.test(userAgent));
|
89
|
-
});
|
90
|
-
});
|
91
|
-
|
92
|
-
describe('call handler', () => {
|
93
|
-
it('produce new user agents that pass the same filters', () => {
|
94
|
-
const userAgent = UserAgent.random({ userAgent: /Chrome/ });
|
95
|
-
range.forEach(() => {
|
96
|
-
assert(/Chrome/.test(userAgent()));
|
97
|
-
});
|
98
|
-
});
|
99
|
-
});
|
100
|
-
|
101
|
-
describe('cumulativeWeightIndexPairs', () => {
|
102
|
-
it('have a length greater than 100', () => {
|
103
|
-
const userAgent = new UserAgent();
|
104
|
-
assert(userAgent.cumulativeWeightIndexPairs.length > 100);
|
105
|
-
});
|
106
|
-
|
107
|
-
it('have a shorter length when a filter is applied', () => {
|
108
|
-
const userAgent = new UserAgent();
|
109
|
-
const filteredUserAgent = new UserAgent({ deviceCategory: 'mobile' });
|
110
|
-
assert(userAgent.cumulativeWeightIndexPairs.length > filteredUserAgent.cumulativeWeightIndexPairs.length);
|
111
|
-
});
|
112
|
-
});
|
113
|
-
});
|