@tgwf/co2 0.10.1 → 0.10.4

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.
package/CHANGELOG.md CHANGED
@@ -12,11 +12,33 @@ 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
+ ## 0.10.4 2022-08-12
18
+
19
+ - 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.
20
+
21
+ ### Added
22
+
23
+ ## 0.10.3 2022-08-12
24
+
25
+ ### Added
26
+
27
+ - Introduced a new `perVisit()` function for the Sustainable Web Design model, which applies [caching and return visits assumptions](https://sustainablewebdesign.org/calculating-digital-emissions/).
28
+
29
+ ## 0.10.2 2022-08-12
30
+
31
+ - 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.
32
+
33
+ ## 0.10.1 2022-08-01
34
+
35
+ This release used a version bump as previously we had released v0.10.0 under a pre-release tag.
36
+
15
37
  ## 0.10.0 2022-06-27
16
38
 
17
39
  - Added ES import syntax as the main way for handling imports and exports of code within the module.
18
40
  - Changed eslint settings to use later version of ecmascript (2020)
19
- - Change the bulid tools to use esbulid with jest instead of babel
41
+ - Change the build tools to use esbulid with jest instead of babel
20
42
  - Added more consistent use of the debug logging library in files using the updated import syntax
21
43
  - 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
44
 
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
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.
6
6
 
7
7
  ## [Documentation](https://developers.thegreenwebfoundation.org/co2js/overview/)
8
+ ## [Changelog](/CHANGELOG.md)
8
9
 
9
10
  ## What is CO2.js?
10
11
 
@@ -52,6 +53,14 @@ You can also build the CO2.js library from the source code. To do this:
52
53
  - `dist/esm` - An ES Modules compatible build.
53
54
  - `dist/iife` - An Immediately Invoked Function Expression (IIFE) version of the library.
54
55
 
56
+ ## Publishing to NPM
57
+
58
+ We use [`np`](https://www.npmjs.com/package/np) to publish new versions of this library to NPM. To do this:
59
+
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.
55
64
  ## Licenses
56
65
 
57
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
32
  this.model = new import_byte.default();
33
- if (options) {
34
- this.model = new options.model();
33
+ if ((options == null ? void 0 : options.model) === "swd") {
34
+ this.model = new import_sustainable_web_design.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": "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",
4
+ "sourcesContent": ["\"use strict\";\n\nimport OneByte from \"./1byte.js\";\nimport SustainableWebDesign from \"./sustainable-web-design.js\";\n\nclass CO2 {\n constructor(options) {\n this.model = new OneByte();\n // Using optional chaining allows an empty object to be passed\n // in without breaking the code.\n if (options?.model === \"swd\") {\n this.model = new SustainableWebDesign();\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(\n \"The model you have selected does not support perVisit. Using perByte instead.\"\n );\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
  }
@@ -41,6 +41,11 @@ describe("co2", () => {
41
41
  expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_GREEN.toPrecision(5));
42
42
  });
43
43
  });
44
+ describe("perVisit", () => {
45
+ it("returns a CO2 number for data transfer using 'grey' power", () => {
46
+ expect(co2.perVisit(MILLION).toPrecision(5)).toBe(MILLION_GREY.toPrecision(5));
47
+ });
48
+ });
44
49
  describe("perPage", () => {
45
50
  it("returns CO2 for total transfer for page", () => {
46
51
  const pages = import_pagexray.default.convert(har);
@@ -130,15 +135,17 @@ describe("co2", () => {
130
135
  });
131
136
  });
132
137
  });
133
- describe("Sustainable Web Design model", () => {
138
+ describe("Sustainable Web Design model as simple option", () => {
134
139
  const MILLION = 1e6;
135
- const MILLION_GREY = 0.33343;
136
- const MILLION_GREEN = 0.28908;
137
- const TGWF_GREY_VALUE = 0.23501;
140
+ const MILLION_GREY = 0.35802;
141
+ const MILLION_GREEN = 0.31039;
142
+ const MILLION_PERVISIT_GREY = 0.27031;
143
+ const MILLION_PERVISIT_GREEN = 0.23435;
144
+ const TGWF_GREY_VALUE = 0.25234;
138
145
  const TGWF_GREEN_VALUE = 0.54704;
139
- const TGWF_MIXED_VALUE = 0.20652;
146
+ const TGWF_MIXED_VALUE = 0.22175;
140
147
  beforeEach(() => {
141
- co2 = new import_co2.default({ model: import_sustainable_web_design.default });
148
+ co2 = new import_co2.default({ model: "swd" });
142
149
  har = JSON.parse(import_fs.default.readFileSync(import_path.default.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
143
150
  });
144
151
  describe("perByte", () => {
@@ -151,6 +158,16 @@ describe("co2", () => {
151
158
  expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_GREEN.toPrecision(5));
152
159
  });
153
160
  });
161
+ describe("perVisit", () => {
162
+ it("returns a CO2 number for data transfer per visit with caching assumptions from the Sustainable Web Design model", () => {
163
+ co2.perVisit(MILLION);
164
+ expect(co2.perVisit(MILLION).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
165
+ });
166
+ it("returns a lower CO2 number for data transfer from domains using entirely 'green' power", () => {
167
+ expect(co2.perVisit(MILLION, false).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
168
+ expect(co2.perVisit(MILLION, true).toPrecision(5)).toBe(MILLION_PERVISIT_GREEN.toPrecision(5));
169
+ });
170
+ });
154
171
  describe("perPage", () => {
155
172
  it("returns CO2 for total transfer for page", () => {
156
173
  const pages = import_pagexray.default.convert(har);
@@ -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 // We're not passing in a model parameter here to ensure that 1byte gets used as 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 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 // This test is to make sure that the fallback works.\n // Since there is no perVisit fuction in the 1byte model, the perByte function is used.\n describe(\"perVisit\", () => {\n it(\"returns a CO2 number for data transfer using 'grey' power\", () => {\n expect(co2.perVisit(MILLION).toPrecision(5)).toBe(\n MILLION_GREY.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.23435;\n\n const TGWF_GREY_VALUE = 0.25234;\n const TGWF_GREEN_VALUE = 0.54704;\n const TGWF_MIXED_VALUE = 0.22175;\n\n // Passing in the SWD parameter here\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(\"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.perVisit(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"],
5
+ "mappings": "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",
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,7 +56,7 @@ 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
60
  returnCO2ByComponent[key] = value * carbonIntensity;
61
61
  } else {
62
62
  returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;
@@ -79,6 +79,21 @@ class SustainableWebDesign {
79
79
  const co2Values = Object.values(co2ValuesbyComponent);
80
80
  return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
81
81
  }
82
+ perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {
83
+ const energyBycomponent = this.energyPerVisitByComponent(bytes);
84
+ if (Boolean(carbonIntensity) === false) {
85
+ carbonIntensity = GLOBAL_INTENSITY;
86
+ }
87
+ if (carbonIntensity === true) {
88
+ carbonIntensity = RENEWABLES_INTENSITY;
89
+ }
90
+ if (typeof carbonIntensity !== "number") {
91
+ throw new Error(`perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`);
92
+ }
93
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity);
94
+ const co2Values = Object.values(co2ValuesbyComponent);
95
+ return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
96
+ }
82
97
  energyPerByte(bytes) {
83
98
  const energyByComponent = this.energyPerByteByComponent(bytes);
84
99
  const energyValues = Object.values(energyByComponent);
@@ -89,8 +104,6 @@ class SustainableWebDesign {
89
104
  const cacheAdjustedSegmentEnergy = {};
90
105
  log({ energyBycomponent });
91
106
  const energyValues = Object.values(energyBycomponent);
92
- const v9recombinedNoCaching = energyValues.reduce((prevValue, currentValue) => prevValue + currentValue);
93
- log({ v9recombinedNoCaching });
94
107
  for (const [key, value] of Object.entries(energyBycomponent)) {
95
108
  cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;
96
109
  cacheAdjustedSegmentEnergy[`${key} - subsequent`] = value * returnView * dataReloadRatio;
@@ -99,35 +112,20 @@ class SustainableWebDesign {
99
112
  return cacheAdjustedSegmentEnergy;
100
113
  }
101
114
  energyPerVisit(bytes) {
102
- let v9firstVisits = 0;
103
- let v9subsequentVisits = 0;
115
+ let firstVisits = 0;
116
+ let subsequentVisits = 0;
104
117
  const energyBycomponent = Object.entries(this.energyPerVisitByComponent(bytes));
105
118
  for (const [key, val] of energyBycomponent) {
106
119
  if (key.indexOf("first") > 0) {
107
- v9firstVisits += val;
120
+ firstVisits += val;
108
121
  }
109
122
  }
110
123
  for (const [key, val] of energyBycomponent) {
111
124
  if (key.indexOf("subsequent") > 0) {
112
- v9subsequentVisits += val;
125
+ subsequentVisits += val;
113
126
  }
114
127
  }
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;
128
+ return firstVisits + subsequentVisits;
131
129
  }
132
130
  emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {
133
131
  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 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 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
  }
@@ -20,16 +20,16 @@ describe("sustainable web design model", () => {
20
20
  describe("energyPerByteByComponent", () => {
21
21
  it("should return a object with numbers for each system component", () => {
22
22
  const groupedEnergy = swd.energyPerByteByComponent(averageWebsiteInBytes);
23
- expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(88564e-8, 8);
24
- expect(groupedEnergy.networkEnergy).toBeCloseTo(23844e-8, 8);
25
- expect(groupedEnergy.productionEnergy).toBeCloseTo(3236e-7, 8);
26
- expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(25547e-8, 8);
23
+ expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(95095e-8, 8);
24
+ expect(groupedEnergy.networkEnergy).toBeCloseTo(25602e-8, 7);
25
+ expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(27431e-8, 8);
26
+ expect(groupedEnergy.productionEnergy).toBeCloseTo(3475e-7, 7);
27
27
  });
28
28
  });
29
29
  describe("energyPerByte", () => {
30
30
  it("should return a number in kilowatt hours for the given data transfer in bytes", () => {
31
31
  const energyForTransfer = swd.energyPerByte(averageWebsiteInBytes);
32
- expect(energyForTransfer).toBeCloseTo(170316e-8, 7);
32
+ expect(energyForTransfer).toBeCloseTo(182874e-8, 7);
33
33
  });
34
34
  });
35
35
  describe("perByte", () => {
@@ -37,28 +37,27 @@ describe("sustainable web design model", () => {
37
37
  expect(typeof swd.perByte(22577152e-1)).toBe("number");
38
38
  });
39
39
  });
40
+ describe("perVisit", () => {
41
+ it("should return a single number for CO2 emissions", () => {
42
+ expect(typeof swd.perVisit(22577152e-1)).toBe("number");
43
+ });
44
+ });
40
45
  describe("energyPerVisit", () => {
41
46
  it("should return a number", () => {
42
47
  expect(typeof swd.energyPerVisit(averageWebsiteInBytes)).toBe("number");
43
48
  });
44
49
  it("should calculate the correct energy", () => {
45
- expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(0.001285882415771485);
46
- });
47
- it("should match the old calculations", () => {
48
- const v9currentEnergyCalc = swd.energyPerVisit(averageWebsiteInBytes);
49
- const v8VersionEnergyCalc = swd.energyPerVisitV8(averageWebsiteInBytes);
50
- expect(swd.emissionsPerVisitInGrams(v9currentEnergyCalc)).toEqual(0.57);
51
- expect(swd.emissionsPerVisitInGrams(v8VersionEnergyCalc)).toEqual(0.2);
50
+ expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(0.0013807057305600004);
52
51
  });
53
52
  });
54
53
  describe("emissionsPerVisitInGrams", () => {
55
54
  it("should calculate the correct co2 per visit", () => {
56
55
  const energy = swd.energyPerVisit(averageWebsiteInBytes);
57
- expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.57);
56
+ expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.61);
58
57
  });
59
58
  it("should accept a dynamic KwH value", () => {
60
59
  const energy = swd.energyPerVisit(averageWebsiteInBytes);
61
- expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.32);
60
+ expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.34);
62
61
  });
63
62
  });
64
63
  describe("annualEnergyInKwh", () => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/sustainable-web-design.test.js"],
4
- "sourcesContent": ["import SustainableWebDesign from \"./sustainable-web-design.js\";\n\ndescribe(\"sustainable web design model\", () => {\n const swd = new SustainableWebDesign();\n const averageWebsiteInBytes = 2257715.2;\n\n describe(\"energyPerByteByComponent\", () => {\n it(\"should return a object with numbers for each system component\", () => {\n const groupedEnergy = swd.energyPerByteByComponent(averageWebsiteInBytes);\n\n expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(0.00088564, 8);\n expect(groupedEnergy.networkEnergy).toBeCloseTo(0.00023844, 8);\n expect(groupedEnergy.productionEnergy).toBeCloseTo(0.0003236, 8);\n expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(0.00025547, 8);\n });\n });\n\n describe(\"energyPerByte\", () => {\n it(\"should return a number in kilowatt hours for the given data transfer in bytes\", () => {\n const energyForTransfer = swd.energyPerByte(averageWebsiteInBytes);\n expect(energyForTransfer).toBeCloseTo(0.00170316, 7);\n });\n });\n\n describe(\"perByte\", () => {\n it(\"should return a single number for CO2 emissions\", () => {\n expect(typeof swd.perByte(2257715.2)).toBe(\"number\");\n });\n });\n\n describe(\"energyPerVisit\", () => {\n it(\"should return a number\", () => {\n expect(typeof swd.energyPerVisit(averageWebsiteInBytes)).toBe(\"number\");\n });\n\n it(\"should calculate the correct energy\", () => {\n expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(\n 0.001285882415771485\n );\n });\n\n it(\"should match the old calculations\", () => {\n // Test the v0.9.0 updates to the SWD method to identify differences\n const v9currentEnergyCalc = swd.energyPerVisit(averageWebsiteInBytes);\n const v8VersionEnergyCalc = swd.energyPerVisitV8(averageWebsiteInBytes);\n\n // expect(currentEnergyCalc).toBe(0.0004513362121582032);\n // expect(oldVersionEnergyCalc).toBe(0.0012858824157714846);\n\n // with the constants switched around so the first view is 0.75, now 0.25\n // we should we the page come back at 0.57g not 0.2\n expect(swd.emissionsPerVisitInGrams(v9currentEnergyCalc)).toEqual(0.57);\n expect(swd.emissionsPerVisitInGrams(v8VersionEnergyCalc)).toEqual(0.2);\n });\n });\n\n describe(\"emissionsPerVisitInGrams\", () => {\n it(\"should calculate the correct co2 per visit\", () => {\n const energy = swd.energyPerVisit(averageWebsiteInBytes);\n expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.57);\n });\n\n it(\"should accept a dynamic KwH value\", () => {\n const energy = swd.energyPerVisit(averageWebsiteInBytes);\n expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.32);\n });\n });\n\n describe(\"annualEnergyInKwh\", () => {\n it(\"should calculate the correct energy in kWh\", () => {\n expect(swd.annualEnergyInKwh(averageWebsiteInBytes)).toBe(27092582400);\n });\n });\n\n describe(\"annualEmissionsInGrams\", () => {\n it(\"should calculate the corrent energy in grams\", () => {\n expect(swd.annualEmissionsInGrams(averageWebsiteInBytes)).toBe(\n 27092582400\n );\n });\n });\n\n describe(\"annualSegmentEnergy\", () => {\n it(\"should return the correct values\", () => {\n expect(swd.annualSegmentEnergy(averageWebsiteInBytes)).toEqual({\n consumerDeviceEnergy: 1174011.9,\n dataCenterEnergy: 338657.28,\n networkEnergy: 316080.13,\n productionEnergy: 428965.89,\n });\n });\n });\n});\n"],
5
- "mappings": ";;;;;;;;;;;;;;;AAAA,oCAAiC;AAEjC,SAAS,gCAAgC,MAAM;AAC7C,QAAM,MAAM,IAAI,sCAAqB;AACrC,QAAM,wBAAwB;AAE9B,WAAS,4BAA4B,MAAM;AACzC,OAAG,iEAAiE,MAAM;AACxE,YAAM,gBAAgB,IAAI,yBAAyB,qBAAqB;AAExE,aAAO,cAAc,oBAAoB,EAAE,YAAY,UAAY,CAAC;AACpE,aAAO,cAAc,aAAa,EAAE,YAAY,UAAY,CAAC;AAC7D,aAAO,cAAc,gBAAgB,EAAE,YAAY,SAAW,CAAC;AAC/D,aAAO,cAAc,gBAAgB,EAAE,YAAY,UAAY,CAAC;AAAA,IAClE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,iBAAiB,MAAM;AAC9B,OAAG,iFAAiF,MAAM;AACxF,YAAM,oBAAoB,IAAI,cAAc,qBAAqB;AACjE,aAAO,iBAAiB,EAAE,YAAY,WAAY,CAAC;AAAA,IACrD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,WAAW,MAAM;AACxB,OAAG,mDAAmD,MAAM;AAC1D,aAAO,OAAO,IAAI,QAAQ,WAAS,CAAC,EAAE,KAAK,QAAQ;AAAA,IACrD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,kBAAkB,MAAM;AAC/B,OAAG,0BAA0B,MAAM;AACjC,aAAO,OAAO,IAAI,eAAe,qBAAqB,CAAC,EAAE,KAAK,QAAQ;AAAA,IACxE,CAAC;AAED,OAAG,uCAAuC,MAAM;AAC9C,aAAO,IAAI,eAAe,qBAAqB,CAAC,EAAE,KAChD,oBACF;AAAA,IACF,CAAC;AAED,OAAG,qCAAqC,MAAM;AAE5C,YAAM,sBAAsB,IAAI,eAAe,qBAAqB;AACpE,YAAM,sBAAsB,IAAI,iBAAiB,qBAAqB;AAOtE,aAAO,IAAI,yBAAyB,mBAAmB,CAAC,EAAE,QAAQ,IAAI;AACtE,aAAO,IAAI,yBAAyB,mBAAmB,CAAC,EAAE,QAAQ,GAAG;AAAA,IACvE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,4BAA4B,MAAM;AACzC,OAAG,8CAA8C,MAAM;AACrD,YAAM,SAAS,IAAI,eAAe,qBAAqB;AACvD,aAAO,IAAI,yBAAyB,MAAM,CAAC,EAAE,QAAQ,IAAI;AAAA,IAC3D,CAAC;AAED,OAAG,qCAAqC,MAAM;AAC5C,YAAM,SAAS,IAAI,eAAe,qBAAqB;AACvD,aAAO,IAAI,yBAAyB,QAAQ,GAAG,CAAC,EAAE,QAAQ,IAAI;AAAA,IAChE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,qBAAqB,MAAM;AAClC,OAAG,8CAA8C,MAAM;AACrD,aAAO,IAAI,kBAAkB,qBAAqB,CAAC,EAAE,KAAK,WAAW;AAAA,IACvE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,0BAA0B,MAAM;AACvC,OAAG,gDAAgD,MAAM;AACvD,aAAO,IAAI,uBAAuB,qBAAqB,CAAC,EAAE,KACxD,WACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,WAAS,uBAAuB,MAAM;AACpC,OAAG,oCAAoC,MAAM;AAC3C,aAAO,IAAI,oBAAoB,qBAAqB,CAAC,EAAE,QAAQ;AAAA,QAC7D,sBAAsB;AAAA,QACtB,kBAAkB;AAAA,QAClB,eAAe;AAAA,QACf,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
4
+ "sourcesContent": ["import SustainableWebDesign from \"./sustainable-web-design.js\";\n\ndescribe(\"sustainable web design model\", () => {\n const swd = new SustainableWebDesign();\n const averageWebsiteInBytes = 2257715.2;\n\n describe(\"energyPerByteByComponent\", () => {\n it(\"should return a object with numbers for each system component\", () => {\n // Compare these with the carbon intensity tab C7-C10.\n // https://docs.google.com/spreadsheets/d/1eFlHhSBus_HqmoXqX237eAYr0PREUhTr6YBxznQC4jI/edit#gid=0\n\n // These numbers should match the spreadsheet\n const groupedEnergy = swd.energyPerByteByComponent(averageWebsiteInBytes);\n expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(0.00095095, 8);\n expect(groupedEnergy.networkEnergy).toBeCloseTo(0.00025602, 7);\n expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(0.00027431, 8);\n expect(groupedEnergy.productionEnergy).toBeCloseTo(0.0003475, 7);\n });\n });\n\n describe(\"energyPerByte\", () => {\n it(\"should return a number in kilowatt hours for the given data transfer in bytes\", () => {\n const energyForTransfer = swd.energyPerByte(averageWebsiteInBytes);\n expect(energyForTransfer).toBeCloseTo(0.00182874, 7);\n });\n });\n\n describe(\"perByte\", () => {\n it(\"should return a single number for CO2 emissions\", () => {\n expect(typeof swd.perByte(2257715.2)).toBe(\"number\");\n });\n });\n\n describe(\"perVisit\", () => {\n it(\"should return a single number for CO2 emissions\", () => {\n expect(typeof swd.perVisit(2257715.2)).toBe(\"number\");\n });\n });\n\n describe(\"energyPerVisit\", () => {\n it(\"should return a number\", () => {\n expect(typeof swd.energyPerVisit(averageWebsiteInBytes)).toBe(\"number\");\n });\n\n it(\"should calculate the correct energy\", () => {\n expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(\n 0.0013807057305600004\n );\n });\n });\n\n describe(\"emissionsPerVisitInGrams\", () => {\n it(\"should calculate the correct co2 per visit\", () => {\n const energy = swd.energyPerVisit(averageWebsiteInBytes);\n expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.61);\n });\n\n it(\"should accept a dynamic KwH value\", () => {\n const energy = swd.energyPerVisit(averageWebsiteInBytes);\n expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.34);\n });\n });\n\n describe(\"annualEnergyInKwh\", () => {\n it(\"should calculate the correct energy in kWh\", () => {\n expect(swd.annualEnergyInKwh(averageWebsiteInBytes)).toBe(27092582400);\n });\n });\n\n describe(\"annualEmissionsInGrams\", () => {\n it(\"should calculate the corrent energy in grams\", () => {\n expect(swd.annualEmissionsInGrams(averageWebsiteInBytes)).toBe(\n 27092582400\n );\n });\n });\n\n describe(\"annualSegmentEnergy\", () => {\n it(\"should return the correct values\", () => {\n expect(swd.annualSegmentEnergy(averageWebsiteInBytes)).toEqual({\n consumerDeviceEnergy: 1174011.9,\n dataCenterEnergy: 338657.28,\n networkEnergy: 316080.13,\n productionEnergy: 428965.89,\n });\n });\n });\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;AAAA,oCAAiC;AAEjC,SAAS,gCAAgC,MAAM;AAC7C,QAAM,MAAM,IAAI,sCAAqB;AACrC,QAAM,wBAAwB;AAE9B,WAAS,4BAA4B,MAAM;AACzC,OAAG,iEAAiE,MAAM;AAKxE,YAAM,gBAAgB,IAAI,yBAAyB,qBAAqB;AACxE,aAAO,cAAc,oBAAoB,EAAE,YAAY,UAAY,CAAC;AACpE,aAAO,cAAc,aAAa,EAAE,YAAY,UAAY,CAAC;AAC7D,aAAO,cAAc,gBAAgB,EAAE,YAAY,UAAY,CAAC;AAChE,aAAO,cAAc,gBAAgB,EAAE,YAAY,SAAW,CAAC;AAAA,IACjE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,iBAAiB,MAAM;AAC9B,OAAG,iFAAiF,MAAM;AACxF,YAAM,oBAAoB,IAAI,cAAc,qBAAqB;AACjE,aAAO,iBAAiB,EAAE,YAAY,WAAY,CAAC;AAAA,IACrD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,WAAW,MAAM;AACxB,OAAG,mDAAmD,MAAM;AAC1D,aAAO,OAAO,IAAI,QAAQ,WAAS,CAAC,EAAE,KAAK,QAAQ;AAAA,IACrD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,YAAY,MAAM;AACzB,OAAG,mDAAmD,MAAM;AAC1D,aAAO,OAAO,IAAI,SAAS,WAAS,CAAC,EAAE,KAAK,QAAQ;AAAA,IACtD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,kBAAkB,MAAM;AAC/B,OAAG,0BAA0B,MAAM;AACjC,aAAO,OAAO,IAAI,eAAe,qBAAqB,CAAC,EAAE,KAAK,QAAQ;AAAA,IACxE,CAAC;AAED,OAAG,uCAAuC,MAAM;AAC9C,aAAO,IAAI,eAAe,qBAAqB,CAAC,EAAE,KAChD,qBACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,WAAS,4BAA4B,MAAM;AACzC,OAAG,8CAA8C,MAAM;AACrD,YAAM,SAAS,IAAI,eAAe,qBAAqB;AACvD,aAAO,IAAI,yBAAyB,MAAM,CAAC,EAAE,QAAQ,IAAI;AAAA,IAC3D,CAAC;AAED,OAAG,qCAAqC,MAAM;AAC5C,YAAM,SAAS,IAAI,eAAe,qBAAqB;AACvD,aAAO,IAAI,yBAAyB,QAAQ,GAAG,CAAC,EAAE,QAAQ,IAAI;AAAA,IAChE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,qBAAqB,MAAM;AAClC,OAAG,8CAA8C,MAAM;AACrD,aAAO,IAAI,kBAAkB,qBAAqB,CAAC,EAAE,KAAK,WAAW;AAAA,IACvE,CAAC;AAAA,EACH,CAAC;AAED,WAAS,0BAA0B,MAAM;AACvC,OAAG,gDAAgD,MAAM;AACvD,aAAO,IAAI,uBAAuB,qBAAqB,CAAC,EAAE,KACxD,WACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,WAAS,uBAAuB,MAAM;AACpC,OAAG,oCAAoC,MAAM;AAC3C,aAAO,IAAI,oBAAoB,qBAAqB,CAAC,EAAE,QAAQ;AAAA,QAC7D,sBAAsB;AAAA,QACtB,kBAAkB;AAAA,QAClB,eAAe;AAAA,QACf,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
6
6
  "names": []
7
7
  }
package/dist/esm/co2.js CHANGED
@@ -1,16 +1,25 @@
1
1
  "use strict";
2
2
  import OneByte from "./1byte.js";
3
+ import SustainableWebDesign from "./sustainable-web-design.js";
3
4
  class CO2 {
4
5
  constructor(options) {
5
- this.options = options;
6
6
  this.model = new OneByte();
7
- if (options) {
8
- this.model = new options.model();
7
+ if ((options == null ? void 0 : options.model) === "swd") {
8
+ this.model = new SustainableWebDesign();
9
9
  }
10
10
  }
11
11
  perByte(bytes, green) {
12
12
  return this.model.perByte(bytes, green);
13
13
  }
14
+ perVisit(bytes, green) {
15
+ var _a;
16
+ if ((_a = this.model) == null ? void 0 : _a.perVisit) {
17
+ return this.model.perVisit(bytes, green);
18
+ } else {
19
+ console.warn("The model you have selected does not support perVisit. Using perByte instead.");
20
+ return this.model.perByte(bytes, green);
21
+ }
22
+ }
14
23
  perDomain(pageXray, greenDomains) {
15
24
  const co2PerDomain = [];
16
25
  for (let domain of Object.keys(pageXray.domains)) {
@@ -3,7 +3,7 @@ import fs from "fs";
3
3
  import path from "path";
4
4
  import pagexray from "pagexray";
5
5
  import CO2 from "./co2.js";
6
- import swd from "./sustainable-web-design.js";
6
+ import SustainableWebDesign from "./sustainable-web-design.js";
7
7
  describe("co2", () => {
8
8
  let har, co2;
9
9
  describe("1 byte model", () => {
@@ -26,6 +26,11 @@ describe("co2", () => {
26
26
  expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_GREEN.toPrecision(5));
27
27
  });
28
28
  });
29
+ describe("perVisit", () => {
30
+ it("returns a CO2 number for data transfer using 'grey' power", () => {
31
+ expect(co2.perVisit(MILLION).toPrecision(5)).toBe(MILLION_GREY.toPrecision(5));
32
+ });
33
+ });
29
34
  describe("perPage", () => {
30
35
  it("returns CO2 for total transfer for page", () => {
31
36
  const pages = pagexray.convert(har);
@@ -115,15 +120,17 @@ describe("co2", () => {
115
120
  });
116
121
  });
117
122
  });
118
- describe("Sustainable Web Design model", () => {
123
+ describe("Sustainable Web Design model as simple option", () => {
119
124
  const MILLION = 1e6;
120
- const MILLION_GREY = 0.33343;
121
- const MILLION_GREEN = 0.28908;
122
- const TGWF_GREY_VALUE = 0.23501;
125
+ const MILLION_GREY = 0.35802;
126
+ const MILLION_GREEN = 0.31039;
127
+ const MILLION_PERVISIT_GREY = 0.27031;
128
+ const MILLION_PERVISIT_GREEN = 0.23435;
129
+ const TGWF_GREY_VALUE = 0.25234;
123
130
  const TGWF_GREEN_VALUE = 0.54704;
124
- const TGWF_MIXED_VALUE = 0.20652;
131
+ const TGWF_MIXED_VALUE = 0.22175;
125
132
  beforeEach(() => {
126
- co2 = new CO2({ model: swd });
133
+ co2 = new CO2({ model: "swd" });
127
134
  har = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
128
135
  });
129
136
  describe("perByte", () => {
@@ -136,6 +143,16 @@ describe("co2", () => {
136
143
  expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(MILLION_GREEN.toPrecision(5));
137
144
  });
138
145
  });
146
+ describe("perVisit", () => {
147
+ it("returns a CO2 number for data transfer per visit with caching assumptions from the Sustainable Web Design model", () => {
148
+ co2.perVisit(MILLION);
149
+ expect(co2.perVisit(MILLION).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
150
+ });
151
+ it("returns a lower CO2 number for data transfer from domains using entirely 'green' power", () => {
152
+ expect(co2.perVisit(MILLION, false).toPrecision(5)).toBe(MILLION_PERVISIT_GREY.toPrecision(5));
153
+ expect(co2.perVisit(MILLION, true).toPrecision(5)).toBe(MILLION_PERVISIT_GREEN.toPrecision(5));
154
+ });
155
+ });
139
156
  describe("perPage", () => {
140
157
  it("returns CO2 for total transfer for page", () => {
141
158
  const pages = pagexray.convert(har);
@@ -1,4 +1,4 @@
1
- const GIGABYTE = 1024 * 1024 * 1024;
1
+ const GIGABYTE = 1e3 * 1e3 * 1e3;
2
2
  var file_size_default = {
3
3
  GIGABYTE
4
4
  };
@@ -30,7 +30,7 @@ class SustainableWebDesign {
30
30
  co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {
31
31
  const returnCO2ByComponent = {};
32
32
  for (const [key, value] of Object.entries(energyBycomponent)) {
33
- if (key === "dataCenterEnergy") {
33
+ if (key.startsWith("dataCenterEnergy")) {
34
34
  returnCO2ByComponent[key] = value * carbonIntensity;
35
35
  } else {
36
36
  returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;
@@ -53,6 +53,21 @@ class SustainableWebDesign {
53
53
  const co2Values = Object.values(co2ValuesbyComponent);
54
54
  return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
55
55
  }
56
+ perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {
57
+ const energyBycomponent = this.energyPerVisitByComponent(bytes);
58
+ if (Boolean(carbonIntensity) === false) {
59
+ carbonIntensity = GLOBAL_INTENSITY;
60
+ }
61
+ if (carbonIntensity === true) {
62
+ carbonIntensity = RENEWABLES_INTENSITY;
63
+ }
64
+ if (typeof carbonIntensity !== "number") {
65
+ throw new Error(`perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`);
66
+ }
67
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity);
68
+ const co2Values = Object.values(co2ValuesbyComponent);
69
+ return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
70
+ }
56
71
  energyPerByte(bytes) {
57
72
  const energyByComponent = this.energyPerByteByComponent(bytes);
58
73
  const energyValues = Object.values(energyByComponent);
@@ -63,8 +78,6 @@ class SustainableWebDesign {
63
78
  const cacheAdjustedSegmentEnergy = {};
64
79
  log({ energyBycomponent });
65
80
  const energyValues = Object.values(energyBycomponent);
66
- const v9recombinedNoCaching = energyValues.reduce((prevValue, currentValue) => prevValue + currentValue);
67
- log({ v9recombinedNoCaching });
68
81
  for (const [key, value] of Object.entries(energyBycomponent)) {
69
82
  cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;
70
83
  cacheAdjustedSegmentEnergy[`${key} - subsequent`] = value * returnView * dataReloadRatio;
@@ -73,35 +86,20 @@ class SustainableWebDesign {
73
86
  return cacheAdjustedSegmentEnergy;
74
87
  }
75
88
  energyPerVisit(bytes) {
76
- let v9firstVisits = 0;
77
- let v9subsequentVisits = 0;
89
+ let firstVisits = 0;
90
+ let subsequentVisits = 0;
78
91
  const energyBycomponent = Object.entries(this.energyPerVisitByComponent(bytes));
79
92
  for (const [key, val] of energyBycomponent) {
80
93
  if (key.indexOf("first") > 0) {
81
- v9firstVisits += val;
94
+ firstVisits += val;
82
95
  }
83
96
  }
84
97
  for (const [key, val] of energyBycomponent) {
85
98
  if (key.indexOf("subsequent") > 0) {
86
- v9subsequentVisits += val;
99
+ subsequentVisits += val;
87
100
  }
88
101
  }
89
- log({ v9firstVisits });
90
- log({ v9subsequentVisits });
91
- return v9firstVisits + v9subsequentVisits;
92
- }
93
- energyPerVisitV8(bytes) {
94
- log({ bytes });
95
- const transferedBytesToGb = bytes / fileSize.GIGABYTE;
96
- const v8visitWithNoCaching = transferedBytesToGb * KWH_PER_GB;
97
- const v8firstVisit = transferedBytesToGb * KWH_PER_GB * RETURNING_VISITOR_PERCENTAGE;
98
- const v8subsequentVisits = transferedBytesToGb * KWH_PER_GB * FIRST_TIME_VIEWING_PERCENTAGE * PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD;
99
- const v8firstAndSubsequentVisits = v8firstVisit + v8subsequentVisits;
100
- log({ v8visitWithNoCaching });
101
- log({ v8firstVisit });
102
- log({ v8subsequentVisits });
103
- log({ v8firstAndSubsequentVisits });
104
- return v8firstVisit + v8subsequentVisits;
102
+ return firstVisits + subsequentVisits;
105
103
  }
106
104
  emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {
107
105
  return formatNumber(energyPerVisit * carbonintensity);
@@ -5,16 +5,16 @@ describe("sustainable web design model", () => {
5
5
  describe("energyPerByteByComponent", () => {
6
6
  it("should return a object with numbers for each system component", () => {
7
7
  const groupedEnergy = swd.energyPerByteByComponent(averageWebsiteInBytes);
8
- expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(88564e-8, 8);
9
- expect(groupedEnergy.networkEnergy).toBeCloseTo(23844e-8, 8);
10
- expect(groupedEnergy.productionEnergy).toBeCloseTo(3236e-7, 8);
11
- expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(25547e-8, 8);
8
+ expect(groupedEnergy.consumerDeviceEnergy).toBeCloseTo(95095e-8, 8);
9
+ expect(groupedEnergy.networkEnergy).toBeCloseTo(25602e-8, 7);
10
+ expect(groupedEnergy.dataCenterEnergy).toBeCloseTo(27431e-8, 8);
11
+ expect(groupedEnergy.productionEnergy).toBeCloseTo(3475e-7, 7);
12
12
  });
13
13
  });
14
14
  describe("energyPerByte", () => {
15
15
  it("should return a number in kilowatt hours for the given data transfer in bytes", () => {
16
16
  const energyForTransfer = swd.energyPerByte(averageWebsiteInBytes);
17
- expect(energyForTransfer).toBeCloseTo(170316e-8, 7);
17
+ expect(energyForTransfer).toBeCloseTo(182874e-8, 7);
18
18
  });
19
19
  });
20
20
  describe("perByte", () => {
@@ -22,28 +22,27 @@ describe("sustainable web design model", () => {
22
22
  expect(typeof swd.perByte(22577152e-1)).toBe("number");
23
23
  });
24
24
  });
25
+ describe("perVisit", () => {
26
+ it("should return a single number for CO2 emissions", () => {
27
+ expect(typeof swd.perVisit(22577152e-1)).toBe("number");
28
+ });
29
+ });
25
30
  describe("energyPerVisit", () => {
26
31
  it("should return a number", () => {
27
32
  expect(typeof swd.energyPerVisit(averageWebsiteInBytes)).toBe("number");
28
33
  });
29
34
  it("should calculate the correct energy", () => {
30
- expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(0.001285882415771485);
31
- });
32
- it("should match the old calculations", () => {
33
- const v9currentEnergyCalc = swd.energyPerVisit(averageWebsiteInBytes);
34
- const v8VersionEnergyCalc = swd.energyPerVisitV8(averageWebsiteInBytes);
35
- expect(swd.emissionsPerVisitInGrams(v9currentEnergyCalc)).toEqual(0.57);
36
- expect(swd.emissionsPerVisitInGrams(v8VersionEnergyCalc)).toEqual(0.2);
35
+ expect(swd.energyPerVisit(averageWebsiteInBytes)).toBe(0.0013807057305600004);
37
36
  });
38
37
  });
39
38
  describe("emissionsPerVisitInGrams", () => {
40
39
  it("should calculate the correct co2 per visit", () => {
41
40
  const energy = swd.energyPerVisit(averageWebsiteInBytes);
42
- expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.57);
41
+ expect(swd.emissionsPerVisitInGrams(energy)).toEqual(0.61);
43
42
  });
44
43
  it("should accept a dynamic KwH value", () => {
45
44
  const energy = swd.energyPerVisit(averageWebsiteInBytes);
46
- expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.32);
45
+ expect(swd.emissionsPerVisitInGrams(energy, 245)).toEqual(0.34);
47
46
  });
48
47
  });
49
48
  describe("annualEnergyInKwh", () => {
@@ -0,0 +1,2 @@
1
+ var co2=(()=>{var ue=Object.create;var B=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var de=Object.getPrototypeOf,Ce=Object.prototype.hasOwnProperty;var T=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),pe=(r,t)=>{for(var e in t)B(r,e,{get:t[e],enumerable:!0})},Y=(r,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of le(t))!Ce.call(r,o)&&o!==e&&B(r,o,{get:()=>t[o],enumerable:!(n=fe(t,o))||n.enumerable});return r};var I=(r,t,e)=>(e=r!=null?ue(de(r)):{},Y(t||!r||!r.__esModule?B(e,"default",{value:r,enumerable:!0}):e,r)),me=r=>Y(B({},"__esModule",{value:!0}),r);var $=T((ze,L)=>{var E=1e3,F=E*60,_=F*60,y=_*24,ye=y*7,he=y*365.25;L.exports=function(r,t){t=t||{};var e=typeof r;if(e==="string"&&r.length>0)return ge(r);if(e==="number"&&isFinite(r))return t.long?Fe(r):Ee(r);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(r))};function ge(r){if(r=String(r),!(r.length>100)){var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(r);if(!!t){var e=parseFloat(t[1]),n=(t[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return e*he;case"weeks":case"week":case"w":return e*ye;case"days":case"day":case"d":return e*y;case"hours":case"hour":case"hrs":case"hr":case"h":return e*_;case"minutes":case"minute":case"mins":case"min":case"m":return e*F;case"seconds":case"second":case"secs":case"sec":case"s":return e*E;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return e;default:return}}}}function Ee(r){var t=Math.abs(r);return t>=y?Math.round(r/y)+"d":t>=_?Math.round(r/_)+"h":t>=F?Math.round(r/F)+"m":t>=E?Math.round(r/E)+"s":r+"ms"}function Fe(r){var t=Math.abs(r);return t>=y?v(r,t,y,"day"):t>=_?v(r,t,_,"hour"):t>=F?v(r,t,F,"minute"):t>=E?v(r,t,E,"second"):r+" ms"}function v(r,t,e,n){var o=t>=e*1.5;return Math.round(r/e)+" "+n+(o?"s":"")}});var M=T((Ye,U)=>{function _e(r){e.debug=e,e.default=e,e.coerce=g,e.disable=s,e.enable=o,e.enabled=a,e.humanize=$(),e.destroy=p,Object.keys(r).forEach(i=>{e[i]=r[i]}),e.names=[],e.skips=[],e.formatters={};function t(i){let c=0;for(let u=0;u<i.length;u++)c=(c<<5)-c+i.charCodeAt(u),c|=0;return e.colors[Math.abs(c)%e.colors.length]}e.selectColor=t;function e(i){let c,u=null,b,j;function C(...d){if(!C.enabled)return;let m=C,R=Number(new Date),ie=R-(c||R);m.diff=ie,m.prev=c,m.curr=R,c=R,d[0]=e.coerce(d[0]),typeof d[0]!="string"&&d.unshift("%O");let O=0;d[0]=d[0].replace(/%([a-zA-Z%])/g,(P,ce)=>{if(P==="%%")return"%";O++;let z=e.formatters[ce];if(typeof z=="function"){let ae=d[O];P=z.call(m,ae),d.splice(O,1),O--}return P}),e.formatArgs.call(m,d),(m.log||e.log).apply(m,d)}return C.namespace=i,C.useColors=e.useColors(),C.color=e.selectColor(i),C.extend=n,C.destroy=e.destroy,Object.defineProperty(C,"enabled",{enumerable:!0,configurable:!1,get:()=>u!==null?u:(b!==e.namespaces&&(b=e.namespaces,j=e.enabled(i)),j),set:d=>{u=d}}),typeof e.init=="function"&&e.init(C),C}function n(i,c){let u=e(this.namespace+(typeof c>"u"?":":c)+i);return u.log=this.log,u}function o(i){e.save(i),e.namespaces=i,e.names=[],e.skips=[];let c,u=(typeof i=="string"?i:"").split(/[\s,]+/),b=u.length;for(c=0;c<b;c++)!u[c]||(i=u[c].replace(/\*/g,".*?"),i[0]==="-"?e.skips.push(new RegExp("^"+i.slice(1)+"$")):e.names.push(new RegExp("^"+i+"$")))}function s(){let i=[...e.names.map(l),...e.skips.map(l).map(c=>"-"+c)].join(",");return e.enable(""),i}function a(i){if(i[i.length-1]==="*")return!0;let c,u;for(c=0,u=e.skips.length;c<u;c++)if(e.skips[c].test(i))return!1;for(c=0,u=e.names.length;c<u;c++)if(e.names[c].test(i))return!0;return!1}function l(i){return i.toString().substring(2,i.toString().length-2).replace(/\.\*\?$/,"*")}function g(i){return i instanceof Error?i.stack||i.message:i}function p(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return e.enable(e.load()),e}U.exports=_e});var x=T((f,N)=>{f.formatArgs=be;f.save=Re;f.load=Oe;f.useColors=we;f.storage=Be();f.destroy=(()=>{let r=!1;return()=>{r||(r=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();f.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function we(){return typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)?!0:typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)?!1:typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function be(r){if(r[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+r[0]+(this.useColors?"%c ":" ")+"+"+N.exports.humanize(this.diff),!this.useColors)return;let t="color: "+this.color;r.splice(1,0,t,"color: inherit");let e=0,n=0;r[0].replace(/%[a-zA-Z%]/g,o=>{o!=="%%"&&(e++,o==="%c"&&(n=e))}),r.splice(n,0,t)}f.log=console.debug||console.log||(()=>{});function Re(r){try{r?f.storage.setItem("debug",r):f.storage.removeItem("debug")}catch{}}function Oe(){let r;try{r=f.storage.getItem("debug")}catch{}return!r&&typeof process<"u"&&"env"in process&&(r=process.env.DEBUG),r}function Be(){try{return localStorage}catch{}}N.exports=M()(f);var{formatters:ve}=N.exports;ve.j=function(r){try{return JSON.stringify(r)}catch(t){return"[UnexpectedJSONParseError]: "+t.message}}});var De={};pe(De,{co2:()=>D,default:()=>We,hosting:()=>K});var A=4883333333333333e-25;var S=class{constructor(t){this.options=t,this.KWH_PER_BYTE_FOR_NETWORK=A}perByte(t,e){if(t<1)return 0;if(e){let o=t*72e-12*0,s=t*A*475;return o+s}let n=72e-12+A;return t*n*519}};var H=S;var te=I(x());var k={GIGABYTE:1e9};var w=r=>parseFloat(r.toFixed(2));var q=(0,te.default)("tgwf:sustainable-web-design"),Ne=.81,J=.52,Z=.14,Q=.15,X=.19,h=442,ee=50,xe=.75,Pe=.25,Te=.02,G=class{constructor(t){this.options=t}energyPerByteByComponent(t){let n=t/k.GIGABYTE*Ne;return{consumerDeviceEnergy:n*J,networkEnergy:n*Z,productionEnergy:n*X,dataCenterEnergy:n*Q}}co2byComponent(t,e=h){let n={};for(let[o,s]of Object.entries(t))o.startsWith("dataCenterEnergy")?n[o]=s*e:n[o]=s*h;return n}perByte(t,e=h){let n=this.energyPerByteByComponent(t);if(Boolean(e)===!1&&(e=h),e===!0&&(e=ee),typeof e!="number")throw new Error(`perByte expects a numeric value or boolean for the carbon intensity value. Received: ${e}`);let o=this.co2byComponent(n,e);return Object.values(o).reduce((a,l)=>a+l)}perVisit(t,e=h){let n=this.energyPerVisitByComponent(t);if(Boolean(e)===!1&&(e=h),e===!0&&(e=ee),typeof e!="number")throw new Error(`perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${e}`);let o=this.co2byComponent(n,e);return Object.values(o).reduce((a,l)=>a+l)}energyPerByte(t){let e=this.energyPerByteByComponent(t);return Object.values(e).reduce((o,s)=>o+s)}energyPerVisitByComponent(t,e=xe,n=Pe,o=Te){let s=this.energyPerByteByComponent(t),a={};q({energyBycomponent:s});let l=Object.values(s);for(let[g,p]of Object.entries(s))a[`${g} - first`]=p*e,a[`${g} - subsequent`]=p*n*o;return q({cacheAdjustedSegmentEnergy:a}),a}energyPerVisit(t){let e=0,n=0,o=Object.entries(this.energyPerVisitByComponent(t));for(let[s,a]of o)s.indexOf("first")>0&&(e+=a);for(let[s,a]of o)s.indexOf("subsequent")>0&&(n+=a);return e+n}emissionsPerVisitInGrams(t,e=h){return w(t*e)}annualEnergyInKwh(t,e=1e3){return t*e*12}annualEmissionsInGrams(t,e=1e3){return t*e*12}annualSegmentEnergy(t){return{consumerDeviceEnergy:w(t*J),networkEnergy:w(t*Z),dataCenterEnergy:w(t*Q),productionEnergy:w(t*X)}}};var re=G;var W=class{constructor(t){this.model=new H,t?.model==="swd"&&(this.model=new re)}perByte(t,e){return this.model.perByte(t,e)}perVisit(t,e){return this.model?.perVisit?this.model.perVisit(t,e):(console.warn("The model you have selected does not support perVisit. Using perByte instead."),this.model.perByte(t,e))}perDomain(t,e){let n=[];for(let o of Object.keys(t.domains)){let s;e&&e.indexOf(o)>-1?s=this.perByte(t.domains[o].transferSize,!0):s=this.perByte(t.domains[o].transferSize),n.push({domain:o,co2:s,transferSize:t.domains[o].transferSize})}return n.sort((o,s)=>s.co2-o.co2),n}perPage(t,e){let n=this.perDomain(t,e),o=0;for(let s of n)o+=s.co2;return o}perContentType(t,e){let n={};for(let s of t.assets){let a=new URL(s.url).domain,l=s.transferSize,g=this.perByte(l,e&&e.indexOf(a)>-1),p=s.type;n[p]||(n[p]={co2:0,transferSize:0}),n[p].co2+=g,n[p].transferSize+=l}let o=[];for(let s of Object.keys(n))o.push({type:s,co2:n[s].co2,transferSize:n[s].transferSize});return o.sort((s,a)=>a.co2-s.co2),o}dirtiestResources(t,e){let n=[];for(let o of t.assets){let s=new URL(o.url).domain,a=o.transferSize,l=this.perByte(a,e&&e.indexOf(s)>-1);n.push({url:o.url,co2:l,transferSize:a})}return n.sort((o,s)=>s.co2-o.co2),n.slice(0,n.length>10?10:n.length)}perParty(t,e){let n=0,o=0,s=t.firstPartyRegEx;for(let a of Object.keys(t.domains))a.match(s)?n+=this.perByte(t.domains[a].transferSize,e&&e.indexOf(a)>-1):o+=this.perByte(t.domains[a].transferSize,e&&e.indexOf(a)>-1);return{firstParty:n,thirdParty:o}}};var D=W;var se=I(x());var ne=I(x()),V=(0,ne.default)("tgwf:hostingAPI");function Ie(r){return typeof r=="string"?Ae(r):Se(r)}async function Ae(r){return(await(await fetch(`https://api.thegreenwebfoundation.org/greencheck/${r}`)).json()).green}async function Se(r){try{let t="https://api.thegreenwebfoundation.org/v2/greencheckmulti",e=JSON.stringify(r),n=await fetch(`${t}/${e}`);V(`${t}/${e}`),V({req:n});let o=await n.text();V({textResult:o});let s=await n.json();return ke(s)}catch{return[]}}function ke(r){return Object.entries(r).filter(([n,o])=>o.green).map(([n,o])=>o.url)}var oe={check:Ie};var tt=(0,se.default)("tgwf:hosting");function Ge(r,t){return oe.check(r)}var K={check:Ge};var We={co2:D,hosting:K};return me(De);})();
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../node_modules/ms/index.js", "../../node_modules/debug/src/common.js", "../../node_modules/debug/src/browser.js", "../../src/index.js", "../../src/1byte.js", "../../src/sustainable-web-design.js", "../../src/constants/file-size.js", "../../src/helpers/index.js", "../../src/co2.js", "../../src/hosting.js", "../../src/hosting-api.js"],
4
+ "sourcesContent": ["/**\n * Helpers.\n */\n\nvar s = 1000;\nvar m = s * 60;\nvar h = m * 60;\nvar d = h * 24;\nvar w = d * 7;\nvar y = d * 365.25;\n\n/**\n * Parse or format the given `val`.\n *\n * Options:\n *\n * - `long` verbose formatting [false]\n *\n * @param {String|Number} val\n * @param {Object} [options]\n * @throws {Error} throw an error if val is not a non-empty string or a number\n * @return {String|Number}\n * @api public\n */\n\nmodule.exports = function(val, options) {\n options = options || {};\n var type = typeof val;\n if (type === 'string' && val.length > 0) {\n return parse(val);\n } else if (type === 'number' && isFinite(val)) {\n return options.long ? fmtLong(val) : fmtShort(val);\n }\n throw new Error(\n 'val is not a non-empty string or a valid number. val=' +\n JSON.stringify(val)\n );\n};\n\n/**\n * Parse the given `str` and return milliseconds.\n *\n * @param {String} str\n * @return {Number}\n * @api private\n */\n\nfunction parse(str) {\n str = String(str);\n if (str.length > 100) {\n return;\n }\n var match = /^(-?(?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(\n str\n );\n if (!match) {\n return;\n }\n var n = parseFloat(match[1]);\n var type = (match[2] || 'ms').toLowerCase();\n switch (type) {\n case 'years':\n case 'year':\n case 'yrs':\n case 'yr':\n case 'y':\n return n * y;\n case 'weeks':\n case 'week':\n case 'w':\n return n * w;\n case 'days':\n case 'day':\n case 'd':\n return n * d;\n case 'hours':\n case 'hour':\n case 'hrs':\n case 'hr':\n case 'h':\n return n * h;\n case 'minutes':\n case 'minute':\n case 'mins':\n case 'min':\n case 'm':\n return n * m;\n case 'seconds':\n case 'second':\n case 'secs':\n case 'sec':\n case 's':\n return n * s;\n case 'milliseconds':\n case 'millisecond':\n case 'msecs':\n case 'msec':\n case 'ms':\n return n;\n default:\n return undefined;\n }\n}\n\n/**\n * Short format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtShort(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return Math.round(ms / d) + 'd';\n }\n if (msAbs >= h) {\n return Math.round(ms / h) + 'h';\n }\n if (msAbs >= m) {\n return Math.round(ms / m) + 'm';\n }\n if (msAbs >= s) {\n return Math.round(ms / s) + 's';\n }\n return ms + 'ms';\n}\n\n/**\n * Long format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtLong(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return plural(ms, msAbs, d, 'day');\n }\n if (msAbs >= h) {\n return plural(ms, msAbs, h, 'hour');\n }\n if (msAbs >= m) {\n return plural(ms, msAbs, m, 'minute');\n }\n if (msAbs >= s) {\n return plural(ms, msAbs, s, 'second');\n }\n return ms + ' ms';\n}\n\n/**\n * Pluralization helper.\n */\n\nfunction plural(ms, msAbs, n, name) {\n var isPlural = msAbs >= n * 1.5;\n return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');\n}\n", "\n/**\n * This is the common logic for both the Node.js and web browser\n * implementations of `debug()`.\n */\n\nfunction setup(env) {\n\tcreateDebug.debug = createDebug;\n\tcreateDebug.default = createDebug;\n\tcreateDebug.coerce = coerce;\n\tcreateDebug.disable = disable;\n\tcreateDebug.enable = enable;\n\tcreateDebug.enabled = enabled;\n\tcreateDebug.humanize = require('ms');\n\tcreateDebug.destroy = destroy;\n\n\tObject.keys(env).forEach(key => {\n\t\tcreateDebug[key] = env[key];\n\t});\n\n\t/**\n\t* The currently active debug mode names, and names to skip.\n\t*/\n\n\tcreateDebug.names = [];\n\tcreateDebug.skips = [];\n\n\t/**\n\t* Map of special \"%n\" handling functions, for the debug \"format\" argument.\n\t*\n\t* Valid key names are a single, lower or upper-case letter, i.e. \"n\" and \"N\".\n\t*/\n\tcreateDebug.formatters = {};\n\n\t/**\n\t* Selects a color for a debug namespace\n\t* @param {String} namespace The namespace string for the debug instance to be colored\n\t* @return {Number|String} An ANSI color code for the given namespace\n\t* @api private\n\t*/\n\tfunction selectColor(namespace) {\n\t\tlet hash = 0;\n\n\t\tfor (let i = 0; i < namespace.length; i++) {\n\t\t\thash = ((hash << 5) - hash) + namespace.charCodeAt(i);\n\t\t\thash |= 0; // Convert to 32bit integer\n\t\t}\n\n\t\treturn createDebug.colors[Math.abs(hash) % createDebug.colors.length];\n\t}\n\tcreateDebug.selectColor = selectColor;\n\n\t/**\n\t* Create a debugger with the given `namespace`.\n\t*\n\t* @param {String} namespace\n\t* @return {Function}\n\t* @api public\n\t*/\n\tfunction createDebug(namespace) {\n\t\tlet prevTime;\n\t\tlet enableOverride = null;\n\t\tlet namespacesCache;\n\t\tlet enabledCache;\n\n\t\tfunction debug(...args) {\n\t\t\t// Disabled?\n\t\t\tif (!debug.enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst self = debug;\n\n\t\t\t// Set `diff` timestamp\n\t\t\tconst curr = Number(new Date());\n\t\t\tconst ms = curr - (prevTime || curr);\n\t\t\tself.diff = ms;\n\t\t\tself.prev = prevTime;\n\t\t\tself.curr = curr;\n\t\t\tprevTime = curr;\n\n\t\t\targs[0] = createDebug.coerce(args[0]);\n\n\t\t\tif (typeof args[0] !== 'string') {\n\t\t\t\t// Anything else let's inspect with %O\n\t\t\t\targs.unshift('%O');\n\t\t\t}\n\n\t\t\t// Apply any `formatters` transformations\n\t\t\tlet index = 0;\n\t\t\targs[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {\n\t\t\t\t// If we encounter an escaped % then don't increase the array index\n\t\t\t\tif (match === '%%') {\n\t\t\t\t\treturn '%';\n\t\t\t\t}\n\t\t\t\tindex++;\n\t\t\t\tconst formatter = createDebug.formatters[format];\n\t\t\t\tif (typeof formatter === 'function') {\n\t\t\t\t\tconst val = args[index];\n\t\t\t\t\tmatch = formatter.call(self, val);\n\n\t\t\t\t\t// Now we need to remove `args[index]` since it's inlined in the `format`\n\t\t\t\t\targs.splice(index, 1);\n\t\t\t\t\tindex--;\n\t\t\t\t}\n\t\t\t\treturn match;\n\t\t\t});\n\n\t\t\t// Apply env-specific formatting (colors, etc.)\n\t\t\tcreateDebug.formatArgs.call(self, args);\n\n\t\t\tconst logFn = self.log || createDebug.log;\n\t\t\tlogFn.apply(self, args);\n\t\t}\n\n\t\tdebug.namespace = namespace;\n\t\tdebug.useColors = createDebug.useColors();\n\t\tdebug.color = createDebug.selectColor(namespace);\n\t\tdebug.extend = extend;\n\t\tdebug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.\n\n\t\tObject.defineProperty(debug, 'enabled', {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: false,\n\t\t\tget: () => {\n\t\t\t\tif (enableOverride !== null) {\n\t\t\t\t\treturn enableOverride;\n\t\t\t\t}\n\t\t\t\tif (namespacesCache !== createDebug.namespaces) {\n\t\t\t\t\tnamespacesCache = createDebug.namespaces;\n\t\t\t\t\tenabledCache = createDebug.enabled(namespace);\n\t\t\t\t}\n\n\t\t\t\treturn enabledCache;\n\t\t\t},\n\t\t\tset: v => {\n\t\t\t\tenableOverride = v;\n\t\t\t}\n\t\t});\n\n\t\t// Env-specific initialization logic for debug instances\n\t\tif (typeof createDebug.init === 'function') {\n\t\t\tcreateDebug.init(debug);\n\t\t}\n\n\t\treturn debug;\n\t}\n\n\tfunction extend(namespace, delimiter) {\n\t\tconst newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);\n\t\tnewDebug.log = this.log;\n\t\treturn newDebug;\n\t}\n\n\t/**\n\t* Enables a debug mode by namespaces. This can include modes\n\t* separated by a colon and wildcards.\n\t*\n\t* @param {String} namespaces\n\t* @api public\n\t*/\n\tfunction enable(namespaces) {\n\t\tcreateDebug.save(namespaces);\n\t\tcreateDebug.namespaces = namespaces;\n\n\t\tcreateDebug.names = [];\n\t\tcreateDebug.skips = [];\n\n\t\tlet i;\n\t\tconst split = (typeof namespaces === 'string' ? namespaces : '').split(/[\\s,]+/);\n\t\tconst len = split.length;\n\n\t\tfor (i = 0; i < len; i++) {\n\t\t\tif (!split[i]) {\n\t\t\t\t// ignore empty strings\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tnamespaces = split[i].replace(/\\*/g, '.*?');\n\n\t\t\tif (namespaces[0] === '-') {\n\t\t\t\tcreateDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));\n\t\t\t} else {\n\t\t\t\tcreateDebug.names.push(new RegExp('^' + namespaces + '$'));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t* Disable debug output.\n\t*\n\t* @return {String} namespaces\n\t* @api public\n\t*/\n\tfunction disable() {\n\t\tconst namespaces = [\n\t\t\t...createDebug.names.map(toNamespace),\n\t\t\t...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)\n\t\t].join(',');\n\t\tcreateDebug.enable('');\n\t\treturn namespaces;\n\t}\n\n\t/**\n\t* Returns true if the given mode name is enabled, false otherwise.\n\t*\n\t* @param {String} name\n\t* @return {Boolean}\n\t* @api public\n\t*/\n\tfunction enabled(name) {\n\t\tif (name[name.length - 1] === '*') {\n\t\t\treturn true;\n\t\t}\n\n\t\tlet i;\n\t\tlet len;\n\n\t\tfor (i = 0, len = createDebug.skips.length; i < len; i++) {\n\t\t\tif (createDebug.skips[i].test(name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0, len = createDebug.names.length; i < len; i++) {\n\t\t\tif (createDebug.names[i].test(name)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t* Convert regexp to namespace\n\t*\n\t* @param {RegExp} regxep\n\t* @return {String} namespace\n\t* @api private\n\t*/\n\tfunction toNamespace(regexp) {\n\t\treturn regexp.toString()\n\t\t\t.substring(2, regexp.toString().length - 2)\n\t\t\t.replace(/\\.\\*\\?$/, '*');\n\t}\n\n\t/**\n\t* Coerce `val`.\n\t*\n\t* @param {Mixed} val\n\t* @return {Mixed}\n\t* @api private\n\t*/\n\tfunction coerce(val) {\n\t\tif (val instanceof Error) {\n\t\t\treturn val.stack || val.message;\n\t\t}\n\t\treturn val;\n\t}\n\n\t/**\n\t* XXX DO NOT USE. This is a temporary stub function.\n\t* XXX It WILL be removed in the next major release.\n\t*/\n\tfunction destroy() {\n\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t}\n\n\tcreateDebug.enable(createDebug.load());\n\n\treturn createDebug;\n}\n\nmodule.exports = setup;\n", "/* eslint-env browser */\n\n/**\n * This is the web browser implementation of `debug()`.\n */\n\nexports.formatArgs = formatArgs;\nexports.save = save;\nexports.load = load;\nexports.useColors = useColors;\nexports.storage = localstorage();\nexports.destroy = (() => {\n\tlet warned = false;\n\n\treturn () => {\n\t\tif (!warned) {\n\t\t\twarned = true;\n\t\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t\t}\n\t};\n})();\n\n/**\n * Colors.\n */\n\nexports.colors = [\n\t'#0000CC',\n\t'#0000FF',\n\t'#0033CC',\n\t'#0033FF',\n\t'#0066CC',\n\t'#0066FF',\n\t'#0099CC',\n\t'#0099FF',\n\t'#00CC00',\n\t'#00CC33',\n\t'#00CC66',\n\t'#00CC99',\n\t'#00CCCC',\n\t'#00CCFF',\n\t'#3300CC',\n\t'#3300FF',\n\t'#3333CC',\n\t'#3333FF',\n\t'#3366CC',\n\t'#3366FF',\n\t'#3399CC',\n\t'#3399FF',\n\t'#33CC00',\n\t'#33CC33',\n\t'#33CC66',\n\t'#33CC99',\n\t'#33CCCC',\n\t'#33CCFF',\n\t'#6600CC',\n\t'#6600FF',\n\t'#6633CC',\n\t'#6633FF',\n\t'#66CC00',\n\t'#66CC33',\n\t'#9900CC',\n\t'#9900FF',\n\t'#9933CC',\n\t'#9933FF',\n\t'#99CC00',\n\t'#99CC33',\n\t'#CC0000',\n\t'#CC0033',\n\t'#CC0066',\n\t'#CC0099',\n\t'#CC00CC',\n\t'#CC00FF',\n\t'#CC3300',\n\t'#CC3333',\n\t'#CC3366',\n\t'#CC3399',\n\t'#CC33CC',\n\t'#CC33FF',\n\t'#CC6600',\n\t'#CC6633',\n\t'#CC9900',\n\t'#CC9933',\n\t'#CCCC00',\n\t'#CCCC33',\n\t'#FF0000',\n\t'#FF0033',\n\t'#FF0066',\n\t'#FF0099',\n\t'#FF00CC',\n\t'#FF00FF',\n\t'#FF3300',\n\t'#FF3333',\n\t'#FF3366',\n\t'#FF3399',\n\t'#FF33CC',\n\t'#FF33FF',\n\t'#FF6600',\n\t'#FF6633',\n\t'#FF9900',\n\t'#FF9933',\n\t'#FFCC00',\n\t'#FFCC33'\n];\n\n/**\n * Currently only WebKit-based Web Inspectors, Firefox >= v31,\n * and the Firebug extension (any Firefox version) are known\n * to support \"%c\" CSS customizations.\n *\n * TODO: add a `localStorage` variable to explicitly enable/disable colors\n */\n\n// eslint-disable-next-line complexity\nfunction useColors() {\n\t// NB: In an Electron preload script, document will be defined but not fully\n\t// initialized. Since we know we're in Chrome, we'll just detect this case\n\t// explicitly\n\tif (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {\n\t\treturn true;\n\t}\n\n\t// Internet Explorer and Edge do not support colors.\n\tif (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\\/(\\d+)/)) {\n\t\treturn false;\n\t}\n\n\t// Is webkit? http://stackoverflow.com/a/16459606/376773\n\t// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632\n\treturn (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||\n\t\t// Is firebug? http://stackoverflow.com/a/398120/376773\n\t\t(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||\n\t\t// Is firefox >= v31?\n\t\t// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||\n\t\t// Double check webkit in userAgent just in case we are in a worker\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/));\n}\n\n/**\n * Colorize log arguments if enabled.\n *\n * @api public\n */\n\nfunction formatArgs(args) {\n\targs[0] = (this.useColors ? '%c' : '') +\n\t\tthis.namespace +\n\t\t(this.useColors ? ' %c' : ' ') +\n\t\targs[0] +\n\t\t(this.useColors ? '%c ' : ' ') +\n\t\t'+' + module.exports.humanize(this.diff);\n\n\tif (!this.useColors) {\n\t\treturn;\n\t}\n\n\tconst c = 'color: ' + this.color;\n\targs.splice(1, 0, c, 'color: inherit');\n\n\t// The final \"%c\" is somewhat tricky, because there could be other\n\t// arguments passed either before or after the %c, so we need to\n\t// figure out the correct index to insert the CSS into\n\tlet index = 0;\n\tlet lastC = 0;\n\targs[0].replace(/%[a-zA-Z%]/g, match => {\n\t\tif (match === '%%') {\n\t\t\treturn;\n\t\t}\n\t\tindex++;\n\t\tif (match === '%c') {\n\t\t\t// We only are interested in the *last* %c\n\t\t\t// (the user may have provided their own)\n\t\t\tlastC = index;\n\t\t}\n\t});\n\n\targs.splice(lastC, 0, c);\n}\n\n/**\n * Invokes `console.debug()` when available.\n * No-op when `console.debug` is not a \"function\".\n * If `console.debug` is not available, falls back\n * to `console.log`.\n *\n * @api public\n */\nexports.log = console.debug || console.log || (() => {});\n\n/**\n * Save `namespaces`.\n *\n * @param {String} namespaces\n * @api private\n */\nfunction save(namespaces) {\n\ttry {\n\t\tif (namespaces) {\n\t\t\texports.storage.setItem('debug', namespaces);\n\t\t} else {\n\t\t\texports.storage.removeItem('debug');\n\t\t}\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\n/**\n * Load `namespaces`.\n *\n * @return {String} returns the previously persisted debug modes\n * @api private\n */\nfunction load() {\n\tlet r;\n\ttry {\n\t\tr = exports.storage.getItem('debug');\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n\n\t// If debug isn't set in LS, and we're in Electron, try to load $DEBUG\n\tif (!r && typeof process !== 'undefined' && 'env' in process) {\n\t\tr = process.env.DEBUG;\n\t}\n\n\treturn r;\n}\n\n/**\n * Localstorage attempts to return the localstorage.\n *\n * This is necessary because safari throws\n * when a user disables cookies/localstorage\n * and you attempt to access it.\n *\n * @return {LocalStorage}\n * @api private\n */\n\nfunction localstorage() {\n\ttry {\n\t\t// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context\n\t\t// The Browser also has localStorage in the global context.\n\t\treturn localStorage;\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\nmodule.exports = require('./common')(exports);\n\nconst {formatters} = module.exports;\n\n/**\n * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.\n */\n\nformatters.j = function (v) {\n\ttry {\n\t\treturn JSON.stringify(v);\n\t} catch (error) {\n\t\treturn '[UnexpectedJSONParseError]: ' + error.message;\n\t}\n};\n", "import co2 from \"./co2.js\";\nimport hosting from \"./hosting.js\";\n\nexport { co2, hosting };\nexport default { co2, hosting };\n", "// Use the 1byte model for now from the Shift Project, and assume a US grid mix figure they use of around 519 g co2 for the time being. It's lower for Europe, and in particular, France, but for v1, we don't include this\nconst CO2_PER_KWH_IN_DC_GREY = 519;\n\n// this figure is from the IEA's 2018 report for a global average:\nconst CO2_PER_KWH_NETWORK_GREY = 475;\n\n// TODO - these figures need to be updated, as the figures for green\n// shouldn't really be zero now we know that carbon intensity figures\n// for renewables still usually include the life cycle emissions\nconst CO2_PER_KWH_IN_DC_GREEN = 0;\n\n// the 1 byte model gives figures for energy usage for:\n\n// datacentres\n// networks\n// the device used to access a site/app\n\n// The device usage figure combines figures for:\n// 1. the usage for devices (which is small proportion of the energy use)\n// 2. the *making* the device, which is comparatively high.\n\nconst KWH_PER_BYTE_IN_DC = 7.2e-11;\n\n// this is probably best left as something users can define, or\n// a weighted average based on total usage.\n// Using a simple mean for now, as while web traffic to end users might trend\n// towards wifi and mobile,\n// Web traffic between servers is likely wired networks\n\nconst FIXED_NETWORK_WIRED = 4.29e-10;\nconst FIXED_NETWORK_WIFI = 1.52e-10;\nconst FOUR_G_MOBILE = 8.84e-10;\n\n// Pull requests gratefully accepted\nconst KWH_PER_BYTE_FOR_NETWORK =\n (FIXED_NETWORK_WIRED + FIXED_NETWORK_WIFI + FOUR_G_MOBILE) / 3;\n\nconst KWH_PER_BYTE_FOR_DEVICES = 1.3e-10;\n\nclass OneByte {\n constructor(options) {\n this.options = options;\n\n this.KWH_PER_BYTE_FOR_NETWORK = KWH_PER_BYTE_FOR_NETWORK;\n }\n\n perByte(bytes, green) {\n if (bytes < 1) {\n return 0;\n }\n\n if (green) {\n // if we have a green datacentre, use the lower figure for renewable energy\n const Co2ForDC = bytes * KWH_PER_BYTE_IN_DC * CO2_PER_KWH_IN_DC_GREEN;\n\n // but for the worest of the internet, we can't easily check, so assume\n // grey for now\n const Co2forNetwork =\n bytes * KWH_PER_BYTE_FOR_NETWORK * CO2_PER_KWH_NETWORK_GREY;\n\n return Co2ForDC + Co2forNetwork;\n }\n\n const KwHPerByte = KWH_PER_BYTE_IN_DC + KWH_PER_BYTE_FOR_NETWORK;\n return bytes * KwHPerByte * CO2_PER_KWH_IN_DC_GREY;\n }\n}\n\nexport { OneByte };\nexport default OneByte;\n", "\"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 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 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", "const GIGABYTE = 1000 * 1000 * 1000;\n\nexport default {\n GIGABYTE,\n};\n", "const formatNumber = (num) => parseFloat(num.toFixed(2));\n\nexport { formatNumber };\n", "\"use strict\";\n\nimport OneByte from \"./1byte.js\";\nimport SustainableWebDesign from \"./sustainable-web-design.js\";\n\nclass CO2 {\n constructor(options) {\n this.model = new OneByte();\n // Using optional chaining allows an empty object to be passed\n // in without breaking the code.\n if (options?.model === \"swd\") {\n this.model = new SustainableWebDesign();\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(\n \"The model you have selected does not support perVisit. Using perByte instead.\"\n );\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", "\"use strict\";\n\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:hosting\");\n\nimport hostingAPI from \"./hosting-api.js\";\n\nfunction check(domain, db) {\n return hostingAPI.check(domain);\n}\n\nexport default {\n check,\n};\n", "\"use strict\";\n\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:hostingAPI\");\n\nfunction check(domain) {\n // is it a single domain or an array of them?\n if (typeof domain === \"string\") {\n return checkAgainstAPI(domain);\n } else {\n return checkDomainsAgainstAPI(domain);\n }\n}\n\nasync function checkAgainstAPI(domain) {\n const req = await fetch(\n `https://api.thegreenwebfoundation.org/greencheck/${domain}`\n );\n const res = await req.json();\n return res.green;\n}\n\nasync function checkDomainsAgainstAPI(domains) {\n try {\n const apiPath = \"https://api.thegreenwebfoundation.org/v2/greencheckmulti\";\n const domainsString = JSON.stringify(domains);\n\n const req = await fetch(`${apiPath}/${domainsString}`);\n\n // sanity check API result. Is this the library or\n // the actual API request that's the problem?\n // Is nock mocking node-native fetch API calls properly?\n log(`${apiPath}/${domainsString}`);\n log({ req });\n const textResult = await req.text();\n log({ textResult });\n\n const allGreenCheckResults = await req.json();\n\n return greenDomainsFromResults(allGreenCheckResults);\n } catch (e) {\n return [];\n }\n}\n\nfunction greenDomainsFromResults(greenResults) {\n const entries = Object.entries(greenResults);\n const greenEntries = entries.filter(([key, val]) => val.green);\n return greenEntries.map(([key, val]) => val.url);\n}\n\nexport default {\n check,\n};\n"],
5
+ "mappings": "ioBAAA,iBAIA,GAAI,GAAI,IACJ,EAAI,EAAI,GACR,EAAI,EAAI,GACR,EAAI,EAAI,GACR,GAAI,EAAI,EACR,GAAI,EAAI,OAgBZ,EAAO,QAAU,SAAS,EAAK,EAAS,CACtC,EAAU,GAAW,CAAC,EACtB,GAAI,GAAO,MAAO,GAClB,GAAI,IAAS,UAAY,EAAI,OAAS,EACpC,MAAO,IAAM,CAAG,EACX,GAAI,IAAS,UAAY,SAAS,CAAG,EAC1C,MAAO,GAAQ,KAAO,GAAQ,CAAG,EAAI,GAAS,CAAG,EAEnD,KAAM,IAAI,OACR,wDACE,KAAK,UAAU,CAAG,CACtB,CACF,EAUA,YAAe,EAAK,CAElB,GADA,EAAM,OAAO,CAAG,EACZ,IAAI,OAAS,KAGjB,IAAI,GAAQ,mIAAmI,KAC7I,CACF,EACA,GAAI,EAAC,EAGL,IAAI,GAAI,WAAW,EAAM,EAAE,EACvB,EAAQ,GAAM,IAAM,MAAM,YAAY,EAC1C,OAAQ,OACD,YACA,WACA,UACA,SACA,IACH,MAAO,GAAI,OACR,YACA,WACA,IACH,MAAO,GAAI,OACR,WACA,UACA,IACH,MAAO,GAAI,MACR,YACA,WACA,UACA,SACA,IACH,MAAO,GAAI,MACR,cACA,aACA,WACA,UACA,IACH,MAAO,GAAI,MACR,cACA,aACA,WACA,UACA,IACH,MAAO,GAAI,MACR,mBACA,kBACA,YACA,WACA,KACH,MAAO,WAEP,SAEN,CAUA,YAAkB,EAAI,CACpB,GAAI,GAAQ,KAAK,IAAI,CAAE,EACvB,MAAI,IAAS,EACJ,KAAK,MAAM,EAAK,CAAC,EAAI,IAE1B,GAAS,EACJ,KAAK,MAAM,EAAK,CAAC,EAAI,IAE1B,GAAS,EACJ,KAAK,MAAM,EAAK,CAAC,EAAI,IAE1B,GAAS,EACJ,KAAK,MAAM,EAAK,CAAC,EAAI,IAEvB,EAAK,IACd,CAUA,YAAiB,EAAI,CACnB,GAAI,GAAQ,KAAK,IAAI,CAAE,EACvB,MAAI,IAAS,EACJ,EAAO,EAAI,EAAO,EAAG,KAAK,EAE/B,GAAS,EACJ,EAAO,EAAI,EAAO,EAAG,MAAM,EAEhC,GAAS,EACJ,EAAO,EAAI,EAAO,EAAG,QAAQ,EAElC,GAAS,EACJ,EAAO,EAAI,EAAO,EAAG,QAAQ,EAE/B,EAAK,KACd,CAMA,WAAgB,EAAI,EAAO,EAAG,EAAM,CAClC,GAAI,GAAW,GAAS,EAAI,IAC5B,MAAO,MAAK,MAAM,EAAK,CAAC,EAAI,IAAM,EAAQ,GAAW,IAAM,GAC7D,ICjKA,iBAMA,YAAe,EAAK,CACnB,EAAY,MAAQ,EACpB,EAAY,QAAU,EACtB,EAAY,OAAS,EACrB,EAAY,QAAU,EACtB,EAAY,OAAS,EACrB,EAAY,QAAU,EACtB,EAAY,SAAW,IACvB,EAAY,QAAU,EAEtB,OAAO,KAAK,CAAG,EAAE,QAAQ,GAAO,CAC/B,EAAY,GAAO,EAAI,EACxB,CAAC,EAMD,EAAY,MAAQ,CAAC,EACrB,EAAY,MAAQ,CAAC,EAOrB,EAAY,WAAa,CAAC,EAQ1B,WAAqB,EAAW,CAC/B,GAAI,GAAO,EAEX,OAAS,GAAI,EAAG,EAAI,EAAU,OAAQ,IACrC,EAAS,IAAQ,GAAK,EAAQ,EAAU,WAAW,CAAC,EACpD,GAAQ,EAGT,MAAO,GAAY,OAAO,KAAK,IAAI,CAAI,EAAI,EAAY,OAAO,OAC/D,CACA,EAAY,YAAc,EAS1B,WAAqB,EAAW,CAC/B,GAAI,GACA,EAAiB,KACjB,EACA,EAEJ,cAAkB,EAAM,CAEvB,GAAI,CAAC,EAAM,QACV,OAGD,GAAM,GAAO,EAGP,EAAO,OAAO,GAAI,KAAM,EACxB,GAAK,EAAQ,IAAY,GAC/B,EAAK,KAAO,GACZ,EAAK,KAAO,EACZ,EAAK,KAAO,EACZ,EAAW,EAEX,EAAK,GAAK,EAAY,OAAO,EAAK,EAAE,EAEhC,MAAO,GAAK,IAAO,UAEtB,EAAK,QAAQ,IAAI,EAIlB,GAAI,GAAQ,EACZ,EAAK,GAAK,EAAK,GAAG,QAAQ,gBAAiB,CAAC,EAAO,KAAW,CAE7D,GAAI,IAAU,KACb,MAAO,IAER,IACA,GAAM,GAAY,EAAY,WAAW,IACzC,GAAI,MAAO,IAAc,WAAY,CACpC,GAAM,IAAM,EAAK,GACjB,EAAQ,EAAU,KAAK,EAAM,EAAG,EAGhC,EAAK,OAAO,EAAO,CAAC,EACpB,GACD,CACA,MAAO,EACR,CAAC,EAGD,EAAY,WAAW,KAAK,EAAM,CAAI,EAGtC,AADc,GAAK,KAAO,EAAY,KAChC,MAAM,EAAM,CAAI,CACvB,CAEA,SAAM,UAAY,EAClB,EAAM,UAAY,EAAY,UAAU,EACxC,EAAM,MAAQ,EAAY,YAAY,CAAS,EAC/C,EAAM,OAAS,EACf,EAAM,QAAU,EAAY,QAE5B,OAAO,eAAe,EAAO,UAAW,CACvC,WAAY,GACZ,aAAc,GACd,IAAK,IACA,IAAmB,KACf,EAEJ,KAAoB,EAAY,YACnC,GAAkB,EAAY,WAC9B,EAAe,EAAY,QAAQ,CAAS,GAGtC,GAER,IAAK,GAAK,CACT,EAAiB,CAClB,CACD,CAAC,EAGG,MAAO,GAAY,MAAS,YAC/B,EAAY,KAAK,CAAK,EAGhB,CACR,CAEA,WAAgB,EAAW,EAAW,CACrC,GAAM,GAAW,EAAY,KAAK,UAAa,OAAO,GAAc,IAAc,IAAM,GAAa,CAAS,EAC9G,SAAS,IAAM,KAAK,IACb,CACR,CASA,WAAgB,EAAY,CAC3B,EAAY,KAAK,CAAU,EAC3B,EAAY,WAAa,EAEzB,EAAY,MAAQ,CAAC,EACrB,EAAY,MAAQ,CAAC,EAErB,GAAI,GACE,EAAS,OAAO,IAAe,SAAW,EAAa,IAAI,MAAM,QAAQ,EACzE,EAAM,EAAM,OAElB,IAAK,EAAI,EAAG,EAAI,EAAK,IACpB,AAAI,CAAC,EAAM,IAKX,GAAa,EAAM,GAAG,QAAQ,MAAO,KAAK,EAE1C,AAAI,EAAW,KAAO,IACrB,EAAY,MAAM,KAAK,GAAI,QAAO,IAAM,EAAW,MAAM,CAAC,EAAI,GAAG,CAAC,EAElE,EAAY,MAAM,KAAK,GAAI,QAAO,IAAM,EAAa,GAAG,CAAC,EAG5D,CAQA,YAAmB,CAClB,GAAM,GAAa,CAClB,GAAG,EAAY,MAAM,IAAI,CAAW,EACpC,GAAG,EAAY,MAAM,IAAI,CAAW,EAAE,IAAI,GAAa,IAAM,CAAS,CACvE,EAAE,KAAK,GAAG,EACV,SAAY,OAAO,EAAE,EACd,CACR,CASA,WAAiB,EAAM,CACtB,GAAI,EAAK,EAAK,OAAS,KAAO,IAC7B,MAAO,GAGR,GAAI,GACA,EAEJ,IAAK,EAAI,EAAG,EAAM,EAAY,MAAM,OAAQ,EAAI,EAAK,IACpD,GAAI,EAAY,MAAM,GAAG,KAAK,CAAI,EACjC,MAAO,GAIT,IAAK,EAAI,EAAG,EAAM,EAAY,MAAM,OAAQ,EAAI,EAAK,IACpD,GAAI,EAAY,MAAM,GAAG,KAAK,CAAI,EACjC,MAAO,GAIT,MAAO,EACR,CASA,WAAqB,EAAQ,CAC5B,MAAO,GAAO,SAAS,EACrB,UAAU,EAAG,EAAO,SAAS,EAAE,OAAS,CAAC,EACzC,QAAQ,UAAW,GAAG,CACzB,CASA,WAAgB,EAAK,CACpB,MAAI,aAAe,OACX,EAAI,OAAS,EAAI,QAElB,CACR,CAMA,YAAmB,CAClB,QAAQ,KAAK,uIAAuI,CACrJ,CAEA,SAAY,OAAO,EAAY,KAAK,CAAC,EAE9B,CACR,CAEA,EAAO,QAAU,KCjRjB,gBAMA,EAAQ,WAAa,GACrB,EAAQ,KAAO,GACf,EAAQ,KAAO,GACf,EAAQ,UAAY,GACpB,EAAQ,QAAU,GAAa,EAC/B,EAAQ,QAAW,KAAM,CACxB,GAAI,GAAS,GAEb,MAAO,IAAM,CACZ,AAAK,GACJ,GAAS,GACT,QAAQ,KAAK,uIAAuI,EAEtJ,CACD,GAAG,EAMH,EAAQ,OAAS,CAChB,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,SACD,EAWA,aAAqB,CAIpB,MAAI,OAAO,QAAW,KAAe,OAAO,SAAY,QAAO,QAAQ,OAAS,YAAc,OAAO,QAAQ,QACrG,GAIJ,MAAO,WAAc,KAAe,UAAU,WAAa,UAAU,UAAU,YAAY,EAAE,MAAM,uBAAuB,EACtH,GAKA,MAAO,UAAa,KAAe,SAAS,iBAAmB,SAAS,gBAAgB,OAAS,SAAS,gBAAgB,MAAM,kBAEtI,MAAO,QAAW,KAAe,OAAO,SAAY,QAAO,QAAQ,SAAY,OAAO,QAAQ,WAAa,OAAO,QAAQ,QAG1H,MAAO,WAAc,KAAe,UAAU,WAAa,UAAU,UAAU,YAAY,EAAE,MAAM,gBAAgB,GAAK,SAAS,OAAO,GAAI,EAAE,GAAK,IAEnJ,MAAO,WAAc,KAAe,UAAU,WAAa,UAAU,UAAU,YAAY,EAAE,MAAM,oBAAoB,CAC1H,CAQA,YAAoB,EAAM,CAQzB,GAPA,EAAK,GAAM,MAAK,UAAY,KAAO,IAClC,KAAK,UACJ,MAAK,UAAY,MAAQ,KAC1B,EAAK,GACJ,MAAK,UAAY,MAAQ,KAC1B,IAAM,EAAO,QAAQ,SAAS,KAAK,IAAI,EAEpC,CAAC,KAAK,UACT,OAGD,GAAM,GAAI,UAAY,KAAK,MAC3B,EAAK,OAAO,EAAG,EAAG,EAAG,gBAAgB,EAKrC,GAAI,GAAQ,EACR,EAAQ,EACZ,EAAK,GAAG,QAAQ,cAAe,GAAS,CACvC,AAAI,IAAU,MAGd,KACI,IAAU,MAGb,GAAQ,GAEV,CAAC,EAED,EAAK,OAAO,EAAO,EAAG,CAAC,CACxB,CAUA,EAAQ,IAAM,QAAQ,OAAS,QAAQ,KAAQ,KAAM,CAAC,GAQtD,YAAc,EAAY,CACzB,GAAI,CACH,AAAI,EACH,EAAQ,QAAQ,QAAQ,QAAS,CAAU,EAE3C,EAAQ,QAAQ,WAAW,OAAO,CAEpC,MAAE,CAGF,CACD,CAQA,aAAgB,CACf,GAAI,GACJ,GAAI,CACH,EAAI,EAAQ,QAAQ,QAAQ,OAAO,CACpC,MAAE,CAGF,CAGA,MAAI,CAAC,GAAK,MAAO,SAAY,KAAe,OAAS,UACpD,GAAI,QAAQ,IAAI,OAGV,CACR,CAaA,aAAwB,CACvB,GAAI,CAGH,MAAO,aACR,MAAE,CAGF,CACD,CAEA,EAAO,QAAU,IAAoB,CAAO,EAE5C,GAAM,CAAC,eAAc,EAAO,QAM5B,GAAW,EAAI,SAAU,EAAG,CAC3B,GAAI,CACH,MAAO,MAAK,UAAU,CAAC,CACxB,OAAS,EAAP,CACD,MAAO,+BAAiC,EAAM,OAC/C,CACD,IC5QA,0DCkCA,GAAM,GACH,qBAIH,GAAM,GAAN,KAAc,CACZ,YAAY,EAAS,CACnB,KAAK,QAAU,EAEf,KAAK,yBAA2B,CAClC,CAEA,QAAQ,EAAO,EAAO,CACpB,GAAI,EAAQ,EACV,MAAO,GAGT,GAAI,EAAO,CAET,GAAM,GAAW,EAAQ,OAAqB,EAIxC,EACJ,EAAQ,EAA2B,IAErC,MAAO,GAAW,CACpB,CAEA,GAAM,GAAa,OAAqB,EACxC,MAAO,GAAQ,EAAa,GAC9B,CACF,EAGA,GAAO,GAAQ,EC3Df,OAAyB,OCRzB,GAAO,GAAQ,CACb,YACF,ECJA,GAAM,GAAe,AAAC,GAAQ,WAAW,EAAI,QAAQ,CAAC,CAAC,EFWvD,GAAM,GAAM,eAAa,6BAA6B,EAOhD,GAAa,IAIb,EAAyB,IACzB,EAAiB,IACjB,EAAoB,IACpB,EAAoB,IAIpB,EAAmB,IACnB,GAAuB,GAIvB,GAAgC,IAChC,GAA+B,IAC/B,GAA+C,IAE/C,EAAN,KAA2B,CACzB,YAAY,EAAS,CACnB,KAAK,QAAU,CACjB,CAUA,yBAAyB,EAAO,CAE9B,GAAM,GAAc,AADQ,EAAQ,EAAS,SACH,GAG1C,MAAO,CACL,qBAAsB,EAAc,EACpC,cAAe,EAAc,EAC7B,iBAAkB,EAAc,EAChC,iBAAkB,EAAc,CAClC,CACF,CASA,eAAe,EAAmB,EAAkB,EAAkB,CACpE,GAAM,GAAuB,CAAC,EAC9B,OAAW,CAAC,EAAK,IAAU,QAAO,QAAQ,CAAiB,EAGzD,AAAI,EAAI,WAAW,kBAAkB,EACnC,EAAqB,GAAO,EAAQ,EAKpC,EAAqB,GAAO,EAAQ,EAGxC,MAAO,EACT,CAYA,QAAQ,EAAO,EAAkB,EAAkB,CACjD,GAAM,GAAoB,KAAK,yBAAyB,CAAK,EAa7D,GAVI,QAAQ,CAAe,IAAM,IAC/B,GAAkB,GAIhB,IAAoB,IACtB,GAAkB,IAIhB,MAAO,IAAoB,SAC7B,KAAM,IAAI,OACR,wFAAwF,GAC1F,EAGF,GAAM,GAAuB,KAAK,eAChC,EACA,CACF,EAMA,MAAO,AAHW,QAAO,OAAO,CAAoB,EAGnC,OACf,CAAC,EAAW,IAAiB,EAAY,CAC3C,CACF,CAUA,SAAS,EAAO,EAAkB,EAAkB,CAClD,GAAM,GAAoB,KAAK,0BAA0B,CAAK,EAa9D,GAVI,QAAQ,CAAe,IAAM,IAC/B,GAAkB,GAIhB,IAAoB,IACtB,GAAkB,IAIhB,MAAO,IAAoB,SAC7B,KAAM,IAAI,OACR,yFAAyF,GAC3F,EAGF,GAAM,GAAuB,KAAK,eAChC,EACA,CACF,EAMA,MAAO,AAHW,QAAO,OAAO,CAAoB,EAGnC,OACf,CAAC,EAAW,IAAiB,EAAY,CAC3C,CACF,CASA,cAAc,EAAO,CACnB,GAAM,GAAoB,KAAK,yBAAyB,CAAK,EAM7D,MAAO,AAHc,QAAO,OAAO,CAAiB,EAGhC,OAClB,CAAC,EAAW,IAAiB,EAAY,CAC3C,CACF,CAgBA,0BACE,EACA,EAAY,GACZ,EAAa,GACb,EAAkB,GAClB,CACA,GAAM,GAAoB,KAAK,yBAAyB,CAAK,EACvD,EAA6B,CAAC,EAEpC,EAAI,CAAE,mBAAkB,CAAC,EACzB,GAAM,GAAe,OAAO,OAAO,CAAiB,EAGpD,OAAW,CAAC,EAAK,IAAU,QAAO,QAAQ,CAAiB,EAEzD,EAA2B,GAAG,aAAiB,EAAQ,EAGvD,EAA2B,GAAG,kBAC5B,EAAQ,EAAa,EAEzB,SAAI,CAAE,4BAA2B,CAAC,EAE3B,CACT,CASA,eAAe,EAAO,CAIpB,GAAI,GAAc,EACd,EAAmB,EAEjB,EAAoB,OAAO,QAC/B,KAAK,0BAA0B,CAAK,CACtC,EAEA,OAAW,CAAC,EAAK,IAAQ,GACvB,AAAI,EAAI,QAAQ,OAAO,EAAI,GACzB,IAAe,GAInB,OAAW,CAAC,EAAK,IAAQ,GACvB,AAAI,EAAI,QAAQ,YAAY,EAAI,GAC9B,IAAoB,GAIxB,MAAO,GAAc,CACvB,CAIA,yBAAyB,EAAgB,EAAkB,EAAkB,CAC3E,MAAO,GAAa,EAAiB,CAAe,CACtD,CAEA,kBAAkB,EAAgB,EAAkB,IAAM,CACxD,MAAO,GAAiB,EAAkB,EAC5C,CAEA,uBAAuB,EAAU,EAAkB,IAAM,CACvD,MAAO,GAAW,EAAkB,EACtC,CAEA,oBAAoB,EAAc,CAChC,MAAO,CACL,qBAAsB,EAAa,EAAe,CAAsB,EACxE,cAAe,EAAa,EAAe,CAAc,EACzD,iBAAkB,EAAa,EAAe,CAAiB,EAC/D,iBAAkB,EAAa,EAAe,CAAiB,CACjE,CACF,CACF,EAGA,GAAO,IAAQ,EG9Rf,GAAM,GAAN,KAAU,CACR,YAAY,EAAS,CACnB,KAAK,MAAQ,GAAI,GAGb,GAAS,QAAU,OACrB,MAAK,MAAQ,GAAI,IAErB,CAWA,QAAQ,EAAO,EAAO,CACpB,MAAO,MAAK,MAAM,QAAQ,EAAO,CAAK,CACxC,CAWA,SAAS,EAAO,EAAO,CACrB,MAAI,MAAK,OAAO,SACP,KAAK,MAAM,SAAS,EAAO,CAAK,EAEvC,SAAQ,KACN,+EACF,EACO,KAAK,MAAM,QAAQ,EAAO,CAAK,EAE1C,CAEA,UAAU,EAAU,EAAc,CAChC,GAAM,GAAe,CAAC,EACtB,OAAS,KAAU,QAAO,KAAK,EAAS,OAAO,EAAG,CAChD,GAAI,GACJ,AAAI,GAAgB,EAAa,QAAQ,CAAM,EAAI,GACjD,EAAM,KAAK,QAAQ,EAAS,QAAQ,GAAQ,aAAc,EAAI,EAE9D,EAAM,KAAK,QAAQ,EAAS,QAAQ,GAAQ,YAAY,EAE1D,EAAa,KAAK,CAChB,SACA,MACA,aAAc,EAAS,QAAQ,GAAQ,YACzC,CAAC,CACH,CACA,SAAa,KAAK,CAAC,EAAG,IAAM,EAAE,IAAM,EAAE,GAAG,EAElC,CACT,CAEA,QAAQ,EAAU,EAAO,CAQvB,GAAM,GAAY,KAAK,UAAU,EAAU,CAAK,EAC5C,EAAW,EACf,OAAS,KAAU,GACjB,GAAY,EAAO,IAErB,MAAO,EACT,CAEA,eAAe,EAAU,EAAc,CACrC,GAAM,GAAoB,CAAC,EAC3B,OAAS,KAAS,GAAS,OAAQ,CACjC,GAAM,GAAS,GAAI,KAAI,EAAM,GAAG,EAAE,OAC5B,EAAe,EAAM,aACrB,EAAiB,KAAK,QAC1B,EACA,GAAgB,EAAa,QAAQ,CAAM,EAAI,EACjD,EACM,EAAc,EAAM,KAC1B,AAAK,EAAkB,IACrB,GAAkB,GAAe,CAAE,IAAK,EAAG,aAAc,CAAE,GAE7D,EAAkB,GAAa,KAAO,EACtC,EAAkB,GAAa,cAAgB,CACjD,CAEA,GAAM,GAAM,CAAC,EACb,OAAS,KAAQ,QAAO,KAAK,CAAiB,EAC5C,EAAI,KAAK,CACP,OACA,IAAK,EAAkB,GAAM,IAC7B,aAAc,EAAkB,GAAM,YACxC,CAAC,EAEH,SAAI,KAAK,CAAC,EAAG,IAAM,EAAE,IAAM,EAAE,GAAG,EACzB,CACT,CAEA,kBAAkB,EAAU,EAAc,CACxC,GAAM,GAAY,CAAC,EACnB,OAAS,KAAS,GAAS,OAAQ,CACjC,GAAM,GAAS,GAAI,KAAI,EAAM,GAAG,EAAE,OAC5B,EAAe,EAAM,aACrB,EAAiB,KAAK,QAC1B,EACA,GAAgB,EAAa,QAAQ,CAAM,EAAI,EACjD,EACA,EAAU,KAAK,CAAE,IAAK,EAAM,IAAK,IAAK,EAAgB,cAAa,CAAC,CACtE,CACA,SAAU,KAAK,CAAC,EAAG,IAAM,EAAE,IAAM,EAAE,GAAG,EAE/B,EAAU,MAAM,EAAG,EAAU,OAAS,GAAK,GAAK,EAAU,MAAM,CACzE,CAEA,SAAS,EAAU,EAAc,CAC/B,GAAI,GAAa,EACb,EAAa,EAEX,EAAkB,EAAS,gBACjC,OAAS,KAAK,QAAO,KAAK,EAAS,OAAO,EACxC,AAAK,EAAE,MAAM,CAAe,EAM1B,GAAc,KAAK,QACjB,EAAS,QAAQ,GAAG,aACpB,GAAgB,EAAa,QAAQ,CAAC,EAAI,EAC5C,EARA,GAAc,KAAK,QACjB,EAAS,QAAQ,GAAG,aACpB,GAAgB,EAAa,QAAQ,CAAC,EAAI,EAC5C,EAQJ,MAAO,CAAE,aAAY,YAAW,CAClC,CACF,EAGA,GAAO,GAAQ,ECtJf,OAAyB,OCAzB,OAAyB,OACnB,EAAM,eAAa,iBAAiB,EAE1C,YAAe,EAAQ,CAErB,MAAI,OAAO,IAAW,SACb,GAAgB,CAAM,EAEtB,GAAuB,CAAM,CAExC,CAEA,kBAA+B,EAAQ,CAKrC,MAAO,AADK,MAAM,AAHN,MAAM,OAChB,oDAAoD,GACtD,GACsB,KAAK,GAChB,KACb,CAEA,kBAAsC,EAAS,CAC7C,GAAI,CACF,GAAM,GAAU,2DACV,EAAgB,KAAK,UAAU,CAAO,EAEtC,EAAM,KAAM,OAAM,GAAG,KAAW,GAAe,EAKrD,EAAI,GAAG,KAAW,GAAe,EACjC,EAAI,CAAE,KAAI,CAAC,EACX,GAAM,GAAa,KAAM,GAAI,KAAK,EAClC,EAAI,CAAE,YAAW,CAAC,EAElB,GAAM,GAAuB,KAAM,GAAI,KAAK,EAE5C,MAAO,IAAwB,CAAoB,CACrD,MAAE,CACA,MAAO,CAAC,CACV,CACF,CAEA,YAAiC,EAAc,CAG7C,MAAO,AADc,AADL,QAAO,QAAQ,CAAY,EACd,OAAO,CAAC,CAAC,EAAK,KAAS,EAAI,KAAK,EACzC,IAAI,CAAC,CAAC,EAAK,KAAS,EAAI,GAAG,CACjD,CAEA,GAAO,IAAQ,CACb,QACF,EDlDA,GAAM,IAAM,eAAa,cAAc,EAIvC,YAAe,EAAQ,EAAI,CACzB,MAAO,IAAW,MAAM,CAAM,CAChC,CAEA,GAAO,GAAQ,CACb,QACF,ENTA,GAAO,IAAQ,CAAE,MAAK,SAAQ",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tgwf/co2",
3
- "version": "0.10.1",
3
+ "version": "0.10.4",
4
4
  "description": "Work out the co2 of your digital services",
5
5
  "main": "dist/cjs/index-node.js",
6
6
  "module": "dist/esm/index.js",
@@ -20,6 +20,7 @@
20
20
  "build:esm": "node .esbuild.esm.js && ./fixup",
21
21
  "build:browser": "node .esbuild.browser.js",
22
22
  "build:node": "node .esbuild.node.js && ./fixup",
23
+ "release:patch": "npm run build && np patch",
23
24
  "gitpod": "npm run build && cp ./dist/iife/index.js ./public && npm run serve"
24
25
  },
25
26
  "keywords": [
package/public/index.html CHANGED
@@ -26,7 +26,9 @@
26
26
 
27
27
  <script src="./index.js"></script>
28
28
  <script>
29
- let emissions = new co2.co2();
29
+ let emissions = new co2.co2({
30
+ model: 'swd'
31
+ });
30
32
 
31
33
  async function main() {
32
34
 
package/src/co2.js CHANGED
@@ -1,16 +1,15 @@
1
1
  "use strict";
2
2
 
3
3
  import OneByte from "./1byte.js";
4
+ import SustainableWebDesign from "./sustainable-web-design.js";
4
5
 
5
6
  class CO2 {
6
7
  constructor(options) {
7
- this.options = options;
8
-
9
- // default model
10
8
  this.model = new OneByte();
11
-
12
- if (options) {
13
- this.model = new options.model();
9
+ // Using optional chaining allows an empty object to be passed
10
+ // in without breaking the code.
11
+ if (options?.model === "swd") {
12
+ this.model = new SustainableWebDesign();
14
13
  }
15
14
  }
16
15
 
@@ -27,6 +26,26 @@ class CO2 {
27
26
  return this.model.perByte(bytes, green);
28
27
  }
29
28
 
29
+ /**
30
+ * Accept a figure in bytes for data transfer, and a boolean for whether
31
+ * the domain shows as 'green', and return a CO2 figure for energy used to shift the corresponding
32
+ * the data transfer.
33
+ *
34
+ * @param {number} bytes
35
+ * @param {boolean} green
36
+ * @return {number} the amount of CO2 in grammes
37
+ */
38
+ perVisit(bytes, green) {
39
+ if (this.model?.perVisit) {
40
+ return this.model.perVisit(bytes, green);
41
+ } else {
42
+ console.warn(
43
+ "The model you have selected does not support perVisit. Using perByte instead."
44
+ );
45
+ return this.model.perByte(bytes, green);
46
+ }
47
+ }
48
+
30
49
  perDomain(pageXray, greenDomains) {
31
50
  const co2PerDomain = [];
32
51
  for (let domain of Object.keys(pageXray.domains)) {
@@ -74,7 +74,7 @@ class SustainableWebDesign {
74
74
  for (const [key, value] of Object.entries(energyBycomponent)) {
75
75
  // we update the datacentre, as that's what we have information
76
76
  // about.
77
- if (key === "dataCenterEnergy") {
77
+ if (key.startsWith("dataCenterEnergy")) {
78
78
  returnCO2ByComponent[key] = value * carbonIntensity;
79
79
  } else {
80
80
  // We don't have info about the device location,
@@ -130,6 +130,48 @@ class SustainableWebDesign {
130
130
  );
131
131
  }
132
132
 
133
+ /**
134
+ * Accept a figure for bytes transferred and return a single figure for CO2
135
+ * emissions. This method applies caching assumptions from the original Sustainable Web Design model.
136
+ *
137
+ * @param {number} bytes - the data transferred in bytes
138
+ * @param {number} `carbonIntensity` the carbon intensity for datacentre (average figures, not marginal ones)
139
+ * @return {number} the total number in grams of CO2 equivalent emissions
140
+ */
141
+ perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {
142
+ const energyBycomponent = this.energyPerVisitByComponent(bytes);
143
+
144
+ // when faced with falsy values, fallback to global intensity
145
+ if (Boolean(carbonIntensity) === false) {
146
+ carbonIntensity = GLOBAL_INTENSITY;
147
+ }
148
+ // if we have a boolean, we have a green result from the green web checker
149
+ // use the renewables intensity
150
+ if (carbonIntensity === true) {
151
+ carbonIntensity = RENEWABLES_INTENSITY;
152
+ }
153
+
154
+ // otherwise when faced with non numeric values throw an error
155
+ if (typeof carbonIntensity !== "number") {
156
+ throw new Error(
157
+ `perVisit expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`
158
+ );
159
+ }
160
+
161
+ const co2ValuesbyComponent = this.co2byComponent(
162
+ energyBycomponent,
163
+ carbonIntensity
164
+ );
165
+
166
+ // pull out our values…
167
+ const co2Values = Object.values(co2ValuesbyComponent);
168
+
169
+ // so we can return their sum
170
+ return co2Values.reduce(
171
+ (prevValue, currentValue) => prevValue + currentValue
172
+ );
173
+ }
174
+
133
175
  /**
134
176
  * Accept a figure for bytes transferred and return the number of kilowatt hours used
135
177
  * by the total system for this data transfer