@tgwf/co2 0.11.3 → 0.12.0

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 (53) hide show
  1. package/.esbuild.esm.js +1 -1
  2. package/.esbuild.node.js +1 -1
  3. package/README.md +3 -3
  4. package/dist/cjs/1byte.js.map +2 -2
  5. package/dist/cjs/co2.js +67 -4
  6. package/dist/cjs/co2.js.map +2 -2
  7. package/dist/cjs/constants/index.js +23 -1
  8. package/dist/cjs/constants/index.js.map +2 -2
  9. package/dist/cjs/helpers/index.js +139 -1
  10. package/dist/cjs/helpers/index.js.map +2 -2
  11. package/dist/cjs/hosting-api.js +0 -4
  12. package/dist/cjs/hosting-api.js.map +2 -2
  13. package/dist/cjs/hosting-json.node.js +2 -1
  14. package/dist/cjs/hosting-json.node.js.map +2 -2
  15. package/dist/cjs/sustainable-web-design.js +67 -49
  16. package/dist/cjs/sustainable-web-design.js.map +2 -2
  17. package/dist/esm/co2.js +70 -4
  18. package/dist/esm/constants/index.js +23 -1
  19. package/dist/esm/helpers/index.js +144 -1
  20. package/dist/esm/hosting-api.js +0 -4
  21. package/dist/esm/sustainable-web-design.js +90 -41
  22. package/dist/iife/index.js +19 -3
  23. package/dist/iife/index.js.map +3 -3
  24. package/package.json +1 -1
  25. package/src/1byte.js +7 -0
  26. package/src/co2.js +157 -4
  27. package/src/constants/index.js +38 -1
  28. package/src/helpers/index.js +173 -1
  29. package/src/hosting-api.js +5 -4
  30. package/src/hosting-json.node.js +1 -0
  31. package/src/sustainable-web-design.js +113 -72
  32. package/dist/cjs/1byte.test.js +0 -27
  33. package/dist/cjs/1byte.test.js.map +0 -7
  34. package/dist/cjs/co2.test.js +0 -281
  35. package/dist/cjs/co2.test.js.map +0 -7
  36. package/dist/cjs/hosting-api.test.js +0 -47
  37. package/dist/cjs/hosting-api.test.js.map +0 -7
  38. package/dist/cjs/hosting-database.node.test.js +0 -36
  39. package/dist/cjs/hosting-database.node.test.js.map +0 -7
  40. package/dist/cjs/hosting-json.node.test.js +0 -43
  41. package/dist/cjs/hosting-json.node.test.js.map +0 -7
  42. package/dist/cjs/hosting.test.js +0 -74
  43. package/dist/cjs/hosting.test.js.map +0 -7
  44. package/dist/cjs/sustainable-web-design.test.js +0 -84
  45. package/dist/cjs/sustainable-web-design.test.js.map +0 -7
  46. package/dist/esm/1byte.test.js +0 -11
  47. package/dist/esm/co2.test.js +0 -265
  48. package/dist/esm/hosting-api.test.js +0 -31
  49. package/dist/esm/hosting-database.node.test.js +0 -20
  50. package/dist/esm/hosting-json.node.test.js +0 -27
  51. package/dist/esm/hosting.test.js +0 -58
  52. package/dist/esm/sustainable-web-design.test.js +0 -68
  53. package/src/readme.md +0 -66
@@ -29,77 +29,95 @@ var import_debug = __toESM(require("debug"));
29
29
  var import_constants = require("./constants/index.js");
30
30
  var import_helpers = require("./helpers/index.js");
