@tgwf/co2 0.10.0 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  require('esbuild').buildSync({
2
2
  entryPoints: ['src/index.js'],
3
- outdir: 'public',
3
+ outdir: 'dist/iife',
4
4
  globalName: 'co2',
5
5
  format: 'iife',
6
6
  platform: 'browser',
package/.gitpod.yml ADDED
@@ -0,0 +1,8 @@
1
+ # Commands to start on workspace startup
2
+ tasks:
3
+ - init: npm install
4
+ command: npm run gitpod
5
+ # Ports to expose on workspace startup
6
+ ports:
7
+ - port: 8000
8
+ onOpen: open-preview
package/CHANGELOG.md CHANGED
@@ -12,11 +12,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
12
  > **Fixed** for any bug fixes.
13
13
  > **Security** in case of vulnerabilities.
14
14
 
15
+ ## Unreleased
16
+
17
+ ### Added
18
+
19
+ - Introduced a new `perVisit()` function for the Sustainable Web Design model, which applies [caching and return visits assumptions](https://sustainablewebdesign.org/calculating-digital-emissions/).
20
+
21
+ ## 0.10.2 2022-08-12
22
+
23
+ - 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.
24
+
25
+ ## 0.10.1 2022-08-01
26
+
27
+ This release used a version bump as previously we had released v0.10.0 under a pre-release tag.
28
+
15
29
  ## 0.10.0 2022-06-27
16
30
 
17
31
  - Added ES import syntax as the main way for handling imports and exports of code within the module.
18
32
  - Changed eslint settings to use later version of ecmascript (2020)
19
- - Change the bulid tools to use esbulid with jest instead of babel
33
+ - Change the build tools to use esbulid with jest instead of babel
20
34
  - Added more consistent use of the debug logging library in files using the updated import syntax
21
35
  - 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.
22
36
 
package/README.md CHANGED
@@ -1,92 +1,66 @@
1
- # CO2
1
+ # CO2.js
2
2
 
3
3
  <img src="https://github.com/thegreenwebfoundation/co2.js/actions/workflows/unittests.yml/badge.svg" />
4
4
 
5
+ One day, there internet will be powered by renewable energy. Until that day comes, there’ll be a CO2 cost that comes with every byte of data that’s uploaded or downloaded. By being able to calculate these emissions, developers can be empowered to create more efficient, lower carbon apps, websites, and software.
5
6
 
6
- We know computers use electricity, and because most of the electricity we use comes from burning fossil fuels, there is an environmental cost to every upload and download we make over the internet.
7
+ ## [Documentation](https://developers.thegreenwebfoundation.org/co2js/overview/)
8
+ ## [Changelog](/CHANGELOG.md)
7
9
 
8
- We can do something about this though. The same way we use performance budgets to make apps and websites faster and cheaper to run, we can use carbon budgets to make them faster, cheaper and _greener_.
10
+ ## What is CO2.js?
9
11
 
10
- The CO2 package from [The Green Web Foundation][tgwf] lets you quickly estimate these emissions, to make measurable improvements as part of your workflow.
12
+ CO2.js is a JavaScript library that enables developers a way to estimate the emissions related to use of their apps, websites, and software.
11
13
 
12
- ### How it works
14
+ ## Why use it?
13
15
 
14
- It does this by implementing various models for converting the measurable usage of digital services into a figure for the estimated CO2 emissions.
16
+ Being able to estimate the CO2 emissions associated with digital activities can be of benefit to both developers and users.
15
17
 
16
- This defaults to the 1byte model as used by the Shift Project, as introduced in their report on CO2 emissions from digital infrastructure, [Lean ICT: for a sober digital][soberDigital], with the [Sustainable Web Design model][swd] as a popular alternative..
18
+ Internally, you may want to use this library to create a *carbon budget* for your site or app. It is also useful for inclusion in dashboards and monitoring tools.
17
19
 
18
- For more information, see the documentation [for when to use the different models, with code samples to start you off](./src/readme.md).
20
+ For user facing applications, CO2.js could be used to check & block the uploading of carbon intensive files. Or, to present users with information about the carbon impact of their online activities (such as browsing a website).
19
21
 
20
- ### Who uses it
22
+ The above a just a few examples of the many and varied ways CO2.js can be applied to provide carbon estimates for data transfer. If you’re using CO2.js in production we’d love to hear how! [Contact us](https://www.thegreenwebfoundation.org/support-form/) via our website.
21
23
 
22
- It is currently used in the web performance tool [sitespeed.io][], [ecoping][], [Websitecarbon.com](websitecarbon), and [ecograder][] to help developers build greener, more planet friendly digital services.
24
+ ## Installation
23
25
 
24
- If you want to build this kind of environmental information into your own software, and want some advice, we'd be happy to hear from you - please open an issue, remembering to link to your project.
26
+ ### Using NPM
25
27
 
26
- This is open source software, with all the guarantees associated. So if you want professional advice, to a deadline, and you have a budget please see the services offered by the [Green Web Foundation][tgwf-services].
27
-
28
-
29
- [sitespeed.io]: https://sitespeed.io
30
- [ecoping]: https://ecoping.earth
31
- [ecograder]: https://ecograder.com
32
- [websitecarbon]: https://www.websitecarbon.com
33
- [tgwf]: https://www.thegreenwebfoundation.org
34
- [tgwf-services]: https://www.thegreenwebfoundation.org/services
35
- [swd]: https://sustainablewebdesign.org/calculating-digital-emissions
36
- [soberDigital]: https://theshiftproject.org/en/lean-ict-2/
37
-
38
-
39
- ## Usage
40
-
41
- ### Calculating emissions per byte
42
-
43
- #### Server-side
44
-
45
- This approach relies on the `fs` module and so can only be used on platforms like Node.js, that support this.
46
-
47
- ```js
48
-
49
- const CO2 = require('@tgwf/co2')
50
- const bytesSent = (1024 * 1024 * 1024)
51
- const co2Emission = new CO2();
52
- estimatedCO2 = co2Emission.perByte(bytesSent)
53
-
54
- console.log(`Sending a gigabyte, had a carbon footprint of ${estimatedCO2.toFixed(3)} grams of CO2`)
28
+ You can install CO2.js as a dependency for your projects using NPM.
55
29
 
30
+ ```bash
31
+ npm install @tgwf/co2
56
32
  ```
57
33
 
58
- #### Browser-side
34
+ ### Using Skypack
59
35
 
60
- For example, like this:
36
+ You can import the CO2.js library into projects using Skypack.
61
37
 
62
38
  ```js
63
-
64
- import { co2 } from '@tgwf/co2'
65
- const bytesSent = (1024 * 1024 * 1024)
66
- const co2Emission = new co2();
67
- estimatedCO2 = co2Emission.perByte(bytesSent)
68
-
69
- console.log(`Sending a gigabyte, had a carbon footprint of ${estimatedCO2.toFixed(3)} grams of CO2`)
70
- ****
39
+ import tgwf from 'https://cdn.skypack.dev/@tgwf/co2';
71
40
  ```
72
41
 
73
- ### Checking for green power
74
42
 
75
- Because different digital services and websites use different forms of power, there is also a module for checking if a domain uses green power or not, and whether the domains linked to on a page use green power as well.
43
+ ### Build it yourself
76
44
 
77
- ```js
78
-
79
- import { hosting } from '@tgwf/co2'
45
+ You can also build the CO2.js library from the source code. To do this:
80
46
 
81
- // returns true if green, otherwise false
82
- hosting.check("google.com")
47
+ 1. Go to the [CO2.js repository](https://github.com/thegreenwebfoundation/co2.js) on GitHub.
48
+ 1. Clone or fork the repository.
49
+ 1. Navigate to the folder on your machine and run `npm run build` in your terminal.
50
+ 1. Once the build has finished running, you will find a `/dist` folder has been created. Inside you can find:
51
+
52
+ - `dist/cjs` - A CommonJS compatible build.
53
+ - `dist/esm` - An ES Modules compatible build.
54
+ - `dist/iife` - An Immediately Invoked Function Expression (IIFE) version of the library.
83
55
 
84
- // returns an array of the green domains, in this case ["google.com"].
85
- hosting.check(["google.com", "kochindustries.com"])]
56
+ ## Publishing to NPM
86
57
 
58
+ We use [`np`](https://www.npmjs.com/package/np) to publish new versions of this library to NPM. To do this:
87
59
 
88
- ```
89
-
90
- # Licenses
60
+ 1. First login to NPM by running the `npm login` command in your terminal.
61
+ 2. Then run `npx np <VERSION>`.
62
+ 3. `np` will run several automated steps to publish the new package to NPM.
63
+ 4. If everything runs successfully, you can then add release notes to GitHub for the newly published package.
64
+ ## Licenses
91
65
 
92
66
  Apache 2.0
package/dist/cjs/co2.js CHANGED
@@ -26,17 +26,26 @@ __export(co2_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(co2_exports);
28
28
  var import_byte = __toESM(require("./1byte.js"));
29
+ var import_sustainable_web_design = __toESM(require("./sustainable-web-design.js"));
29
30
  class CO2 {
30
31
  constructor(options) {
31
- this.options = options;
32
- this.model = new import_byte.default();
33
- if (options) {
34
- this.model = new options.model();
32
+ this.model = new import_sustainable_web_design.default();
33
+ if ((options == null ? void 0 : options.model) === "1byte") {
34
+ this.model = new import_byte.default();
35
35
  }
36
36
  }
37
37
  perByte(bytes, green) {
38
38
  return this.model.perByte(bytes, green);
39
39
  }
40
+ perVisit(bytes, green) {
41
+ var _a;
42
+ if ((_a = this.model) == null ? void 0 : _a.perVisit) {
43
+ return this.model.perVisit(bytes, green);
44
+ } else {
45
+ console.warn("The model you have selected does not support perVisit. Using perByte instead.");
46
+ return this.model.perByte(bytes, green);
47
+ }
48
+ }
40
49
  perDomain(pageXray, greenDomains) {
41
50
  const co2PerDomain = [];
42
51
  for (let domain of Object.keys(pageXray.domains)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/co2.js"],
4
- "sourcesContent": ["\"use strict\";\n\nimport OneByte from \"./1byte.js\";\n\nclass CO2 {\n constructor(options) {\n this.options = options;\n\n // default model\n this.model = new OneByte();\n\n if (options) {\n this.model = new options.model();\n }\n }\n\n /**\n * Accept a figure in bytes for data transfer, and a boolean for whether\n * the domain shows as 'green', and return a CO2 figure for energy used to shift the corresponding\n * the data transfer.\n *\n * @param {number} bytes\n * @param {boolean} green\n * @return {number} the amount of CO2 in grammes\n */\n perByte(bytes, green) {\n return this.model.perByte(bytes, green);\n }\n\n perDomain(pageXray, greenDomains) {\n const co2PerDomain = [];\n for (let domain of Object.keys(pageXray.domains)) {\n let co2;\n if (greenDomains && greenDomains.indexOf(domain) > -1) {\n co2 = this.perByte(pageXray.domains[domain].transferSize, true);\n } else {\n co2 = this.perByte(pageXray.domains[domain].transferSize);\n }\n co2PerDomain.push({\n domain,\n co2,\n transferSize: pageXray.domains[domain].transferSize,\n });\n }\n co2PerDomain.sort((a, b) => b.co2 - a.co2);\n\n return co2PerDomain;\n }\n\n perPage(pageXray, green) {\n // Accept an xray object, and if we receive a boolean as the second\n // argument, we assume every request we make is sent to a server\n // running on renwewable power.\n\n // if we receive an array of domains, return a number accounting the\n // reduced CO2 from green hosted domains\n\n const domainCO2 = this.perDomain(pageXray, green);\n let totalCO2 = 0;\n for (let domain of domainCO2) {\n totalCO2 += domain.co2;\n }\n return totalCO2;\n }\n\n perContentType(pageXray, greenDomains) {\n const co2PerContentType = {};\n for (let asset of pageXray.assets) {\n const domain = new URL(asset.url).domain;\n const transferSize = asset.transferSize;\n const co2ForTransfer = this.perByte(\n transferSize,\n greenDomains && greenDomains.indexOf(domain) > -1\n );\n const contentType = asset.type;\n if (!co2PerContentType[contentType]) {\n co2PerContentType[contentType] = { co2: 0, transferSize: 0 };\n }\n co2PerContentType[contentType].co2 += co2ForTransfer;\n co2PerContentType[contentType].transferSize += transferSize;\n }\n // restructure and sort\n const all = [];\n for (let type of Object.keys(co2PerContentType)) {\n all.push({\n type,\n co2: co2PerContentType[type].co2,\n transferSize: co2PerContentType[type].transferSize,\n });\n }\n all.sort((a, b) => b.co2 - a.co2);\n return all;\n }\n\n dirtiestResources(pageXray, greenDomains) {\n const allAssets = [];\n for (let asset of pageXray.assets) {\n const domain = new URL(asset.url).domain;\n const transferSize = asset.transferSize;\n const co2ForTransfer = this.perByte(\n transferSize,\n greenDomains && greenDomains.indexOf(domain) > -1\n );\n allAssets.push({ url: asset.url, co2: co2ForTransfer, transferSize });\n }\n allAssets.sort((a, b) => b.co2 - a.co2);\n\n return allAssets.slice(0, allAssets.length > 10 ? 10 : allAssets.length);\n }\n\n perParty(pageXray, greenDomains) {\n let firstParty = 0;\n let thirdParty = 0;\n // calculate co2 per first/third party\n const firstPartyRegEx = pageXray.firstPartyRegEx;\n for (let d of Object.keys(pageXray.domains)) {\n if (!d.match(firstPartyRegEx)) {\n thirdParty += this.perByte(\n pageXray.domains[d].transferSize,\n greenDomains && greenDomains.indexOf(d) > -1\n );\n } else {\n firstParty += this.perByte(\n pageXray.domains[d].transferSize,\n greenDomains && greenDomains.indexOf(d) > -1\n );\n }\n }\n return { firstParty, thirdParty };\n }\n}\n\nexport { CO2 };\nexport default CO2;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,kBAAoB;AAEpB,MAAM,IAAI;AAAA,EACR,YAAY,SAAS;AACnB,SAAK,UAAU;AAGf,SAAK,QAAQ,IAAI,oBAAQ;AAEzB,QAAI,SAAS;AACX,WAAK,QAAQ,IAAI,QAAQ,MAAM;AAAA,IACjC;AAAA,EACF;AAAA,EAWA,QAAQ,OAAO,OAAO;AACpB,WAAO,KAAK,MAAM,QAAQ,OAAO,KAAK;AAAA,EACxC;AAAA,EAEA,UAAU,UAAU,cAAc;AAChC,UAAM,eAAe,CAAC;AACtB,aAAS,UAAU,OAAO,KAAK,SAAS,OAAO,GAAG;AAChD,UAAI;AACJ,UAAI,gBAAgB,aAAa,QAAQ,MAAM,IAAI,IAAI;AACrD,cAAM,KAAK,QAAQ,SAAS,QAAQ,QAAQ,cAAc,IAAI;AAAA,MAChE,OAAO;AACL,cAAM,KAAK,QAAQ,SAAS,QAAQ,QAAQ,YAAY;AAAA,MAC1D;AACA,mBAAa,KAAK;AAAA,QAChB;AAAA,QACA;AAAA,QACA,cAAc,SAAS,QAAQ,QAAQ;AAAA,MACzC,CAAC;AAAA,IACH;AACA,iBAAa,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAEzC,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,UAAU,OAAO;AAQvB,UAAM,YAAY,KAAK,UAAU,UAAU,KAAK;AAChD,QAAI,WAAW;AACf,aAAS,UAAU,WAAW;AAC5B,kBAAY,OAAO;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,UAAU,cAAc;AACrC,UAAM,oBAAoB,CAAC;AAC3B,aAAS,SAAS,SAAS,QAAQ;AACjC,YAAM,SAAS,IAAI,IAAI,MAAM,GAAG,EAAE;AAClC,YAAM,eAAe,MAAM;AAC3B,YAAM,iBAAiB,KAAK,QAC1B,cACA,gBAAgB,aAAa,QAAQ,MAAM,IAAI,EACjD;AACA,YAAM,cAAc,MAAM;AAC1B,UAAI,CAAC,kBAAkB,cAAc;AACnC,0BAAkB,eAAe,EAAE,KAAK,GAAG,cAAc,EAAE;AAAA,MAC7D;AACA,wBAAkB,aAAa,OAAO;AACtC,wBAAkB,aAAa,gBAAgB;AAAA,IACjD;AAEA,UAAM,MAAM,CAAC;AACb,aAAS,QAAQ,OAAO,KAAK,iBAAiB,GAAG;AAC/C,UAAI,KAAK;AAAA,QACP;AAAA,QACA,KAAK,kBAAkB,MAAM;AAAA,QAC7B,cAAc,kBAAkB,MAAM;AAAA,MACxC,CAAC;AAAA,IACH;AACA,QAAI,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,kBAAkB,UAAU,cAAc;AACxC,UAAM,YAAY,CAAC;AACnB,aAAS,SAAS,SAAS,QAAQ;AACjC,YAAM,SAAS,IAAI,IAAI,MAAM,GAAG,EAAE;AAClC,YAAM,eAAe,MAAM;AAC3B,YAAM,iBAAiB,KAAK,QAC1B,cACA,gBAAgB,aAAa,QAAQ,MAAM,IAAI,EACjD;AACA,gBAAU,KAAK,EAAE,KAAK,MAAM,KAAK,KAAK,gBAAgB,aAAa,CAAC;AAAA,IACtE;AACA,cAAU,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAEtC,WAAO,UAAU,MAAM,GAAG,UAAU,SAAS,KAAK,KAAK,UAAU,MAAM;AAAA,EACzE;AAAA,EAEA,SAAS,UAAU,cAAc;AAC/B,QAAI,aAAa;AACjB,QAAI,aAAa;AAEjB,UAAM,kBAAkB,SAAS;AACjC,aAAS,KAAK,OAAO,KAAK,SAAS,OAAO,GAAG;AAC3C,UAAI,CAAC,EAAE,MAAM,eAAe,GAAG;AAC7B,sBAAc,KAAK,QACjB,SAAS,QAAQ,GAAG,cACpB,gBAAgB,aAAa,QAAQ,CAAC,IAAI,EAC5C;AAAA,MACF,OAAO;AACL,sBAAc,KAAK,QACjB,SAAS,QAAQ,GAAG,cACpB,gBAAgB,aAAa,QAAQ,CAAC,IAAI,EAC5C;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,YAAY,WAAW;AAAA,EAClC;AACF;AAGA,IAAO,cAAQ;",
4
+ "sourcesContent": ["\"use strict\";\n\nimport OneByte from \"./1byte.js\";\nimport SustainableWebDesign from \"./sustainable-web-design.js\";\n\nclass CO2 {\n constructor(options) {\n // default model\n this.model = new SustainableWebDesign();\n\n // Using optional chaining allows an empty object to be passed in without breaking the code.\n if (options?.model === \"1byte\") {\n this.model = new OneByte();\n }\n }\n\n /**\n * Accept a figure in bytes for data transfer, and a boolean for whether\n * the domain shows as 'green', and return a CO2 figure for energy used to shift the corresponding\n * the data transfer.\n *\n * @param {number} bytes\n * @param {boolean} green\n * @return {number} the amount of CO2 in grammes\n */\n perByte(bytes, green) {\n return this.model.perByte(bytes, green);\n }\n\n /**\n * Accept a figure in bytes for data transfer, and a boolean for whether\n * the domain shows as 'green', and return a CO2 figure for energy used to shift the corresponding\n * the data transfer.\n *\n * @param {number} bytes\n * @param {boolean} green\n * @return {number} the amount of CO2 in grammes\n */\n perVisit(bytes, green) {\n if (this.model?.perVisit) {\n return this.model.perVisit(bytes, green);\n } else {\n console.warn(\"The model you have selected does not support perVisit. Using perByte instead.\");\n return this.model.perByte(bytes, green);\n }\n }\n\n perDomain(pageXray, greenDomains) {\n const co2PerDomain = [];\n for (let domain of Object.keys(pageXray.domains)) {\n let co2;\n if (greenDomains && greenDomains.indexOf(domain) > -1) {\n co2 = this.perByte(pageXray.domains[domain].transferSize, true);\n } else {\n co2 = this.perByte(pageXray.domains[domain].transferSize);\n }\n co2PerDomain.push({\n domain,\n co2,\n transferSize: pageXray.domains[domain].transferSize,\n });\n }\n co2PerDomain.sort((a, b) => b.co2 - a.co2);\n\n return co2PerDomain;\n }\n\n perPage(pageXray, green) {\n // Accept an xray object, and if we receive a boolean as the second\n // argument, we assume every request we make is sent to a server\n // running on renwewable power.\n\n // if we receive an array of domains, return a number accounting the\n // reduced CO2 from green hosted domains\n\n const domainCO2 = this.perDomain(pageXray, green);\n let totalCO2 = 0;\n for (let domain of domainCO2) {\n totalCO2 += domain.co2;\n }\n return totalCO2;\n }\n\n perContentType(pageXray, greenDomains) {\n const co2PerContentType = {};\n for (let asset of pageXray.assets) {\n const domain = new URL(asset.url).domain;\n const transferSize = asset.transferSize;\n const co2ForTransfer = this.perByte(\n transferSize,\n greenDomains && greenDomains.indexOf(domain) > -1\n );\n const contentType = asset.type;\n if (!co2PerContentType[contentType]) {\n co2PerContentType[contentType] = { co2: 0, transferSize: 0 };\n }\n co2PerContentType[contentType].co2 += co2ForTransfer;\n co2PerContentType[contentType].transferSize += transferSize;\n }\n // restructure and sort\n const all = [];\n for (let type of Object.keys(co2PerContentType)) {\n all.push({\n type,\n co2: co2PerContentType[type].co2,\n transferSize: co2PerContentType[type].transferSize,\n });\n }\n all.sort((a, b) => b.co2 - a.co2);\n return all;\n }\n\n dirtiestResources(pageXray, greenDomains) {\n const allAssets = [];\n for (let asset of pageXray.assets) {\n const domain = new URL(asset.url).domain;\n const transferSize = asset.transferSize;\n const co2ForTransfer = this.perByte(\n transferSize,\n greenDomains && greenDomains.indexOf(domain) > -1\n );\n allAssets.push({ url: asset.url, co2: co2ForTransfer, transferSize });\n }\n allAssets.sort((a, b) => b.co2 - a.co2);\n\n return allAssets.slice(0, allAssets.length > 10 ? 10 : allAssets.length);\n }\n\n perParty(pageXray, greenDomains) {\n let firstParty = 0;\n let thirdParty = 0;\n // calculate co2 per first/third party\n const firstPartyRegEx = pageXray.firstPartyRegEx;\n for (let d of Object.keys(pageXray.domains)) {\n if (!d.match(firstPartyRegEx)) {\n thirdParty += this.perByte(\n pageXray.domains[d].transferSize,\n greenDomains && greenDomains.indexOf(d) > -1\n );\n } else {\n firstParty += this.perByte(\n pageXray.domains[d].transferSize,\n greenDomains && greenDomains.indexOf(d) > -1\n );\n }\n }\n return { firstParty, thirdParty };\n }\n}\n\nexport { CO2 };\nexport default CO2;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -29,7 +29,7 @@ describe("co2", () => {
29
29
  const MILLION_GREY = 0.29081;
30
30
  const MILLION_GREEN = 0.23196;
31
31
  beforeEach(() => {
32
- co2 = new import_co2.default();
32
+ co2 = new import_co2.default({ model: "1byte" });
33
33
  har = JSON.parse(import_fs.default.readFileSync(import_path.default.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
34
34
  });
35
35
  describe("perByte", () => {
@@ -130,15 +130,17 @@ describe("co2", () => {
130
130
  });
131
131
  });
132
132
  });
133
- describe("Sustainable Web Design model", () => {
133
+ describe("Sustainable Web Design model as simple option", () => {
134
134
  const MILLION = 1e6;
135
- const MILLION_GREY = 0.33343;
136
- const MILLION_GREEN = 0.28908;
137
- const TGWF_GREY_VALUE = 0.23501;
135
+ const MILLION_GREY = 0.35802;
136
+ const MILLION_GREEN = 0.31039;
137
+ const MILLION_PERVISIT_GREY = 0.27031;
138
+ const MILLION_PERVISIT_GREEN = 0.23434;
139
+ const TGWF_GREY_VALUE = 0.25234;
138
140
  const TGWF_GREEN_VALUE = 0.54704;
139
- const TGWF_MIXED_VALUE = 0.20652;
141
+ const TGWF_MIXED_VALUE = 0.22175;
140
142
  beforeEach(() => {
141
- co2 = new import_co2.default({ model: import_sustainable_web_design.default });
143
+ co2 = new import_co2.default();
142
144
  har = JSON.parse(import_fs.default.readFileSync(import_path.default.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
143
145
  });
144
146
  describe("perByte", () => {
@@ -151,6 +153,16 @@ describe("co2", () => {
151
153
  expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_GREEN.toPrecision(5));
152
154
  });
153
155
  });
156
+ describe("perVisit", () => {
157
+ it("returns a CO2 number for data transfer per visit with caching assumptions from the Sustainable Web Design model", () => {
158
+ co2.perVisit(MILLION);
159
+ expect(co2.perVisit(MILLION).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
160
+ });
161
+ it("returns a lower CO2 number for data transfer from domains using entirely 'green' power", () => {
162
+ expect(co2.perVisit(MILLION, false).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
163
+ expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_PERVISIT_GREEN.toPrecision(5));
164
+ });
165
+ });
154
166
  describe("perPage", () => {
155
167
  it("returns CO2 for total transfer for page", () => {
156
168
  const pages = import_pagexray.default.convert(har);
@@ -240,5 +252,19 @@ describe("co2", () => {
240
252
  });
241
253
  });
242
254
  });
255
+ describe("New model that implements the same API", () => {
256
+ const MILLION = 1e6;
257
+ const MILLION_GREY = 0.35802;
258
+ beforeEach(() => {
259
+ co2 = new import_co2.default({ model: import_sustainable_web_design.default });
260
+ har = JSON.parse(import_fs.default.readFileSync(import_path.default.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
261
+ });
262
+ describe("perByte", () => {
263
+ it("returns a CO2 number for data transfer", () => {
264
+ co2.perByte(MILLION);
265
+ expect(co2.perByte(MILLION).toPrecision(5)).toBe(MILLION_GREY.toPrecision(5));
266
+ });
267
+ });
268
+ });
243
269
  });
244
270
  //# sourceMappingURL=co2.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/co2.test.js"],
4
- "sourcesContent": ["\"use strict\";\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nimport pagexray from \"pagexray\";\n\nimport CO2 from \"./co2.js\";\nimport swd from \"./sustainable-web-design.js\";\n\ndescribe(\"co2\", () => {\n let har, co2;\n\n describe(\"1 byte model\", () => {\n const TGWF_GREY_VALUE = 0.20497;\n const TGWF_GREEN_VALUE = 0.54704;\n const TGWF_MIXED_VALUE = 0.16718;\n\n const MILLION = 1000000;\n const MILLION_GREY = 0.29081;\n const MILLION_GREEN = 0.23196;\n\n beforeEach(() => {\n co2 = new CO2();\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n\n describe(\"perByte\", () => {\n it(\"returns a CO2 number for data transfer using 'grey' power\", () => {\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n });\n\n it(\"returns a lower CO2 number for data transfer from domains using entirely 'green' power\", () => {\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(\n MILLION_GREEN.toPrecision(5)\n );\n });\n });\n\n describe(\"perPage\", () => {\n it(\"returns CO2 for total transfer for page\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n expect(co2.perPage(pageXrayRun).toPrecision(5)).toBe(\n TGWF_GREY_VALUE.toPrecision(5)\n );\n });\n it(\"returns lower CO2 for page served from green site\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green)).toBeLessThan(TGWF_GREY_VALUE);\n });\n it(\"returns a lower CO2 number where *some* domains use green power\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n // green can be true, or a array containing entries\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green).toPrecision(5)).toBe(\n TGWF_MIXED_VALUE.toPrecision(5)\n );\n });\n });\n describe(\"perDomain\", () => {\n it(\"shows object listing Co2 for each domain\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n const res = co2.perDomain(pageXrayRun);\n\n const domains = [\n \"thegreenwebfoundation.org\",\n \"www.thegreenwebfoundation.org\",\n \"maxcdn.bootstrapcdn.com\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n\n for (let obj of res) {\n expect(domains.indexOf(obj.domain)).toBeGreaterThan(-1);\n expect(typeof obj.co2).toBe(\"number\");\n }\n });\n it(\"shows lower Co2 for green domains\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n const greenDomains = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n const res = co2.perDomain(pageXrayRun);\n const resWithGreen = co2.perDomain(pageXrayRun, greenDomains);\n\n for (let obj of res) {\n expect(typeof obj.co2).toBe(\"number\");\n }\n for (let obj of greenDomains) {\n let index = 0;\n expect(resWithGreen[index].co2).toBeLessThan(res[index].co2);\n index++;\n }\n });\n });\n });\n\n describe(\"Sustainable Web Design model\", () => {\n // the SWD model should have slightly higher values as\n // we include more of the system in calculations for the\n // same levels of data transfer\n const MILLION = 1000000;\n const MILLION_GREY = 0.33343;\n const MILLION_GREEN = 0.28908;\n\n const TGWF_GREY_VALUE = 0.23501;\n const TGWF_GREEN_VALUE = 0.54704;\n const TGWF_MIXED_VALUE = 0.20652;\n\n beforeEach(() => {\n co2 = new CO2({ model: swd });\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n\n describe(\"perByte\", () => {\n it(\"returns a CO2 number for data transfer\", () => {\n co2.perByte(MILLION);\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n });\n\n it(\"returns a lower CO2 number for data transfer from domains using entirely 'green' power\", () => {\n expect(co2.perByte(MILLION, false).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n\n expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(\n MILLION_GREEN.toPrecision(5)\n );\n });\n });\n\n describe(\"perPage\", () => {\n it(\"returns CO2 for total transfer for page\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n expect(co2.perPage(pageXrayRun).toPrecision(5)).toBe(\n TGWF_GREY_VALUE.toPrecision(5)\n );\n });\n it(\"returns lower CO2 for page served from green site\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green)).toBeLessThan(TGWF_GREY_VALUE);\n });\n it(\"returns a lower CO2 number where *some* domains use green power\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n // green can be true, or a array containing entries\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green).toPrecision(5)).toBe(\n TGWF_MIXED_VALUE.toPrecision(5)\n );\n });\n });\n describe(\"perDomain\", () => {\n it(\"shows object listing Co2 for each domain\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n const res = co2.perDomain(pageXrayRun);\n\n const domains = [\n \"thegreenwebfoundation.org\",\n \"www.thegreenwebfoundation.org\",\n \"maxcdn.bootstrapcdn.com\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n\n for (let obj of res) {\n expect(domains.indexOf(obj.domain)).toBeGreaterThan(-1);\n expect(typeof obj.co2).toBe(\"number\");\n }\n });\n it(\"shows lower Co2 for green domains\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n const greenDomains = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n const res = co2.perDomain(pageXrayRun);\n const resWithGreen = co2.perDomain(pageXrayRun, greenDomains);\n\n for (let obj of res) {\n expect(typeof obj.co2).toBe(\"number\");\n }\n for (let obj of greenDomains) {\n let index = 0;\n expect(resWithGreen[index].co2).toBeLessThan(res[index].co2);\n index++;\n }\n });\n });\n });\n});\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use strict\";\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nimport pagexray from \"pagexray\";\n\nimport CO2 from \"./co2.js\";\nimport SustainableWebDesign from \"./sustainable-web-design.js\";\n\ndescribe(\"co2\", () => {\n let har, co2;\n\n describe(\"1 byte model\", () => {\n const TGWF_GREY_VALUE = 0.20497;\n const TGWF_GREEN_VALUE = 0.54704;\n const TGWF_MIXED_VALUE = 0.16718;\n\n const MILLION = 1000000;\n const MILLION_GREY = 0.29081;\n const MILLION_GREEN = 0.23196;\n\n beforeEach(() => {\n co2 = new CO2({ model: \"1byte\" });\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n\n describe(\"perByte\", () => {\n it(\"returns a CO2 number for data transfer using 'grey' power\", () => {\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n });\n\n it(\"returns a lower CO2 number for data transfer from domains using entirely 'green' power\", () => {\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(\n MILLION_GREEN.toPrecision(5)\n );\n });\n });\n\n describe(\"perPage\", () => {\n it(\"returns CO2 for total transfer for page\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n expect(co2.perPage(pageXrayRun).toPrecision(5)).toBe(\n TGWF_GREY_VALUE.toPrecision(5)\n );\n });\n it(\"returns lower CO2 for page served from green site\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green)).toBeLessThan(TGWF_GREY_VALUE);\n });\n it(\"returns a lower CO2 number where *some* domains use green power\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n // green can be true, or a array containing entries\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green).toPrecision(5)).toBe(\n TGWF_MIXED_VALUE.toPrecision(5)\n );\n });\n });\n describe(\"perDomain\", () => {\n it(\"shows object listing Co2 for each domain\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n const res = co2.perDomain(pageXrayRun);\n\n const domains = [\n \"thegreenwebfoundation.org\",\n \"www.thegreenwebfoundation.org\",\n \"maxcdn.bootstrapcdn.com\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n\n for (let obj of res) {\n expect(domains.indexOf(obj.domain)).toBeGreaterThan(-1);\n expect(typeof obj.co2).toBe(\"number\");\n }\n });\n it(\"shows lower Co2 for green domains\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n const greenDomains = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n const res = co2.perDomain(pageXrayRun);\n const resWithGreen = co2.perDomain(pageXrayRun, greenDomains);\n\n for (let obj of res) {\n expect(typeof obj.co2).toBe(\"number\");\n }\n for (let obj of greenDomains) {\n let index = 0;\n expect(resWithGreen[index].co2).toBeLessThan(res[index].co2);\n index++;\n }\n });\n });\n });\n\n describe(\"Sustainable Web Design model as simple option\", () => {\n // the SWD model should have slightly higher values as\n // we include more of the system in calculations for the\n // same levels of data transfer\n const MILLION = 1000000;\n const MILLION_GREY = 0.35802;\n const MILLION_GREEN = 0.31039;\n const MILLION_PERVISIT_GREY = 0.27031;\n const MILLION_PERVISIT_GREEN = 0.23434;\n\n const TGWF_GREY_VALUE = 0.25234;\n const TGWF_GREEN_VALUE = 0.54704;\n const TGWF_MIXED_VALUE = 0.22175;\n\n // We're not passing a model parameter here\n // this allows us to verify SWD is being used by default\n beforeEach(() => {\n co2 = new CO2();\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n\n describe(\"perByte\", () => {\n it(\"returns a CO2 number for data transfer\", () => {\n co2.perByte(MILLION);\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n });\n\n it(\"returns a lower CO2 number for data transfer from domains using entirely 'green' power\", () => {\n expect(co2.perByte(MILLION, false).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n\n expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(\n MILLION_GREEN.toPrecision(5)\n );\n });\n });\n\n describe(\"perVisit\", () => {\n it(\"returns a CO2 number for data transfer per visit with caching assumptions from the Sustainable Web Design model\", () => {\n co2.perVisit(MILLION);\n expect(co2.perVisit(MILLION).toPrecision(5)).toBe(\n MILLION_PERVISIT_GREY.toPrecision(5)\n );\n });\n\n it(\"returns a lower CO2 number for data transfer from domains using entirely 'green' power\", () => {\n expect(co2.perVisit(MILLION, false).toPrecision(5)).toBe(\n MILLION_PERVISIT_GREY.toPrecision(5)\n );\n\n expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(\n MILLION_PERVISIT_GREEN.toPrecision(5)\n );\n });\n });\n\n describe(\"perPage\", () => {\n it(\"returns CO2 for total transfer for page\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n expect(co2.perPage(pageXrayRun).toPrecision(5)).toBe(\n TGWF_GREY_VALUE.toPrecision(5)\n );\n });\n it(\"returns lower CO2 for page served from green site\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green)).toBeLessThan(TGWF_GREY_VALUE);\n });\n it(\"returns a lower CO2 number where *some* domains use green power\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n // green can be true, or a array containing entries\n let green = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n expect(co2.perPage(pageXrayRun, green).toPrecision(5)).toBe(\n TGWF_MIXED_VALUE.toPrecision(5)\n );\n });\n });\n describe(\"perDomain\", () => {\n it(\"shows object listing Co2 for each domain\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n const res = co2.perDomain(pageXrayRun);\n\n const domains = [\n \"thegreenwebfoundation.org\",\n \"www.thegreenwebfoundation.org\",\n \"maxcdn.bootstrapcdn.com\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n\n for (let obj of res) {\n expect(domains.indexOf(obj.domain)).toBeGreaterThan(-1);\n expect(typeof obj.co2).toBe(\"number\");\n }\n });\n it(\"shows lower Co2 for green domains\", () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n\n const greenDomains = [\n \"www.thegreenwebfoundation.org\",\n \"fonts.googleapis.com\",\n \"ajax.googleapis.com\",\n \"assets.digitalclimatestrike.net\",\n \"cdnjs.cloudflare.com\",\n \"graphite.thegreenwebfoundation.org\",\n \"analytics.thegreenwebfoundation.org\",\n \"fonts.gstatic.com\",\n \"api.thegreenwebfoundation.org\",\n ];\n const res = co2.perDomain(pageXrayRun);\n const resWithGreen = co2.perDomain(pageXrayRun, greenDomains);\n\n for (let obj of res) {\n expect(typeof obj.co2).toBe(\"number\");\n }\n for (let obj of greenDomains) {\n let index = 0;\n expect(resWithGreen[index].co2).toBeLessThan(res[index].co2);\n index++;\n }\n });\n });\n });\n\n describe(\"New model that implements the same API\", () => {\n const MILLION = 1000000;\n const MILLION_GREY = 0.35802;\n\n beforeEach(() => {\n // we use the SustainableWebDesign to demonstrate passing\n // in a complex object instead of a simple string\n co2 = new CO2({ model: SustainableWebDesign });\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n\n describe(\"perByte\", () => {\n it(\"returns a CO2 number for data transfer\", () => {\n co2.perByte(MILLION);\n expect(co2.perByte(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.toPrecision(5)\n );\n });\n });\n });\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAEA,gBAAe;AACf,kBAAiB;AAEjB,sBAAqB;AAErB,iBAAgB;AAChB,oCAAiC;AAEjC,SAAS,OAAO,MAAM;AACpB,MAAI,KAAK;AAET,WAAS,gBAAgB,MAAM;AAC7B,UAAM,kBAAkB;AACxB,UAAM,mBAAmB;AACzB,UAAM,mBAAmB;AAEzB,UAAM,UAAU;AAChB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AAEtB,eAAW,MAAM;AACf,YAAM,IAAI,mBAAI,EAAE,OAAO,QAAQ,CAAC;AAChC,YAAM,KAAK,MACT,kBAAG,aACD,oBAAK,QAAQ,WAAW,2BAA2B,GACnD,MACF,CACF;AAAA,IACF,CAAC;AAED,aAAS,WAAW,MAAM;AACxB,SAAG,6DAA6D,MAAM;AACpE,eAAO,IAAI,QAAQ,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAC1C,aAAa,YAAY,CAAC,CAC5B;AAAA,MACF,CAAC;AAED,SAAG,0FAA0F,MAAM;AACjG,eAAO,IAAI,QAAQ,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAC1C,aAAa,YAAY,CAAC,CAC5B;AACA,eAAO,IAAI,QAAQ,SAAS,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,KAChD,cAAc,YAAY,CAAC,CAC7B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,aAAS,WAAW,MAAM;AACxB,SAAG,2CAA2C,MAAM;AAClD,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,eAAO,IAAI,QAAQ,WAAW,EAAE,YAAY,CAAC,CAAC,EAAE,KAC9C,gBAAgB,YAAY,CAAC,CAC/B;AAAA,MACF,CAAC;AACD,SAAG,qDAAqD,MAAM;AAC5D,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAC1B,YAAI,QAAQ;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,IAAI,QAAQ,aAAa,KAAK,CAAC,EAAE,aAAa,eAAe;AAAA,MACtE,CAAC;AACD,SAAG,mEAAmE,MAAM;AAC1E,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,YAAI,QAAQ;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,IAAI,QAAQ,aAAa,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,KACrD,iBAAiB,YAAY,CAAC,CAChC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,aAAS,aAAa,MAAM;AAC1B,SAAG,4CAA4C,MAAM;AACnD,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAC1B,cAAM,MAAM,IAAI,UAAU,WAAW;AAErC,cAAM,UAAU;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,iBAAS,OAAO,KAAK;AACnB,iBAAO,QAAQ,QAAQ,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE;AACtD,iBAAO,OAAO,IAAI,GAAG,EAAE,KAAK,QAAQ;AAAA,QACtC;AAAA,MACF,CAAC;AACD,SAAG,qCAAqC,MAAM;AAC5C,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,cAAM,eAAe;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,MAAM,IAAI,UAAU,WAAW;AACrC,cAAM,eAAe,IAAI,UAAU,aAAa,YAAY;AAE5D,iBAAS,OAAO,KAAK;AACnB,iBAAO,OAAO,IAAI,GAAG,EAAE,KAAK,QAAQ;AAAA,QACtC;AACA,iBAAS,OAAO,cAAc;AAC5B,cAAI,QAAQ;AACZ,iBAAO,aAAa,OAAO,GAAG,EAAE,aAAa,IAAI,OAAO,GAAG;AAC3D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,WAAS,iDAAiD,MAAM;AAI9D,UAAM,UAAU;AAChB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AACtB,UAAM,wBAAwB;AAC9B,UAAM,yBAAyB;AAE/B,UAAM,kBAAkB;AACxB,UAAM,mBAAmB;AACzB,UAAM,mBAAmB;AAIzB,eAAW,MAAM;AACf,YAAM,IAAI,mBAAI;AACd,YAAM,KAAK,MACT,kBAAG,aACD,oBAAK,QAAQ,WAAW,2BAA2B,GACnD,MACF,CACF;AAAA,IACF,CAAC;AAED,aAAS,WAAW,MAAM;AACxB,SAAG,0CAA0C,MAAM;AACjD,YAAI,QAAQ,OAAO;AACnB,eAAO,IAAI,QAAQ,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAC1C,aAAa,YAAY,CAAC,CAC5B;AAAA,MACF,CAAC;AAED,SAAG,0FAA0F,MAAM;AACjG,eAAO,IAAI,QAAQ,SAAS,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,KACjD,aAAa,YAAY,CAAC,CAC5B;AAEA,eAAO,IAAI,QAAQ,SAAS,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,KAChD,cAAc,YAAY,CAAC,CAC7B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,aAAS,YAAY,MAAM;AACzB,SAAG,mHAAmH,MAAM;AAC1H,YAAI,SAAS,OAAO;AACpB,eAAO,IAAI,SAAS,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAC3C,sBAAsB,YAAY,CAAC,CACrC;AAAA,MACF,CAAC;AAED,SAAG,0FAA0F,MAAM;AACjG,eAAO,IAAI,SAAS,SAAS,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,KAClD,sBAAsB,YAAY,CAAC,CACrC;AAEA,eAAO,IAAI,QAAQ,SAAS,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,KAChD,uBAAuB,YAAY,CAAC,CACtC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,aAAS,WAAW,MAAM;AACxB,SAAG,2CAA2C,MAAM;AAClD,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,eAAO,IAAI,QAAQ,WAAW,EAAE,YAAY,CAAC,CAAC,EAAE,KAC9C,gBAAgB,YAAY,CAAC,CAC/B;AAAA,MACF,CAAC;AACD,SAAG,qDAAqD,MAAM;AAC5D,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAC1B,YAAI,QAAQ;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,IAAI,QAAQ,aAAa,KAAK,CAAC,EAAE,aAAa,eAAe;AAAA,MACtE,CAAC;AACD,SAAG,mEAAmE,MAAM;AAC1E,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,YAAI,QAAQ;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,IAAI,QAAQ,aAAa,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,KACrD,iBAAiB,YAAY,CAAC,CAChC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,aAAS,aAAa,MAAM;AAC1B,SAAG,4CAA4C,MAAM;AACnD,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAC1B,cAAM,MAAM,IAAI,UAAU,WAAW;AAErC,cAAM,UAAU;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,iBAAS,OAAO,KAAK;AACnB,iBAAO,QAAQ,QAAQ,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE;AACtD,iBAAO,OAAO,IAAI,GAAG,EAAE,KAAK,QAAQ;AAAA,QACtC;AAAA,MACF,CAAC;AACD,SAAG,qCAAqC,MAAM;AAC5C,cAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,cAAM,cAAc,MAAM;AAE1B,cAAM,eAAe;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,MAAM,IAAI,UAAU,WAAW;AACrC,cAAM,eAAe,IAAI,UAAU,aAAa,YAAY;AAE5D,iBAAS,OAAO,KAAK;AACnB,iBAAO,OAAO,IAAI,GAAG,EAAE,KAAK,QAAQ;AAAA,QACtC;AACA,iBAAS,OAAO,cAAc;AAC5B,cAAI,QAAQ;AACZ,iBAAO,aAAa,OAAO,GAAG,EAAE,aAAa,IAAI,OAAO,GAAG;AAC3D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,WAAS,0CAA0C,MAAM;AACvD,UAAM,UAAU;AAChB,UAAM,eAAe;AAErB,eAAW,MAAM;AAGf,YAAM,IAAI,mBAAI,EAAE,OAAO,sCAAqB,CAAC;AAC7C,YAAM,KAAK,MACT,kBAAG,aACD,oBAAK,QAAQ,WAAW,2BAA2B,GACnD,MACF,CACF;AAAA,IACF,CAAC;AAED,aAAS,WAAW,MAAM;AACxB,SAAG,0CAA0C,MAAM;AACjD,YAAI,QAAQ,OAAO;AACnB,eAAO,IAAI,QAAQ,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAC1C,aAAa,YAAY,CAAC,CAC5B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
6
6
  "names": []
7
7
  }
@@ -20,7 +20,7 @@ __export(file_size_exports, {
20
20
  default: () => file_size_default
21
21
  });
22
22
  module.exports = __toCommonJS(file_size_exports);
23
- const GIGABYTE = 1024 * 1024 * 1024;
23
+ const GIGABYTE = 1e3 * 1e3 * 1e3;
24
24
  var file_size_default = {
25
25
  GIGABYTE
26
26
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/constants/file-size.js"],
4
- "sourcesContent": ["const GIGABYTE = 1024 * 1024 * 1024;\n\nexport default {\n GIGABYTE,\n};\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,WAAW,OAAO,OAAO;AAE/B,IAAO,oBAAQ;AAAA,EACb;AACF;",
4
+ "sourcesContent": ["const GIGABYTE = 1000 * 1000 * 1000;\n\nexport default {\n GIGABYTE,\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,WAAW,MAAO,MAAO;AAE/B,IAAO,oBAAQ;AAAA,EACb;AACF;",
6
6
  "names": []
7
7
  }
@@ -56,8 +56,10 @@ class SustainableWebDesign {
56
56
  co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {
57
57
  const returnCO2ByComponent = {};
58
58
  for (const [key, value] of Object.entries(energyBycomponent)) {
59
- if (key === "dataCenterEnergy") {
59
+ if (key.startsWith("dataCenterEnergy")) {
60
+ console.log(key);
60
61
  returnCO2ByComponent[key] = value * carbonIntensity;
62
+ console.log(returnCO2ByComponent[key]);
61
63
  } else {
62
64
  returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;
63
65
  }
@@ -79,6 +81,21 @@ class SustainableWebDesign {
79
81
  const co2Values = Object.values(co2ValuesbyComponent);
80
82
  return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
81
83
  }
84
+ perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {
85
+ const energyBycomponent = this.energyPerVisitByComponent(bytes);
86
+ if (Boolean(carbonIntensity) === false) {
87
+ carbonIntensity = GLOBAL_INTENSITY;
88
+ }
89
+ if (carbonIntensity === true) {
90
+ carbonIntensity = RENEWABLES_INTENSITY;
91
+ }
92
+ if (typeof carbonIntensity !== "number") {
93
+ throw new Error(`perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`);
94
+ }
95
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity);
96
+ const co2Values = Object.values(co2ValuesbyComponent);
97
+ return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
98
+ }
82
99
  energyPerByte(bytes) {
83
100
  const energyByComponent = this.energyPerByteByComponent(bytes);
84
101
  const energyValues = Object.values(energyByComponent);
@@ -89,8 +106,6 @@ class SustainableWebDesign {
89
106
  const cacheAdjustedSegmentEnergy = {};
90
107
  log({ energyBycomponent });
91
108
  const energyValues = Object.values(energyBycomponent);
92
- const v9recombinedNoCaching = energyValues.reduce((prevValue, currentValue) => prevValue + currentValue);
93
- log({ v9recombinedNoCaching });
94
109
  for (const [key, value] of Object.entries(energyBycomponent)) {
95
110
  cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;
96
111
  cacheAdjustedSegmentEnergy[`${key} - subsequent`] = value * returnView * dataReloadRatio;
@@ -99,35 +114,20 @@ class SustainableWebDesign {
99
114
  return cacheAdjustedSegmentEnergy;
100
115
  }
101
116
  energyPerVisit(bytes) {
102
- let v9firstVisits = 0;
103
- let v9subsequentVisits = 0;
117
+ let firstVisits = 0;
118
+ let subsequentVisits = 0;
104
119
  const energyBycomponent = Object.entries(this.energyPerVisitByComponent(bytes));
105
120
  for (const [key, val] of energyBycomponent) {
106
121
  if (key.indexOf("first") > 0) {
107
- v9firstVisits += val;
122
+ firstVisits += val;
108
123
  }
109
124
  }
110
125
  for (const [key, val] of energyBycomponent) {
111
126
  if (key.indexOf("subsequent") > 0) {
112
- v9subsequentVisits += val;
127
+ subsequentVisits += val;
113
128
  }
114
129
  }
115
- log({ v9firstVisits });
116
- log({ v9subsequentVisits });
117
- return v9firstVisits + v9subsequentVisits;
118
- }
119
- energyPerVisitV8(bytes) {
120
- log({ bytes });
121
- const transferedBytesToGb = bytes / import_constants.fileSize.GIGABYTE;
122
- const v8visitWithNoCaching = transferedBytesToGb * KWH_PER_GB;
123
- const v8firstVisit = transferedBytesToGb * KWH_PER_GB * RETURNING_VISITOR_PERCENTAGE;
124
- const v8subsequentVisits = transferedBytesToGb * KWH_PER_GB * FIRST_TIME_VIEWING_PERCENTAGE * PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD;
125
- const v8firstAndSubsequentVisits = v8firstVisit + v8subsequentVisits;
126
- log({ v8visitWithNoCaching });
127
- log({ v8firstVisit });
128
- log({ v8subsequentVisits });
129
- log({ v8firstAndSubsequentVisits });
130
- return v8firstVisit + v8subsequentVisits;
130
+ return firstVisits + subsequentVisits;
131
131
  }
132
132
  emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {
133
133
  return (0, import_helpers.formatNumber)(energyPerVisit * carbonintensity);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/sustainable-web-design.js"],
4
- "sourcesContent": ["\"use strict\";\n\n/**\n * Sustainable Web Design\n *\n * Updated calculations and figures from\n * https://sustainablewebdesign.org/calculating-digital-emissions/\n *\n *\n */\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:sustainable-web-design\");\n\nimport { fileSize } from \"./constants/index.js\";\nimport { formatNumber } from \"./helpers/index.js\";\n\n// this refers to the estimated total energy use for the internet around 2000 TWh,\n// divided by the total transfer it enables around 2500 exabytes\nconst KWH_PER_GB = 0.81;\n\n// these constants outline how the energy is attributed to\n// different parts of the system in the SWD model\nconst END_USER_DEVICE_ENERGY = 0.52;\nconst NETWORK_ENERGY = 0.14;\nconst DATACENTER_ENERGY = 0.15;\nconst PRODUCTION_ENERGY = 0.19;\n\n// These carbon intensity figures https://ember-climate.org/data/data-explorer\n// - Global carbon intensity for 2021\nconst GLOBAL_INTENSITY = 442;\nconst RENEWABLES_INTENSITY = 50;\n\n// Taken from: https://gitlab.com/wholegrain/carbon-api-2-0/-/blob/master/includes/carbonapi.php\n\nconst FIRST_TIME_VIEWING_PERCENTAGE = 0.75;\nconst RETURNING_VISITOR_PERCENTAGE = 0.25;\nconst PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD = 0.02;\n\nclass SustainableWebDesign {\n constructor(options) {\n this.options = options;\n }\n\n /**\n * Accept a figure for bytes transferred and return an object representing\n * the share of the total enrgy use of the entire system, broken down\n * by each corresponding system component\n *\n * @param {number} bytes - the data transferred in bytes\n * @return {object} Object containing the energy in kilowatt hours, keyed by system component\n */\n energyPerByteByComponent(bytes) {\n const transferedBytesToGb = bytes / fileSize.GIGABYTE;\n const energyUsage = transferedBytesToGb * KWH_PER_GB;\n\n // return the total energy, with breakdown by component\n return {\n consumerDeviceEnergy: energyUsage * END_USER_DEVICE_ENERGY,\n networkEnergy: energyUsage * NETWORK_ENERGY,\n productionEnergy: energyUsage * PRODUCTION_ENERGY,\n dataCenterEnergy: energyUsage * DATACENTER_ENERGY,\n };\n }\n /**\n * Accept an object keys by the different system components, and\n * return an object with the co2 figures key by the each component\n *\n * @param {object} energyBycomponent - energy grouped by the four system components\n * @param {number} [carbonIntensity] - carbon intensity to apply to the datacentre values\n * @return {number} the total number in grams of CO2 equivalent emissions\n */\n co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {\n const returnCO2ByComponent = {};\n for (const [key, value] of Object.entries(energyBycomponent)) {\n // we update the datacentre, as that's what we have information\n // about.\n if (key === \"dataCenterEnergy\") {\n returnCO2ByComponent[key] = value * carbonIntensity;\n } else {\n // We don't have info about the device location,\n // nor the network path used, nor the production emissions\n // so we revert to global figures\n returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;\n }\n }\n return returnCO2ByComponent;\n }\n\n /**\n * Accept a figure for bytes transferred and return a single figure for CO2\n * emissions. Where information exists about the origin data is being\n * fetched from, a different carbon intensity figure\n * is applied for the datacentre share of the carbon intensity.\n *\n * @param {number} bytes - the data transferred in bytes\n * @param {number} `carbonIntensity` the carbon intensity for datacentre (average figures, not marginal ones)\n * @return {number} the total number in grams of CO2 equivalent emissions\n */\n perByte(bytes, carbonIntensity = GLOBAL_INTENSITY) {\n const energyBycomponent = this.energyPerByteByComponent(bytes);\n\n // when faced with falsy values, fallback to global intensity\n if (Boolean(carbonIntensity) === false) {\n carbonIntensity = GLOBAL_INTENSITY;\n }\n // if we have a boolean, we have a green result from the green web checker\n // use the renewables intensity\n if (carbonIntensity === true) {\n carbonIntensity = RENEWABLES_INTENSITY;\n }\n\n // otherwise when faced with non numeric values throw an error\n if (typeof carbonIntensity !== \"number\") {\n throw new Error(\n `perByte expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`\n );\n }\n\n const co2ValuesbyComponent = this.co2byComponent(\n energyBycomponent,\n carbonIntensity\n );\n\n // pull out our values\u2026\n const co2Values = Object.values(co2ValuesbyComponent);\n\n // so we can return their sum\n return co2Values.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n }\n\n /**\n * Accept a figure for bytes transferred and return the number of kilowatt hours used\n * by the total system for this data transfer\n *\n * @param {number} bytes\n * @return {number} the number of kilowatt hours used\n */\n energyPerByte(bytes) {\n const energyByComponent = this.energyPerByteByComponent(bytes);\n\n // pull out our values\u2026\n const energyValues = Object.values(energyByComponent);\n\n // so we can return their sum\n return energyValues.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n }\n\n /**\n * Accept a figure for bytes transferred, and return an object containing figures\n * per system component, with the caching assumptions applied. This tries to account\n * for webpages being loaded from a cache by browsers, so if you had a thousand page views,\n * and tried to work out the energy per visit, the numbers would reflect the reduced amounts\n * of transfer.\n *\n * @param {number} bytes - the data transferred in bytes for loading a webpage\n * @param {number} firstView - what percentage of visits are loading this page for the first time\n * @param {number} returnView - what percentage of visits are loading this page for subsequent times\n * @param {number} dataReloadRatio - what percentage of a page is reloaded on each subsequent page view\n *\n * @return {object} Object containing the energy in kilowatt hours, keyed by system component\n */\n energyPerVisitByComponent(\n bytes,\n firstView = FIRST_TIME_VIEWING_PERCENTAGE,\n returnView = RETURNING_VISITOR_PERCENTAGE,\n dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD\n ) {\n const energyBycomponent = this.energyPerByteByComponent(bytes);\n const cacheAdjustedSegmentEnergy = {};\n\n log({ energyBycomponent });\n const energyValues = Object.values(energyBycomponent);\n\n // sanity check that these numbers add back up\n const v9recombinedNoCaching = energyValues.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n\n // energyBycomponent doesn't apply any caching logic, to should be the\n // same number as the total in the v8\n log({ v9recombinedNoCaching });\n\n // for this, we want\n for (const [key, value] of Object.entries(energyBycomponent)) {\n // represent the first load\n cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;\n\n // then represent the subsequent load\n cacheAdjustedSegmentEnergy[`${key} - subsequent`] =\n value * returnView * dataReloadRatio;\n }\n log({ cacheAdjustedSegmentEnergy });\n\n return cacheAdjustedSegmentEnergy;\n }\n\n /**\n * Accept a figure for bytes, and return the total figure for energy per visit\n * using the default caching assumptions for loading a single website\n *\n * @param {number} bytes\n * @return {number} the total energy use for the visit, after applying the caching assumptions\n */\n energyPerVisit(bytes) {\n // fetch the values using the default caching assumptions\n // const energyValues = Object.values(this.energyPerVisitByComponent(bytes));\n\n let v9firstVisits = 0;\n let v9subsequentVisits = 0;\n\n const energyBycomponent = Object.entries(\n this.energyPerVisitByComponent(bytes)\n );\n\n for (const [key, val] of energyBycomponent) {\n if (key.indexOf(\"first\") > 0) {\n v9firstVisits += val;\n }\n }\n\n for (const [key, val] of energyBycomponent) {\n if (key.indexOf(\"subsequent\") > 0) {\n v9subsequentVisits += val;\n }\n }\n\n log({ v9firstVisits });\n log({ v9subsequentVisits });\n return v9firstVisits + v9subsequentVisits;\n }\n\n /*\n * JUST FOR TEST PURPOSES\n * Testing v0.8.0 => v0.9.0 versions\n *\n *\n */\n energyPerVisitV8(bytes) {\n log({ bytes });\n const transferedBytesToGb = bytes / fileSize.GIGABYTE;\n\n const v8visitWithNoCaching = transferedBytesToGb * KWH_PER_GB;\n\n const v8firstVisit =\n transferedBytesToGb * KWH_PER_GB * RETURNING_VISITOR_PERCENTAGE;\n\n const v8subsequentVisits =\n transferedBytesToGb *\n KWH_PER_GB *\n FIRST_TIME_VIEWING_PERCENTAGE *\n PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD;\n\n const v8firstAndSubsequentVisits = v8firstVisit + v8subsequentVisits;\n\n log({ v8visitWithNoCaching });\n log({ v8firstVisit });\n log({ v8subsequentVisits });\n log({ v8firstAndSubsequentVisits });\n\n return v8firstVisit + v8subsequentVisits;\n }\n\n // TODO: this method looks like it applies the carbon intensity\n // change to the *entire* system, not just the datacenter.\n emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {\n return formatNumber(energyPerVisit * carbonintensity);\n }\n\n annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1000) {\n return energyPerVisit * monthlyVisitors * 12;\n }\n\n annualEmissionsInGrams(co2grams, monthlyVisitors = 1000) {\n return co2grams * monthlyVisitors * 12;\n }\n\n annualSegmentEnergy(annualEnergy) {\n return {\n consumerDeviceEnergy: formatNumber(annualEnergy * END_USER_DEVICE_ENERGY),\n networkEnergy: formatNumber(annualEnergy * NETWORK_ENERGY),\n dataCenterEnergy: formatNumber(annualEnergy * DATACENTER_ENERGY),\n productionEnergy: formatNumber(annualEnergy * PRODUCTION_ENERGY),\n };\n }\n}\n\nexport { SustainableWebDesign };\nexport default SustainableWebDesign;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use strict\";\n\n/**\n * Sustainable Web Design\n *\n * Updated calculations and figures from\n * https://sustainablewebdesign.org/calculating-digital-emissions/\n *\n *\n */\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:sustainable-web-design\");\n\nimport { fileSize } from \"./constants/index.js\";\nimport { formatNumber } from \"./helpers/index.js\";\n\n// this refers to the estimated total energy use for the internet around 2000 TWh,\n// divided by the total transfer it enables around 2500 exabytes\nconst KWH_PER_GB = 0.81;\n\n// these constants outline how the energy is attributed to\n// different parts of the system in the SWD model\nconst END_USER_DEVICE_ENERGY = 0.52;\nconst NETWORK_ENERGY = 0.14;\nconst DATACENTER_ENERGY = 0.15;\nconst PRODUCTION_ENERGY = 0.19;\n\n// These carbon intensity figures https://ember-climate.org/data/data-explorer\n// - Global carbon intensity for 2021\nconst GLOBAL_INTENSITY = 442;\nconst RENEWABLES_INTENSITY = 50;\n\n// Taken from: https://gitlab.com/wholegrain/carbon-api-2-0/-/blob/master/includes/carbonapi.php\n\nconst FIRST_TIME_VIEWING_PERCENTAGE = 0.75;\nconst RETURNING_VISITOR_PERCENTAGE = 0.25;\nconst PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD = 0.02;\n\nclass SustainableWebDesign {\n constructor(options) {\n this.options = options;\n }\n\n /**\n * Accept a figure for bytes transferred and return an object representing\n * the share of the total enrgy use of the entire system, broken down\n * by each corresponding system component\n *\n * @param {number} bytes - the data transferred in bytes\n * @return {object} Object containing the energy in kilowatt hours, keyed by system component\n */\n energyPerByteByComponent(bytes) {\n const transferedBytesToGb = bytes / fileSize.GIGABYTE;\n const energyUsage = transferedBytesToGb * KWH_PER_GB;\n\n // return the total energy, with breakdown by component\n return {\n consumerDeviceEnergy: energyUsage * END_USER_DEVICE_ENERGY,\n networkEnergy: energyUsage * NETWORK_ENERGY,\n productionEnergy: energyUsage * PRODUCTION_ENERGY,\n dataCenterEnergy: energyUsage * DATACENTER_ENERGY,\n };\n }\n /**\n * Accept an object keys by the different system components, and\n * return an object with the co2 figures key by the each component\n *\n * @param {object} energyBycomponent - energy grouped by the four system components\n * @param {number} [carbonIntensity] - carbon intensity to apply to the datacentre values\n * @return {number} the total number in grams of CO2 equivalent emissions\n */\n co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {\n const returnCO2ByComponent = {};\n for (const [key, value] of Object.entries(energyBycomponent)) {\n // we update the datacentre, as that's what we have information\n // about.\n if (key.startsWith(\"dataCenterEnergy\")) {\n console.log(key)\n returnCO2ByComponent[key] = value * carbonIntensity;\n console.log(returnCO2ByComponent[key])\n } else {\n // We don't have info about the device location,\n // nor the network path used, nor the production emissions\n // so we revert to global figures\n returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;\n }\n }\n return returnCO2ByComponent;\n }\n\n /**\n * Accept a figure for bytes transferred and return a single figure for CO2\n * emissions. Where information exists about the origin data is being\n * fetched from, a different carbon intensity figure\n * is applied for the datacentre share of the carbon intensity.\n *\n * @param {number} bytes - the data transferred in bytes\n * @param {number} `carbonIntensity` the carbon intensity for datacentre (average figures, not marginal ones)\n * @return {number} the total number in grams of CO2 equivalent emissions\n */\n perByte(bytes, carbonIntensity = GLOBAL_INTENSITY) {\n const energyBycomponent = this.energyPerByteByComponent(bytes);\n\n // when faced with falsy values, fallback to global intensity\n if (Boolean(carbonIntensity) === false) {\n carbonIntensity = GLOBAL_INTENSITY;\n }\n // if we have a boolean, we have a green result from the green web checker\n // use the renewables intensity\n if (carbonIntensity === true) {\n carbonIntensity = RENEWABLES_INTENSITY;\n }\n\n // otherwise when faced with non numeric values throw an error\n if (typeof carbonIntensity !== \"number\") {\n throw new Error(\n `perByte expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`\n );\n }\n\n const co2ValuesbyComponent = this.co2byComponent(\n energyBycomponent,\n carbonIntensity\n );\n\n // pull out our values\u2026\n const co2Values = Object.values(co2ValuesbyComponent);\n\n // so we can return their sum\n return co2Values.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n }\n\n /**\n * Accept a figure for bytes transferred and return a single figure for CO2\n * emissions. This method applies caching assumptions from the original Sustainable Web Design model.\n *\n * @param {number} bytes - the data transferred in bytes\n * @param {number} `carbonIntensity` the carbon intensity for datacentre (average figures, not marginal ones)\n * @return {number} the total number in grams of CO2 equivalent emissions\n */\n perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {\n const energyBycomponent = this.energyPerVisitByComponent(bytes);\n\n // when faced with falsy values, fallback to global intensity\n if (Boolean(carbonIntensity) === false) {\n carbonIntensity = GLOBAL_INTENSITY;\n }\n // if we have a boolean, we have a green result from the green web checker\n // use the renewables intensity\n if (carbonIntensity === true) {\n carbonIntensity = RENEWABLES_INTENSITY;\n }\n\n // otherwise when faced with non numeric values throw an error\n if (typeof carbonIntensity !== \"number\") {\n throw new Error(\n `perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`\n );\n }\n\n const co2ValuesbyComponent = this.co2byComponent(\n energyBycomponent,\n carbonIntensity\n );\n\n // pull out our values\u2026\n const co2Values = Object.values(co2ValuesbyComponent);\n\n // so we can return their sum\n return co2Values.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n }\n\n /**\n * Accept a figure for bytes transferred and return the number of kilowatt hours used\n * by the total system for this data transfer\n *\n * @param {number} bytes\n * @return {number} the number of kilowatt hours used\n */\n energyPerByte(bytes) {\n const energyByComponent = this.energyPerByteByComponent(bytes);\n\n // pull out our values\u2026\n const energyValues = Object.values(energyByComponent);\n\n // so we can return their sum\n return energyValues.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n }\n\n /**\n * Accept a figure for bytes transferred, and return an object containing figures\n * per system component, with the caching assumptions applied. This tries to account\n * for webpages being loaded from a cache by browsers, so if you had a thousand page views,\n * and tried to work out the energy per visit, the numbers would reflect the reduced amounts\n * of transfer.\n *\n * @param {number} bytes - the data transferred in bytes for loading a webpage\n * @param {number} firstView - what percentage of visits are loading this page for the first time\n * @param {number} returnView - what percentage of visits are loading this page for subsequent times\n * @param {number} dataReloadRatio - what percentage of a page is reloaded on each subsequent page view\n *\n * @return {object} Object containing the energy in kilowatt hours, keyed by system component\n */\n energyPerVisitByComponent(\n bytes,\n firstView = FIRST_TIME_VIEWING_PERCENTAGE,\n returnView = RETURNING_VISITOR_PERCENTAGE,\n dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD\n ) {\n const energyBycomponent = this.energyPerByteByComponent(bytes);\n const cacheAdjustedSegmentEnergy = {};\n\n log({ energyBycomponent });\n const energyValues = Object.values(energyBycomponent);\n\n // for this, we want\n for (const [key, value] of Object.entries(energyBycomponent)) {\n // represent the first load\n cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;\n\n // then represent the subsequent load\n cacheAdjustedSegmentEnergy[`${key} - subsequent`] =\n value * returnView * dataReloadRatio;\n }\n log({ cacheAdjustedSegmentEnergy });\n\n return cacheAdjustedSegmentEnergy;\n }\n\n /**\n * Accept a figure for bytes, and return the total figure for energy per visit\n * using the default caching assumptions for loading a single website\n *\n * @param {number} bytes\n * @return {number} the total energy use for the visit, after applying the caching assumptions\n */\n energyPerVisit(bytes) {\n // fetch the values using the default caching assumptions\n // const energyValues = Object.values(this.energyPerVisitByComponent(bytes));\n\n let firstVisits = 0;\n let subsequentVisits = 0;\n\n const energyBycomponent = Object.entries(\n this.energyPerVisitByComponent(bytes)\n );\n\n for (const [key, val] of energyBycomponent) {\n if (key.indexOf(\"first\") > 0) {\n firstVisits += val;\n }\n }\n\n for (const [key, val] of energyBycomponent) {\n if (key.indexOf(\"subsequent\") > 0) {\n subsequentVisits += val;\n }\n }\n\n return firstVisits + subsequentVisits;\n }\n\n // TODO: this method looks like it applies the carbon intensity\n // change to the *entire* system, not just the datacenter.\n emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {\n return formatNumber(energyPerVisit * carbonintensity);\n }\n\n annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1000) {\n return energyPerVisit * monthlyVisitors * 12;\n }\n\n annualEmissionsInGrams(co2grams, monthlyVisitors = 1000) {\n return co2grams * monthlyVisitors * 12;\n }\n\n annualSegmentEnergy(annualEnergy) {\n return {\n consumerDeviceEnergy: formatNumber(annualEnergy * END_USER_DEVICE_ENERGY),\n networkEnergy: formatNumber(annualEnergy * NETWORK_ENERGY),\n dataCenterEnergy: formatNumber(annualEnergy * DATACENTER_ENERGY),\n productionEnergy: formatNumber(annualEnergy * PRODUCTION_ENERGY),\n };\n }\n}\n\nexport { SustainableWebDesign };\nexport default SustainableWebDesign;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }