From 9de35bbd8f3c0fe82501dbb2885bb934d83a4df7 Mon Sep 17 00:00:00 2001 From: Simon Fishel Date: Sat, 30 Dec 2023 21:12:02 -0800 Subject: [PATCH 01/17] Adds user agent header for requests to Green Web Foundation APIs the version from package.json is used to inject an environment variable when esbuild runs, which the running code can read to create the correct user agent string when making requests. fixes #181 --- .esbuild.browser.js | 3 +++ .esbuild.common.js | 7 +++++++ .esbuild.esm.js | 4 +++- .esbuild.node.js | 2 ++ src/helpers/index.js | 11 ++++++++++- src/hosting-api.js | 11 +++++++++-- src/hosting-api.test.js | 17 +++++++++++++++++ src/hosting-node.js | 33 +++++++++++++++++++-------------- src/hosting.test.js | 17 +++++++++++++++++ 9 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 .esbuild.common.js diff --git a/.esbuild.browser.js b/.esbuild.browser.js index 8fee5a6..d50dd28 100644 --- a/.esbuild.browser.js +++ b/.esbuild.browser.js @@ -1,4 +1,7 @@ +const esbuildCommon = require("./.esbuild.common"); + require('esbuild').buildSync({ + ...esbuildCommon, entryPoints: ['src/index.js'], outdir: 'dist/iife', globalName: 'co2', diff --git a/.esbuild.common.js b/.esbuild.common.js new file mode 100644 index 0000000..b93abed --- /dev/null +++ b/.esbuild.common.js @@ -0,0 +1,7 @@ +const CO2JS_VERSION = require("./package.json").version; + +module.exports = { + define: { + "process.env.CO2JS_VERSION": JSON.stringify(CO2JS_VERSION), + }, +}; diff --git a/.esbuild.esm.js b/.esbuild.esm.js index a8e4066..327f527 100644 --- a/.esbuild.esm.js +++ b/.esbuild.esm.js @@ -3,7 +3,8 @@ const esbuild = require('esbuild') // For this build however we need to filter out some extra files // that are used for nodejs, but not in browsers, so we use the // library directly instead of using `esbuild-plugin-glob` as a plugin -const glob = require('tiny-glob'); +const glob = require('tiny-glob') +const esbuildCommon = require('./.esbuild.common') async function main() { const results = await glob('src/**/!(*.test.js|test-constants.js|!(*.js))') @@ -12,6 +13,7 @@ async function main() { const justBrowserCompatibleFiles = results.filter(filepath => !filepath.endsWith('node.js')) esbuild.build({ + ...esbuildCommon, entryPoints: justBrowserCompatibleFiles, bundle: false, minify: false, diff --git a/.esbuild.node.js b/.esbuild.node.js index 8254d8e..6f5991b 100644 --- a/.esbuild.node.js +++ b/.esbuild.node.js @@ -1,7 +1,9 @@ const { globPlugin } = require('esbuild-plugin-glob'); +const esbuildCommon = require('./.esbuild.common'); function main() { require('esbuild').build({ + ...esbuildCommon, entryPoints: ['src/**/!(*.test.js|test-constants.js|!(*.js))'], bundle: false, minify: false, diff --git a/src/helpers/index.js b/src/helpers/index.js index 9faf7ac..9e218f8 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -172,4 +172,13 @@ function parseOptions(options) { return adjustments; } -export { formatNumber, parseOptions }; +/** + * Returns an object containing all the HTTP headers to use when making a request to the Green Web Foundation API. + * + * @returns {import('http').OutgoingHttpHeaders} + */ +function getApiRequestHeaders() { + return { "user-agent": `co2js/${process.env.CO2JS_VERSION}` }; +} + +export { formatNumber, parseOptions, getApiRequestHeaders }; diff --git a/src/hosting-api.js b/src/hosting-api.js index fc75235..2953f82 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -1,5 +1,7 @@ "use strict"; +import { getApiRequestHeaders } from "./helpers"; + /** * Check if a string or array of domains has been provided * @param {string|array} domain - The domain to check, or an array of domains to be checked. @@ -21,7 +23,10 @@ function check(domain) { */ async function checkAgainstAPI(domain) { const req = await fetch( - `https://api.thegreenwebfoundation.org/greencheck/${domain}` + `https://api.thegreenwebfoundation.org/greencheck/${domain}`, + { + headers: getApiRequestHeaders(), + } ); const res = await req.json(); return res.green; @@ -38,7 +43,9 @@ async function checkDomainsAgainstAPI(domains) { const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti"; const domainsString = JSON.stringify(domains); - const req = await fetch(`${apiPath}/${domainsString}`); + const req = await fetch(`${apiPath}/${domainsString}`, { + headers: getApiRequestHeaders(), + }); const allGreenCheckResults = await req.json(); diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 8e5646a..1c7394f 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -3,6 +3,9 @@ import hosting from "./hosting-node.js"; import nock from "nock"; /* eslint-disable jest/no-disabled-tests */ + +process.env.CO2JS_VERSION = "1.2.34"; + describe("hostingAPI", () => { describe("checking a single domain with #check", () => { it.skip("using the API", async () => { @@ -15,6 +18,20 @@ describe("hostingAPI", () => { const res = await hosting.check("google.com"); expect(res).toEqual(true); }); + it("sets the correct user agent header", async () => { + let userAgent; + const scope = nock("https://api.thegreenwebfoundation.org/") + .get("/greencheck/google.com") + .reply(200, function () { + userAgent = this.req.headers["user-agent"]; + return { + url: "google.com", + green: true, + }; + }); + const res = await hosting.check("google.com"); + expect(userAgent).toEqual("co2js/1.2.34"); + }); }); describe("implicitly checking multiple domains with #check", () => { it.skip("using the API", async () => { diff --git a/src/hosting-node.js b/src/hosting-node.js index b1ecda8..189eb54 100644 --- a/src/hosting-node.js +++ b/src/hosting-node.js @@ -11,6 +11,7 @@ This lets us keep the total library small, and dependencies minimal. import https from "https"; import hostingJSON from "./hosting-json.node.js"; +import { getApiRequestHeaders } from "./helpers/index.js"; /** * Accept a url and perform an http request, returning the body @@ -22,22 +23,26 @@ import hostingJSON from "./hosting-json.node.js"; async function getBody(url) { return new Promise(function (resolve, reject) { // Do async job - const req = https.get(url, function (res) { - if (res.statusCode < 200 || res.statusCode >= 300) { - return reject( - new Error( - `Could not get info from: ${url}. Status Code: ${res.statusCode}` - ) - ); - } - const data = []; + const req = https.get( + url, + { headers: getApiRequestHeaders() }, + function (res) { + if (res.statusCode < 200 || res.statusCode >= 300) { + return reject( + new Error( + `Could not get info from: ${url}. Status Code: ${res.statusCode}` + ) + ); + } + const data = []; - res.on("data", (chunk) => { - data.push(chunk); - }); + res.on("data", (chunk) => { + data.push(chunk); + }); - res.on("end", () => resolve(Buffer.concat(data).toString())); - }); + res.on("end", () => resolve(Buffer.concat(data).toString())); + } + ); req.end(); }); } diff --git a/src/hosting.test.js b/src/hosting.test.js index a280120..e6be283 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -1,12 +1,15 @@ "use strict"; import fs from "fs"; +import https from "https"; import path from "path"; import pagexray from "pagexray"; import hosting from "./hosting-node.js"; +process.env.CO2JS_VERSION = "1.2.34"; + const jsonPath = path.resolve( __dirname, "..", @@ -17,6 +20,7 @@ const jsonPath = path.resolve( describe("hosting", () => { let har; + let httpsGetSpy; beforeEach(() => { har = JSON.parse( fs.readFileSync( @@ -24,6 +28,10 @@ describe("hosting", () => { "utf8" ) ); + httpsGetSpy = jest.spyOn(https, "get"); + }); + afterEach(() => { + jest.restoreAllMocks(); }); describe("checking all domains on a page object with #checkPage", () => { it("returns a list of green domains, when passed a page object", async () => { @@ -57,6 +65,15 @@ describe("hosting", () => { const res = await hosting.check("google.com"); expect(res).toEqual(true); }); + it("sets the correct user agent header", async () => { + await hosting.check("google.com"); + expect(httpsGetSpy).toHaveBeenCalledTimes(1); + expect(httpsGetSpy).toHaveBeenLastCalledWith( + expect.any(String), + expect.objectContaining({ headers: { "user-agent": "co2js/1.2.34" } }), + expect.any(Function) + ); + }); }); describe("checking multiple domains with #check", () => { it("Use the API", async () => { From ac057969b576b1e6eb1903a5c4dc0bf6fd7a58ca Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:35:44 +0800 Subject: [PATCH 02/17] Merged main v0.14.1 commit 02464004d1afff53a4066e88758d61029956060c Author: fershad <27988517+fershad@users.noreply.github.com> Date: Tue Jan 9 16:49:57 2024 +0800 update changelog commit fcd392c2931fa02724234bc268294325aa875f38 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Tue Jan 9 14:29:30 2024 +0800 0.14.1 commit 6555fcf645dd1546c50bfa4e2a87b0a7e5b3c984 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Tue Jan 9 14:12:01 2024 +0800 build with latest grid intensity figures commit 27e2ed442c4d7c824115ba57569f29732d17142a Merge: b22bf09 e53da02 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 12:14:09 2024 +0800 Use type definitions from DefinitelyTyped. commit e53da02e085ee1d08c5e0bf9db11b62c4f2dc511 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 12:09:31 2024 +0800 change title commit bf834fe8225dc8a778a7252069d12c844733aab2 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 12:06:51 2024 +0800 add links to npm and DT commit b5d88284b0e939bb0fceb7fc4605124f2fa7c7d8 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 12:03:02 2024 +0800 add guidance on installing type definitions commit ff6f767fac8a263eaba578329989bc21c09deb16 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 12:02:46 2024 +0800 remove index.d.ts file commit b22bf09bbaef8e5d48df296ab3875c9ebdd5655c Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 11:52:53 2024 +0800 update test constants commit dbd9d7dc9673e72abd0be242fefe2c554a5f1f18 Merge: 49da1c4 d7eca01 Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 8 11:47:56 2024 +0800 Reduce NPM package size commit 49da1c4b013687db527e8344d8c8ef3f0cf53093 Merge: 51b85e2 84384da Author: fershad <27988517+fershad@users.noreply.github.com> Date: Thu Jan 4 16:34:48 2024 +0800 [AUTOMATED] Update average annual grid intensities commit 84384da1df1c5cc65cd547e75e2feed85751049b Author: fershad Date: Wed Jan 3 10:09:01 2024 +0000 Update average annual grid intensities commit d7eca01d3494b2b9ea4a67b27db131af61694ccc Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 1 17:42:28 2024 +0800 commit iife bundle commit f4a68349c4eb0fae6ae32a7bd53cd434863847dd Author: fershad <27988517+fershad@users.noreply.github.com> Date: Mon Jan 1 17:09:36 2024 +0800 add file to ignore when packaging for npm --- .gitignore | 5 +- .npmignore | 20 ++- CHANGELOG.md | 180 ++++++++++++++++----------- README.md | 10 ++ data/output/average-intensities.js | 6 +- data/output/average-intensities.json | 6 +- dist/iife/index.js | 20 +++ index.d.ts | 2 - package-lock.json | 4 +- package.json | 2 +- src/constants/test-constants.js | 2 +- src/data/average-intensities.min.js | 2 +- 12 files changed, 166 insertions(+), 93 deletions(-) create mode 100644 dist/iife/index.js delete mode 100644 index.d.ts diff --git a/.gitignore b/.gitignore index 7db62e0..a7bc4ea 100644 --- a/.gitignore +++ b/.gitignore @@ -95,7 +95,10 @@ url2green.db url2green.json # don't track generated content -dist +dist/* + +# but commit the bundled IIFE code for anyone who needs it +!dist/iife # don't track source maps *.js.map diff --git a/.npmignore b/.npmignore index 5a20065..62a39bb 100644 --- a/.npmignore +++ b/.npmignore @@ -18,11 +18,7 @@ src/constants/test-constants.js # Data # exclude mode data data/** - -# but include the output js / json files -!data/output/** -# and include the minimized versions -!src/data/** +dist/data/** # Local sqlite db url2green.db @@ -30,4 +26,16 @@ url2green.json # don't track workspace files *.vscode -*.code-workspace \ No newline at end of file +*.code-workspace + +# don't publish IIFE files, will commit these to git for people who need them +dist/iife/** + +# don't publish the src folder, that code can be found on github +src/** + +## ignore the fixup file +fixup + +## don't publish the dotfiles +.* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0656d..de072ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,58 +12,92 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 > - **Fixed** for any bug fixes. > - **Security** in case of vulnerabilities. -# Unreleased +## Unreleased -- _(Add a summary of your feature, and if relevant the issue, in your PR for merging into `main`)_ +- Adds user agent header for requests to Green Web Foundation APIs + -# Released +## Released -## [0.13.8] - 2023-10-09 +### [0.14.1] - 2024-01-09 + +#### Changed + +- Removed `index.d.ts` in favour of importing type definitions from `@types/tgwf__co2`. +- Reduce package size by excluding files from publish NPM package. +- Automated monthly update of annual average grid intensity data. + +### [0.13.10] - 2023-12-16 + +#### Changed -- Fix: Properly use value of 0 for system segments and variables in the perByteTrace and perVisitTrace functions. - Automated monthly update of annual average grid intensity data. -## [0.13.7] - 2023-09-13 +### [0.13.9] - 2023-11-07 + +#### Fixed + +- Fix to return expected results when variables with 0 value are passed into function + +#### Changed - Automated monthly update of annual average grid intensity data. -## [0.13.6] - 2023-08-08 +### [0.13.8] - 2023-10-09 + +#### Fixed + +- Properly use value of 0 for system segments and variables in the perByteTrace and perVisitTrace functions. + +#### Changed + +- Automated monthly update of annual average grid intensity data. + +### [0.13.7] - 2023-09-13 + +#### Changed + +- Automated monthly update of annual average grid intensity data. + +### [0.13.6] - 2023-08-08 + +#### Changed - Automated monthly update of annual average grid intensity data. - Create FUNDING.yml to allow sponsor contribution to this project. - Store segment flag on CO2 instance, not models. -## [0.13.5] - 2023-07-5 +### [0.13.5] - 2023-07-5 -### Changed +#### Changed - Automated monthly update of annual average grid intensity data. -## [0.13.4] - 2023-05-24 +### [0.13.4] - 2023-05-24 -### Fixed +#### Fixed - Fixed an error when try to use global grid intensities in IIFE. (PR #147) -## [0.13.3] - 2023-05-18 +### [0.13.3] - 2023-05-18 -### Changed +#### Changed - Updated the global grid intensity constant to use the latest WORLD grid intensity value from Ember. (PR #142) -## [0.13.2] - 2023-04-21 +### [0.13.2] - 2023-04-21 - Fix to ensure that region names that are keys in the average annual grid intensity export are capitalised. -## [0.13.1] - 2023-04-20 +### [0.13.1] - 2023-04-20 -### Fixed +#### Fixed - Fixed the import of average grid intensities in node. (PR #137) -## [0.13.0] - 2023-04-13 +### [0.13.0] - 2023-04-13 -### Changed +#### Changed In PR #135 there were significant changes made to how average annual grid intensities are fetched and generated. @@ -73,59 +107,59 @@ In PR #135 there were significant changes made to how average annual grid intens - Get the _latest_ annual average values for each country/region. - Renamed the average grid intensities export file. -## [0.12.2] - 2023-03-01 +### [0.12.2] - 2023-03-01 -### Added +#### Added - Add a module declaration for use from typescript (PR #131) -### Changed +#### Changed - Updated 2021 average annual grid intensity values (PR #133) -## [0.12.1] - 2023-02-02 +### [0.12.1] - 2023-02-02 -### Fixed +#### Fixed - Removed incorrectly imported constants in tests. -## [0.12.0] - 2023-02-02 +### [0.12.0] - 2023-02-02 -### Added +#### Added - Introduced two new functions `perByteTrace` and `perVisitTrace` which allow developers to pass an options object containing customised values for the constants used in the Sustainable Web Design model. (PR #126) -### Changed +#### Changed - Allowed developers now have the option to return a breakdown of carbon emissions estimates by system segment when using the Sustainable Web Design model. (PR #113) -## [0.11.4] - 2022-12-02 +### [0.11.4] - 2022-12-02 -### Fixed +#### Fixed - Updated the `greenCheckMulti` function to work properly in ESM. (PR #123) -## [0.11.3] - 2022-10-13 +### [0.11.3] - 2022-10-13 -### Fixed +#### Fixed - Corrected the Node export for the hosting raised in issue #110. (PR #118) -## [0.11.2] - 2022-10-11 +### [0.11.2] - 2022-10-11 -### Fixed +#### Fixed - v0.11.x updates increased library size to 17MB + when published to NPM. This has been raised in [#108](https://github.com/thegreenwebfoundation/co2.js/issues/108) and it was found data files were being included in the published package. (PR #117) -## [0.11.1] - 2022-10-07 +### [0.11.1] - 2022-10-07 -### Changed +#### Changed - We have used generic filenames for data files, to avoid any confusion around the data being provided in this library. (PR #112) -## [0.11.0] - 2022-10-03 +### [0.11.0] - 2022-10-03 -### Added +#### Added - Introduced average and marginal carbon intensity data into the library. This comes from [Ember Climate](https://ember-climate.org/data/data-explorer/) (for average carbon intensity data), and [The Green Web Foundation](https://developers.thegreenwebfoundation.org/co2js/data) (marginal intensity data, originally sourced from the UNFCCC - the United Nations Framework Convention on Climate Change). For more, [see our release guide for v0.11](https://www.thegreenwebfoundation.org/news/release-guide-co2-js-v0-11/) about the differences between the kinds of data. See [#64](https://github.com/thegreenwebfoundation/co2.js/issues/64), and [#97](https://github.com/thegreenwebfoundation/co2.js/issues/97) for more. - Added new paths to `import` and `require` the annual, country-level average and marginal carbon intensity data mentioned above like, as javascript objects, or as JSON. See [#104 for more](https://github.com/thegreenwebfoundation/co2.js/issues/104). @@ -133,32 +167,32 @@ In PR #135 there were significant changes made to how average annual grid intens - Introduced scripts to automate the generation of grid intensity data based of Ember & UNFCCC source files. - Introduced a `release:minor` command, to automate the publishing process, to complement `release:patch`. -### Changed +#### Changed - Changed the default model for transfer based CO2 calculations from the _1byte_ model to the _Sustainable Web Design_ model instead. See for guidance on the differences and how to migrate between them. See [#94 for more](https://github.com/thegreenwebfoundation/co2.js/issues/94). - Updated our release commands to generate and format the carbon intensity data as part of the release process. -## [0.10.4] - 2022-08-12 +### [0.10.4] - 2022-08-12 -### Added +#### Added - Introduced a `release:patch` command, to automate the publishing process. This is designed to make sure we always publish the most recent compiled code, by adding a rebuild step that can be easy to forget. -## [0.10.3] - 2022-08-12 +### [0.10.3] - 2022-08-12 -### Added +#### Added - Introduced a new `perVisit()` function for the Sustainable Web Design model, which applies [caching and return visits assumptions](https://sustainablewebdesign.org/calculating-digital-emissions/). -## [0.10.2] - 2022-08-12 +### [0.10.2] - 2022-08-12 - Added the ability to set the model used by CO2.js to the Sustainable Web Design model, using a simple 'swd' string, instead of needing to pass in a class. -## [0.10.1] - 2022-08-01 +### [0.10.1] - 2022-08-01 This release used a version bump as previously we had released v0.10.0 under a pre-release tag. -## [0.10.0] - 2022-06-27 +### [0.10.0] - 2022-06-27 - Added ES import syntax as the main way for handling imports and exports of code within the module. - Changed eslint settings to use later version of ecmascript (2020) @@ -166,104 +200,104 @@ This release used a version bump as previously we had released v0.10.0 under a p - Added more consistent use of the debug logging library in files using the updated import syntax - Fixed the incorrect order of FIRST_TIME_VIEWING_PERCENTAGE and RETURNING_VISITOR_PERCENTAGE constants in the SWD model. This will result in **larger** values for calculations using the sustainable web design, and the default caching assumptions. -## [0.9.0] - 2022-03-28 +### [0.9.0] - 2022-03-28 -### Added +#### Added - Added newly implemented Sustainable Web Design model [thanks @dryden!] - Added new readme page for using both emissions models - Added new source of data to the Sustainable Web Design model from Ember Climate. -### Changed +#### Changed - Changed the CO2 class to accept either the One Byte model or the Sustainable Web Design model -### Fixed +#### Fixed - Fixed various typos. -## [0.8.0] - 2021-11-28 +### [0.8.0] - 2021-11-28 -###  Fixed +####  Fixed - Update further dependencies - Fix embarassing order of magnitude typo in 1byte model (thanks @mstaschik!) -## Added +#### Added - Read JSON blob also as gzipped #44 (thanks @soulgalore) -### Changed +#### Changed - The 1byte model will give different numbers now. It's mentioned in `#fixed` but it's worth repeating. -## [0.7.0] - 2021-11-28 +### [0.7.0] - 2021-11-28 -### Fixed +#### Fixed - Update tests to avoid network requests #50 - Update dependencies across the board -###  Changed +####  Changed - Switch to github actions instead of travis for CI. -## [0.6.1] - 2020-03-15 +### [0.6.1] - 2020-03-15 -### Fixed +#### Fixed - Added the function to load JSON, on the tgwg.hosting object, for use in the sustaiable web sitespeed plugin. -## [0.6.0] - 2020-03-15 +### [0.6.0] - 2020-03-15 -### Added +#### Added - Added the hosting-JSON for running local checks against an array instead of SQLite. -### Changed +#### Changed - Swapped out checking against a sqlite database `hosting-json`in favour of simple array in, - Updated conventions for style - using kebab-cases over CamelCase for naming files -### Removed +#### Removed - Extracted sqlite usage and dependencies into a separate module, `url2green`. This means you no longer need to compile SQLite on install. -## [0.5.0] - 2020-03-03 +### [0.5.0] - 2020-03-03 -### Changed +#### Changed - Updated README - Updated the emissions figured for green energy after further research on methodology with @@JamieBeevor - Incorporated class based CO2 models from @soulgalore - Credit contributors -## [0.4.7] - 2020-03-02 +### [0.4.7] - 2020-03-02 -### Added +#### Added - Added a changelog at last! -## [0.4.6] - 2020-03-01 +### [0.4.6] - 2020-03-01 -### Added +#### Added - Changelog inconsistency section in Bad Practices -## [0.4.4] - 2020-03-01 +### [0.4.4] - 2020-03-01 -### Added +#### Added Added the (currently unused) green byte model. -### Changed +#### Changed Update the 1byte model to use an average of devices, rather than just wifi -## [0.4.3] - 2020-03-01 +### [0.4.3] - 2020-03-01 -### Added +#### Added -### Changed +#### Changed Split hosting API into two separate files (one for sqlite, and one relying on the greencheck API) diff --git a/README.md b/README.md index b702fc7..6c48f18 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # CO2.js + [![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-) + @@ -75,6 +77,14 @@ You can also build the CO2.js library from the source code. To do this: - `dist/esm` - An ES Modules compatible build. - `dist/iife` - An Immediately Invoked Function Expression (IIFE) version of the library. +## TypeScript support + +Type definitions for CO2.js are [published in the DefinitelyTyped project](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tgwf__co2), and are [available on NPM](https://www.npmjs.com/package/@types/tgwf__co2) at `@types/tgwf__co2`. + +If you want to use type definitions in your project, they should be installed as a devDependency. + +`npm install --dev @types/tgwf__co2` + ## Marginal and average emissions intensity data CO2.js includes yearly average grid intensity data from [Ember](https://ember-climate.org/data/data-explorer/), as well as marginal intensity data from the [UNFCCC](https://unfccc.int/) (United Nations Framework Convention on Climate Change). You can find the data in JSON and CommonJS Module format in the `data/output` folder. diff --git a/data/output/average-intensities.js b/data/output/average-intensities.js index 1529205..3ff517a 100644 --- a/data/output/average-intensities.js +++ b/data/output/average-intensities.js @@ -10,7 +10,7 @@ const data = { ARM: 222.68, ABW: 591.4, ASEAN: 508.2, - ASIA: 534.83, + ASIA: 534.89, AUS: 501.7, AUT: 158.22, AZE: 469.58, @@ -131,7 +131,7 @@ const data = { MEX: 423.81, "MIDDLE EAST": 519.92, MDA: 666.67, - MNG: 642.37, + MNG: 749.66, MNE: 392.75, MSR: 1000, MAR: 630.75, @@ -217,7 +217,7 @@ const data = { VNM: 386.49, VGB: 714.29, VIR: 685.71, - WORLD: 437.63, + WORLD: 437.66, YEM: 559.66, ZMB: 84.7, ZWE: 392.28, diff --git a/data/output/average-intensities.json b/data/output/average-intensities.json index 0a624e5..9fdd27e 100644 --- a/data/output/average-intensities.json +++ b/data/output/average-intensities.json @@ -69,7 +69,7 @@ "country_code": "", "country_or_region": "Asia", "year": 2022, - "emissions_intensity_gco2_per_kwh": 534.83 + "emissions_intensity_gco2_per_kwh": 534.89 }, "AUS": { "country_code": "AUS", @@ -795,7 +795,7 @@ "country_code": "MNG", "country_or_region": "Mongolia", "year": 2022, - "emissions_intensity_gco2_per_kwh": 642.37 + "emissions_intensity_gco2_per_kwh": 749.66 }, "MNE": { "country_code": "MNE", @@ -1311,7 +1311,7 @@ "country_code": "", "country_or_region": "World", "year": 2022, - "emissions_intensity_gco2_per_kwh": 437.63 + "emissions_intensity_gco2_per_kwh": 437.66 }, "YEM": { "country_code": "YEM", diff --git a/dist/iife/index.js b/dist/iife/index.js new file mode 100644 index 0000000..ac43d40 --- /dev/null +++ b/dist/iife/index.js @@ -0,0 +1,20 @@ +var co2=(()=>{var A=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var F=(a,e)=>{for(var t in e)A(a,t,{get:e[t],enumerable:!0})},W=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of K(e))!w.call(a,r)&&r!==t&&A(a,r,{get:()=>e[r],enumerable:!(n=U(e,r))||n.enumerable});return a};var Y=a=>W(A({},"__esModule",{value:!0}),a);var ee={};F(ee,{averageIntensity:()=>E,co2:()=>M,default:()=>X,hosting:()=>b,marginalIntensity:()=>V});var T=4883333333333333e-25;var m=class{constructor(e){this.options=e,this.KWH_PER_BYTE_FOR_NETWORK=T}perByte(e,t){if(e<1)return 0;if(t){let r=e*72e-12*0,i=e*T*475;return r+i}let n=72e-12+T;return e*n*519}};var L=m;var C={GIGABYTE:1e9};var H={AFG:120.48,AFRICA:484.7,ALB:23.44,DZA:485.49,ASM:687.5,AGO:195.98,ATG:657.14,ARG:344.31,ARM:222.68,ABW:591.4,ASEAN:508.2,ASIA:534.89,AUS:501.7,AUT:158.22,AZE:469.58,BHS:698.11,BHR:494.02,BGD:574.28,BRB:644.86,BLR:425.9,BEL:167.11,BLZ:484.38,BEN:666.67,BTN:24.44,BOL:335.4,BIH:553.47,BWA:794.52,BRA:102.04,BRN:493.59,BGR:399.72,BFA:611.43,BDI:250,CPV:600,KHM:400.46,CMR:278.26,CAN:125.84,CYM:684.93,CAF:0,TCD:677.42,CHL:332.61,CHN:533.98,COL:163.99,COM:714.29,COG:395.52,COD:25.36,COK:400,CRI:37.21,CIV:410.75,HRV:246.29,CUB:654.68,CYP:589.35,CZE:414.8,DNK:180.42,DJI:666.67,DMA:529.41,DOM:549.8,ECU:183.63,EGY:469.63,SLV:194.23,GNQ:492.96,ERI:688.89,EST:460.26,SWZ:189.19,ETH:25.19,EU:276.63,EUROPE:297.05,FLK:500,FRO:428.57,FJI:289.47,FIN:131.71,FRA:84.88,GUF:254.72,PYF:471.43,G20:442.57,G7:344.31,GAB:397.38,GMB:700,GEO:134.83,DEU:385.39,GHA:361.2,GRC:344.41,GRL:133.33,GRD:714.29,GLP:623.53,GUM:670.33,GTM:304.71,GIN:208.63,GNB:750,GUY:642.28,HTI:606.06,HND:373.96,HKG:609.93,HUN:222.1,ISL:28.56,IND:633.4,IDN:619.03,IRN:487.86,IRQ:531.36,IRL:346.43,ISR:537.57,ITA:372.63,JAM:537.93,JPN:494.86,JOR:391.13,KAZ:635.57,KEN:101.13,KIR:666.67,XKX:767,KWT:574.56,KGZ:104.43,LAO:242.18,"LATIN AMERICA AND CARIBBEAN":237.91,LVA:183.43,LBN:663.1,LSO:20,LBR:304.35,LBY:558.85,LTU:195.7,LUX:162.6,MAC:491.53,MDG:483.25,MWI:133.8,MYS:543.87,MDV:651.52,MLI:463.13,MLT:433.48,MTQ:698.63,MRT:526.6,MUS:611.11,MEX:423.81,"MIDDLE EAST":519.92,MDA:666.67,MNG:749.66,MNE:392.75,MSR:1e3,MAR:630.75,MOZ:126.63,MMR:330.8,NAM:63.69,NRU:750,NPL:24.51,NLD:354.31,NCL:610.12,NZL:105.22,NIC:354.21,NER:622.22,NGA:368.11,"NORTH AMERICA":336.68,PRK:102.42,MKD:543.71,NOR:28.93,OCEANIA:450.73,OECD:341.08,OMN:488.27,PAK:344.16,PSE:465.12,PAN:152.68,PNG:526.75,PRY:25.49,PER:256.51,POL:633.23,PRT:234.61,PRI:612.39,QAT:490.28,REU:519.03,ROU:264.24,RUS:363.68,RWA:294.12,KNA:681.82,LCA:685.71,SPM:800,VCT:500,WSM:470.59,STP:600,SAU:557.78,SEN:523.13,SRB:582.13,SYC:615.39,SLE:47.62,SGP:488.78,SVK:140.14,SVN:237.38,SLB:727.27,SOM:634.15,ZAF:708.21,KOR:437.6,SSD:684.21,ESP:217.42,LKA:501.53,SDN:288.13,SUR:356.44,SWE:45.12,CHE:41.28,SYR:541.17,TWN:560.98,TJK:83.63,TZA:366.75,THA:501.57,PHL:594.45,TGO:460.32,TON:625,TTO:491.41,TUN:469.43,TUR:413.6,TKM:490.19,TCA:703.7,UGA:52.27,UKR:232.74,ARE:407.98,GBR:261.16,USA:368.1,URY:150.13,UZB:505.41,VUT:571.43,VEN:212.48,VNM:386.49,VGB:714.29,VIR:685.71,WORLD:437.66,YEM:559.66,ZMB:84.7,ZWE:392.28},k="average";var E={data:H,type:k};var D=.81,O=.52,S=.14,p=.15,P=.19,c=E.data.WORLD,f=50,g=.75,y=.25,N=.02;var R=a=>parseFloat(a.toFixed(2));function B(a){if(typeof a!="object")throw new Error("Options must be an object");let e={};if(a?.gridIntensity){e.gridIntensity={};let{device:t,dataCenter:n,network:r}=a.gridIntensity;(t||t===0)&&(typeof t=="object"?(E.data[t.country?.toUpperCase()]||(console.warn(`"${t.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. +Falling back to global average grid intensity.`),e.gridIntensity.device={value:c}),e.gridIntensity.device={country:t.country,value:parseFloat(E.data[t.country?.toUpperCase()])}):typeof t=="number"?e.gridIntensity.device={value:t}:(e.gridIntensity.device={value:c},console.warn(`The device grid intensity must be a number or an object. You passed in a ${typeof t}. +Falling back to global average grid intensity.`))),(n||n===0)&&(typeof n=="object"?(E.data[n.country?.toUpperCase()]||(console.warn(`"${n.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. +Falling back to global average grid intensity.`),e.gridIntensity.dataCenter={value:c}),e.gridIntensity.dataCenter={country:n.country,value:parseFloat(E.data[n.country?.toUpperCase()])}):typeof n=="number"?e.gridIntensity.dataCenter={value:n}:(e.gridIntensity.dataCenter={value:c},console.warn(`The data center grid intensity must be a number or an object. You passed in a ${typeof n}. +Falling back to global average grid intensity.`))),(r||r===0)&&(typeof r=="object"?(E.data[r.country?.toUpperCase()]||(console.warn(`"${r.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. Falling back to global average grid intensity. +Falling back to global average grid intensity.`),e.gridIntensity.network={value:c}),e.gridIntensity.network={country:r.country,value:parseFloat(E.data[r.country?.toUpperCase()])}):typeof r=="number"?e.gridIntensity.network={value:r}:(e.gridIntensity.network={value:c},console.warn(`The network grid intensity must be a number or an object. You passed in a ${typeof r}. +Falling back to global average grid intensity.`)))}return(a?.dataReloadRatio||a.dataReloadRatio===0)&&(typeof a.dataReloadRatio=="number"?a.dataReloadRatio>=0&&a.dataReloadRatio<=1?e.dataReloadRatio=a.dataReloadRatio:(e.dataReloadRatio=N,console.warn(`The dataReloadRatio option must be a number between 0 and 1. You passed in ${a.dataReloadRatio}. +Falling back to default value.`)):(e.dataReloadRatio=N,console.warn(`The dataReloadRatio option must be a number. You passed in a ${typeof a.dataReloadRatio}. +Falling back to default value.`))),(a?.firstVisitPercentage||a.firstVisitPercentage===0)&&(typeof a.firstVisitPercentage=="number"?a.firstVisitPercentage>=0&&a.firstVisitPercentage<=1?e.firstVisitPercentage=a.firstVisitPercentage:(e.firstVisitPercentage=g,console.warn(`The firstVisitPercentage option must be a number between 0 and 1. You passed in ${a.firstVisitPercentage}. +Falling back to default value.`)):(e.firstVisitPercentage=g,console.warn(`The firstVisitPercentage option must be a number. You passed in a ${typeof a.firstVisitPercentage}. +Falling back to default value.`))),(a?.returnVisitPercentage||a.returnVisitPercentage===0)&&(typeof a.returnVisitPercentage=="number"?a.returnVisitPercentage>=0&&a.returnVisitPercentage<=1?e.returnVisitPercentage=a.returnVisitPercentage:(e.returnVisitPercentage=y,console.warn(`The returnVisitPercentage option must be a number between 0 and 1. You passed in ${a.returnVisitPercentage}. +Falling back to default value.`)):(e.returnVisitPercentage=y,console.warn(`The returnVisitPercentage option must be a number. You passed in a ${typeof a.returnVisitPercentage}. +Falling back to default value.`))),e}var _=class{constructor(e){this.options=e}energyPerByteByComponent(e){let n=e/C.GIGABYTE*D;return{consumerDeviceEnergy:n*O,networkEnergy:n*S,productionEnergy:n*P,dataCenterEnergy:n*p}}co2byComponent(e,t=c,n={}){let r=c,i=c,o=c,d=c;if(n?.gridIntensity){let{device:s,network:l,dataCenter:I}=n.gridIntensity;(s?.value||s?.value===0)&&(r=s.value),(l?.value||l?.value===0)&&(i=l.value),(I?.value||I?.value===0)&&(o=I.value)}t===!0&&(o=f);let u={};for(let[s,l]of Object.entries(e))s.startsWith("dataCenterEnergy")?u[s.replace("Energy","CO2")]=l*o:s.startsWith("consumerDeviceEnergy")?u[s.replace("Energy","CO2")]=l*r:s.startsWith("networkEnergy")?u[s.replace("Energy","CO2")]=l*i:u[s.replace("Energy","CO2")]=l*d;return u}perByte(e,t=!1,n=!1,r={}){e<1&&(e=0);let i=this.energyPerByteByComponent(e,r);if(typeof t!="boolean")throw new Error(`perByte expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}perVisit(e,t=!1,n=!1,r={}){let i=this.energyPerVisitByComponent(e,r);if(typeof t!="boolean")throw new Error(`perVisit expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}energyPerByte(e){let t=this.energyPerByteByComponent(e);return Object.values(t).reduce((r,i)=>r+i)}energyPerVisitByComponent(e,t={},n=g,r=y,i=N){(t.dataReloadRatio||t.dataReloadRatio===0)&&(i=t.dataReloadRatio),(t.firstVisitPercentage||t.firstVisitPercentage===0)&&(n=t.firstVisitPercentage),(t.returnVisitPercentage||t.returnVisitPercentage===0)&&(r=t.returnVisitPercentage);let o=this.energyPerByteByComponent(e),d={},u=Object.values(o);for(let[s,l]of Object.entries(o))d[`${s} - first`]=l*n,d[`${s} - subsequent`]=l*r*i;return d}energyPerVisit(e){let t=0,n=0,r=Object.entries(this.energyPerVisitByComponent(e));for(let[i,o]of r)i.indexOf("first")>0&&(t+=o);for(let[i,o]of r)i.indexOf("subsequent")>0&&(n+=o);return t+n}emissionsPerVisitInGrams(e,t=c){return R(e*t)}annualEnergyInKwh(e,t=1e3){return e*t*12}annualEmissionsInGrams(e,t=1e3){return e*t*12}annualSegmentEnergy(e){return{consumerDeviceEnergy:R(e*O),networkEnergy:R(e*S),dataCenterEnergy:R(e*p),productionEnergy:R(e*P)}}};var G=_;var h=class{constructor(e){if(this.model=new G,e?.model==="1byte")this.model=new L;else if(e?.model==="swd")this.model=new G;else if(e?.model)throw new Error(`"${e.model}" is not a valid model. Please use "1byte" for the OneByte model, and "swd" for the Sustainable Web Design model. +See https://developers.thegreenwebfoundation.org/co2js/models/ to learn more about the models available in CO2.js.`);this._segment=e?.results==="segment"}perByte(e,t=!1){return this.model.perByte(e,t,this._segment)}perVisit(e,t=!1){if(this.model?.perVisit)return this.model.perVisit(e,t,this._segment);throw new Error(`The perVisit() method is not supported in the model you are using. Try using perByte() instead. +See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`)}perByteTrace(e,t=!1,n={}){let r={};return n&&(r=B(n)),{co2:this.model.perByte(e,t,this._segment,r),green:t,variables:{description:"Below are the variables used to calculate this CO2 estimate.",bytes:e,gridIntensity:{description:"The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",network:r?.gridIntensity?.network?.value??c,dataCenter:t?f:r?.gridIntensity?.dataCenter?.value??c,production:c,device:r?.gridIntensity?.device?.value??c}}}}perVisitTrace(e,t=!1,n={}){if(this.model?.perVisit){let r={};return n&&(r=B(n)),{co2:this.model.perVisit(e,t,this._segment,r),green:t,variables:{description:"Below are the variables used to calculate this CO2 estimate.",bytes:e,gridIntensity:{description:"The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",network:r?.gridIntensity?.network?.value??c,dataCenter:t?f:r?.gridIntensity?.dataCenter?.value??c,production:c,device:r?.gridIntensity?.device?.value??c},dataReloadRatio:r?.dataReloadRatio??.02,firstVisitPercentage:r?.firstVisitPercentage??.75,returnVisitPercentage:r?.returnVisitPercentage??.25}}}else throw new Error(`The perVisitDetailed() method is not supported in the model you are using. Try using perByte() instead. +See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`)}perDomain(e,t){let n=[];for(let r of Object.keys(e.domains)){let i;t&&t.indexOf(r)>-1?i=this.perByte(e.domains[r].transferSize,!0):i=this.perByte(e.domains[r].transferSize),n.push({domain:r,co2:i,transferSize:e.domains[r].transferSize})}return n.sort((r,i)=>i.co2-r.co2),n}perPage(e,t){let n=this.perDomain(e,t),r=0;for(let i of n)r+=i.co2;return r}perContentType(e,t){let n={};for(let i of e.assets){let o=new URL(i.url).domain,d=i.transferSize,u=this.perByte(d,t&&t.indexOf(o)>-1),s=i.type;n[s]||(n[s]={co2:0,transferSize:0}),n[s].co2+=u,n[s].transferSize+=d}let r=[];for(let i of Object.keys(n))r.push({type:i,co2:n[i].co2,transferSize:n[i].transferSize});return r.sort((i,o)=>o.co2-i.co2),r}dirtiestResources(e,t){let n=[];for(let r of e.assets){let i=new URL(r.url).domain,o=r.transferSize,d=this.perByte(o,t&&t.indexOf(i)>-1);n.push({url:r.url,co2:d,transferSize:o})}return n.sort((r,i)=>i.co2-r.co2),n.slice(0,n.length>10?10:n.length)}perParty(e,t){let n=0,r=0,i=e.firstPartyRegEx;for(let o of Object.keys(e.domains))o.match(i)?n+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1):r+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1);return{firstParty:n,thirdParty:r}}};var M=h;function j(a){return typeof a=="string"?x(a):Z(a)}async function x(a){return(await(await fetch(`https://api.thegreenwebfoundation.org/greencheck/${a}`)).json()).green}async function Z(a){try{let e="https://api.thegreenwebfoundation.org/v2/greencheckmulti",t=JSON.stringify(a),r=await(await fetch(`${e}/${t}`)).json();return $(r)}catch{return[]}}function $(a){return Object.entries(a).filter(([n,r])=>r.green).map(([n,r])=>r.url)}var v={check:j};function z(a){return v.check(a)}var b={check:z};var J={AFG:"414",ALB:"0",DZA:"528",ASM:"753",AND:"188",AGO:"1476",AIA:"753",ATG:"753",ARG:"478",ARM:"390",ABW:"753",AUS:"808",AUT:"242",AZE:"534","AZORES (PORTUGAL)":"753",BHS:"753",BHR:"726",BGD:"528",BRB:"749",BLR:"400",BEL:"252",BLZ:"403",BEN:"745",BMU:"753",BTN:"0",BOL:"604",BES:"753",BIH:"1197",BWA:"1486",BRA:"284",VGB:"753",BRN:"681",BGR:"911",BFA:"753",BDI:"414",KHM:"1046",CMR:"659",CAN:"372",CYM:"753",CPV:"753",CAF:"188",TCD:"753","CHANNEL ISLANDS (U.K)":"753",CHL:"657",CHN:"899",COL:"410",COM:"753",COD:"0",COG:"659",COK:"753",CRI:"108",CIV:"466",HRV:"294",CUB:"559",CUW:"876",CYP:"751",CZE:"902",DNK:"362",DJI:"753",DMA:"753",DOM:"601",ECU:"560",EGY:"554",SLV:"547",GNQ:"632",ERI:"915",EST:"1057",SWZ:"0",ETH:"0",FLK:"753",FRO:"753",FJI:"640",FIN:"267",FRA:"158",GUF:"423",PYF:"753",GAB:"946",GMB:"753",GEO:"289",DEU:"650",GHA:"495",GIB:"779",GRC:"507",GRL:"264",GRD:"753",GLP:"753",GUM:"753",GTM:"798",GIN:"753",GNB:"753",GUY:"847",HTI:"1048",HND:"662",HUN:"296",ISL:"0",IND:"951",IDN:"783",IRN:"592",IRQ:"1080",IRL:"380",IMN:"436",ISR:"394",ITA:"414",JAM:"711",JPN:"471",JOR:"529",KAZ:"797",KEN:"574",KIR:"753",PRK:"754",KOR:"555",XKX:"1145",KWT:"675",KGZ:"217",LAO:"1069",LVA:"240",LBN:"794",LSO:"0",LBR:"677",LBY:"668",LIE:"151",LTU:"211",LUX:"220",MDG:"876","MADEIRA (PORTUGAL)":"663",MWI:"489",MYS:"551",MDV:"753",MLI:"1076",MLT:"520",MHL:"753",MTQ:"753",MRT:"753",MUS:"700",MYT:"753",MEX:"531",FSM:"753",MDA:"541",MCO:"158",MNG:"1366",MNE:"899",MSR:"753",MAR:"729",MOZ:"234",MMR:"719",NAM:"355",NRU:"753",NPL:"0",NLD:"326",NCL:"779",NZL:"246",NIC:"675",NER:"772",NGA:"526",NIU:"753",MKD:"851",MNP:"753",NOR:"47",OMN:"479",PAK:"592",PLW:"753",PSE:"719",PAN:"477",PNG:"597",PRY:"0",PER:"473",PHL:"672",POL:"828",PRT:"389",PRI:"596",QAT:"503",REU:"772",ROU:"489",RUS:"476",RWA:"712",SHN:"753",KNA:"753",LCA:"753",MAF:"753",SPM:"753",VCT:"753",WSM:"753",SMR:"414",STP:"753",SAU:"592",SEN:"870",SRB:"1086",SYC:"753",SLE:"489",SGP:"379",SXM:"753",SVK:"332",SVN:"620",SLB:"753",SOM:"753",ZAF:"1070",SSD:"890",ESP:"402",LKA:"731",SDN:"736",SUR:"1029",SWE:"68",CHE:"48",SYR:"713",TWN:"484",TJK:"255",TZA:"531",THA:"450",TLS:"753",TGO:"859",TON:"753",TTO:"559",TUN:"468",TUR:"376",TKM:"927",TCA:"753",TUV:"753",UGA:"279",UKR:"768",ARE:"556",GBR:"380",USA:"416",URY:"174",UZB:"612",VUT:"753",VEN:"711",VNM:"560",VIR:"650",YEM:"807",ZMB:"416",ZWE:"1575","MEMO: EU 27":"409"},Q="marginal",q="2021";var V={data:J,type:Q,year:q};var X={co2:M,hosting:b,averageIntensity:E,marginalIntensity:V};return Y(ee);})(); +//# sourceMappingURL=index.js.map diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 4610950..0000000 --- a/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare module '@tgwf/co2'; - diff --git a/package-lock.json b/package-lock.json index dad42cb..d664b49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tgwf/co2", - "version": "0.13.10", + "version": "0.14.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@tgwf/co2", - "version": "0.13.10", + "version": "0.14.1", "license": "Apache-2.0", "devDependencies": { "@tgwf/url2green": "^0.4.0", diff --git a/package.json b/package.json index bcf9e8f..71503e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tgwf/co2", - "version": "0.13.10", + "version": "0.14.1", "description": "Work out the co2 of your digital services", "main": "dist/cjs/index-node.js", "module": "dist/esm/index.js", diff --git a/src/constants/test-constants.js b/src/constants/test-constants.js index 8daa1f6..c645520 100644 --- a/src/constants/test-constants.js +++ b/src/constants/test-constants.js @@ -12,7 +12,7 @@ export const ONEBYTE = { export const SWD = { MILLION_GREY: 0.35418, MILLION_GREEN: 0.30713, - MILLION_PERVISIT_GREY: 0.26763, + MILLION_PERVISIT_GREY: 0.26765, MILLION_PERVISIT_GREEN: 0.23188, TGWF_GREY_VALUE: 0.24963, diff --git a/src/data/average-intensities.min.js b/src/data/average-intensities.min.js index d66c8e9..23fe097 100644 --- a/src/data/average-intensities.min.js +++ b/src/data/average-intensities.min.js @@ -1 +1 @@ -const data = {"AFG":120.48,"AFRICA":484.7,"ALB":23.44,"DZA":485.49,"ASM":687.5,"AGO":195.98,"ATG":657.14,"ARG":344.31,"ARM":222.68,"ABW":591.4,"ASEAN":508.2,"ASIA":534.83,"AUS":501.7,"AUT":158.22,"AZE":469.58,"BHS":698.11,"BHR":494.02,"BGD":574.28,"BRB":644.86,"BLR":425.9,"BEL":167.11,"BLZ":484.38,"BEN":666.67,"BTN":24.44,"BOL":335.4,"BIH":553.47,"BWA":794.52,"BRA":102.04,"BRN":493.59,"BGR":399.72,"BFA":611.43,"BDI":250,"CPV":600,"KHM":400.46,"CMR":278.26,"CAN":125.84,"CYM":684.93,"CAF":0,"TCD":677.42,"CHL":332.61,"CHN":533.98,"COL":163.99,"COM":714.29,"COG":395.52,"COD":25.36,"COK":400,"CRI":37.21,"CIV":410.75,"HRV":246.29,"CUB":654.68,"CYP":589.35,"CZE":414.8,"DNK":180.42,"DJI":666.67,"DMA":529.41,"DOM":549.8,"ECU":183.63,"EGY":469.63,"SLV":194.23,"GNQ":492.96,"ERI":688.89,"EST":460.26,"SWZ":189.19,"ETH":25.19,"EU":276.63,"EUROPE":297.05,"FLK":500,"FRO":428.57,"FJI":289.47,"FIN":131.71,"FRA":84.88,"GUF":254.72,"PYF":471.43,"G20":442.57,"G7":344.31,"GAB":397.38,"GMB":700,"GEO":134.83,"DEU":385.39,"GHA":361.2,"GRC":344.41,"GRL":133.33,"GRD":714.29,"GLP":623.53,"GUM":670.33,"GTM":304.71,"GIN":208.63,"GNB":750,"GUY":642.28,"HTI":606.06,"HND":373.96,"HKG":609.93,"HUN":222.1,"ISL":28.56,"IND":633.4,"IDN":619.03,"IRN":487.86,"IRQ":531.36,"IRL":346.43,"ISR":537.57,"ITA":372.63,"JAM":537.93,"JPN":494.86,"JOR":391.13,"KAZ":635.57,"KEN":101.13,"KIR":666.67,"XKX":767,"KWT":574.56,"KGZ":104.43,"LAO":242.18,"LATIN AMERICA AND CARIBBEAN":237.91,"LVA":183.43,"LBN":663.1,"LSO":20,"LBR":304.35,"LBY":558.85,"LTU":195.7,"LUX":162.6,"MAC":491.53,"MDG":483.25,"MWI":133.8,"MYS":543.87,"MDV":651.52,"MLI":463.13,"MLT":433.48,"MTQ":698.63,"MRT":526.6,"MUS":611.11,"MEX":423.81,"MIDDLE EAST":519.92,"MDA":666.67,"MNG":642.37,"MNE":392.75,"MSR":1000,"MAR":630.75,"MOZ":126.63,"MMR":330.8,"NAM":63.69,"NRU":750,"NPL":24.51,"NLD":354.31,"NCL":610.12,"NZL":105.22,"NIC":354.21,"NER":622.22,"NGA":368.11,"NORTH AMERICA":336.68,"PRK":102.42,"MKD":543.71,"NOR":28.93,"OCEANIA":450.73,"OECD":341.08,"OMN":488.27,"PAK":344.16,"PSE":465.12,"PAN":152.68,"PNG":526.75,"PRY":25.49,"PER":256.51,"POL":633.23,"PRT":234.61,"PRI":612.39,"QAT":490.28,"REU":519.03,"ROU":264.24,"RUS":363.68,"RWA":294.12,"KNA":681.82,"LCA":685.71,"SPM":800,"VCT":500,"WSM":470.59,"STP":600,"SAU":557.78,"SEN":523.13,"SRB":582.13,"SYC":615.39,"SLE":47.62,"SGP":488.78,"SVK":140.14,"SVN":237.38,"SLB":727.27,"SOM":634.15,"ZAF":708.21,"KOR":437.6,"SSD":684.21,"ESP":217.42,"LKA":501.53,"SDN":288.13,"SUR":356.44,"SWE":45.12,"CHE":41.28,"SYR":541.17,"TWN":560.98,"TJK":83.63,"TZA":366.75,"THA":501.57,"PHL":594.45,"TGO":460.32,"TON":625,"TTO":491.41,"TUN":469.43,"TUR":413.6,"TKM":490.19,"TCA":703.7,"UGA":52.27,"UKR":232.74,"ARE":407.98,"GBR":261.16,"USA":368.1,"URY":150.13,"UZB":505.41,"VUT":571.43,"VEN":212.48,"VNM":386.49,"VGB":714.29,"VIR":685.71,"WORLD":437.63,"YEM":559.66,"ZMB":84.7,"ZWE":392.28}; const type = "average"; export { data, type }; export default { data, type }; \ No newline at end of file +const data = {"AFG":120.48,"AFRICA":484.7,"ALB":23.44,"DZA":485.49,"ASM":687.5,"AGO":195.98,"ATG":657.14,"ARG":344.31,"ARM":222.68,"ABW":591.4,"ASEAN":508.2,"ASIA":534.89,"AUS":501.7,"AUT":158.22,"AZE":469.58,"BHS":698.11,"BHR":494.02,"BGD":574.28,"BRB":644.86,"BLR":425.9,"BEL":167.11,"BLZ":484.38,"BEN":666.67,"BTN":24.44,"BOL":335.4,"BIH":553.47,"BWA":794.52,"BRA":102.04,"BRN":493.59,"BGR":399.72,"BFA":611.43,"BDI":250,"CPV":600,"KHM":400.46,"CMR":278.26,"CAN":125.84,"CYM":684.93,"CAF":0,"TCD":677.42,"CHL":332.61,"CHN":533.98,"COL":163.99,"COM":714.29,"COG":395.52,"COD":25.36,"COK":400,"CRI":37.21,"CIV":410.75,"HRV":246.29,"CUB":654.68,"CYP":589.35,"CZE":414.8,"DNK":180.42,"DJI":666.67,"DMA":529.41,"DOM":549.8,"ECU":183.63,"EGY":469.63,"SLV":194.23,"GNQ":492.96,"ERI":688.89,"EST":460.26,"SWZ":189.19,"ETH":25.19,"EU":276.63,"EUROPE":297.05,"FLK":500,"FRO":428.57,"FJI":289.47,"FIN":131.71,"FRA":84.88,"GUF":254.72,"PYF":471.43,"G20":442.57,"G7":344.31,"GAB":397.38,"GMB":700,"GEO":134.83,"DEU":385.39,"GHA":361.2,"GRC":344.41,"GRL":133.33,"GRD":714.29,"GLP":623.53,"GUM":670.33,"GTM":304.71,"GIN":208.63,"GNB":750,"GUY":642.28,"HTI":606.06,"HND":373.96,"HKG":609.93,"HUN":222.1,"ISL":28.56,"IND":633.4,"IDN":619.03,"IRN":487.86,"IRQ":531.36,"IRL":346.43,"ISR":537.57,"ITA":372.63,"JAM":537.93,"JPN":494.86,"JOR":391.13,"KAZ":635.57,"KEN":101.13,"KIR":666.67,"XKX":767,"KWT":574.56,"KGZ":104.43,"LAO":242.18,"LATIN AMERICA AND CARIBBEAN":237.91,"LVA":183.43,"LBN":663.1,"LSO":20,"LBR":304.35,"LBY":558.85,"LTU":195.7,"LUX":162.6,"MAC":491.53,"MDG":483.25,"MWI":133.8,"MYS":543.87,"MDV":651.52,"MLI":463.13,"MLT":433.48,"MTQ":698.63,"MRT":526.6,"MUS":611.11,"MEX":423.81,"MIDDLE EAST":519.92,"MDA":666.67,"MNG":749.66,"MNE":392.75,"MSR":1000,"MAR":630.75,"MOZ":126.63,"MMR":330.8,"NAM":63.69,"NRU":750,"NPL":24.51,"NLD":354.31,"NCL":610.12,"NZL":105.22,"NIC":354.21,"NER":622.22,"NGA":368.11,"NORTH AMERICA":336.68,"PRK":102.42,"MKD":543.71,"NOR":28.93,"OCEANIA":450.73,"OECD":341.08,"OMN":488.27,"PAK":344.16,"PSE":465.12,"PAN":152.68,"PNG":526.75,"PRY":25.49,"PER":256.51,"POL":633.23,"PRT":234.61,"PRI":612.39,"QAT":490.28,"REU":519.03,"ROU":264.24,"RUS":363.68,"RWA":294.12,"KNA":681.82,"LCA":685.71,"SPM":800,"VCT":500,"WSM":470.59,"STP":600,"SAU":557.78,"SEN":523.13,"SRB":582.13,"SYC":615.39,"SLE":47.62,"SGP":488.78,"SVK":140.14,"SVN":237.38,"SLB":727.27,"SOM":634.15,"ZAF":708.21,"KOR":437.6,"SSD":684.21,"ESP":217.42,"LKA":501.53,"SDN":288.13,"SUR":356.44,"SWE":45.12,"CHE":41.28,"SYR":541.17,"TWN":560.98,"TJK":83.63,"TZA":366.75,"THA":501.57,"PHL":594.45,"TGO":460.32,"TON":625,"TTO":491.41,"TUN":469.43,"TUR":413.6,"TKM":490.19,"TCA":703.7,"UGA":52.27,"UKR":232.74,"ARE":407.98,"GBR":261.16,"USA":368.1,"URY":150.13,"UZB":505.41,"VUT":571.43,"VEN":212.48,"VNM":386.49,"VGB":714.29,"VIR":685.71,"WORLD":437.66,"YEM":559.66,"ZMB":84.7,"ZWE":392.28}; const type = "average"; export { data, type }; export default { data, type }; \ No newline at end of file From e321143ee75e1979ad911babf8dae0803cadc363 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:37:26 +0800 Subject: [PATCH 03/17] capitalise User-Agent --- src/helpers/index.js | 2 +- src/hosting-api.test.js | 2 +- src/hosting.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/index.js b/src/helpers/index.js index 9e218f8..593efb0 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -178,7 +178,7 @@ function parseOptions(options) { * @returns {import('http').OutgoingHttpHeaders} */ function getApiRequestHeaders() { - return { "user-agent": `co2js/${process.env.CO2JS_VERSION}` }; + return { "User-Agent": `co2js/${process.env.CO2JS_VERSION}` }; } export { formatNumber, parseOptions, getApiRequestHeaders }; diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 1c7394f..49e9fdc 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -23,7 +23,7 @@ describe("hostingAPI", () => { const scope = nock("https://api.thegreenwebfoundation.org/") .get("/greencheck/google.com") .reply(200, function () { - userAgent = this.req.headers["user-agent"]; + userAgent = this.req.headers["User-Agent"]; return { url: "google.com", green: true, diff --git a/src/hosting.test.js b/src/hosting.test.js index e6be283..d47e2dc 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -70,7 +70,7 @@ describe("hosting", () => { expect(httpsGetSpy).toHaveBeenCalledTimes(1); expect(httpsGetSpy).toHaveBeenLastCalledWith( expect.any(String), - expect.objectContaining({ headers: { "user-agent": "co2js/1.2.34" } }), + expect.objectContaining({ headers: { "User-Agent": "co2js/1.2.34" } }), expect.any(Function) ); }); From 7657e6ada3c9447d5e71947e7ec31ab88fcc83ed Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:43:33 +0800 Subject: [PATCH 04/17] add additional comment param --- src/helpers/index.js | 5 +++-- src/hosting-api.js | 10 ++++++---- src/hosting-api.test.js | 7 ++++--- src/hosting-node.js | 19 +++++++++++++------ src/hosting.test.js | 7 +++++-- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/helpers/index.js b/src/helpers/index.js index 593efb0..29db234 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -174,11 +174,12 @@ function parseOptions(options) { /** * Returns an object containing all the HTTP headers to use when making a request to the Green Web Foundation API. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * * @returns {import('http').OutgoingHttpHeaders} */ -function getApiRequestHeaders() { - return { "User-Agent": `co2js/${process.env.CO2JS_VERSION}` }; +function getApiRequestHeaders(comment = "") { + return { "User-Agent": `co2js/${process.env.CO2JS_VERSION} ${comment}` }; } export { formatNumber, parseOptions, getApiRequestHeaders }; diff --git a/src/hosting-api.js b/src/hosting-api.js index 2953f82..89aa6d7 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -19,13 +19,14 @@ function check(domain) { /** * Check if a domain is hosted by a green web host by querying the Green Web Foundation API. * @param {string} domain - The domain to check. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host. */ -async function checkAgainstAPI(domain) { +async function checkAgainstAPI(domain, comment) { const req = await fetch( `https://api.thegreenwebfoundation.org/greencheck/${domain}`, { - headers: getApiRequestHeaders(), + headers: getApiRequestHeaders(comment), } ); const res = await req.json(); @@ -35,16 +36,17 @@ async function checkAgainstAPI(domain) { /** * Check if an array of domains is hosted by a green web host by querying the Green Web Foundation API. * @param {array} domains - An array of domains to check. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {array} - An array of domains that are hosted by a green web host. */ -async function checkDomainsAgainstAPI(domains) { +async function checkDomainsAgainstAPI(domains, comment) { try { const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti"; const domainsString = JSON.stringify(domains); const req = await fetch(`${apiPath}/${domainsString}`, { - headers: getApiRequestHeaders(), + headers: getApiRequestHeaders(comment), }); const allGreenCheckResults = await req.json(); diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 49e9fdc..a679353 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -5,6 +5,7 @@ import nock from "nock"; /* eslint-disable jest/no-disabled-tests */ process.env.CO2JS_VERSION = "1.2.34"; +const requestHeaderComment = "Test Runner"; describe("hostingAPI", () => { describe("checking a single domain with #check", () => { @@ -15,7 +16,7 @@ describe("hostingAPI", () => { url: "google.com", green: true, }); - const res = await hosting.check("google.com"); + const res = await hosting.check("google.com", requestHeaderComment); expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { @@ -29,8 +30,8 @@ describe("hostingAPI", () => { green: true, }; }); - const res = await hosting.check("google.com"); - expect(userAgent).toEqual("co2js/1.2.34"); + const res = await hosting.check("google.com", requestHeaderComment); + expect(userAgent).toEqual("co2js/1.2.34 Test Runner"); }); }); describe("implicitly checking multiple domains with #check", () => { diff --git a/src/hosting-node.js b/src/hosting-node.js index 189eb54..72fba87 100644 --- a/src/hosting-node.js +++ b/src/hosting-node.js @@ -18,14 +18,15 @@ import { getApiRequestHeaders } from "./helpers/index.js"; * for parsing as JSON. * * @param {string} url + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @return {string} */ -async function getBody(url) { +async function getBody(url, comment) { return new Promise(function (resolve, reject) { // Do async job const req = https.get( url, - { headers: getApiRequestHeaders() }, + { headers: getApiRequestHeaders(comment) }, function (res) { if (res.statusCode < 200 || res.statusCode >= 300) { return reject( @@ -70,11 +71,15 @@ function check(domain, db) { /** * Check if a domain is hosted by a green web host by querying the Green Web Foundation API. * @param {string} domain - The domain to check. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host. */ -async function checkAgainstAPI(domain) { +async function checkAgainstAPI(domain, comment) { const res = JSON.parse( - await getBody(`https://api.thegreenwebfoundation.org/greencheck/${domain}`) + await getBody( + `https://api.thegreenwebfoundation.org/greencheck/${domain}`, + comment + ) ); return res.green; } @@ -82,15 +87,17 @@ async function checkAgainstAPI(domain) { /** * Check if an array of domains is hosted by a green web host by querying the Green Web Foundation API. * @param {array} domains - An array of domains to check. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {array} - An array of domains that are hosted by a green web host. */ -async function checkDomainsAgainstAPI(domains) { +async function checkDomainsAgainstAPI(domains, comment) { try { const allGreenCheckResults = JSON.parse( await getBody( `https://api.thegreenwebfoundation.org/v2/greencheckmulti/${JSON.stringify( domains - )}` + )}`, + comment ) ); return hostingJSON.greenDomainsFromResults(allGreenCheckResults); diff --git a/src/hosting.test.js b/src/hosting.test.js index d47e2dc..8d5ff0c 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -9,6 +9,7 @@ import pagexray from "pagexray"; import hosting from "./hosting-node.js"; process.env.CO2JS_VERSION = "1.2.34"; +const requestHeaderComment = "Test Runner"; const jsonPath = path.resolve( __dirname, @@ -66,11 +67,13 @@ describe("hosting", () => { expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { - await hosting.check("google.com"); + await hosting.check("google.com", requestHeaderComment); expect(httpsGetSpy).toHaveBeenCalledTimes(1); expect(httpsGetSpy).toHaveBeenLastCalledWith( expect.any(String), - expect.objectContaining({ headers: { "User-Agent": "co2js/1.2.34" } }), + expect.objectContaining({ + headers: { "User-Agent": "co2js/1.2.34 Test Runner" }, + }), expect.any(Function) ); }); From 1254eed2f41bea3af47a596f8c216d2164724379 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:51:33 +0800 Subject: [PATCH 05/17] add additional comment param --- src/hosting-api.js | 6 +++--- src/hosting-api.test.js | 4 ++-- src/hosting-node.js | 7 ++++--- src/hosting.js | 4 ++-- src/hosting.test.js | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/hosting-api.js b/src/hosting-api.js index 89aa6d7..b0a47c1 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -7,12 +7,12 @@ import { getApiRequestHeaders } from "./helpers"; * @param {string|array} domain - The domain to check, or an array of domains to be checked. */ -function check(domain) { +function check(domain, comment) { // is it a single domain or an array of them? if (typeof domain === "string") { - return checkAgainstAPI(domain); + return checkAgainstAPI(domain, comment); } else { - return checkDomainsAgainstAPI(domain); + return checkDomainsAgainstAPI(domain, comment); } } diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index a679353..51586d8 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -16,7 +16,7 @@ describe("hostingAPI", () => { url: "google.com", green: true, }); - const res = await hosting.check("google.com", requestHeaderComment); + const res = await hosting.check("google.com", null, requestHeaderComment); expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { @@ -30,7 +30,7 @@ describe("hostingAPI", () => { green: true, }; }); - const res = await hosting.check("google.com", requestHeaderComment); + const res = await hosting.check("google.com", null, requestHeaderComment); expect(userAgent).toEqual("co2js/1.2.34 Test Runner"); }); }); diff --git a/src/hosting-node.js b/src/hosting-node.js index 72fba87..4d94715 100644 --- a/src/hosting-node.js +++ b/src/hosting-node.js @@ -52,19 +52,20 @@ async function getBody(url, comment) { * Check if a domain is hosted by a green web host. * @param {string|array} domain - The domain to check, or an array of domains to be checked. * @param {object} db - Optional. A database object to use for lookups. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {boolean|array} - A boolean if a string was provided, or an array of booleans if an array of domains was provided. */ -function check(domain, db) { +function check(domain, db, comment) { if (db) { return hostingJSON.check(domain, db); } // is it a single domain or an array of them? if (typeof domain === "string") { - return checkAgainstAPI(domain); + return checkAgainstAPI(domain, comment); } else { - return checkDomainsAgainstAPI(domain); + return checkDomainsAgainstAPI(domain, comment); } } diff --git a/src/hosting.js b/src/hosting.js index e9974f1..d04eb68 100644 --- a/src/hosting.js +++ b/src/hosting.js @@ -7,8 +7,8 @@ import hostingAPI from "./hosting-api.js"; * @param {string|array} domain - The domain to check, or an array of domains to be checked. * @returns {boolean|array} - A boolean if a string was provided, or an array of booleans if an array of domains was provided. */ -function check(domain) { - return hostingAPI.check(domain); +function check(domain, comment) { + return hostingAPI.check(domain, comment); } export default { diff --git a/src/hosting.test.js b/src/hosting.test.js index 8d5ff0c..bfc788f 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -67,7 +67,7 @@ describe("hosting", () => { expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { - await hosting.check("google.com", requestHeaderComment); + await hosting.check("google.com", null, requestHeaderComment); expect(httpsGetSpy).toHaveBeenCalledTimes(1); expect(httpsGetSpy).toHaveBeenLastCalledWith( expect.any(String), From 0b0c8234db817c972f0f5184e1a76ac9c354127b Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:28:30 +0800 Subject: [PATCH 06/17] spy on https.get, remove nock --- src/hosting-api.test.js | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 51586d8..2a7e36d 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -1,37 +1,35 @@ "use strict"; import hosting from "./hosting-node.js"; -import nock from "nock"; +import https from "https"; /* eslint-disable jest/no-disabled-tests */ process.env.CO2JS_VERSION = "1.2.34"; const requestHeaderComment = "Test Runner"; describe("hostingAPI", () => { + let httpsGetSpy; + beforeEach(() => { + httpsGetSpy = jest.spyOn(https, "get"); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); describe("checking a single domain with #check", () => { it.skip("using the API", async () => { - const scope = nock("https://api.thegreenwebfoundation.org/") - .get("/greencheck/google.com") - .reply(200, { - url: "google.com", - green: true, - }); const res = await hosting.check("google.com", null, requestHeaderComment); expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { - let userAgent; - const scope = nock("https://api.thegreenwebfoundation.org/") - .get("/greencheck/google.com") - .reply(200, function () { - userAgent = this.req.headers["User-Agent"]; - return { - url: "google.com", - green: true, - }; - }); const res = await hosting.check("google.com", null, requestHeaderComment); - expect(userAgent).toEqual("co2js/1.2.34 Test Runner"); + expect(httpsGetSpy).toHaveBeenCalledTimes(1); + expect(httpsGetSpy).toHaveBeenLastCalledWith( + expect.any(String), + expect.objectContaining({ + headers: { "User-Agent": "co2js/1.2.34 Test Runner" }, + }), + expect.any(Function) + ); }); }); describe("implicitly checking multiple domains with #check", () => { From 00aa63b833e3af4879ef661d7475916bfb6b98ef Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:31:52 +0800 Subject: [PATCH 07/17] remove nock --- src/hosting-api.test.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 2a7e36d..1c2bbc2 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -34,18 +34,6 @@ describe("hostingAPI", () => { }); describe("implicitly checking multiple domains with #check", () => { it.skip("using the API", async () => { - const scope = nock("https://api.thegreenwebfoundation.org/") - .get("/v2/greencheckmulti/[%22google.com%22,%22kochindustries.com%22]") - .reply(200, { - "google.com": { - url: "google.com", - green: true, - }, - "kochindustries.com": { - url: "kochindustries.com", - green: null, - }, - }); const res = await hosting.check(["google.com", "kochindustries.com"]); expect(res).toContain("google.com"); }); From d924f5a8ea300d918c5c2bd6a6dfe13a5f02116a Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:43:38 +0800 Subject: [PATCH 08/17] add npm version hook --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 71503e6..51abf65 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "intensity-data:average": "node data/functions/generate_average_co2.js", "intensity-data:marginal": "node data/functions/generate_marginal_co2.js", "intensity-data": "npm run intensity-data:average && npm run intensity-data:marginal && npm run format-data", - "format-data": "cd data && prettier --write '**/*.{js,json}'" + "format-data": "cd data && prettier --write '**/*.{js,json}'", + "version": "npm run build" }, "keywords": [ "sustainability", @@ -75,4 +76,4 @@ "type": "git", "url": "https://github.com/thegreenwebfoundation/co2.js.git" } -} +} \ No newline at end of file From d1f3952cd31c8ac50cd3c3128b611e03dfcda563 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:27:16 +0800 Subject: [PATCH 09/17] specify filename for import --- src/hosting-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hosting-api.js b/src/hosting-api.js index b0a47c1..7a15a80 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -1,6 +1,6 @@ "use strict"; -import { getApiRequestHeaders } from "./helpers"; +import { getApiRequestHeaders } from "./helpers/index.js"; /** * Check if a string or array of domains has been provided From 7351266559b32c73f547c94ad4b76593f90c4f56 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:29:00 +0800 Subject: [PATCH 10/17] concatinate test string --- src/hosting-api.test.js | 4 ++-- src/hosting.test.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 1c2bbc2..c8a7a9f 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -5,7 +5,7 @@ import https from "https"; /* eslint-disable jest/no-disabled-tests */ process.env.CO2JS_VERSION = "1.2.34"; -const requestHeaderComment = "Test Runner"; +const requestHeaderComment = "TestRunner"; describe("hostingAPI", () => { let httpsGetSpy; @@ -26,7 +26,7 @@ describe("hostingAPI", () => { expect(httpsGetSpy).toHaveBeenLastCalledWith( expect.any(String), expect.objectContaining({ - headers: { "User-Agent": "co2js/1.2.34 Test Runner" }, + headers: { "User-Agent": "co2js/1.2.34 TestRunner" }, }), expect.any(Function) ); diff --git a/src/hosting.test.js b/src/hosting.test.js index bfc788f..dcc7b9d 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -9,7 +9,7 @@ import pagexray from "pagexray"; import hosting from "./hosting-node.js"; process.env.CO2JS_VERSION = "1.2.34"; -const requestHeaderComment = "Test Runner"; +const requestHeaderComment = "TestRunner"; const jsonPath = path.resolve( __dirname, @@ -72,7 +72,7 @@ describe("hosting", () => { expect(httpsGetSpy).toHaveBeenLastCalledWith( expect.any(String), expect.objectContaining({ - headers: { "User-Agent": "co2js/1.2.34 Test Runner" }, + headers: { "User-Agent": "co2js/1.2.34 TestRunner" }, }), expect.any(Function) ); From 9e4839b8f45aa4896317a3c11ba85172f2950c02 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:29:20 +0800 Subject: [PATCH 11/17] update iife dist --- dist/iife/index.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dist/iife/index.js b/dist/iife/index.js index ac43d40..c04008e 100644 --- a/dist/iife/index.js +++ b/dist/iife/index.js @@ -1,20 +1,20 @@ -var co2=(()=>{var A=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var F=(a,e)=>{for(var t in e)A(a,t,{get:e[t],enumerable:!0})},W=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of K(e))!w.call(a,r)&&r!==t&&A(a,r,{get:()=>e[r],enumerable:!(n=U(e,r))||n.enumerable});return a};var Y=a=>W(A({},"__esModule",{value:!0}),a);var ee={};F(ee,{averageIntensity:()=>E,co2:()=>M,default:()=>X,hosting:()=>b,marginalIntensity:()=>V});var T=4883333333333333e-25;var m=class{constructor(e){this.options=e,this.KWH_PER_BYTE_FOR_NETWORK=T}perByte(e,t){if(e<1)return 0;if(t){let r=e*72e-12*0,i=e*T*475;return r+i}let n=72e-12+T;return e*n*519}};var L=m;var C={GIGABYTE:1e9};var H={AFG:120.48,AFRICA:484.7,ALB:23.44,DZA:485.49,ASM:687.5,AGO:195.98,ATG:657.14,ARG:344.31,ARM:222.68,ABW:591.4,ASEAN:508.2,ASIA:534.89,AUS:501.7,AUT:158.22,AZE:469.58,BHS:698.11,BHR:494.02,BGD:574.28,BRB:644.86,BLR:425.9,BEL:167.11,BLZ:484.38,BEN:666.67,BTN:24.44,BOL:335.4,BIH:553.47,BWA:794.52,BRA:102.04,BRN:493.59,BGR:399.72,BFA:611.43,BDI:250,CPV:600,KHM:400.46,CMR:278.26,CAN:125.84,CYM:684.93,CAF:0,TCD:677.42,CHL:332.61,CHN:533.98,COL:163.99,COM:714.29,COG:395.52,COD:25.36,COK:400,CRI:37.21,CIV:410.75,HRV:246.29,CUB:654.68,CYP:589.35,CZE:414.8,DNK:180.42,DJI:666.67,DMA:529.41,DOM:549.8,ECU:183.63,EGY:469.63,SLV:194.23,GNQ:492.96,ERI:688.89,EST:460.26,SWZ:189.19,ETH:25.19,EU:276.63,EUROPE:297.05,FLK:500,FRO:428.57,FJI:289.47,FIN:131.71,FRA:84.88,GUF:254.72,PYF:471.43,G20:442.57,G7:344.31,GAB:397.38,GMB:700,GEO:134.83,DEU:385.39,GHA:361.2,GRC:344.41,GRL:133.33,GRD:714.29,GLP:623.53,GUM:670.33,GTM:304.71,GIN:208.63,GNB:750,GUY:642.28,HTI:606.06,HND:373.96,HKG:609.93,HUN:222.1,ISL:28.56,IND:633.4,IDN:619.03,IRN:487.86,IRQ:531.36,IRL:346.43,ISR:537.57,ITA:372.63,JAM:537.93,JPN:494.86,JOR:391.13,KAZ:635.57,KEN:101.13,KIR:666.67,XKX:767,KWT:574.56,KGZ:104.43,LAO:242.18,"LATIN AMERICA AND CARIBBEAN":237.91,LVA:183.43,LBN:663.1,LSO:20,LBR:304.35,LBY:558.85,LTU:195.7,LUX:162.6,MAC:491.53,MDG:483.25,MWI:133.8,MYS:543.87,MDV:651.52,MLI:463.13,MLT:433.48,MTQ:698.63,MRT:526.6,MUS:611.11,MEX:423.81,"MIDDLE EAST":519.92,MDA:666.67,MNG:749.66,MNE:392.75,MSR:1e3,MAR:630.75,MOZ:126.63,MMR:330.8,NAM:63.69,NRU:750,NPL:24.51,NLD:354.31,NCL:610.12,NZL:105.22,NIC:354.21,NER:622.22,NGA:368.11,"NORTH AMERICA":336.68,PRK:102.42,MKD:543.71,NOR:28.93,OCEANIA:450.73,OECD:341.08,OMN:488.27,PAK:344.16,PSE:465.12,PAN:152.68,PNG:526.75,PRY:25.49,PER:256.51,POL:633.23,PRT:234.61,PRI:612.39,QAT:490.28,REU:519.03,ROU:264.24,RUS:363.68,RWA:294.12,KNA:681.82,LCA:685.71,SPM:800,VCT:500,WSM:470.59,STP:600,SAU:557.78,SEN:523.13,SRB:582.13,SYC:615.39,SLE:47.62,SGP:488.78,SVK:140.14,SVN:237.38,SLB:727.27,SOM:634.15,ZAF:708.21,KOR:437.6,SSD:684.21,ESP:217.42,LKA:501.53,SDN:288.13,SUR:356.44,SWE:45.12,CHE:41.28,SYR:541.17,TWN:560.98,TJK:83.63,TZA:366.75,THA:501.57,PHL:594.45,TGO:460.32,TON:625,TTO:491.41,TUN:469.43,TUR:413.6,TKM:490.19,TCA:703.7,UGA:52.27,UKR:232.74,ARE:407.98,GBR:261.16,USA:368.1,URY:150.13,UZB:505.41,VUT:571.43,VEN:212.48,VNM:386.49,VGB:714.29,VIR:685.71,WORLD:437.66,YEM:559.66,ZMB:84.7,ZWE:392.28},k="average";var E={data:H,type:k};var D=.81,O=.52,S=.14,p=.15,P=.19,c=E.data.WORLD,f=50,g=.75,y=.25,N=.02;var R=a=>parseFloat(a.toFixed(2));function B(a){if(typeof a!="object")throw new Error("Options must be an object");let e={};if(a?.gridIntensity){e.gridIntensity={};let{device:t,dataCenter:n,network:r}=a.gridIntensity;(t||t===0)&&(typeof t=="object"?(E.data[t.country?.toUpperCase()]||(console.warn(`"${t.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +var co2=(()=>{var A=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var W=(a,e)=>{for(var t in e)A(a,t,{get:e[t],enumerable:!0})},Y=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of w(e))!F.call(a,r)&&r!==t&&A(a,r,{get:()=>e[r],enumerable:!(n=K(e,r))||n.enumerable});return a};var H=a=>Y(A({},"__esModule",{value:!0}),a);var te={};W(te,{averageIntensity:()=>R,co2:()=>V,default:()=>ee,hosting:()=>b,marginalIntensity:()=>L});var T=4883333333333333e-25;var m=class{constructor(e){this.options=e,this.KWH_PER_BYTE_FOR_NETWORK=T}perByte(e,t){if(e<1)return 0;if(t){let r=e*72e-12*0,i=e*T*475;return r+i}let n=72e-12+T;return e*n*519}};var D=m;var C={GIGABYTE:1e9};var k={AFG:120.48,AFRICA:484.7,ALB:23.44,DZA:485.49,ASM:687.5,AGO:195.98,ATG:657.14,ARG:344.31,ARM:222.68,ABW:591.4,ASEAN:508.2,ASIA:534.89,AUS:501.7,AUT:158.22,AZE:469.58,BHS:698.11,BHR:494.02,BGD:574.28,BRB:644.86,BLR:425.9,BEL:167.11,BLZ:484.38,BEN:666.67,BTN:24.44,BOL:335.4,BIH:553.47,BWA:794.52,BRA:102.04,BRN:493.59,BGR:399.72,BFA:611.43,BDI:250,CPV:600,KHM:400.46,CMR:278.26,CAN:125.84,CYM:684.93,CAF:0,TCD:677.42,CHL:332.61,CHN:533.98,COL:163.99,COM:714.29,COG:395.52,COD:25.36,COK:400,CRI:37.21,CIV:410.75,HRV:246.29,CUB:654.68,CYP:589.35,CZE:414.8,DNK:180.42,DJI:666.67,DMA:529.41,DOM:549.8,ECU:183.63,EGY:469.63,SLV:194.23,GNQ:492.96,ERI:688.89,EST:460.26,SWZ:189.19,ETH:25.19,EU:276.63,EUROPE:297.05,FLK:500,FRO:428.57,FJI:289.47,FIN:131.71,FRA:84.88,GUF:254.72,PYF:471.43,G20:442.57,G7:344.31,GAB:397.38,GMB:700,GEO:134.83,DEU:385.39,GHA:361.2,GRC:344.41,GRL:133.33,GRD:714.29,GLP:623.53,GUM:670.33,GTM:304.71,GIN:208.63,GNB:750,GUY:642.28,HTI:606.06,HND:373.96,HKG:609.93,HUN:222.1,ISL:28.56,IND:633.4,IDN:619.03,IRN:487.86,IRQ:531.36,IRL:346.43,ISR:537.57,ITA:372.63,JAM:537.93,JPN:494.86,JOR:391.13,KAZ:635.57,KEN:101.13,KIR:666.67,XKX:767,KWT:574.56,KGZ:104.43,LAO:242.18,"LATIN AMERICA AND CARIBBEAN":237.91,LVA:183.43,LBN:663.1,LSO:20,LBR:304.35,LBY:558.85,LTU:195.7,LUX:162.6,MAC:491.53,MDG:483.25,MWI:133.8,MYS:543.87,MDV:651.52,MLI:463.13,MLT:433.48,MTQ:698.63,MRT:526.6,MUS:611.11,MEX:423.81,"MIDDLE EAST":519.92,MDA:666.67,MNG:749.66,MNE:392.75,MSR:1e3,MAR:630.75,MOZ:126.63,MMR:330.8,NAM:63.69,NRU:750,NPL:24.51,NLD:354.31,NCL:610.12,NZL:105.22,NIC:354.21,NER:622.22,NGA:368.11,"NORTH AMERICA":336.68,PRK:102.42,MKD:543.71,NOR:28.93,OCEANIA:450.73,OECD:341.08,OMN:488.27,PAK:344.16,PSE:465.12,PAN:152.68,PNG:526.75,PRY:25.49,PER:256.51,POL:633.23,PRT:234.61,PRI:612.39,QAT:490.28,REU:519.03,ROU:264.24,RUS:363.68,RWA:294.12,KNA:681.82,LCA:685.71,SPM:800,VCT:500,WSM:470.59,STP:600,SAU:557.78,SEN:523.13,SRB:582.13,SYC:615.39,SLE:47.62,SGP:488.78,SVK:140.14,SVN:237.38,SLB:727.27,SOM:634.15,ZAF:708.21,KOR:437.6,SSD:684.21,ESP:217.42,LKA:501.53,SDN:288.13,SUR:356.44,SWE:45.12,CHE:41.28,SYR:541.17,TWN:560.98,TJK:83.63,TZA:366.75,THA:501.57,PHL:594.45,TGO:460.32,TON:625,TTO:491.41,TUN:469.43,TUR:413.6,TKM:490.19,TCA:703.7,UGA:52.27,UKR:232.74,ARE:407.98,GBR:261.16,USA:368.1,URY:150.13,UZB:505.41,VUT:571.43,VEN:212.48,VNM:386.49,VGB:714.29,VIR:685.71,WORLD:437.66,YEM:559.66,ZMB:84.7,ZWE:392.28},j="average";var R={data:k,type:j};var v=.81,O=.52,S=.14,p=.15,P=.19,c=R.data.WORLD,f=50,g=.75,N=.25,y=.02;var E=a=>parseFloat(a.toFixed(2));function B(a){if(typeof a!="object")throw new Error("Options must be an object");let e={};if(a?.gridIntensity){e.gridIntensity={};let{device:t,dataCenter:n,network:r}=a.gridIntensity;(t||t===0)&&(typeof t=="object"?(R.data[t.country?.toUpperCase()]||(console.warn(`"${t.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. -Falling back to global average grid intensity.`),e.gridIntensity.device={value:c}),e.gridIntensity.device={country:t.country,value:parseFloat(E.data[t.country?.toUpperCase()])}):typeof t=="number"?e.gridIntensity.device={value:t}:(e.gridIntensity.device={value:c},console.warn(`The device grid intensity must be a number or an object. You passed in a ${typeof t}. -Falling back to global average grid intensity.`))),(n||n===0)&&(typeof n=="object"?(E.data[n.country?.toUpperCase()]||(console.warn(`"${n.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +Falling back to global average grid intensity.`),e.gridIntensity.device={value:c}),e.gridIntensity.device={country:t.country,value:parseFloat(R.data[t.country?.toUpperCase()])}):typeof t=="number"?e.gridIntensity.device={value:t}:(e.gridIntensity.device={value:c},console.warn(`The device grid intensity must be a number or an object. You passed in a ${typeof t}. +Falling back to global average grid intensity.`))),(n||n===0)&&(typeof n=="object"?(R.data[n.country?.toUpperCase()]||(console.warn(`"${n.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. -Falling back to global average grid intensity.`),e.gridIntensity.dataCenter={value:c}),e.gridIntensity.dataCenter={country:n.country,value:parseFloat(E.data[n.country?.toUpperCase()])}):typeof n=="number"?e.gridIntensity.dataCenter={value:n}:(e.gridIntensity.dataCenter={value:c},console.warn(`The data center grid intensity must be a number or an object. You passed in a ${typeof n}. -Falling back to global average grid intensity.`))),(r||r===0)&&(typeof r=="object"?(E.data[r.country?.toUpperCase()]||(console.warn(`"${r.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. +Falling back to global average grid intensity.`),e.gridIntensity.dataCenter={value:c}),e.gridIntensity.dataCenter={country:n.country,value:parseFloat(R.data[n.country?.toUpperCase()])}):typeof n=="number"?e.gridIntensity.dataCenter={value:n}:(e.gridIntensity.dataCenter={value:c},console.warn(`The data center grid intensity must be a number or an object. You passed in a ${typeof n}. +Falling back to global average grid intensity.`))),(r||r===0)&&(typeof r=="object"?(R.data[r.country?.toUpperCase()]||(console.warn(`"${r.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code. See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. Falling back to global average grid intensity. -Falling back to global average grid intensity.`),e.gridIntensity.network={value:c}),e.gridIntensity.network={country:r.country,value:parseFloat(E.data[r.country?.toUpperCase()])}):typeof r=="number"?e.gridIntensity.network={value:r}:(e.gridIntensity.network={value:c},console.warn(`The network grid intensity must be a number or an object. You passed in a ${typeof r}. -Falling back to global average grid intensity.`)))}return(a?.dataReloadRatio||a.dataReloadRatio===0)&&(typeof a.dataReloadRatio=="number"?a.dataReloadRatio>=0&&a.dataReloadRatio<=1?e.dataReloadRatio=a.dataReloadRatio:(e.dataReloadRatio=N,console.warn(`The dataReloadRatio option must be a number between 0 and 1. You passed in ${a.dataReloadRatio}. -Falling back to default value.`)):(e.dataReloadRatio=N,console.warn(`The dataReloadRatio option must be a number. You passed in a ${typeof a.dataReloadRatio}. +Falling back to global average grid intensity.`),e.gridIntensity.network={value:c}),e.gridIntensity.network={country:r.country,value:parseFloat(R.data[r.country?.toUpperCase()])}):typeof r=="number"?e.gridIntensity.network={value:r}:(e.gridIntensity.network={value:c},console.warn(`The network grid intensity must be a number or an object. You passed in a ${typeof r}. +Falling back to global average grid intensity.`)))}return(a?.dataReloadRatio||a.dataReloadRatio===0)&&(typeof a.dataReloadRatio=="number"?a.dataReloadRatio>=0&&a.dataReloadRatio<=1?e.dataReloadRatio=a.dataReloadRatio:(e.dataReloadRatio=y,console.warn(`The dataReloadRatio option must be a number between 0 and 1. You passed in ${a.dataReloadRatio}. +Falling back to default value.`)):(e.dataReloadRatio=y,console.warn(`The dataReloadRatio option must be a number. You passed in a ${typeof a.dataReloadRatio}. Falling back to default value.`))),(a?.firstVisitPercentage||a.firstVisitPercentage===0)&&(typeof a.firstVisitPercentage=="number"?a.firstVisitPercentage>=0&&a.firstVisitPercentage<=1?e.firstVisitPercentage=a.firstVisitPercentage:(e.firstVisitPercentage=g,console.warn(`The firstVisitPercentage option must be a number between 0 and 1. You passed in ${a.firstVisitPercentage}. Falling back to default value.`)):(e.firstVisitPercentage=g,console.warn(`The firstVisitPercentage option must be a number. You passed in a ${typeof a.firstVisitPercentage}. -Falling back to default value.`))),(a?.returnVisitPercentage||a.returnVisitPercentage===0)&&(typeof a.returnVisitPercentage=="number"?a.returnVisitPercentage>=0&&a.returnVisitPercentage<=1?e.returnVisitPercentage=a.returnVisitPercentage:(e.returnVisitPercentage=y,console.warn(`The returnVisitPercentage option must be a number between 0 and 1. You passed in ${a.returnVisitPercentage}. -Falling back to default value.`)):(e.returnVisitPercentage=y,console.warn(`The returnVisitPercentage option must be a number. You passed in a ${typeof a.returnVisitPercentage}. -Falling back to default value.`))),e}var _=class{constructor(e){this.options=e}energyPerByteByComponent(e){let n=e/C.GIGABYTE*D;return{consumerDeviceEnergy:n*O,networkEnergy:n*S,productionEnergy:n*P,dataCenterEnergy:n*p}}co2byComponent(e,t=c,n={}){let r=c,i=c,o=c,d=c;if(n?.gridIntensity){let{device:s,network:l,dataCenter:I}=n.gridIntensity;(s?.value||s?.value===0)&&(r=s.value),(l?.value||l?.value===0)&&(i=l.value),(I?.value||I?.value===0)&&(o=I.value)}t===!0&&(o=f);let u={};for(let[s,l]of Object.entries(e))s.startsWith("dataCenterEnergy")?u[s.replace("Energy","CO2")]=l*o:s.startsWith("consumerDeviceEnergy")?u[s.replace("Energy","CO2")]=l*r:s.startsWith("networkEnergy")?u[s.replace("Energy","CO2")]=l*i:u[s.replace("Energy","CO2")]=l*d;return u}perByte(e,t=!1,n=!1,r={}){e<1&&(e=0);let i=this.energyPerByteByComponent(e,r);if(typeof t!="boolean")throw new Error(`perByte expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}perVisit(e,t=!1,n=!1,r={}){let i=this.energyPerVisitByComponent(e,r);if(typeof t!="boolean")throw new Error(`perVisit expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}energyPerByte(e){let t=this.energyPerByteByComponent(e);return Object.values(t).reduce((r,i)=>r+i)}energyPerVisitByComponent(e,t={},n=g,r=y,i=N){(t.dataReloadRatio||t.dataReloadRatio===0)&&(i=t.dataReloadRatio),(t.firstVisitPercentage||t.firstVisitPercentage===0)&&(n=t.firstVisitPercentage),(t.returnVisitPercentage||t.returnVisitPercentage===0)&&(r=t.returnVisitPercentage);let o=this.energyPerByteByComponent(e),d={},u=Object.values(o);for(let[s,l]of Object.entries(o))d[`${s} - first`]=l*n,d[`${s} - subsequent`]=l*r*i;return d}energyPerVisit(e){let t=0,n=0,r=Object.entries(this.energyPerVisitByComponent(e));for(let[i,o]of r)i.indexOf("first")>0&&(t+=o);for(let[i,o]of r)i.indexOf("subsequent")>0&&(n+=o);return t+n}emissionsPerVisitInGrams(e,t=c){return R(e*t)}annualEnergyInKwh(e,t=1e3){return e*t*12}annualEmissionsInGrams(e,t=1e3){return e*t*12}annualSegmentEnergy(e){return{consumerDeviceEnergy:R(e*O),networkEnergy:R(e*S),dataCenterEnergy:R(e*p),productionEnergy:R(e*P)}}};var G=_;var h=class{constructor(e){if(this.model=new G,e?.model==="1byte")this.model=new L;else if(e?.model==="swd")this.model=new G;else if(e?.model)throw new Error(`"${e.model}" is not a valid model. Please use "1byte" for the OneByte model, and "swd" for the Sustainable Web Design model. +Falling back to default value.`))),(a?.returnVisitPercentage||a.returnVisitPercentage===0)&&(typeof a.returnVisitPercentage=="number"?a.returnVisitPercentage>=0&&a.returnVisitPercentage<=1?e.returnVisitPercentage=a.returnVisitPercentage:(e.returnVisitPercentage=N,console.warn(`The returnVisitPercentage option must be a number between 0 and 1. You passed in ${a.returnVisitPercentage}. +Falling back to default value.`)):(e.returnVisitPercentage=N,console.warn(`The returnVisitPercentage option must be a number. You passed in a ${typeof a.returnVisitPercentage}. +Falling back to default value.`))),e}function _(a=""){return{"User-Agent":`co2js/0.14.1 ${a}`}}var G=class{constructor(e){this.options=e}energyPerByteByComponent(e){let n=e/C.GIGABYTE*v;return{consumerDeviceEnergy:n*O,networkEnergy:n*S,productionEnergy:n*P,dataCenterEnergy:n*p}}co2byComponent(e,t=c,n={}){let r=c,i=c,o=c,d=c;if(n?.gridIntensity){let{device:s,network:l,dataCenter:I}=n.gridIntensity;(s?.value||s?.value===0)&&(r=s.value),(l?.value||l?.value===0)&&(i=l.value),(I?.value||I?.value===0)&&(o=I.value)}t===!0&&(o=f);let u={};for(let[s,l]of Object.entries(e))s.startsWith("dataCenterEnergy")?u[s.replace("Energy","CO2")]=l*o:s.startsWith("consumerDeviceEnergy")?u[s.replace("Energy","CO2")]=l*r:s.startsWith("networkEnergy")?u[s.replace("Energy","CO2")]=l*i:u[s.replace("Energy","CO2")]=l*d;return u}perByte(e,t=!1,n=!1,r={}){e<1&&(e=0);let i=this.energyPerByteByComponent(e,r);if(typeof t!="boolean")throw new Error(`perByte expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}perVisit(e,t=!1,n=!1,r={}){let i=this.energyPerVisitByComponent(e,r);if(typeof t!="boolean")throw new Error(`perVisit expects a boolean for the carbon intensity value. Received: ${t}`);let o=this.co2byComponent(i,t,r),u=Object.values(o).reduce((s,l)=>s+l);return n?{...o,total:u}:u}energyPerByte(e){let t=this.energyPerByteByComponent(e);return Object.values(t).reduce((r,i)=>r+i)}energyPerVisitByComponent(e,t={},n=g,r=N,i=y){(t.dataReloadRatio||t.dataReloadRatio===0)&&(i=t.dataReloadRatio),(t.firstVisitPercentage||t.firstVisitPercentage===0)&&(n=t.firstVisitPercentage),(t.returnVisitPercentage||t.returnVisitPercentage===0)&&(r=t.returnVisitPercentage);let o=this.energyPerByteByComponent(e),d={},u=Object.values(o);for(let[s,l]of Object.entries(o))d[`${s} - first`]=l*n,d[`${s} - subsequent`]=l*r*i;return d}energyPerVisit(e){let t=0,n=0,r=Object.entries(this.energyPerVisitByComponent(e));for(let[i,o]of r)i.indexOf("first")>0&&(t+=o);for(let[i,o]of r)i.indexOf("subsequent")>0&&(n+=o);return t+n}emissionsPerVisitInGrams(e,t=c){return E(e*t)}annualEnergyInKwh(e,t=1e3){return e*t*12}annualEmissionsInGrams(e,t=1e3){return e*t*12}annualSegmentEnergy(e){return{consumerDeviceEnergy:E(e*O),networkEnergy:E(e*S),dataCenterEnergy:E(e*p),productionEnergy:E(e*P)}}};var h=G;var M=class{constructor(e){if(this.model=new h,e?.model==="1byte")this.model=new D;else if(e?.model==="swd")this.model=new h;else if(e?.model)throw new Error(`"${e.model}" is not a valid model. Please use "1byte" for the OneByte model, and "swd" for the Sustainable Web Design model. See https://developers.thegreenwebfoundation.org/co2js/models/ to learn more about the models available in CO2.js.`);this._segment=e?.results==="segment"}perByte(e,t=!1){return this.model.perByte(e,t,this._segment)}perVisit(e,t=!1){if(this.model?.perVisit)return this.model.perVisit(e,t,this._segment);throw new Error(`The perVisit() method is not supported in the model you are using. Try using perByte() instead. See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`)}perByteTrace(e,t=!1,n={}){let r={};return n&&(r=B(n)),{co2:this.model.perByte(e,t,this._segment,r),green:t,variables:{description:"Below are the variables used to calculate this CO2 estimate.",bytes:e,gridIntensity:{description:"The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",network:r?.gridIntensity?.network?.value??c,dataCenter:t?f:r?.gridIntensity?.dataCenter?.value??c,production:c,device:r?.gridIntensity?.device?.value??c}}}}perVisitTrace(e,t=!1,n={}){if(this.model?.perVisit){let r={};return n&&(r=B(n)),{co2:this.model.perVisit(e,t,this._segment,r),green:t,variables:{description:"Below are the variables used to calculate this CO2 estimate.",bytes:e,gridIntensity:{description:"The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",network:r?.gridIntensity?.network?.value??c,dataCenter:t?f:r?.gridIntensity?.dataCenter?.value??c,production:c,device:r?.gridIntensity?.device?.value??c},dataReloadRatio:r?.dataReloadRatio??.02,firstVisitPercentage:r?.firstVisitPercentage??.75,returnVisitPercentage:r?.returnVisitPercentage??.25}}}else throw new Error(`The perVisitDetailed() method is not supported in the model you are using. Try using perByte() instead. -See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`)}perDomain(e,t){let n=[];for(let r of Object.keys(e.domains)){let i;t&&t.indexOf(r)>-1?i=this.perByte(e.domains[r].transferSize,!0):i=this.perByte(e.domains[r].transferSize),n.push({domain:r,co2:i,transferSize:e.domains[r].transferSize})}return n.sort((r,i)=>i.co2-r.co2),n}perPage(e,t){let n=this.perDomain(e,t),r=0;for(let i of n)r+=i.co2;return r}perContentType(e,t){let n={};for(let i of e.assets){let o=new URL(i.url).domain,d=i.transferSize,u=this.perByte(d,t&&t.indexOf(o)>-1),s=i.type;n[s]||(n[s]={co2:0,transferSize:0}),n[s].co2+=u,n[s].transferSize+=d}let r=[];for(let i of Object.keys(n))r.push({type:i,co2:n[i].co2,transferSize:n[i].transferSize});return r.sort((i,o)=>o.co2-i.co2),r}dirtiestResources(e,t){let n=[];for(let r of e.assets){let i=new URL(r.url).domain,o=r.transferSize,d=this.perByte(o,t&&t.indexOf(i)>-1);n.push({url:r.url,co2:d,transferSize:o})}return n.sort((r,i)=>i.co2-r.co2),n.slice(0,n.length>10?10:n.length)}perParty(e,t){let n=0,r=0,i=e.firstPartyRegEx;for(let o of Object.keys(e.domains))o.match(i)?n+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1):r+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1);return{firstParty:n,thirdParty:r}}};var M=h;function j(a){return typeof a=="string"?x(a):Z(a)}async function x(a){return(await(await fetch(`https://api.thegreenwebfoundation.org/greencheck/${a}`)).json()).green}async function Z(a){try{let e="https://api.thegreenwebfoundation.org/v2/greencheckmulti",t=JSON.stringify(a),r=await(await fetch(`${e}/${t}`)).json();return $(r)}catch{return[]}}function $(a){return Object.entries(a).filter(([n,r])=>r.green).map(([n,r])=>r.url)}var v={check:j};function z(a){return v.check(a)}var b={check:z};var J={AFG:"414",ALB:"0",DZA:"528",ASM:"753",AND:"188",AGO:"1476",AIA:"753",ATG:"753",ARG:"478",ARM:"390",ABW:"753",AUS:"808",AUT:"242",AZE:"534","AZORES (PORTUGAL)":"753",BHS:"753",BHR:"726",BGD:"528",BRB:"749",BLR:"400",BEL:"252",BLZ:"403",BEN:"745",BMU:"753",BTN:"0",BOL:"604",BES:"753",BIH:"1197",BWA:"1486",BRA:"284",VGB:"753",BRN:"681",BGR:"911",BFA:"753",BDI:"414",KHM:"1046",CMR:"659",CAN:"372",CYM:"753",CPV:"753",CAF:"188",TCD:"753","CHANNEL ISLANDS (U.K)":"753",CHL:"657",CHN:"899",COL:"410",COM:"753",COD:"0",COG:"659",COK:"753",CRI:"108",CIV:"466",HRV:"294",CUB:"559",CUW:"876",CYP:"751",CZE:"902",DNK:"362",DJI:"753",DMA:"753",DOM:"601",ECU:"560",EGY:"554",SLV:"547",GNQ:"632",ERI:"915",EST:"1057",SWZ:"0",ETH:"0",FLK:"753",FRO:"753",FJI:"640",FIN:"267",FRA:"158",GUF:"423",PYF:"753",GAB:"946",GMB:"753",GEO:"289",DEU:"650",GHA:"495",GIB:"779",GRC:"507",GRL:"264",GRD:"753",GLP:"753",GUM:"753",GTM:"798",GIN:"753",GNB:"753",GUY:"847",HTI:"1048",HND:"662",HUN:"296",ISL:"0",IND:"951",IDN:"783",IRN:"592",IRQ:"1080",IRL:"380",IMN:"436",ISR:"394",ITA:"414",JAM:"711",JPN:"471",JOR:"529",KAZ:"797",KEN:"574",KIR:"753",PRK:"754",KOR:"555",XKX:"1145",KWT:"675",KGZ:"217",LAO:"1069",LVA:"240",LBN:"794",LSO:"0",LBR:"677",LBY:"668",LIE:"151",LTU:"211",LUX:"220",MDG:"876","MADEIRA (PORTUGAL)":"663",MWI:"489",MYS:"551",MDV:"753",MLI:"1076",MLT:"520",MHL:"753",MTQ:"753",MRT:"753",MUS:"700",MYT:"753",MEX:"531",FSM:"753",MDA:"541",MCO:"158",MNG:"1366",MNE:"899",MSR:"753",MAR:"729",MOZ:"234",MMR:"719",NAM:"355",NRU:"753",NPL:"0",NLD:"326",NCL:"779",NZL:"246",NIC:"675",NER:"772",NGA:"526",NIU:"753",MKD:"851",MNP:"753",NOR:"47",OMN:"479",PAK:"592",PLW:"753",PSE:"719",PAN:"477",PNG:"597",PRY:"0",PER:"473",PHL:"672",POL:"828",PRT:"389",PRI:"596",QAT:"503",REU:"772",ROU:"489",RUS:"476",RWA:"712",SHN:"753",KNA:"753",LCA:"753",MAF:"753",SPM:"753",VCT:"753",WSM:"753",SMR:"414",STP:"753",SAU:"592",SEN:"870",SRB:"1086",SYC:"753",SLE:"489",SGP:"379",SXM:"753",SVK:"332",SVN:"620",SLB:"753",SOM:"753",ZAF:"1070",SSD:"890",ESP:"402",LKA:"731",SDN:"736",SUR:"1029",SWE:"68",CHE:"48",SYR:"713",TWN:"484",TJK:"255",TZA:"531",THA:"450",TLS:"753",TGO:"859",TON:"753",TTO:"559",TUN:"468",TUR:"376",TKM:"927",TCA:"753",TUV:"753",UGA:"279",UKR:"768",ARE:"556",GBR:"380",USA:"416",URY:"174",UZB:"612",VUT:"753",VEN:"711",VNM:"560",VIR:"650",YEM:"807",ZMB:"416",ZWE:"1575","MEMO: EU 27":"409"},Q="marginal",q="2021";var V={data:J,type:Q,year:q};var X={co2:M,hosting:b,averageIntensity:E,marginalIntensity:V};return Y(ee);})(); +See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`)}perDomain(e,t){let n=[];for(let r of Object.keys(e.domains)){let i;t&&t.indexOf(r)>-1?i=this.perByte(e.domains[r].transferSize,!0):i=this.perByte(e.domains[r].transferSize),n.push({domain:r,co2:i,transferSize:e.domains[r].transferSize})}return n.sort((r,i)=>i.co2-r.co2),n}perPage(e,t){let n=this.perDomain(e,t),r=0;for(let i of n)r+=i.co2;return r}perContentType(e,t){let n={};for(let i of e.assets){let o=new URL(i.url).domain,d=i.transferSize,u=this.perByte(d,t&&t.indexOf(o)>-1),s=i.type;n[s]||(n[s]={co2:0,transferSize:0}),n[s].co2+=u,n[s].transferSize+=d}let r=[];for(let i of Object.keys(n))r.push({type:i,co2:n[i].co2,transferSize:n[i].transferSize});return r.sort((i,o)=>o.co2-i.co2),r}dirtiestResources(e,t){let n=[];for(let r of e.assets){let i=new URL(r.url).domain,o=r.transferSize,d=this.perByte(o,t&&t.indexOf(i)>-1);n.push({url:r.url,co2:d,transferSize:o})}return n.sort((r,i)=>i.co2-r.co2),n.slice(0,n.length>10?10:n.length)}perParty(e,t){let n=0,r=0,i=e.firstPartyRegEx;for(let o of Object.keys(e.domains))o.match(i)?n+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1):r+=this.perByte(e.domains[o].transferSize,t&&t.indexOf(o)>-1);return{firstParty:n,thirdParty:r}}};var V=M;function x(a,e){return typeof a=="string"?Z(a,e):$(a,e)}async function Z(a,e){return(await(await fetch(`https://api.thegreenwebfoundation.org/greencheck/${a}`,{headers:_(e)})).json()).green}async function $(a,e){try{let t="https://api.thegreenwebfoundation.org/v2/greencheckmulti",n=JSON.stringify(a),i=await(await fetch(`${t}/${n}`,{headers:_(e)})).json();return z(i)}catch{return[]}}function z(a){return Object.entries(a).filter(([n,r])=>r.green).map(([n,r])=>r.url)}var U={check:x};function J(a,e){return U.check(a,e)}var b={check:J};var Q={AFG:"414",ALB:"0",DZA:"528",ASM:"753",AND:"188",AGO:"1476",AIA:"753",ATG:"753",ARG:"478",ARM:"390",ABW:"753",AUS:"808",AUT:"242",AZE:"534","AZORES (PORTUGAL)":"753",BHS:"753",BHR:"726",BGD:"528",BRB:"749",BLR:"400",BEL:"252",BLZ:"403",BEN:"745",BMU:"753",BTN:"0",BOL:"604",BES:"753",BIH:"1197",BWA:"1486",BRA:"284",VGB:"753",BRN:"681",BGR:"911",BFA:"753",BDI:"414",KHM:"1046",CMR:"659",CAN:"372",CYM:"753",CPV:"753",CAF:"188",TCD:"753","CHANNEL ISLANDS (U.K)":"753",CHL:"657",CHN:"899",COL:"410",COM:"753",COD:"0",COG:"659",COK:"753",CRI:"108",CIV:"466",HRV:"294",CUB:"559",CUW:"876",CYP:"751",CZE:"902",DNK:"362",DJI:"753",DMA:"753",DOM:"601",ECU:"560",EGY:"554",SLV:"547",GNQ:"632",ERI:"915",EST:"1057",SWZ:"0",ETH:"0",FLK:"753",FRO:"753",FJI:"640",FIN:"267",FRA:"158",GUF:"423",PYF:"753",GAB:"946",GMB:"753",GEO:"289",DEU:"650",GHA:"495",GIB:"779",GRC:"507",GRL:"264",GRD:"753",GLP:"753",GUM:"753",GTM:"798",GIN:"753",GNB:"753",GUY:"847",HTI:"1048",HND:"662",HUN:"296",ISL:"0",IND:"951",IDN:"783",IRN:"592",IRQ:"1080",IRL:"380",IMN:"436",ISR:"394",ITA:"414",JAM:"711",JPN:"471",JOR:"529",KAZ:"797",KEN:"574",KIR:"753",PRK:"754",KOR:"555",XKX:"1145",KWT:"675",KGZ:"217",LAO:"1069",LVA:"240",LBN:"794",LSO:"0",LBR:"677",LBY:"668",LIE:"151",LTU:"211",LUX:"220",MDG:"876","MADEIRA (PORTUGAL)":"663",MWI:"489",MYS:"551",MDV:"753",MLI:"1076",MLT:"520",MHL:"753",MTQ:"753",MRT:"753",MUS:"700",MYT:"753",MEX:"531",FSM:"753",MDA:"541",MCO:"158",MNG:"1366",MNE:"899",MSR:"753",MAR:"729",MOZ:"234",MMR:"719",NAM:"355",NRU:"753",NPL:"0",NLD:"326",NCL:"779",NZL:"246",NIC:"675",NER:"772",NGA:"526",NIU:"753",MKD:"851",MNP:"753",NOR:"47",OMN:"479",PAK:"592",PLW:"753",PSE:"719",PAN:"477",PNG:"597",PRY:"0",PER:"473",PHL:"672",POL:"828",PRT:"389",PRI:"596",QAT:"503",REU:"772",ROU:"489",RUS:"476",RWA:"712",SHN:"753",KNA:"753",LCA:"753",MAF:"753",SPM:"753",VCT:"753",WSM:"753",SMR:"414",STP:"753",SAU:"592",SEN:"870",SRB:"1086",SYC:"753",SLE:"489",SGP:"379",SXM:"753",SVK:"332",SVN:"620",SLB:"753",SOM:"753",ZAF:"1070",SSD:"890",ESP:"402",LKA:"731",SDN:"736",SUR:"1029",SWE:"68",CHE:"48",SYR:"713",TWN:"484",TJK:"255",TZA:"531",THA:"450",TLS:"753",TGO:"859",TON:"753",TTO:"559",TUN:"468",TUR:"376",TKM:"927",TCA:"753",TUV:"753",UGA:"279",UKR:"768",ARE:"556",GBR:"380",USA:"416",URY:"174",UZB:"612",VUT:"753",VEN:"711",VNM:"560",VIR:"650",YEM:"807",ZMB:"416",ZWE:"1575","MEMO: EU 27":"409"},q="marginal",X="2021";var L={data:Q,type:q,year:X};var ee={co2:V,hosting:b,averageIntensity:R,marginalIntensity:L};return H(te);})(); //# sourceMappingURL=index.js.map From 45ae7b4963c61f2d6c631b8c7243aab97f7958c3 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:39:49 +0800 Subject: [PATCH 12/17] add JSDoc comments --- src/hosting-api.js | 1 + src/hosting.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/hosting-api.js b/src/hosting-api.js index 7a15a80..bb7809a 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -5,6 +5,7 @@ import { getApiRequestHeaders } from "./helpers/index.js"; /** * Check if a string or array of domains has been provided * @param {string|array} domain - The domain to check, or an array of domains to be checked. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. */ function check(domain, comment) { diff --git a/src/hosting.js b/src/hosting.js index d04eb68..f906f03 100644 --- a/src/hosting.js +++ b/src/hosting.js @@ -5,6 +5,7 @@ import hostingAPI from "./hosting-api.js"; /** * Check if a domain is hosted by a green web host. * @param {string|array} domain - The domain to check, or an array of domains to be checked. + * @param {string} comment - Optional. The app, site, or organisation that is making the request. * @returns {boolean|array} - A boolean if a string was provided, or an array of booleans if an array of domains was provided. */ function check(domain, comment) { From df02bc3aab9b1e15367c6c1f1a9b470ddd925638 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:39:58 +0800 Subject: [PATCH 13/17] use mocks for api tests --- src/hosting-api.test.js | 56 ++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index c8a7a9f..79d7f7d 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -1,40 +1,66 @@ "use strict"; -import hosting from "./hosting-node.js"; -import https from "https"; +import hosting from "./hosting-api.js"; /* eslint-disable jest/no-disabled-tests */ process.env.CO2JS_VERSION = "1.2.34"; const requestHeaderComment = "TestRunner"; +global.fetch = jest.fn(() => + Promise.resolve({ + json: () => Promise.resolve({ green: true }), + }) +); + describe("hostingAPI", () => { - let httpsGetSpy; beforeEach(() => { - httpsGetSpy = jest.spyOn(https, "get"); - }); - afterEach(() => { - jest.restoreAllMocks(); + fetch.mockClear(); }); describe("checking a single domain with #check", () => { - it.skip("using the API", async () => { - const res = await hosting.check("google.com", null, requestHeaderComment); + it("using the API", async () => { + const res = await hosting.check("google.com"); + expect(fetch).toHaveBeenCalledTimes(1); expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { - const res = await hosting.check("google.com", null, requestHeaderComment); - expect(httpsGetSpy).toHaveBeenCalledTimes(1); - expect(httpsGetSpy).toHaveBeenLastCalledWith( + const res = await hosting.check("google.com", requestHeaderComment); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenLastCalledWith( expect.any(String), expect.objectContaining({ headers: { "User-Agent": "co2js/1.2.34 TestRunner" }, - }), - expect.any(Function) + }) ); + expect(res).toEqual(true); }); }); describe("implicitly checking multiple domains with #check", () => { - it.skip("using the API", async () => { + it("using the API", async () => { + fetch.mockImplementation(() => + Promise.resolve({ + json: () => + Promise.resolve({ + "google.com": { url: "google.com", green: true }, + }), + }) + ); const res = await hosting.check(["google.com", "kochindustries.com"]); + expect(fetch).toHaveBeenCalledTimes(1); + expect(res).toContain("google.com"); + }); + it("sets the correct user agent header", async () => { + const res = await hosting.check( + ["google.com", "kochindustries.com"], + requestHeaderComment + ); + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenLastCalledWith( + expect.any(String), + expect.objectContaining({ + headers: { "User-Agent": "co2js/1.2.34 TestRunner" }, + }) + ); expect(res).toContain("google.com"); }); }); From af6eb6cab724b2d532dc1c014c0e413b31ae7780 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:24:35 +0800 Subject: [PATCH 14/17] add check for header without comment --- src/hosting-api.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hosting-api.test.js b/src/hosting-api.test.js index 79d7f7d..fc37294 100644 --- a/src/hosting-api.test.js +++ b/src/hosting-api.test.js @@ -20,6 +20,12 @@ describe("hostingAPI", () => { it("using the API", async () => { const res = await hosting.check("google.com"); expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenLastCalledWith( + expect.any(String), + expect.objectContaining({ + headers: { "User-Agent": "co2js/1.2.34 " }, + }) + ); expect(res).toEqual(true); }); it("sets the correct user agent header", async () => { From 4b5d9625dcd0633b028b8afc55906cb6d49cabc7 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:03:03 +0800 Subject: [PATCH 15/17] mock https.get --- __mocks__/https.js | 28 ++++++++++++++++++++++++++++ src/hosting.test.js | 11 ++++------- 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 __mocks__/https.js diff --git a/__mocks__/https.js b/__mocks__/https.js new file mode 100644 index 0000000..a4cee94 --- /dev/null +++ b/__mocks__/https.js @@ -0,0 +1,28 @@ +import { getApiRequestHeaders } from "../src/helpers/index.js"; +const https = jest.createMockFromModule("https"); +import { Stream } from "stream"; + +const stream = new Stream(); + +https.get.mockImplementation((url, options, callback) => { + url, { headers: getApiRequestHeaders("TestRunner") }, callback(stream); + if (url.includes("greencheckmulti")) { + stream.emit( + "data", + Buffer.from( + `{"google.com": {"url":"google.com","hosted_by":"Google Inc.","hosted_by_website":"https://www.google.com","partner":null,"green":true}}` + ) + ); + } else { + stream.emit( + "data", + Buffer.from( + `{"url":"google.com","hosted_by":"Google Inc.","hosted_by_website":"https://www.google.com","partner":null,"green":true}` + ) + ); + } + + stream.emit("end"); +}); + +module.exports = https; diff --git a/src/hosting.test.js b/src/hosting.test.js index dcc7b9d..666eb83 100644 --- a/src/hosting.test.js +++ b/src/hosting.test.js @@ -8,6 +8,8 @@ import pagexray from "pagexray"; import hosting from "./hosting-node.js"; +jest.mock("https"); + process.env.CO2JS_VERSION = "1.2.34"; const requestHeaderComment = "TestRunner"; @@ -30,9 +32,7 @@ describe("hosting", () => { ) ); httpsGetSpy = jest.spyOn(https, "get"); - }); - afterEach(() => { - jest.restoreAllMocks(); + jest.clearAllMocks(); }); describe("checking all domains on a page object with #checkPage", () => { it("returns a list of green domains, when passed a page object", async () => { @@ -62,7 +62,6 @@ describe("hosting", () => { }); describe("checking a single domain with #check", () => { it("use the API instead", async () => { - const db = await hosting.loadJSON(jsonPath); const res = await hosting.check("google.com"); expect(res).toEqual(true); }); @@ -80,9 +79,7 @@ describe("hosting", () => { }); describe("checking multiple domains with #check", () => { it("Use the API", async () => { - const db = await hosting.loadJSON(jsonPath); - - const res = await hosting.check(["google.com", "kochindustries.com"]); + const res = await hosting.check(["google.com", "pchome.com"]); expect(res).toContain("google.com"); }); }); From 8f23d2703bcdd27f51bf5d23debf9c1a81e52cff Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:43:42 +0800 Subject: [PATCH 16/17] change parameter name to userAgentIdentifier --- src/hosting-api.js | 20 ++++++++++---------- src/hosting-node.js | 26 +++++++++++++------------- src/hosting.js | 6 +++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/hosting-api.js b/src/hosting-api.js index bb7809a..60337a7 100644 --- a/src/hosting-api.js +++ b/src/hosting-api.js @@ -5,29 +5,29 @@ import { getApiRequestHeaders } from "./helpers/index.js"; /** * Check if a string or array of domains has been provided * @param {string|array} domain - The domain to check, or an array of domains to be checked. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. */ -function check(domain, comment) { +function check(domain, userAgentIdentifier) { // is it a single domain or an array of them? if (typeof domain === "string") { - return checkAgainstAPI(domain, comment); + return checkAgainstAPI(domain, userAgentIdentifier); } else { - return checkDomainsAgainstAPI(domain, comment); + return checkDomainsAgainstAPI(domain, userAgentIdentifier); } } /** * Check if a domain is hosted by a green web host by querying the Green Web Foundation API. * @param {string} domain - The domain to check. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host. */ -async function checkAgainstAPI(domain, comment) { +async function checkAgainstAPI(domain, userAgentIdentifier) { const req = await fetch( `https://api.thegreenwebfoundation.org/greencheck/${domain}`, { - headers: getApiRequestHeaders(comment), + headers: getApiRequestHeaders(userAgentIdentifier), } ); const res = await req.json(); @@ -37,17 +37,17 @@ async function checkAgainstAPI(domain, comment) { /** * Check if an array of domains is hosted by a green web host by querying the Green Web Foundation API. * @param {array} domains - An array of domains to check. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {array} - An array of domains that are hosted by a green web host. */ -async function checkDomainsAgainstAPI(domains, comment) { +async function checkDomainsAgainstAPI(domains, userAgentIdentifier) { try { const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti"; const domainsString = JSON.stringify(domains); const req = await fetch(`${apiPath}/${domainsString}`, { - headers: getApiRequestHeaders(comment), + headers: getApiRequestHeaders(userAgentIdentifier), }); const allGreenCheckResults = await req.json(); diff --git a/src/hosting-node.js b/src/hosting-node.js index 4d94715..87eadfb 100644 --- a/src/hosting-node.js +++ b/src/hosting-node.js @@ -18,15 +18,15 @@ import { getApiRequestHeaders } from "./helpers/index.js"; * for parsing as JSON. * * @param {string} url - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @return {string} */ -async function getBody(url, comment) { +async function getBody(url, userAgentIdentifier) { return new Promise(function (resolve, reject) { // Do async job const req = https.get( url, - { headers: getApiRequestHeaders(comment) }, + { headers: getApiRequestHeaders(userAgentIdentifier) }, function (res) { if (res.statusCode < 200 || res.statusCode >= 300) { return reject( @@ -52,34 +52,34 @@ async function getBody(url, comment) { * Check if a domain is hosted by a green web host. * @param {string|array} domain - The domain to check, or an array of domains to be checked. * @param {object} db - Optional. A database object to use for lookups. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {boolean|array} - A boolean if a string was provided, or an array of booleans if an array of domains was provided. */ -function check(domain, db, comment) { +function check(domain, db, userAgentIdentifier) { if (db) { return hostingJSON.check(domain, db); } // is it a single domain or an array of them? if (typeof domain === "string") { - return checkAgainstAPI(domain, comment); + return checkAgainstAPI(domain, userAgentIdentifier); } else { - return checkDomainsAgainstAPI(domain, comment); + return checkDomainsAgainstAPI(domain, userAgentIdentifier); } } /** * Check if a domain is hosted by a green web host by querying the Green Web Foundation API. * @param {string} domain - The domain to check. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host. */ -async function checkAgainstAPI(domain, comment) { +async function checkAgainstAPI(domain, userAgentIdentifier) { const res = JSON.parse( await getBody( `https://api.thegreenwebfoundation.org/greencheck/${domain}`, - comment + userAgentIdentifier ) ); return res.green; @@ -88,17 +88,17 @@ async function checkAgainstAPI(domain, comment) { /** * Check if an array of domains is hosted by a green web host by querying the Green Web Foundation API. * @param {array} domains - An array of domains to check. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {array} - An array of domains that are hosted by a green web host. */ -async function checkDomainsAgainstAPI(domains, comment) { +async function checkDomainsAgainstAPI(domains, userAgentIdentifier) { try { const allGreenCheckResults = JSON.parse( await getBody( `https://api.thegreenwebfoundation.org/v2/greencheckmulti/${JSON.stringify( domains )}`, - comment + userAgentIdentifier ) ); return hostingJSON.greenDomainsFromResults(allGreenCheckResults); diff --git a/src/hosting.js b/src/hosting.js index f906f03..d0020eb 100644 --- a/src/hosting.js +++ b/src/hosting.js @@ -5,11 +5,11 @@ import hostingAPI from "./hosting-api.js"; /** * Check if a domain is hosted by a green web host. * @param {string|array} domain - The domain to check, or an array of domains to be checked. - * @param {string} comment - Optional. The app, site, or organisation that is making the request. + * @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request. * @returns {boolean|array} - A boolean if a string was provided, or an array of booleans if an array of domains was provided. */ -function check(domain, comment) { - return hostingAPI.check(domain, comment); +function check(domain, userAgentIdentifier) { + return hostingAPI.check(domain, userAgentIdentifier); } export default { From c88a8ff097e1c52db6cfb13536b0536fc4f20a47 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:11:02 +0800 Subject: [PATCH 17/17] add sfishel18 --- .all-contributorsrc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 07a31f1..2069729 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -132,8 +132,17 @@ "bug", "code" ] + }, + { + "login": "sfishel18", + "name": "Simon Fishel", + "avatar_url": "https://avatars.githubusercontent.com/u/294695?v=4", + "profile": "https://github.com/sfishel18", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, "linkToUsage": false -} +} \ No newline at end of file