31
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
32
  class SustainableWebDesign {
43
33
  constructor(options) {
44
34
  this.options = options;
45
35
  }
46
36
  energyPerByteByComponent(bytes) {
47
37
  const transferedBytesToGb = bytes / import_constants.fileSize.GIGABYTE;
48
- const energyUsage = transferedBytesToGb * KWH_PER_GB;
38
+ const energyUsage = transferedBytesToGb * import_constants.KWH_PER_GB;
49
39
  return {
50
- consumerDeviceEnergy: energyUsage * END_USER_DEVICE_ENERGY,
51
- networkEnergy: energyUsage * NETWORK_ENERGY,
52
- productionEnergy: energyUsage * PRODUCTION_ENERGY,
53
- dataCenterEnergy: energyUsage * DATACENTER_ENERGY
40
+ consumerDeviceEnergy: energyUsage * import_constants.END_USER_DEVICE_ENERGY,
41
+ networkEnergy: energyUsage * import_constants.NETWORK_ENERGY,
42
+ productionEnergy: energyUsage * import_constants.PRODUCTION_ENERGY,
43
+ dataCenterEnergy: energyUsage * import_constants.DATACENTER_ENERGY
54
44
  };
55
45
  }
56
- co2byComponent(energyBycomponent, carbonIntensity = GLOBAL_INTENSITY) {
46
+ co2byComponent(energyByComponent, carbonIntensity = import_constants.GLOBAL_GRID_INTENSITY, options = {}) {
47
+ let deviceCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
48
+ let networkCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
49
+ let dataCenterCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
50
+ let globalEmissions = import_constants.GLOBAL_GRID_INTENSITY;
51
+ if (options == null ? void 0 : options.gridIntensity) {
52
+ const { device, network, dataCenter } = options.gridIntensity;
53
+ if (device == null ? void 0 : device.value) {
54
+ deviceCarbonIntensity = device.value;
55
+ }
56
+ if (network == null ? void 0 : network.value) {
57
+ networkCarbonIntensity = network.value;
58
+ }
59
+ if (dataCenter == null ? void 0 : dataCenter.value) {
60
+ dataCenterCarbonIntensity = dataCenter.value;
61
+ }
62
+ }
63
+ if (carbonIntensity === true) {
64
+ dataCenterCarbonIntensity = import_constants.RENEWABLES_GRID_INTENSITY;
65
+ }
57
66
  const returnCO2ByComponent = {};
58
- for (const [key, value] of Object.entries(energyBycomponent)) {
67
+ for (const [key, value] of Object.entries(energyByComponent)) {
59
68
  if (key.startsWith("dataCenterEnergy")) {
60
- returnCO2ByComponent[key] = value * carbonIntensity;
69
+ returnCO2ByComponent[key.replace("Energy", "CO2")] = value * dataCenterCarbonIntensity;
70
+ } else if (key.startsWith("consumerDeviceEnergy")) {
71
+ returnCO2ByComponent[key.replace("Energy", "CO2")] = value * deviceCarbonIntensity;
72
+ } else if (key.startsWith("networkEnergy")) {
73
+ returnCO2ByComponent[key.replace("Energy", "CO2")] = value * networkCarbonIntensity;
61
74
  } else {
62
- returnCO2ByComponent[key] = value * GLOBAL_INTENSITY;
75
+ returnCO2ByComponent[key.replace("Energy", "CO2")] = value * globalEmissions;
63
76
  }
64
77
  }
65
78
  return returnCO2ByComponent;
66
79
  }
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;
80
+ perByte(bytes, carbonIntensity = false, segmentResults = false, options = {}) {
81
+ const energyBycomponent = this.energyPerByteByComponent(bytes, options);
82
+ if (typeof carbonIntensity !== "boolean") {
83
+ throw new Error(`perByte expects a boolean for the carbon intensity value. Received: ${carbonIntensity}`);
74
84
  }
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);
85
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity, options);
79
86
  const co2Values = Object.values(co2ValuesbyComponent);
80
- return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
81
- }
82
- perVisit(bytes, carbonIntensity = GLOBAL_INTENSITY) {
83
- const energyBycomponent = this.energyPerVisitByComponent(bytes);
84
- if (Boolean(carbonIntensity) === false) {
85
- carbonIntensity = GLOBAL_INTENSITY;
87
+ const co2ValuesSum = co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
88
+ if (segmentResults) {
89
+ return { ...co2ValuesbyComponent, total: co2ValuesSum };
86
90
  }
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}`);
91
+ return co2ValuesSum;
92
+ }
93
+ perVisit(bytes, carbonIntensity = false, segmentResults = false, options = {}) {
94
+ const energyBycomponent = this.energyPerVisitByComponent(bytes, options);
95
+ if (typeof carbonIntensity !== "boolean") {
96
+ throw new Error(`perVisit expects a boolean for the carbon intensity value. Received: ${carbonIntensity}`);
92
97
  }
93
- const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity);
98
+ const co2ValuesbyComponent = this.co2byComponent(energyBycomponent, carbonIntensity, options);
94
99
  const co2Values = Object.values(co2ValuesbyComponent);
95
- return co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
100
+ const co2ValuesSum = co2Values.reduce((prevValue, currentValue) => prevValue + currentValue);
101
+ if (segmentResults) {
102
+ return { ...co2ValuesbyComponent, total: co2ValuesSum };
103
+ }
104
+ return co2ValuesSum;
96
105
  }
97
106
  energyPerByte(bytes) {
98
107
  const energyByComponent = this.energyPerByteByComponent(bytes);
99
108
  const energyValues = Object.values(energyByComponent);
100
109
  return energyValues.reduce((prevValue, currentValue) => prevValue + currentValue);
101
110
  }
102
- energyPerVisitByComponent(bytes, firstView = FIRST_TIME_VIEWING_PERCENTAGE, returnView = RETURNING_VISITOR_PERCENTAGE, dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD) {
111
+ energyPerVisitByComponent(bytes, options = {}, firstView = import_constants.FIRST_TIME_VIEWING_PERCENTAGE, returnView = import_constants.RETURNING_VISITOR_PERCENTAGE, dataReloadRatio = import_constants.PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD) {
112
+ if (options.dataReloadRatio) {
113
+ dataReloadRatio = options.dataReloadRatio;
114
+ }
115
+ if (options.firstVisitPercentage) {
116
+ firstView = options.firstVisitPercentage;
117
+ }
118
+ if (options.returnVisitPercentage) {
119
+ returnView = options.returnVisitPercentage;
120
+ }
103
121
  const energyBycomponent = this.energyPerByteByComponent(bytes);
104
122
  const cacheAdjustedSegmentEnergy = {};
105
123
  log({ energyBycomponent });
@@ -127,7 +145,7 @@ class SustainableWebDesign {
127
145
  }
128
146
  return firstVisits + subsequentVisits;
129
147
  }
130
- emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_INTENSITY) {
148
+ emissionsPerVisitInGrams(energyPerVisit, carbonintensity = import_constants.GLOBAL_GRID_INTENSITY) {
131
149
  return (0, import_helpers.formatNumber)(energyPerVisit * carbonintensity);
132
150
  }
133
151
  annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1e3) {
@@ -138,10 +156,10 @@ class SustainableWebDesign {
138
156
  }
139
157
  annualSegmentEnergy(annualEnergy) {
140
158
  return {
141
- consumerDeviceEnergy: (0, import_helpers.formatNumber)(annualEnergy * END_USER_DEVICE_ENERGY),
142
- networkEnergy: (0, import_helpers.formatNumber)(annualEnergy * NETWORK_ENERGY),
143
- dataCenterEnergy: (0, import_helpers.formatNumber)(annualEnergy * DATACENTER_ENERGY),
144
- productionEnergy: (0, import_helpers.formatNumber)(annualEnergy * PRODUCTION_ENERGY)
159
+ consumerDeviceEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.END_USER_DEVICE_ENERGY),
160
+ networkEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.NETWORK_ENERGY),
161
+ dataCenterEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.DATACENTER_ENERGY),
162
+ productionEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.PRODUCTION_ENERGY)
145
163
  };
146
164
  }
147
165
  }
@@ -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.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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",
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 */\nimport debugFactory from \"debug\";\nconst log = debugFactory(\"tgwf:sustainable-web-design\");\n\nimport {\n fileSize,\n KWH_PER_GB,\n END_USER_DEVICE_ENERGY,\n NETWORK_ENERGY,\n DATACENTER_ENERGY,\n PRODUCTION_ENERGY,\n GLOBAL_GRID_INTENSITY,\n RENEWABLES_GRID_INTENSITY,\n FIRST_TIME_VIEWING_PERCENTAGE,\n RETURNING_VISITOR_PERCENTAGE,\n PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD,\n} from \"./constants/index.js\";\nimport { formatNumber } from \"./helpers/index.js\";\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(\n energyByComponent,\n carbonIntensity = GLOBAL_GRID_INTENSITY,\n options = {}\n ) {\n let deviceCarbonIntensity = GLOBAL_GRID_INTENSITY;\n let networkCarbonIntensity = GLOBAL_GRID_INTENSITY;\n let dataCenterCarbonIntensity = GLOBAL_GRID_INTENSITY;\n\n let globalEmissions = GLOBAL_GRID_INTENSITY;\n\n if (options?.gridIntensity) {\n const { device, network, dataCenter } = options.gridIntensity;\n\n if (device?.value) {\n deviceCarbonIntensity = device.value;\n }\n if (network?.value) {\n networkCarbonIntensity = network.value;\n }\n // If the user has set a carbon intensity value for the datacentre, then that overrides everything and is used\n if (dataCenter?.value) {\n dataCenterCarbonIntensity = dataCenter.value;\n }\n }\n\n // If the user passes in a TRUE value (green web host), then use the renewables intensity value\n if (carbonIntensity === true) {\n dataCenterCarbonIntensity = RENEWABLES_GRID_INTENSITY;\n }\n\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.replace(\"Energy\", \"CO2\")] =\n value * dataCenterCarbonIntensity;\n } else if (key.startsWith(\"consumerDeviceEnergy\")) {\n returnCO2ByComponent[key.replace(\"Energy\", \"CO2\")] =\n value * deviceCarbonIntensity;\n } else if (key.startsWith(\"networkEnergy\")) {\n returnCO2ByComponent[key.replace(\"Energy\", \"CO2\")] =\n value * networkCarbonIntensity;\n } else {\n // Use the global intensity for the remaining segments\n returnCO2ByComponent[key.replace(\"Energy\", \"CO2\")] =\n value * globalEmissions;\n }\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(\n bytes,\n carbonIntensity = false,\n segmentResults = false,\n options = {}\n ) {\n const energyBycomponent = this.energyPerByteByComponent(bytes, options);\n\n // otherwise when faced with non numeric values throw an error\n if (typeof carbonIntensity !== \"boolean\") {\n throw new Error(\n `perByte expects a boolean for the carbon intensity value. Received: ${carbonIntensity}`\n );\n }\n\n const co2ValuesbyComponent = this.co2byComponent(\n energyBycomponent,\n carbonIntensity,\n options\n );\n\n // pull out our values\u2026\n const co2Values = Object.values(co2ValuesbyComponent);\n const co2ValuesSum = co2Values.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n\n if (segmentResults) {\n return { ...co2ValuesbyComponent, total: co2ValuesSum };\n }\n\n return co2ValuesSum;\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(\n bytes,\n carbonIntensity = false,\n segmentResults = false,\n options = {}\n ) {\n const energyBycomponent = this.energyPerVisitByComponent(bytes, options);\n\n if (typeof carbonIntensity !== \"boolean\") {\n // otherwise when faced with non numeric values throw an error\n throw new Error(\n `perVisit expects a boolean for the carbon intensity value. Received: ${carbonIntensity}`\n );\n }\n\n const co2ValuesbyComponent = this.co2byComponent(\n energyBycomponent,\n carbonIntensity,\n options\n );\n\n // pull out our values\u2026\n const co2Values = Object.values(co2ValuesbyComponent);\n const co2ValuesSum = co2Values.reduce(\n (prevValue, currentValue) => prevValue + currentValue\n );\n\n if (segmentResults) {\n return { ...co2ValuesbyComponent, total: co2ValuesSum };\n }\n\n // so we can return their sum\n return co2ValuesSum;\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 options = {},\n firstView = FIRST_TIME_VIEWING_PERCENTAGE,\n returnView = RETURNING_VISITOR_PERCENTAGE,\n dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD\n ) {\n if (options.dataReloadRatio) {\n dataReloadRatio = options.dataReloadRatio;\n }\n\n if (options.firstVisitPercentage) {\n firstView = options.firstVisitPercentage;\n }\n\n if (options.returnVisitPercentage) {\n returnView = options.returnVisitPercentage;\n }\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 emissionsPerVisitInGrams(\n energyPerVisit,\n carbonintensity = GLOBAL_GRID_INTENSITY\n ) {\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
  }
package/dist/esm/co2.js CHANGED
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  import OneByte from "./1byte.js";
3
3
  import SustainableWebDesign from "./sustainable-web-design.js";
4
+ import {
5
+ GLOBAL_GRID_INTENSITY,
6
+ RENEWABLES_GRID_INTENSITY
7
+ } from "./constants/index.js";
8
+ import { parseOptions } from "./helpers/index.js";
4
9
  class CO2 {
5
10
  constructor(options) {
6
11
  this.model = new SustainableWebDesign();
@@ -12,16 +17,77 @@ class CO2 {
12
17
  throw new Error(`"${options.model}" is not a valid model. Please use "1byte" for the OneByte model, and "swd" for the Sustainable Web Design model.
13
18
  See https://developers.thegreenwebfoundation.org/co2js/models/ to learn more about the models available in CO2.js.`);
14
19
  }
20
+ if ((options == null ? void 0 : options.results) === "segment") {
21
+ this.model.results = {
22
+ segment: true
23
+ };
24
+ } else {
25
+ this.model.results = {
26
+ segment: false
27
+ };
28
+ }
15
29
  }
