browserslist / browserslist-ga Goto Github PK
View Code? Open in Web Editor NEWπ¦ Target browsers tailored to your audience using Google Analytics
License: MIT License
π¦ Target browsers tailored to your audience using Google Analytics
License: MIT License
Is there a way to use, say, env variables to update the file in pure headless mode ?
This would allow hooking it in our CI pipeline, thus updating stats every push in the process
I created my own GoogleApp with Client id OAUTH (Web Client and OAuth Client) with this scope/permision:
But, after login and view the "Success! You can close this tab and go back to the terminal." in my terminal show this erro:
{ Error: invalid_request
at createError (/home/luisangonzalez/.npm/_npx/15390/lib/node_modules/browserslist-ga-qdq/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/luisangonzalez/.npm/_npx/15390/lib/node_modules/browserslist-ga-qdq/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/home/luisangonzalez/.npm/_npx/15390/lib/node_modules/browserslist-ga-qdq/node_modules/axios/lib/adapters/http.js:201:11)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickCallback (internal/process/next_tick.js:181:9)
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'google-api-nodejs-client/1.6.1',
'Content-Length': 282 },
method: 'post',
url: 'https://www.googleapis.com/oauth2/v4/token',
data: 'code=4%2FlQD0TYBTL8wWAJS6gYgh_w19ytTOWK8b_u1hzxMkwNDyKoLDDkLE9KxYJNFNLLUuXt07pUspLJ6Lr7T2Rm3DDoo&client_id=827156059949-ev07hth0oqc5bdsjrl822mbqtibobm52.apps.googleusercontent.com&client_secret=&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000&grant_type=authorization_code&code_verifier=' },
request:
ClientRequest {
domain: null,
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'www.googleapis.com',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'www.googleapis.com',
_readableState: [Object],
readable: false,
domain: null,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 518,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: null,
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
write: [Function: writeAfterFIN],
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 166,
[Symbol(bytesRead)]: 480 },
connection:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'www.googleapis.com',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'www.googleapis.com',
_readableState: [Object],
readable: false,
domain: null,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 518,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: null,
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
write: [Function: writeAfterFIN],
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 166,
[Symbol(bytesRead)]: 480 },
_header: 'POST /oauth2/v4/token HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: google-api-nodejs-client/1.6.1\r\nContent-Length: 282\r\nHost: www.googleapis.com\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
maxCachedSessions: 100,
_sessionCache: [Object] },
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/oauth2/v4/token',
_ended: true,
res:
IncomingMessage {
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Object],
connection: [Object],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [Object],
_consuming: true,
_dumped: false,
req: [Circular],
responseUrl: 'https://www.googleapis.com/oauth2/v4/token',
redirects: [],
read: [Function] },
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable:
Writable {
_writableState: [Object],
writable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 282,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl: 'https://www.googleapis.com/oauth2/v4/token' },
[Symbol(outHeadersKey)]:
{ accept: [Array],
'content-type': [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array] } },
response:
{ status: 400,
statusText: 'Bad Request',
headers:
{ 'content-type': 'application/json; charset=utf-8',
vary: 'X-Origin, Referer, Origin,Accept-Encoding',
date: 'Wed, 14 Nov 2018 09:06:27 GMT',
server: 'ESF',
'cache-control': 'private',
'x-xss-protection': '1; mode=block',
'x-frame-options': 'SAMEORIGIN',
'x-content-type-options': 'nosniff',
'alt-svc': 'quic=":443"; ma=2592000; v="44,43,39,35"',
'accept-ranges': 'none',
connection: 'close' },
config:
{ adapter: [Function: httpAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'post',
url: 'https://www.googleapis.com/oauth2/v4/token',
data: 'code=4%2FlQD0TYBTL8wWAJS6gYgh_w19ytTOWK8b_u1hzxMkwNDyKoLDDkLE9KxYJNFNLLUuXt07pUspLJ6Lr7T2Rm3DDoo&client_id=827156059949-ev07hth0oqc5bdsjrl822mbqtibobm52.apps.googleusercontent.com&client_secret=&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000&grant_type=authorization_code&code_verifier=' },
request:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'POST /oauth2/v4/token HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: google-api-nodejs-client/1.6.1\r\nContent-Length: 282\r\nHost: www.googleapis.com\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/oauth2/v4/token',
_ended: true,
res: [Object],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Object],
[Symbol(outHeadersKey)]: [Object] },
data:
{ error: 'invalid_request',
error_description: 'client_secret is missing.' } },
code: '400' }
And not generate .json
--
The older version of googleapis
is currently flagged as a high vulnerability by npmaudit.
https://www.npmjs.com/advisories/791
needs updating to v38
Hi,
I'm currently playing around with browserslist-ga-export which relies on browserslist-ga
under the hood. Because the iOS Safari Usage fix was merged in but not released as a new patch version, usage of browserslist-ga-export
is not correctly parsing iOS Safari.
Can we release a new patch version of browserslist-ga
? I've modified the content in node_modules
to match that PR as a temporary workaround but I'm also writing up steps to reproduce the work for generating the data and this is currently a blocker. Thanks!
When attempting to use the app, with the default BROWSERSLIST_GA_CLIENT_ID
a 400 - Invalid Request error.
The loopback flow has been blocked in order to keep users secure. Follow the Loopback IP Address flow migration guide linked in the developer docs below to migrate your app to an alternative method.
Related developer documentation
From my understand, Google OAuth2 no longer supports redirecting back to http://127.0.0.1
or http://localhost
unless the app is OAuth Client App is specified as Desktop.
I have created my own app and am able to work without issue but it might make sense to update the default OAuth Client ID.
Hello,
I was looking into using this tool but the resulting browserslist-stats.json
data is outputting 0 for all Samsung Internet (SI) versions:
"samsung": {
"4": 0,
"5": 0,
"6.2": 0,
"7.2": 0
},
I'm using this tool to generate the JSON from a CSV rather than login directly. It pulls in the caniuse-parser.js
. I can see the correct SI data in the CSV, just 0 in the output data. I believe this is because Samsung Internet isn't being parsed in this file.
Thanks!
I cloned the repo and ran npx browserlist-ga
as it says in the readme.
This is the response:
# neejoh at mac.local in ~/Applications/Analytics/browserslist-ga on git:master β [8:58:24]
β npx browserlist-ga
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/browserlist-ga - Not found
npm ERR! 404
npm ERR! 404 'browserlist-ga@latest' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/neejoh/.npm/_logs/2020-02-25T07_58_32_952Z-debug.log
Install for [ 'browserlist-ga@latest' ] failed with code 1
Am I missing something?
For some reason the app is blocked by Google, I am getting this error when trying to connect the app to my Google account:
Logging with Google account is temporary blocked for this application
You can't log into the app using Google account because the app has not yet been verified
Or something like that, I have the warning in Russian, so it's not worded exactly like that.
As per browserslist/browserslist#753
I don't know why but when following https://github.com/browserslist/browserslist-ga-export#usage, I get a stats file which has Android 103, and caniuse doesn't recognise that any more.
The suggestion from @ai was for me to manually update it to use and_chr
instead. Perhaps worth updating this package as I'm pretty sure many other users will start running into this issue as well.
Given that https://github.com/browserslist/browserslist-ga-export relies on the parse
method of the caniuse-parser
module, would you consider a pull request to expose/document the method as part of the official API?
I'm not sure if this is a bug but it seems wrong that the output of browserslist-ga does not sum to 100%.
This is then confusing when you run a browserslist query with the using the file with the --stats option
Please find for example the output I had for my website, which sums to ~48.5%
To get those results, I enabled the analytics api for my project and created an oauth client id, then configured my terminal with it and ran npx browserslist-ga
Hello, this tool is great! I'm looking for a way to generate a coverage number based on real usage. But when I import data from GA, the max coverage I can seem to get is around 92%
$ npx browserslist --coverage --stats="./browserslist-stats.json" "cover 100% in my stats"
These browsers account for 92.41% of all users in custom statistics
Which makes sense because the numbers in the stats file don't add up to 100:
> var stats = require('./browserslist-stats.json')
> Object.values(stats).reduce((acc, ss) => acc.concat(Object.values(ss)), []).reduce((acc, n) => acc + n, 0)
92.40687679083089
Is this expected? Where are the 8% are disappearing to?
ios_saf
is current showing 0's for all versions.
A quick glance looks like caniuse_parser.js
is keying on 'iOS Safari' whereas const { agents } = require("caniuse-lite/dist/unpacker/agents"); agents.ios_saf.browser;
is 'Safari on iOS'
The prior fix upgraded only up to v38, the issue is patched in 39.1.0.
$npx browserslist-ga npx: installed 83 in 8.976s Please open this URL in your browser: https://accounts.google.com/o/oauth2/v2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.readonly&response_type=code&client_id=343796874716-6k918h5uajk7k3apdua9n8m6her4igv7.apps.googleusercontent.com&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000
Open URL, log into Google:
Sign in with Google temporarily disabled for this app This app has not been verified yet by Google in order to use Google Sign In.
This stat parsing is not correct:
Windows,7,Chrome,79.0.3945.130,desktop,401Β 754
Speed fix: re-save csv file without space delimiters
Windows,7,Chrome,79.0.3945.130,desktop,401754
It would be useful to know the date the browserlist was generated from GA data.
Could the generated browserslist-stats.json
file have a timestamp field at the top indicating when it was generated? Or would there be a better way to know what date a particular browserlist's data was made from?
According to official documentation, Universal Analytics is going to be deprecated and replaced by GA4 by 1st July 2023. This means that the current solution for browserslist-ga is going to be deprecated too since it depends on GA3.
I have started working on a PR which uses GA4, however I have noticed that the new Google Analytics Data API does not include the browser version dimension in the API (see Stack Overflow).
Is there a known solution or workaround? Is this project going to be deprecated?
Thank you. π
Supposing that a user does not want to log in to a Google account to take advantage of this tool, is there a possibility that this tool could allow the user to specify the path to a manually exported Google Analytics custom report instead, e.g., browserslist-ga --path my-manual-ga-export.csv
? Google Analytics allows exporting of custom reports, so I imagine this tool's documentation could specify the format of said custom report such that this tool could expect and work with a predictable format when the path is specified.
Hello, first off, this is a great tool and it's super easy to use - thank you.
I would love it if the tool could get stats based on something other than sessions? For example, I know that we have to support IE9 because we have a significant amount of revenue attached to a customer who cannot upgrade. This revenue is shown in GA as a goal value, so could the percentages be calculated based on that instead?
What is the reasoning for the max-results
parameter defined in
browserslist-ga/src/google-analytics.js
Line 54 in 60a4848
sort
parameter defined in browserslist-ga/src/google-analytics.js
Line 53 in 60a4848
Following up on browserslist/browserslist-ga-export#3, the isMobile dimension being used by this package is deprecated per https://developers.google.com/analytics/devguides/reporting/core/v2/changelog.
I believe changes would only need to be made in the following places:
https://developers.google.com/analytics/devguides/reporting/core/v2/changelog suggests that it would be as simple as replacing ga:isMobile
with ga:deviceCategory
and entry[4] == "Yes"
with entry[4] == "mobile"
, but https://analyticscanvas.com/google-analytics-ismobile-istablet-left-building/ suggests that it should be entry[4] == "mobile" || entry[4] == "tablet"
. Perhaps further research is needed on this point...
Also, the deprecation notice was published back in 2012 and according to the data deprecation policy, the dimension should have been removed a while ago, so not sure how urgent this is... but there does seem to be at least some path to migrating to the new dimension.
Run npx browserlist-ga
and open the link in Google Chrome.
The response from google is the following:
This app is blocked
This app tried to access sensitive info on your Google Account. To Keep your account safe, Google blocked this access.
I found a thread on google support where someone was having the same issue on another app.
https://support.google.com/accounts/thread/141200979?hl=en
Imagine this situation. The website did not take care of transpiling for the correct audience in the first place, and therefore it only sees users with the latest browsers in its analytics, because everyone else simply can't use it.
Based on the collected analytics data, > 0.5% in my stats
would only make the matters worse, wouldn't it?
https://github.com/browserslist/browserslist-ga/blob/master/src/google-analytics.js#L54
does it mean βmaximum number of results, where results are grouped by these dimensions", or "maximum number of results, where each row is a users/session/pageview"β?
Not sure if you want to add a note to the readme to say that this requires npm >= 7 or if you want to update to remove/polyfill Object.entries?
@dmfrancisco We need to update randomatic
Hi, @dmfrancisco, I have reported a vulnerability issue in package google-p12-pem.
As far as I am aware, vulnerability(high severity) CVE-2020-7720 detected in package node-forge(<0.10.0) is directly referenced by Β [email protected], on which your package [email protected] transitively depends. As such, this vulnerability can also affect [email protected] via the following path:
[email protected] β [email protected] β [email protected] β [email protected] β [email protected] β [email protected](vulnerable version)
Since google-p12-pem has released a new patched version [email protected] to resolve this issue ([email protected] β [email protected](fix version)), then this vulnerability patch can be automatically propagated into your project only if you update your package-lock.json file (delete package-lock.json and re-execute npm install command):
[email protected] β [email protected] β [email protected] β [email protected] β [email protected] β [email protected](vulnerability fix version)
.
A warm tip.^_^
Best regards,
Paimon
I moved Browserslist and Browserslist Example to Browserslist org.
Letβs move browserslist-ga
too :).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.