@tgwf/co2 0.9.0 → 0.10.2

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.
Files changed (92) hide show
  1. package/.esbuild.browser.js +11 -0
  2. package/.esbuild.esm.js +25 -0
  3. package/.esbuild.node.js +16 -0
  4. package/.gitpod.yml +8 -0
  5. package/CHANGELOG.md +22 -1
  6. package/README.md +29 -66
  7. package/dist/cjs/1byte.js +52 -0
  8. package/dist/cjs/1byte.js.map +7 -0
  9. package/dist/cjs/1byte.test.js +27 -0
  10. package/dist/cjs/1byte.test.js.map +7 -0
  11. package/dist/cjs/co2.js +121 -0
  12. package/dist/cjs/co2.js.map +7 -0
  13. package/dist/cjs/co2.test.js +258 -0
  14. package/dist/cjs/co2.test.js.map +7 -0
  15. package/dist/cjs/constants/file-size.js +27 -0
  16. package/dist/cjs/constants/file-size.js.map +7 -0
  17. package/dist/cjs/constants/index.js +27 -0
  18. package/dist/cjs/constants/index.js.map +7 -0
  19. package/dist/cjs/helpers/index.js +24 -0
  20. package/dist/cjs/helpers/index.js.map +7 -0
  21. package/dist/cjs/hosting-api.js +64 -0
  22. package/dist/cjs/hosting-api.js.map +7 -0
  23. package/dist/cjs/hosting-api.test.js +47 -0
  24. package/dist/cjs/hosting-api.test.js.map +7 -0
  25. package/dist/cjs/hosting-database.node.test.js +36 -0
  26. package/dist/cjs/hosting-database.node.test.js.map +7 -0
  27. package/dist/cjs/hosting-json.node.js +73 -0
  28. package/dist/cjs/hosting-json.node.js.map +7 -0
  29. package/dist/cjs/hosting-json.node.test.js +43 -0
  30. package/dist/cjs/hosting-json.node.test.js.map +7 -0
  31. package/dist/cjs/hosting-node.js +78 -0
  32. package/dist/cjs/hosting-node.js.map +7 -0
  33. package/dist/cjs/hosting.js +36 -0
  34. package/dist/cjs/hosting.js.map +7 -0
  35. package/dist/cjs/hosting.test.js +74 -0
  36. package/dist/cjs/hosting.test.js.map +7 -0
  37. package/dist/cjs/index-node.js +29 -0
  38. package/dist/cjs/index-node.js.map +7 -0
  39. package/dist/cjs/index.js +31 -0
  40. package/dist/cjs/index.js.map +7 -0
  41. package/dist/cjs/package.json +3 -0
  42. package/dist/cjs/sustainable-web-design.js +134 -0
  43. package/dist/cjs/sustainable-web-design.js.map +7 -0
  44. package/dist/cjs/sustainable-web-design.test.js +79 -0
  45. package/dist/cjs/sustainable-web-design.test.js.map +7 -0
  46. package/dist/esm/1byte.js +32 -0
  47. package/dist/esm/1byte.test.js +11 -0
  48. package/dist/esm/co2.js +98 -0
  49. package/{src → dist/esm}/co2.test.js +47 -94
  50. package/dist/esm/constants/file-size.js +7 -0
  51. package/dist/esm/constants/index.js +4 -0
  52. package/dist/esm/helpers/index.js +4 -0
  53. package/dist/esm/hosting-api.js +41 -0
  54. package/dist/esm/hosting-api.test.js +31 -0
  55. package/{src/hosting-database.test.js → dist/esm/hosting-database.node.test.js} +7 -18
  56. package/{src/hosting-json.test.js → dist/esm/hosting-json.node.test.js} +4 -21
  57. package/dist/esm/hosting.js +13 -0
  58. package/{src → dist/esm}/hosting.test.js +10 -30
  59. package/dist/esm/index.js +8 -0
  60. package/dist/esm/package.json +3 -0
  61. package/dist/esm/sustainable-web-design.js +111 -0
  62. package/{src → dist/esm}/sustainable-web-design.test.js +19 -37
  63. package/dist/iife/index.js +2 -0
  64. package/dist/iife/index.js.map +7 -0
  65. package/fixup +19 -0
  66. package/package.json +38 -17
  67. package/public/index.html +58 -0
  68. package/public/index.js +2 -0
  69. package/src/1byte.js +2 -3
  70. package/src/co2.js +17 -18
  71. package/src/constants/file-size.js +2 -2
  72. package/src/constants/index.js +2 -2
  73. package/src/helpers/index.js +1 -3
  74. package/src/hosting-api.js +23 -43
  75. package/src/{hosting-json.js → hosting-json.node.js} +9 -11
  76. package/src/hosting-node.js +94 -0
  77. package/src/hosting.js +6 -28
  78. package/src/index-node.js +4 -0
  79. package/src/index.js +4 -6
  80. package/src/sustainable-web-design.js +37 -11
  81. package/.eslintrc.json +0 -21
  82. package/.github/workflows/unittests.yml +0 -26
  83. package/data/Lean-ICT-Materials-1byte-Model-2018.xlsx +0 -0
  84. package/data/Lean-ICT-Materials-Forecast-Model-2018.xlsx +0 -0
  85. package/data/fixtures/tgwf.har +0 -6107
  86. package/data/fixtures/url2green.test.db +0 -0
  87. package/data/fixtures/url2green.test.json +0 -1
  88. package/data/fixtures/url2green.test.json.gz +0 -0
  89. package/images/swd-energy-usage.png +0 -0
  90. package/src/1byte.test.js +0 -17
  91. package/src/green-byte.js +0 -26
  92. package/src/hosting-api.test.js +0 -37
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
17
+ var import_fs = __toESM(require("fs"));
18
+ var import_zlib = __toESM(require("zlib"));
19
+ var import_util = require("util");
20
+ var import_debug = __toESM(require("debug"));
21
+ const readFile = (0, import_util.promisify)(import_fs.default.readFile);
22
+ const gunzip = (0, import_util.promisify)(import_zlib.default.gunzip);
23
+ const log = (0, import_debug.default)("tgwf:hostingCache");
24
+ async function streamToString(stream) {
25
+ return new Promise((resolve, reject) => {
26
+ const chunks = [];
27
+ stream.on("error", reject);
28
+ stream.on("data", (chunk) => chunks.push(chunk));
29
+ stream.on("end", () => resolve(Buffer.concat(chunks)));
30
+ });
31
+ }
32
+ async function getGzippedFileAsJson(jsonPath) {
33
+ const readStream = import_fs.default.createReadStream(jsonPath);
34
+ const text = await streamToString(readStream);
35
+ const unzipped = await gunzip(text);
36
+ return unzipped.toString();
37
+ }
38
+ async function loadJSON(jsonPath) {
39
+ const jsonBuffer = jsonPath.endsWith(".gz") ? await getGzippedFileAsJson(jsonPath) : await readFile(jsonPath);
40
+ return JSON.parse(jsonBuffer);
41
+ }
42
+ async function check(domain, db) {
43
+ if (typeof domain === "string") {
44
+ return checkInJSON(domain, db);
45
+ } else {
46
+ return checkDomainsInJSON(domain, db);
47
+ }
48
+ }
49
+ function checkInJSON(domain, db) {
50
+ if (db.indexOf(domain) > -1) {
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ function greenDomainsFromResults(greenResults) {
56
+ const entries = Object.entries(greenResults);
57
+ const greenEntries = entries.filter(([key, val]) => val.green);
58
+ return greenEntries.map(([key, val]) => val.url);
59
+ }
60
+ function checkDomainsInJSON(domains, db) {
61
+ let greenDomains = [];
62
+ for (let domain of domains) {
63
+ if (db.indexOf(domain) > -1) {
64
+ greenDomains.push(domain);
65
+ }
66
+ }
67
+ return greenDomains;
68
+ }
69
+ module.exports = {
70
+ check,
71
+ loadJSON
72
+ };
73
+ //# sourceMappingURL=hosting-json.node.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hosting-json.node.js"],
4
+ "sourcesContent": ["\"use strict\";\n\nimport fs from \"fs\";\nimport zlib from \"zlib\";\nimport { promisify } from \"util\";\n\nconst readFile = promisify(fs.readFile);\nconst gunzip = promisify(zlib.gunzip);\n\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:hostingCache\");\n\nasync function streamToString(stream) {\n return new Promise((resolve, reject) => {\n const chunks = [];\n stream.on(\"error\", reject);\n stream.on(\"data\", (chunk) => chunks.push(chunk));\n stream.on(\"end\", () => resolve(Buffer.concat(chunks)));\n });\n}\n\nasync function getGzippedFileAsJson(jsonPath) {\n const readStream = fs.createReadStream(jsonPath);\n const text = await streamToString(readStream);\n const unzipped = await gunzip(text);\n return unzipped.toString();\n}\n\nasync function loadJSON(jsonPath) {\n const jsonBuffer = jsonPath.endsWith(\".gz\")\n ? await getGzippedFileAsJson(jsonPath)\n : await readFile(jsonPath);\n return JSON.parse(jsonBuffer);\n}\n\nasync function check(domain, db) {\n // is it a single domain or an array of them?\n if (typeof domain === \"string\") {\n return checkInJSON(domain, db);\n } else {\n return checkDomainsInJSON(domain, db);\n }\n}\n\nfunction checkInJSON(domain, db) {\n if (db.indexOf(domain) > -1) {\n return true;\n }\n return false;\n}\n\nfunction greenDomainsFromResults(greenResults) {\n const entries = Object.entries(greenResults);\n const greenEntries = entries.filter(([key, val]) => val.green);\n\n return greenEntries.map(([key, val]) => val.url);\n}\n\nfunction checkDomainsInJSON(domains, db) {\n let greenDomains = [];\n\n for (let domain of domains) {\n if (db.indexOf(domain) > -1) {\n greenDomains.push(domain);\n }\n }\n return greenDomains;\n}\n\nmodule.exports = {\n check,\n loadJSON,\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAEA,gBAAe;AACf,kBAAiB;AACjB,kBAA0B;AAK1B,mBAAyB;AAHzB,MAAM,WAAW,2BAAU,kBAAG,QAAQ;AACtC,MAAM,SAAS,2BAAU,oBAAK,MAAM;AAGpC,MAAM,MAAM,0BAAa,mBAAmB;AAE5C,8BAA8B,QAAQ;AACpC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,CAAC;AAChB,WAAO,GAAG,SAAS,MAAM;AACzB,WAAO,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,KAAK,CAAC;AAC/C,WAAO,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC;AAAA,EACvD,CAAC;AACH;AAEA,oCAAoC,UAAU;AAC5C,QAAM,aAAa,kBAAG,iBAAiB,QAAQ;AAC/C,QAAM,OAAO,MAAM,eAAe,UAAU;AAC5C,QAAM,WAAW,MAAM,OAAO,IAAI;AAClC,SAAO,SAAS,SAAS;AAC3B;AAEA,wBAAwB,UAAU;AAChC,QAAM,aAAa,SAAS,SAAS,KAAK,IACtC,MAAM,qBAAqB,QAAQ,IACnC,MAAM,SAAS,QAAQ;AAC3B,SAAO,KAAK,MAAM,UAAU;AAC9B;AAEA,qBAAqB,QAAQ,IAAI;AAE/B,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,YAAY,QAAQ,EAAE;AAAA,EAC/B,OAAO;AACL,WAAO,mBAAmB,QAAQ,EAAE;AAAA,EACtC;AACF;AAEA,qBAAqB,QAAQ,IAAI;AAC/B,MAAI,GAAG,QAAQ,MAAM,IAAI,IAAI;AAC3B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,iCAAiC,cAAc;AAC7C,QAAM,UAAU,OAAO,QAAQ,YAAY;AAC3C,QAAM,eAAe,QAAQ,OAAO,CAAC,CAAC,KAAK,SAAS,IAAI,KAAK;AAE7D,SAAO,aAAa,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,GAAG;AACjD;AAEA,4BAA4B,SAAS,IAAI;AACvC,MAAI,eAAe,CAAC;AAEpB,WAAS,UAAU,SAAS;AAC1B,QAAI,GAAG,QAAQ,MAAM,IAAI,IAAI;AAC3B,mBAAa,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,OAAO,UAAU;AAAA,EACf;AAAA,EACA;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
17
+ var import_hosting_json_node = __toESM(require("./hosting-json.node.js"));
18
+ var import_path = __toESM(require("path"));
19
+ describe("hostingJSON", () => {
20
+ const jsonPath = import_path.default.resolve(__dirname, "..", "data", "fixtures", "url2green.test.json");
21
+ const jsonPathGz = import_path.default.resolve(__dirname, "..", "data", "fixtures", "url2green.test.json.gz");
22
+ describe("checking a single domain with #check", () => {
23
+ test("against the list of domains as JSON", async () => {
24
+ const db = await import_hosting_json_node.default.loadJSON(jsonPath);
25
+ const res = await import_hosting_json_node.default.check("google.com", db);
26
+ expect(res).toEqual(true);
27
+ });
28
+ test("against the list of domains as JSON loaded from a gzipped JSON", async () => {
29
+ const db = await import_hosting_json_node.default.loadJSON(jsonPathGz);
30
+ const res = await import_hosting_json_node.default.check("google.com", db);
31
+ expect(res).toEqual(true);
32
+ });
33
+ });
34
+ describe("implicitly checking multiple domains with #check", () => {
35
+ test("against the list of domains as JSON", async () => {
36
+ const db = await import_hosting_json_node.default.loadJSON(jsonPath);
37
+ const domains = ["google.com", "kochindustries.com"];
38
+ const res = await import_hosting_json_node.default.check(domains, db);
39
+ expect(res).toContain("google.com");
40
+ });
41
+ });
42
+ });
43
+ //# sourceMappingURL=hosting-json.node.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hosting-json.node.test.js"],
4
+ "sourcesContent": ["\"use strict\";\n\nimport hosting from \"./hosting-json.node.js\";\nimport path from \"path\";\n\ndescribe(\"hostingJSON\", () => {\n const jsonPath = path.resolve(\n __dirname,\n \"..\",\n \"data\",\n \"fixtures\",\n \"url2green.test.json\"\n );\n const jsonPathGz = path.resolve(\n __dirname,\n \"..\",\n \"data\",\n \"fixtures\",\n \"url2green.test.json.gz\"\n );\n describe(\"checking a single domain with #check\", () => {\n test(\"against the list of domains as JSON\", async () => {\n const db = await hosting.loadJSON(jsonPath);\n const res = await hosting.check(\"google.com\", db);\n expect(res).toEqual(true);\n });\n test(\"against the list of domains as JSON loaded from a gzipped JSON\", async () => {\n const db = await hosting.loadJSON(jsonPathGz);\n const res = await hosting.check(\"google.com\", db);\n expect(res).toEqual(true);\n });\n });\n describe(\"implicitly checking multiple domains with #check\", () => {\n test(\"against the list of domains as JSON\", async () => {\n const db = await hosting.loadJSON(jsonPath);\n const domains = [\"google.com\", \"kochindustries.com\"];\n\n const res = await hosting.check(domains, db);\n expect(res).toContain(\"google.com\");\n });\n });\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAEA,+BAAoB;AACpB,kBAAiB;AAEjB,SAAS,eAAe,MAAM;AAC5B,QAAM,WAAW,oBAAK,QACpB,WACA,MACA,QACA,YACA,qBACF;AACA,QAAM,aAAa,oBAAK,QACtB,WACA,MACA,QACA,YACA,wBACF;AACA,WAAS,wCAAwC,MAAM;AACrD,SAAK,uCAAuC,YAAY;AACtD,YAAM,KAAK,MAAM,iCAAQ,SAAS,QAAQ;AAC1C,YAAM,MAAM,MAAM,iCAAQ,MAAM,cAAc,EAAE;AAChD,aAAO,GAAG,EAAE,QAAQ,IAAI;AAAA,IAC1B,CAAC;AACD,SAAK,kEAAkE,YAAY;AACjF,YAAM,KAAK,MAAM,iCAAQ,SAAS,UAAU;AAC5C,YAAM,MAAM,MAAM,iCAAQ,MAAM,cAAc,EAAE;AAChD,aAAO,GAAG,EAAE,QAAQ,IAAI;AAAA,IAC1B,CAAC;AAAA,EACH,CAAC;AACD,WAAS,oDAAoD,MAAM;AACjE,SAAK,uCAAuC,YAAY;AACtD,YAAM,KAAK,MAAM,iCAAQ,SAAS,QAAQ;AAC1C,YAAM,UAAU,CAAC,cAAc,oBAAoB;AAEnD,YAAM,MAAM,MAAM,iCAAQ,MAAM,SAAS,EAAE;AAC3C,aAAO,GAAG,EAAE,UAAU,YAAY;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,78 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var hosting_node_exports = {};
22
+ __export(hosting_node_exports, {
23
+ default: () => hosting_node_default
24
+ });
25
+ module.exports = __toCommonJS(hosting_node_exports);
26
+ var import_https = __toESM(require("https"));
27
+ var import_debug = __toESM(require("debug"));
28
+ var import_hosting_json_node = __toESM(require("./hosting-json.node.js"));
29
+ const log = (0, import_debug.default)("tgwf:hosting-node");
30
+ async function getBody(url) {
31
+ return new Promise(function(resolve, reject) {
32
+ const req = import_https.default.get(url, function(res) {
33
+ if (res.statusCode < 200 || res.statusCode >= 300) {
34
+ log("Could not get info from the Green Web Foundation API, %s for %s", res.statusCode, url);
35
+ return reject(new Error(`Status Code: ${res.statusCode}`));
36
+ }
37
+ const data = [];
38
+ res.on("data", (chunk) => {
39
+ data.push(chunk);
40
+ });
41
+ res.on("end", () => resolve(Buffer.concat(data).toString()));
42
+ });
43
+ req.end();
44
+ });
45
+ }
46
+ function check(domain, db) {
47
+ if (db) {
48
+ return import_hosting_json_node.default.check(domain, db);
49
+ }
50
+ if (typeof domain === "string") {
51
+ return checkAgainstAPI(domain);
52
+ } else {
53
+ return checkDomainsAgainstAPI(domain);
54
+ }
55
+ }
56
+ async function checkAgainstAPI(domain) {
57
+ const res = JSON.parse(await getBody(`https://api.thegreenwebfoundation.org/greencheck/${domain}`));
58
+ return res.green;
59
+ }
60
+ async function checkDomainsAgainstAPI(domains) {
61
+ try {
62
+ const allGreenCheckResults = JSON.parse(await getBody(`https://api.thegreenwebfoundation.org/v2/greencheckmulti/${JSON.stringify(domains)}`));
63
+ return import_hosting_json_node.default.greenDomainsFromResults(allGreenCheckResults);
64
+ } catch (e) {
65
+ return [];
66
+ }
67
+ }
68
+ async function checkPage(pageXray, db) {
69
+ const domains = Object.keys(pageXray.domains);
70
+ return check(domains, db);
71
+ }
72
+ var hosting_node_default = {
73
+ check,
74
+ checkPage,
75
+ greendomains: import_hosting_json_node.default.greenDomainsFromResults,
76
+ loadJSON: import_hosting_json_node.default.loadJSON
77
+ };
78
+ //# sourceMappingURL=hosting-node.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hosting-node.js"],
4
+ "sourcesContent": ["/*\n\nWe have a separate node-specific hosting.js file for node.\nThis uses the node-specific APIs for making http requests,\nand doing lookups against local JSON and sqlite databases.\nThis is used in the CommonJS build of co2.js\n\nThis lets us keep the total library small, and dependencies minimal.\n*/\n\nimport https from \"https\";\n\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:hosting-node\");\n\nimport hostingJSON from \"./hosting-json.node.js\";\n\n/**\n * Accept a url and perform an http request, returning the body\n * for parsing as JSON.\n *\n * @param {string} url\n * @return {string}\n */\nasync function getBody(url) {\n return new Promise(function (resolve, reject) {\n // Do async job\n const req = https.get(url, function (res) {\n if (res.statusCode < 200 || res.statusCode >= 300) {\n log(\n \"Could not get info from the Green Web Foundation API, %s for %s\",\n res.statusCode,\n url\n );\n return reject(new Error(`Status Code: ${res.statusCode}`));\n }\n const data = [];\n\n res.on(\"data\", (chunk) => {\n data.push(chunk);\n });\n\n res.on(\"end\", () => resolve(Buffer.concat(data).toString()));\n });\n req.end();\n });\n}\n\nfunction check(domain, db) {\n if (db) {\n return hostingJSON.check(domain, db);\n }\n\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 res = JSON.parse(\n await getBody(`https://api.thegreenwebfoundation.org/greencheck/${domain}`)\n );\n return res.green;\n}\n\nasync function checkDomainsAgainstAPI(domains) {\n try {\n const allGreenCheckResults = JSON.parse(\n await getBody(\n `https://api.thegreenwebfoundation.org/v2/greencheckmulti/${JSON.stringify(\n domains\n )}`\n )\n );\n return hostingJSON.greenDomainsFromResults(allGreenCheckResults);\n } catch (e) {\n return [];\n }\n}\n\nasync function checkPage(pageXray, db) {\n const domains = Object.keys(pageXray.domains);\n return check(domains, db);\n}\n\nexport default {\n check,\n checkPage,\n greendomains: hostingJSON.greenDomainsFromResults,\n loadJSON: hostingJSON.loadJSON,\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,mBAAkB;AAElB,mBAAyB;AAGzB,+BAAwB;AAFxB,MAAM,MAAM,0BAAa,mBAAmB;AAW5C,uBAAuB,KAAK;AAC1B,SAAO,IAAI,QAAQ,SAAU,SAAS,QAAQ;AAE5C,UAAM,MAAM,qBAAM,IAAI,KAAK,SAAU,KAAK;AACxC,UAAI,IAAI,aAAa,OAAO,IAAI,cAAc,KAAK;AACjD,YACE,mEACA,IAAI,YACJ,GACF;AACA,eAAO,OAAO,IAAI,MAAM,gBAAgB,IAAI,YAAY,CAAC;AAAA,MAC3D;AACA,YAAM,OAAO,CAAC;AAEd,UAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,aAAK,KAAK,KAAK;AAAA,MACjB,CAAC;AAED,UAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,IAAI,EAAE,SAAS,CAAC,CAAC;AAAA,IAC7D,CAAC;AACD,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,eAAe,QAAQ,IAAI;AACzB,MAAI,IAAI;AACN,WAAO,iCAAY,MAAM,QAAQ,EAAE;AAAA,EACrC;AAGA,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,gBAAgB,MAAM;AAAA,EAC/B,OAAO;AACL,WAAO,uBAAuB,MAAM;AAAA,EACtC;AACF;AAEA,+BAA+B,QAAQ;AACrC,QAAM,MAAM,KAAK,MACf,MAAM,QAAQ,oDAAoD,QAAQ,CAC5E;AACA,SAAO,IAAI;AACb;AAEA,sCAAsC,SAAS;AAC7C,MAAI;AACF,UAAM,uBAAuB,KAAK,MAChC,MAAM,QACJ,4DAA4D,KAAK,UAC/D,OACF,GACF,CACF;AACA,WAAO,iCAAY,wBAAwB,oBAAoB;AAAA,EACjE,SAAS,GAAP;AACA,WAAO,CAAC;AAAA,EACV;AACF;AAEA,yBAAyB,UAAU,IAAI;AACrC,QAAM,UAAU,OAAO,KAAK,SAAS,OAAO;AAC5C,SAAO,MAAM,SAAS,EAAE;AAC1B;AAEA,IAAO,uBAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA,cAAc,iCAAY;AAAA,EAC1B,UAAU,iCAAY;AACxB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var hosting_exports = {};
23
+ __export(hosting_exports, {
24
+ default: () => hosting_default
25
+ });
26
+ module.exports = __toCommonJS(hosting_exports);
27
+ var import_debug = __toESM(require("debug"));
28
+ var import_hosting_api = __toESM(require("./hosting-api.js"));
29
+ const log = (0, import_debug.default)("tgwf:hosting");
30
+ function check(domain, db) {
31
+ return import_hosting_api.default.check(domain);
32
+ }
33
+ var hosting_default = {
34
+ check
35
+ };
36
+ //# sourceMappingURL=hosting.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hosting.js"],
4
+ "sourcesContent": ["\"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"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAAyB;AAGzB,yBAAuB;AAFvB,MAAM,MAAM,0BAAa,cAAc;AAIvC,eAAe,QAAQ,IAAI;AACzB,SAAO,2BAAW,MAAM,MAAM;AAChC;AAEA,IAAO,kBAAQ;AAAA,EACb;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
17
+ var import_fs = __toESM(require("fs"));
18
+ var import_path = __toESM(require("path"));
19
+ var import_pagexray = __toESM(require("pagexray"));
20
+ var import_hosting_node = __toESM(require("./hosting-node.js"));
21
+ const jsonPath = import_path.default.resolve(__dirname, "..", "data", "fixtures", "url2green.test.json");
22
+ describe("hosting", () => {
23
+ let har;
24
+ beforeEach(() => {
25
+ har = JSON.parse(import_fs.default.readFileSync(import_path.default.resolve(__dirname, "../data/fixtures/tgwf.har"), "utf8"));
26
+ });
27
+ describe("checking all domains on a page object with #checkPage", () => {
28
+ it("returns a list of green domains, when passed a page object", async () => {
29
+ const pages = import_pagexray.default.convert(har);
30
+ const pageXrayRun = pages[0];
31
+ const db = await import_hosting_node.default.loadJSON(jsonPath);
32
+ const greenDomains = await import_hosting_node.default.checkPage(pageXrayRun, db);
33
+ expect(greenDomains).toHaveLength(11);
34
+ const expectedGreendomains = [
35
+ "maxcdn.bootstrapcdn.com",
36
+ "thegreenwebfoundation.org",
37
+ "www.thegreenwebfoundation.org",
38
+ "fonts.googleapis.com",
39
+ "ajax.googleapis.com",
40
+ "assets.digitalclimatestrike.net",
41
+ "cdnjs.cloudflare.com",
42
+ "graphite.thegreenwebfoundation.org",
43
+ "analytics.thegreenwebfoundation.org",
44
+ "fonts.gstatic.com",
45
+ "api.thegreenwebfoundation.org"
46
+ ];
47
+ greenDomains.forEach((dom) => {
48
+ expect(expectedGreendomains).toContain(dom);
49
+ });
50
+ });
51
+ });
52
+ describe("checking a single domain with #check", () => {
53
+ it("use the API instead", async () => {
54
+ const db = await import_hosting_node.default.loadJSON(jsonPath);
55
+ const res = await import_hosting_node.default.check("google.com", db);
56
+ expect(res).toEqual(true);
57
+ });
58
+ });
59
+ describe("implicitly checking multiple domains with #check", () => {
60
+ it("Use the API", async () => {
61
+ const db = await import_hosting_node.default.loadJSON(jsonPath);
62
+ const res = await import_hosting_node.default.check(["google.com", "kochindustries.com"], db);
63
+ expect(res).toContain("google.com");
64
+ });
65
+ });
66
+ describe("explicitly checking multiple domains with #checkMulti", () => {
67
+ it("use the API", async () => {
68
+ const db = await import_hosting_node.default.loadJSON(jsonPath);
69
+ const res = await import_hosting_node.default.check(["google.com", "kochindustries.com"], db);
70
+ expect(res).toContain("google.com");
71
+ });
72
+ });
73
+ });
74
+ //# sourceMappingURL=hosting.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hosting.test.js"],
4
+ "sourcesContent": ["\"use strict\";\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nimport pagexray from \"pagexray\";\n\nimport hosting from \"./hosting-node.js\";\n\nconst jsonPath = path.resolve(\n __dirname,\n \"..\",\n \"data\",\n \"fixtures\",\n \"url2green.test.json\"\n);\n\ndescribe(\"hosting\", () => {\n let har;\n beforeEach(() => {\n har = JSON.parse(\n fs.readFileSync(\n path.resolve(__dirname, \"../data/fixtures/tgwf.har\"),\n \"utf8\"\n )\n );\n });\n describe(\"checking all domains on a page object with #checkPage\", () => {\n it(\"returns a list of green domains, when passed a page object\", async () => {\n const pages = pagexray.convert(har);\n const pageXrayRun = pages[0];\n const db = await hosting.loadJSON(jsonPath);\n const greenDomains = await hosting.checkPage(pageXrayRun, db);\n\n expect(greenDomains).toHaveLength(11);\n const expectedGreendomains = [\n \"maxcdn.bootstrapcdn.com\",\n \"thegreenwebfoundation.org\",\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 greenDomains.forEach((dom) => {\n expect(expectedGreendomains).toContain(dom);\n });\n });\n });\n describe(\"checking a single domain with #check\", () => {\n it(\"use the API instead\", async () => {\n const db = await hosting.loadJSON(jsonPath);\n const res = await hosting.check(\"google.com\", db);\n expect(res).toEqual(true);\n });\n });\n describe(\"implicitly checking multiple domains with #check\", () => {\n it(\"Use the API\", async () => {\n const db = await hosting.loadJSON(jsonPath);\n\n const res = await hosting.check([\"google.com\", \"kochindustries.com\"], db);\n expect(res).toContain(\"google.com\");\n });\n });\n describe(\"explicitly checking multiple domains with #checkMulti\", () => {\n it(\"use the API\", async () => {\n const db = await hosting.loadJSON(jsonPath);\n const res = await hosting.check([\"google.com\", \"kochindustries.com\"], db);\n expect(res).toContain(\"google.com\");\n });\n });\n});\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAEA,gBAAe;AACf,kBAAiB;AAEjB,sBAAqB;AAErB,0BAAoB;AAEpB,MAAM,WAAW,oBAAK,QACpB,WACA,MACA,QACA,YACA,qBACF;AAEA,SAAS,WAAW,MAAM;AACxB,MAAI;AACJ,aAAW,MAAM;AACf,UAAM,KAAK,MACT,kBAAG,aACD,oBAAK,QAAQ,WAAW,2BAA2B,GACnD,MACF,CACF;AAAA,EACF,CAAC;AACD,WAAS,yDAAyD,MAAM;AACtE,OAAG,8DAA8D,YAAY;AAC3E,YAAM,QAAQ,wBAAS,QAAQ,GAAG;AAClC,YAAM,cAAc,MAAM;AAC1B,YAAM,KAAK,MAAM,4BAAQ,SAAS,QAAQ;AAC1C,YAAM,eAAe,MAAM,4BAAQ,UAAU,aAAa,EAAE;AAE5D,aAAO,YAAY,EAAE,aAAa,EAAE;AACpC,YAAM,uBAAuB;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,mBAAa,QAAQ,CAAC,QAAQ;AAC5B,eAAO,oBAAoB,EAAE,UAAU,GAAG;AAAA,MAC5C,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACD,WAAS,wCAAwC,MAAM;AACrD,OAAG,uBAAuB,YAAY;AACpC,YAAM,KAAK,MAAM,4BAAQ,SAAS,QAAQ;AAC1C,YAAM,MAAM,MAAM,4BAAQ,MAAM,cAAc,EAAE;AAChD,aAAO,GAAG,EAAE,QAAQ,IAAI;AAAA,IAC1B,CAAC;AAAA,EACH,CAAC;AACD,WAAS,oDAAoD,MAAM;AACjE,OAAG,eAAe,YAAY;AAC5B,YAAM,KAAK,MAAM,4BAAQ,SAAS,QAAQ;AAE1C,YAAM,MAAM,MAAM,4BAAQ,MAAM,CAAC,cAAc,oBAAoB,GAAG,EAAE;AACxE,aAAO,GAAG,EAAE,UAAU,YAAY;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACD,WAAS,yDAAyD,MAAM;AACtE,OAAG,eAAe,YAAY;AAC5B,YAAM,KAAK,MAAM,4BAAQ,SAAS,QAAQ;AAC1C,YAAM,MAAM,MAAM,4BAAQ,MAAM,CAAC,cAAc,oBAAoB,GAAG,EAAE;AACxE,aAAO,GAAG,EAAE,UAAU,YAAY;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,29 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var index_node_exports = {};
22
+ __export(index_node_exports, {
23
+ co2: () => import_co2.default,
24
+ hosting: () => import_hosting_node.default
25
+ });
26
+ module.exports = __toCommonJS(index_node_exports);
27
+ var import_co2 = __toESM(require("./co2.js"));
28
+ var import_hosting_node = __toESM(require("./hosting-node.js"));
29
+ //# sourceMappingURL=index-node.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index-node.js"],
4
+ "sourcesContent": ["import co2 from \"./co2.js\";\nimport hosting from \"./hosting-node.js\";\n\nexport { co2, hosting };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAgB;AAChB,0BAAoB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,31 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ co2: () => import_co2.default,
24
+ default: () => src_default,
25
+ hosting: () => import_hosting.default
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+ var import_co2 = __toESM(require("./co2.js"));
29
+ var import_hosting = __toESM(require("./hosting.js"));
30
+ var src_default = { co2: import_co2.default, hosting: import_hosting.default };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.js"],
4
+ "sourcesContent": ["import co2 from \"./co2.js\";\nimport hosting from \"./hosting.js\";\n\nexport { co2, hosting };\nexport default { co2, hosting };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAgB;AAChB,qBAAoB;AAGpB,IAAO,cAAQ,EAAE,yBAAK,gCAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var sustainable_web_design_exports = {};
23
+ __export(sustainable_web_design_exports, {
24
+ SustainableWebDesign: () => SustainableWebDesign,
25
+ default: () => sustainable_web_design_default
26
+ });
27
+ module.exports = __toCommonJS(sustainable_web_design_exports);
28
+ var import_debug = __toESM(require("debug"));
29
+ var import_constants = require("./constants/index.js");
30
+ var import_helpers = require("./helpers/index.js");
31
+ const log = (0, import_debug.default)("tgwf:sustainable-web-design");
32
+ const KWH_PER_GB = 0.81;
33
+ const END_USER_DEVICE_ENERGY = 0.52;
34
+ const NETWORK_ENERGY = 0.14;
35
+ const DATACENTER_ENERGY = 0.15;
36
+ const PRODUCTION_ENERGY = 0.19;
37
+ const GLOBAL_INTENSITY = 442;
38
+ const RENEWABLES_INTENSITY = 50;
39
+ const FIRST_TIME_VIEWING_PERCENTAGE = 0.75;
40
+ const RETURNING_VISITOR_PERCENTAGE = 0.25;
41
+ const PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD = 0.02;
42
+ class SustainableWebDesign {
43
+ constructor(options) {
44
+ this.options = options;
45
+ }
46
+ energyPerByteByComponent(bytes) {
47
+ const transferedBytesToGb = bytes / import_constants.fileSize.GIGABYTE;
48
+ const energyUsage = transferedBytesToGb * KWH_PER_GB;
49
+ return {
50
+ consumerDeviceEnergy: energyUsage * END_USER_DEVICE_ENERGY,
51
+ networkEnergy: energyUsage * NETWORK_ENERGY,
52
+ productionEnergy: energyUsage * PRODUCTION_ENERGY,
53
+ dataCenterEnergy: energyUsage * DATACENTER_ENERGY
54
+ };
55
+ }
56
+ co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {
57
+ const returnCO2ByComponent = {};
58
+ for (const [key, value] of Object.entries(energyBycomponent)) {
59
+ if (key === "dataCenterEnergy") {
60
+ returnCO2ByComponent[key] = value * carbonIntensity;
61
+ } else {
62
+ returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;
63
+ }
64
+ }
65
+ return returnCO2ByComponent;
66
+ }
67
+ perByte(bytes, carbonIntensity = GLOBAL_INTENSITY) {
68
+ const energyBycomponent = this.energyPerByteByComponent(bytes);
69
+ if (Boolean(carbonIntensity) === false) {
70
+ carbonIntensity = GLOBAL_INTENSITY;
71
+ }
72
+ if (carbonIntensity === true) {
73
+ carbonIntensity = RENEWABLES_INTENSITY;
74
+ }
75
+ if (typeof carbonIntensity !== "number") {
76
+ throw new Error(`perByte expects a numeric value or boolean for the carbon intensity value. Received: ${carbonIntensity}`);
77
+ }
78
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity);
79
+ const co2Values = Object.values(co2ValuesbyComponent);
80
+ return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
81
+ }
82
+ energyPerByte(bytes) {
83
+ const energyByComponent = this.energyPerByteByComponent(bytes);
84
+ const energyValues = Object.values(energyByComponent);
85
+ return energyValues.reduce((prevValue, currentValue) => prevValue + currentValue);
86
+ }
87
+ energyPerVisitByComponent(bytes, firstView = FIRST_TIME_VIEWING_PERCENTAGE, returnView = RETURNING_VISITOR_PERCENTAGE, dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD) {
88
+ const energyBycomponent = this.energyPerByteByComponent(bytes);
89
+ const cacheAdjustedSegmentEnergy = {};
90
+ log({ energyBycomponent });
91
+ const energyValues = Object.values(energyBycomponent);
92
+ for (const [key, value] of Object.entries(energyBycomponent)) {
93
+ cacheAdjustedSegmentEnergy[`${key} - first`] = value * firstView;
94
+ cacheAdjustedSegmentEnergy[`${key} - subsequent`] = value * returnView * dataReloadRatio;
95
+ }
96
+ log({ cacheAdjustedSegmentEnergy });
97
+ return cacheAdjustedSegmentEnergy;
98
+ }
99
+ energyPerVisit(bytes) {
100
+ let firstVisits = 0;
101
+ let subsequentVisits = 0;
102
+ const energyBycomponent = Object.entries(this.energyPerVisitByComponent(bytes));
103
+ for (const [key, val] of energyBycomponent) {
104
+ if (key.indexOf("first") > 0) {
105
+ firstVisits += val;
106
+ }
107
+ }
108
+ for (const [key, val] of energyBycomponent) {
109
+ if (key.indexOf("subsequent") > 0) {
110
+ subsequentVisits += val;
111
+ }
112
+ }
113
+ return firstVisits + subsequentVisits;
114
+ }
115
+ emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {
116
+ return (0, import_helpers.formatNumber)(energyPerVisit * carbonintensity);
117
+ }
118
+ annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1e3) {
119
+ return energyPerVisit * monthlyVisitors * 12;
120
+ }
121
+ annualEmissionsInGrams(co2grams, monthlyVisitors = 1e3) {
122
+ return co2grams * monthlyVisitors * 12;
123
+ }
124
+ annualSegmentEnergy(annualEnergy) {
125
+ return {
126
+ consumerDeviceEnergy: (0, import_helpers.formatNumber)(annualEnergy * END_USER_DEVICE_ENERGY),
127
+ networkEnergy: (0, import_helpers.formatNumber)(annualEnergy * NETWORK_ENERGY),
128
+ dataCenterEnergy: (0, import_helpers.formatNumber)(annualEnergy * DATACENTER_ENERGY),
129
+ productionEnergy: (0, import_helpers.formatNumber)(annualEnergy * PRODUCTION_ENERGY)
130
+ };
131
+ }
132
+ }
133
+ var sustainable_web_design_default = SustainableWebDesign;
134
+ //# sourceMappingURL=sustainable-web-design.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 // 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
+ "names": []
7
+ }