16
- perByte(bytes, green) {
17
- return this.model.perByte(bytes, green);
30
+ perByte(bytes, green = false) {
31
+ return this.model.perByte(bytes, green, this.model.results.segment);
18
32
  }
19
- perVisit(bytes, green) {
33
+ perVisit(bytes, green = false) {
20
34
  var _a;
21
35
  if ((_a = this.model) == null ? void 0 : _a.perVisit) {
22
- return this.model.perVisit(bytes, green);
36
+ return this.model.perVisit(bytes, green, this.model.results.segment);
23
37
  } else {
24
38
  throw new Error(`The perVisit() method is not supported in the model you are using. Try using perByte() instead.
39
+ See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`);
40
+ }
41
+ }
42
+ perByteTrace(bytes, green = false, options = {}) {
43
+ var _a, _b, _c, _d, _e, _f;
44
+ let adjustments = {};
45
+ if (options) {
46
+ adjustments = parseOptions(options);
47
+ }
48
+ return {
49
+ co2: this.model.perByte(bytes, green, this.model.results.segment, adjustments),
50
+ green,
51
+ variables: {
52
+ description: "Below are the variables used to calculate this CO2 estimate.",
53
+ bytes,
54
+ gridIntensity: {
55
+ description: "The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",
56
+ network: ((_b = (_a = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _a.network) == null ? void 0 : _b.value) || GLOBAL_GRID_INTENSITY,
57
+ dataCenter: green ? RENEWABLES_GRID_INTENSITY : ((_d = (_c = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _c.dataCenter) == null ? void 0 : _d.value) || GLOBAL_GRID_INTENSITY,
58
+ production: GLOBAL_GRID_INTENSITY,
59
+ device: ((_f = (_e = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _e.device) == null ? void 0 : _f.value) || GLOBAL_GRID_INTENSITY
60
+ }
61
+ }
62
+ };
63
+ }
64
+ perVisitTrace(bytes, green = false, options = {}) {
65
+ var _a, _b, _c, _d, _e, _f, _g;
66
+ if ((_a = this.model) == null ? void 0 : _a.perVisit) {
67
+ let adjustments = {};
68
+ if (options) {
69
+ adjustments = parseOptions(options);
70
+ }
71
+ return {
72
+ co2: this.model.perVisit(bytes, green, this.model.results.segment, adjustments),
73
+ green,
74
+ variables: {
75
+ description: "Below are the variables used to calculate this CO2 estimate.",
76
+ bytes,
77
+ gridIntensity: {
78
+ description: "The grid intensity (grams per kilowatt-hour) used to calculate this CO2 estimate.",
79
+ network: ((_c = (_b = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _b.network) == null ? void 0 : _c.value) || GLOBAL_GRID_INTENSITY,
80
+ dataCenter: green ? RENEWABLES_GRID_INTENSITY : ((_e = (_d = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _d.dataCenter) == null ? void 0 : _e.value) || GLOBAL_GRID_INTENSITY,
81
+ production: GLOBAL_GRID_INTENSITY,
82
+ device: ((_g = (_f = adjustments == null ? void 0 : adjustments.gridIntensity) == null ? void 0 : _f.device) == null ? void 0 : _g.value) || GLOBAL_GRID_INTENSITY
83
+ },
84
+ dataReloadRatio: (adjustments == null ? void 0 : adjustments.dataReloadRatio) || 0.02,
85
+ firstVisitPercentage: (adjustments == null ? void 0 : adjustments.firstVisitPercentage) || 0.75,
86
+ returnVisitPercentage: (adjustments == null ? void 0 : adjustments.returnVisitPercentage) || 0.25
87
+ }
88
+ };
89
+ } else {
90
+ throw new Error(`The perVisitDetailed() method is not supported in the model you are using. Try using perByte() instead.
25
91
  See https://developers.thegreenwebfoundation.org/co2js/methods/ to learn more about the methods available in CO2.js.`);
26
92
  }
27
93
  }
@@ -1,4 +1,26 @@
1
1
  import fileSize from "./file-size.js";
2
+ import testConstants from "./test-constants.js";
3
+ const KWH_PER_GB = 0.81;
4
+ const END_USER_DEVICE_ENERGY = 0.52;
5
+ const NETWORK_ENERGY = 0.14;
6
+ const DATACENTER_ENERGY = 0.15;
7
+ const PRODUCTION_ENERGY = 0.19;
8
+ const GLOBAL_GRID_INTENSITY = 442;
9
+ const RENEWABLES_GRID_INTENSITY = 50;
10
+ const FIRST_TIME_VIEWING_PERCENTAGE = 0.75;
11
+ const RETURNING_VISITOR_PERCENTAGE = 0.25;
12
+ const PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD = 0.02;
2
13
  export {
3
- fileSize
14
+ DATACENTER_ENERGY,
15
+ END_USER_DEVICE_ENERGY,
16
+ FIRST_TIME_VIEWING_PERCENTAGE,
17
+ GLOBAL_GRID_INTENSITY,
18
+ KWH_PER_GB,
19
+ NETWORK_ENERGY,
20
+ PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD,
21
+ PRODUCTION_ENERGY,
22
+ RENEWABLES_GRID_INTENSITY,
23
+ RETURNING_VISITOR_PERCENTAGE,
24
+ fileSize,
25
+ testConstants
4
26
  };
@@ -1,4 +1,147 @@
1
+ import { averageIntensity } from "../index.js";
2
+ import {
3
+ GLOBAL_GRID_INTENSITY,
4
+ PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD,
5
+ FIRST_TIME_VIEWING_PERCENTAGE,
6
+ RETURNING_VISITOR_PERCENTAGE
7
+ } from "../constants/index.js";
1
8
  const formatNumber = (num) => parseFloat(num.toFixed(2));
9
+ function parseOptions(options) {
10
+ var _a, _b, _c, _d, _e, _f;
11
+ if (typeof options !== "object") {
12
+ throw new Error("Options must be an object");
13
+ }
14
+ const adjustments = {};
15
+ if (options == null ? void 0 : options.gridIntensity) {
16
+ adjustments.gridIntensity = {};
17
+ const { device, dataCenter, network } = options.gridIntensity;
18
+ if (device) {
19
+ if (typeof device === "object") {
20
+ if (!averageIntensity.data[(_a = device.country) == null ? void 0 : _a.toUpperCase()]) {
21
+ console.warn(`"${device.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code.
22
+ See https://developers.thegreenwebfoundation.org/co2js/data/ for more information.
23
+ Falling back to global average grid intensity.`);
24
+ adjustments.gridIntensity["device"] = {
25
+ value: GLOBAL_GRID_INTENSITY
26
+ };
27
+ }
28
+ adjustments.gridIntensity["device"] = {
29
+ country: device.country,
30
+ value: parseFloat(averageIntensity.data[(_b = device.country) == null ? void 0 : _b.toUpperCase()])
31
+ };
32
+ } else if (typeof device === "number") {
33
+ adjustments.gridIntensity["device"] = {
34
+ value: device
35
+ };
36
+ } else {
37
+ adjustments.gridIntensity["device"] = {
38
+ value: GLOBAL_GRID_INTENSITY
39
+ };
40
+ console.warn(`The device grid intensity must be a number or an object. You passed in a ${typeof device}.
41
+ Falling back to global average grid intensity.`);
42
+ }
43
+ }
44
+ if (dataCenter) {
45
+ if (typeof dataCenter === "object") {
46
+ if (!averageIntensity.data[(_c = dataCenter.country) == null ? void 0 : _c.toUpperCase()]) {
47
+ console.warn(`"${dataCenter.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code.
48
+ See https://developers.thegreenwebfoundation.org/co2js/data/ for more information.
49
+ Falling back to global average grid intensity.`);
50
+ adjustments.gridIntensity["dataCenter"] = {
51
+ value: GLOBAL_GRID_INTENSITY
52
+ };
53
+ }
54
+ adjustments.gridIntensity["dataCenter"] = {
55
+ country: dataCenter.country,
56
+ value: parseFloat(averageIntensity.data[(_d = dataCenter.country) == null ? void 0 : _d.toUpperCase()])
57
+ };
58
+ } else if (typeof dataCenter === "number") {
59
+ adjustments.gridIntensity["dataCenter"] = {
60
+ value: dataCenter
61
+ };
62
+ } else {
63
+ adjustments.gridIntensity["dataCenter"] = {
64
+ value: GLOBAL_GRID_INTENSITY
65
+ };
66
+ console.warn(`The data center grid intensity must be a number or an object. You passed in a ${typeof dataCenter}.
67
+ Falling back to global average grid intensity.`);
68
+ }
69
+ }
70
+ if (network) {
71
+ if (typeof network === "object") {
72
+ if (!averageIntensity.data[(_e = network.country) == null ? void 0 : _e.toUpperCase()]) {
73
+ console.warn(`"${network.country}" is not a valid country. Please use a valid 3 digit ISO 3166 country code.
74
+ See https://developers.thegreenwebfoundation.org/co2js/data/ for more information. Falling back to global average grid intensity.
75
+ Falling back to global average grid intensity.`);
76
+ adjustments.gridIntensity["network"] = {
77
+ value: GLOBAL_GRID_INTENSITY
78
+ };
79
+ }
80
+ adjustments.gridIntensity["network"] = {
81
+ country: network.country,
82
+ value: parseFloat(averageIntensity.data[(_f = network.country) == null ? void 0 : _f.toUpperCase()])
83
+ };
84
+ } else if (typeof network === "number") {
85
+ adjustments.gridIntensity["network"] = {
86
+ value: network
87
+ };
88
+ } else {
89
+ adjustments.gridIntensity["network"] = {
90
+ value: GLOBAL_GRID_INTENSITY
91
+ };
92
+ console.warn(`The network grid intensity must be a number or an object. You passed in a ${typeof network}.
93
+ Falling back to global average grid intensity.`);
94
+ }
95
+ }
96
+ }
97
+ if (options == null ? void 0 : options.dataReloadRatio) {
98
+ if (typeof options.dataReloadRatio === "number") {
99
+ if (options.dataReloadRatio >= 0 && options.dataReloadRatio <= 1) {
100
+ adjustments.dataReloadRatio = options.dataReloadRatio;
101
+ } else {
102
+ adjustments.dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD;
103
+ console.warn(`The dataReloadRatio option must be a number between 0 and 1. You passed in ${options.dataReloadRatio}.
104
+ Falling back to default value.`);
105
+ }
106
+ } else {
107
+ adjustments.dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD;
108
+ console.warn(`The dataReloadRatio option must be a number. You passed in a ${typeof options.dataReloadRatio}.
109
+ Falling back to default value.`);
110
+ }
111
+ }
112
+ if (options == null ? void 0 : options.firstVisitPercentage) {
113
+ if (typeof options.firstVisitPercentage === "number") {
114
+ if (options.firstVisitPercentage >= 0 && options.firstVisitPercentage <= 1) {
115
+ adjustments.firstVisitPercentage = options.firstVisitPercentage;
116
+ } else {
117
+ adjustments.firstVisitPercentage = FIRST_TIME_VIEWING_PERCENTAGE;
118
+ console.warn(`The firstVisitPercentage option must be a number between 0 and 1. You passed in ${options.firstVisitPercentage}.
119
+ Falling back to default value.`);
120
+ }
121
+ } else {
122
+ adjustments.firstVisitPercentage = FIRST_TIME_VIEWING_PERCENTAGE;
123
+ console.warn(`The firstVisitPercentage option must be a number. You passed in a ${typeof options.firstVisitPercentage}.
124
+ Falling back to default value.`);
125
+ }
126
+ }
127
+ if (options == null ? void 0 : options.returnVisitPercentage) {
128
+ if (typeof options.returnVisitPercentage === "number") {
129
+ if (options.returnVisitPercentage >= 0 && options.returnVisitPercentage <= 1) {
130
+ adjustments.returnVisitPercentage = options.returnVisitPercentage;
131
+ } else {
132
+ adjustments.returnVisitPercentage = RETURNING_VISITOR_PERCENTAGE;
133
+ console.warn(`The returnVisitPercentage option must be a number between 0 and 1. You passed in ${options.returnVisitPercentage}.
134
+ Falling back to default value.`);
135
+ }
136
+ } else {
137
+ adjustments.returnVisitPercentage = RETURNING_VISITOR_PERCENTAGE;
138
+ console.warn(`The returnVisitPercentage option must be a number. You passed in a ${typeof options.returnVisitPercentage}.
139
+ Falling back to default value.`);
140
+ }
141
+ }
142
+ return adjustments;
143
+ }
2
144
  export {
3
- formatNumber
145
+ formatNumber,
146
+ parseOptions
4
147
  };
@@ -18,10 +18,6 @@ async function checkDomainsAgainstAPI(domains) {
18
18
  const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti";
19
19
  const domainsString = JSON.stringify(domains);
20
20
  const req = await fetch(`${apiPath}/${domainsString}`);
21
- log(`${apiPath}/${domainsString}`);
22
- log({ req });
23
- const textResult = await req.text();
24
- log({ textResult });
25
21
  const allGreenCheckResults = await req.json();
26
22
  return greenDomainsFromResults(allGreenCheckResults);
27
23
  } catch (e) {