@verifyhash/wbgt-heat-stress 0.1.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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/index.js +285 -0
  4. package/package.json +40 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 <OWNER-NAME PLACEHOLDER>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # wbgt-heat-stress
2
+
3
+ <!-- publish-prep -->
4
+ > **TODO (owner): pick the final npm name/scope before publishing.**
5
+ > The npm package name is **not** finalized. `package.json` keeps the working
6
+ > slug `wbgt-heat-stress` as a placeholder — replace it (and the install line
7
+ > below) with the real published name/scope before any `npm publish`.
8
+ > Graduation to npm is a **needs-human** decision, not something this project does.
9
+
10
+ A tiny, **zero-dependency** Node library for the three most-used heat-stress
11
+ indices, each returned with a plain-language **risk category**:
12
+
13
+ - **`heatIndex(tempC, relHumidityPct)`** — the US National Weather Service
14
+ "feels-like" apparent temperature (Rothfusz regression).
15
+ - **`humidex(tempC, dewpointC)`** — Environment Canada's humidex comfort index.
16
+ - **`wbgt(tempC, relHumidityPct, opts)`** — a **Wet-Bulb Globe Temperature
17
+ approximation** with the white/green/yellow/red/black flag band.
18
+
19
+ Every export is a **pure function**: no I/O, no network, no daemon, no
20
+ dependencies, and no mutation of its arguments. Same inputs → same output.
21
+ Units are Celsius, percent RH, and (for outdoor WBGT) m/s wind and W/m² solar.
22
+
23
+ ## Who it's for
24
+
25
+ - **Occupational safety** — flag heat-stress risk for outdoor/industrial crews
26
+ against the WBGT flag conditions used by OSHA guidance and the US military.
27
+ - **Sports & athletics** — apply the American College of Sports Medicine WBGT
28
+ activity bands for training and event go/no-go decisions.
29
+ - **Weather & wellness apps** — show an honest "feels like" number (heat index
30
+ or humidex) without pulling in a heavy scientific package or calling an API.
31
+
32
+ If you already have a psychrometrics library, this one is complementary: it
33
+ focuses on the *stress indices and their risk bands*, not on general moist-air
34
+ math.
35
+
36
+ ## Install / use
37
+
38
+ Zero dependencies — vendor the folder or (after the owner finalizes the name)
39
+ `npm install <final-name>`.
40
+
41
+ ```js
42
+ const { heatIndex, humidex, wbgt, wbgtFlag } = require('wbgt-heat-stress');
43
+
44
+ heatIndex(32.22, 70);
45
+ // { heatIndexF: 105.9, heatIndexC: 41.1, risk: 'danger' }
46
+
47
+ humidex(30, 15);
48
+ // { humidex: 33.97, risk: 'some discomfort' }
49
+
50
+ wbgt(30, 50);
51
+ // { wbgtC: 29.26, flag: 'green', mode: 'indoor' }
52
+
53
+ wbgt(30, 50, { solarWm2: 900, windMs: 1.5 });
54
+ // { wbgtC: ~31.0, flag: 'yellow', mode: 'outdoor' }
55
+
56
+ wbgtFlag(31.5); // 'red' — band a WBGT you measured elsewhere
57
+ ```
58
+
59
+ ## Running the tests
60
+
61
+ One command, no framework, no dependencies:
62
+
63
+ ```sh
64
+ node test/index.test.js
65
+ ```
66
+
67
+ It exits `0` on success and non-zero on any failure. The suite checks fixtures
68
+ at published reference points — a NWS heat-index table cell, an Environment
69
+ Canada humidex example, and every WBGT flag-band boundary — each within a stated
70
+ tolerance, plus the input guards.
71
+
72
+ ## The formulas (and their sources)
73
+
74
+ ### Heat index — NWS/NOAA Rothfusz regression
75
+
76
+ Apparent temperature from air temperature and relative humidity. The NWS first
77
+ evaluates Steadman's simple formula; if that value is **below 80 °F (26.7 °C)**
78
+ the Rothfusz regression does not apply and the simple value is used (the
79
+ documented **low-temperature fallback** — below ~80 °F the heat index is
80
+ essentially the air temperature). Otherwise the full Rothfusz polynomial runs,
81
+ with the low-humidity (RH < 13 %) and high-humidity (RH > 85 %) corrections.
82
+ This is the exact method behind the published NWS heat-index chart and is
83
+ accurate to about **±1.3 °F**. Source: Rothfusz, L.P. (1990), *NWS Technical
84
+ Attachment SR 90-23*; Steadman, R.G. (1979).
85
+
86
+ Risk bands (NWS, °F): `none` < 80, `caution` 80–90, `extreme caution` 90–103,
87
+ `danger` 103–124, `extreme danger` ≥ 124.
88
+
89
+ ### Humidex — Environment Canada
90
+
91
+ ```
92
+ humidex = Tair + 0.5555 * (e - 10)
93
+ e = 6.11 * exp(5417.7530 * (1/273.16 - 1/(Tdew + 273.15))) [hPa]
94
+ ```
95
+
96
+ Humidex is a unitless comfort index reported on the Celsius scale. When the dew
97
+ point implies dry air (`e ≤ 10 hPa`) humidex would fall below the air
98
+ temperature; Environment Canada does not report that, so the value is clamped to
99
+ the air temperature. Source: Masterton, J.M. & Richardson, F.A. (1979),
100
+ Environment Canada.
101
+
102
+ Comfort bands (°C-scale): `none` < 30, `some discomfort` 30–39,
103
+ `great discomfort` 40–45, `dangerous` ≥ 46.
104
+
105
+ ### WBGT — an APPROXIMATION (read this carefully)
106
+
107
+ **True WBGT cannot be computed from air temperature and humidity alone.** The
108
+ real index is `WBGT = 0.7·Tnwb + 0.2·Tg + 0.1·Ta`, where **Tg is the temperature
109
+ of a 150 mm matte-black globe** in the actual sun/wind and **Tnwb is a naturally
110
+ ventilated wet-bulb** — both require physical sensors. This library does **not**
111
+ pretend to that accuracy. It provides two documented approximations:
112
+
113
+ **Indoor / shaded (default).** The Australian Bureau of Meteorology simplified
114
+ regression, valid where there is no direct solar load (indoors, or fully
115
+ shaded):
116
+
117
+ ```
118
+ WBGT = 0.567 * Ta + 0.393 * e + 3.94
119
+ e = (RH/100) * 6.105 * exp(17.27*Ta/(237.7 + Ta)) [hPa]
120
+ ```
121
+
122
+ This is the standard substitute when no globe sensor is available and is what
123
+ many heat-safety apps display for indoor/shaded conditions.
124
+
125
+ **Outdoor (when you pass `opts.solarWm2`).** The shade estimate **plus a coarse
126
+ solar-load term** — direct sun raises the globe temperature and hence WBGT,
127
+ while wind ventilates the globe and lowers it:
128
+
129
+ ```
130
+ dSolar = 3.2 * (solarWm2 / 1000) / sqrt(1 + windMs) [°C], capped at 4 °C
131
+ ```
132
+
133
+ so ~1000 W/m² midday sun in light wind adds a few °C, tapering with wind.
134
+
135
+ **Honest limits.**
136
+ - The outdoor `dSolar` term is a **rough heuristic, not a validated model**. It
137
+ is tuned to give plausible magnitudes, not sensor-grade accuracy. Do not use
138
+ outdoor mode as the sole basis for a real occupational go/no-go decision in
139
+ the sun — **measure with a black-globe WBGT meter** for that.
140
+ - Even the indoor ABM regression carries its own scatter (order ~±1 °C) versus
141
+ instrumented readings and was fit for typical outdoor screen conditions.
142
+ - Humidity must be a real measurement; a bad RH propagates directly into WBGT.
143
+ - This library computes numbers and risk bands only. It is **not** a substitute
144
+ for an employer's heat-illness prevention program or medical judgement.
145
+
146
+ Flag bands (US military / American College of Sports Medicine), °C converted
147
+ from the canonical °F cut-points:
148
+
149
+ | Flag | WBGT (°C) | WBGT (°F) | Meaning |
150
+ |--------|-------------|-------------|----------------------------------|
151
+ | white | < 27.8 | < 82.0 | low risk |
152
+ | green | 27.8 – 29.3 | 82.0 – 84.9 | use discretion, hydrate |
153
+ | yellow | 29.4 – 31.0 | 85.0 – 87.9 | active caution; limit exertion |
154
+ | red | 31.1 – 32.1 | 88.0 – 89.9 | high risk; curtail hard activity |
155
+ | black | ≥ 32.2 | ≥ 90.0 | extreme; cancel/relocate |
156
+
157
+ `wbgtFlag(wbgtC)` is exported separately so you can band a WBGT you obtained
158
+ from a real sensor.
159
+
160
+ ## License
161
+
162
+ MIT — see `LICENSE`. (Copyright holder is a placeholder pending owner sign-off.)
package/index.js ADDED
@@ -0,0 +1,285 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * wbgt-heat-stress — zero-dependency heat-stress indices.
5
+ *
6
+ * Three PURE functions (no I/O, no network, no globals mutated, no arguments
7
+ * mutated, no dependencies — same inputs always give the same output):
8
+ *
9
+ * heatIndex(tempC, relHumidityPct) NWS/NOAA "apparent temperature" (°C)
10
+ * humidex(tempC, dewpointC) Environment Canada humidex (°C-scale)
11
+ * wbgt(tempC, relHumidityPct, opts) Wet-Bulb Globe Temperature APPROXIMATION
12
+ *
13
+ * Each returns the index value AND, where the metric defines one, a risk
14
+ * category. `wbgt` returns { wbgtC, flag, mode }. A standalone `wbgtFlag`
15
+ * classifier is also exported so callers can band a WBGT they computed
16
+ * elsewhere (e.g. from a real black-globe sensor).
17
+ *
18
+ * UNITS
19
+ * tempC, dewpointC degrees Celsius
20
+ * relHumidityPct relative humidity, percent, 0..100
21
+ * opts.windMs 10 m wind speed, metres/second (outdoor mode)
22
+ * opts.solarWm2 global solar irradiance, W/m^2 (outdoor mode)
23
+ *
24
+ * SOURCES (also cited per-function and in README.md)
25
+ * Heat index — Rothfusz, L.P. (1990), NWS Technical Attachment SR 90-23; the
26
+ * regression the US National Weather Service publishes its heat-index table
27
+ * from, with the low-humidity / high-humidity corrections and the Steadman
28
+ * low-temperature fallback.
29
+ * Humidex — Masterton & Richardson (1979), Environment Canada; e computed
30
+ * from dew point with the WMO/Buck-style saturation relation Canada uses.
31
+ * WBGT (shade) — the Australian Bureau of Meteorology simplified WBGT:
32
+ * WBGT = 0.567*Ta + 0.393*e + 3.94, valid for shaded/indoor conditions.
33
+ * WBGT flags — the WBGT flag-colour bands (white/green/yellow/red/black) used
34
+ * by the US military and the American College of Sports Medicine.
35
+ */
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Input guards. Throw rather than silently coerce so a caller's unit bug
39
+ // (e.g. passing RH as a 0..1 fraction) surfaces immediately.
40
+ // ---------------------------------------------------------------------------
41
+ function num(name, v) {
42
+ if (typeof v !== 'number' || !Number.isFinite(v)) {
43
+ throw new TypeError(`${name} must be a finite number, got ${String(v)}`);
44
+ }
45
+ }
46
+
47
+ function validateRh(rhPct) {
48
+ num('relHumidityPct', rhPct);
49
+ if (rhPct < 0 || rhPct > 100) {
50
+ throw new RangeError(
51
+ `relHumidityPct must be within [0, 100], got ${rhPct}`
52
+ );
53
+ }
54
+ }
55
+
56
+ const cToF = (c) => (c * 9) / 5 + 32;
57
+ const fToC = (f) => ((f - 32) * 5) / 9;
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Saturation / actual vapour pressure helpers (hPa == millibars).
61
+ // ---------------------------------------------------------------------------
62
+
63
+ // Tetens-form saturation vapour pressure over water, as used by the Australian
64
+ // Bureau of Meteorology alongside its simplified WBGT regression.
65
+ // es(T) = 6.105 * exp(17.27 * T / (237.7 + T)), T in °C, es in hPa.
66
+ function satVapPressureBoM(tempC) {
67
+ return 6.105 * Math.exp((17.27 * tempC) / (237.7 + tempC));
68
+ }
69
+
70
+ // Saturation vapour pressure over water using the coefficient set Environment
71
+ // Canada uses for humidex: e_sat(Td) = 6.11 * exp(5417.7530 * (1/273.16 - 1/T)),
72
+ // T in kelvin. Evaluated at the dew point it gives the ACTUAL vapour pressure.
73
+ function vapPressureHumidex(dewpointC) {
74
+ const Tk = dewpointC + 273.15;
75
+ return 6.11 * Math.exp(5417.753 * (1 / 273.16 - 1 / Tk));
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // 1) HEAT INDEX (NWS/NOAA Rothfusz regression)
80
+ // ---------------------------------------------------------------------------
81
+ /**
82
+ * Apparent ("feels-like") temperature from air temperature and relative
83
+ * humidity, following the US National Weather Service.
84
+ *
85
+ * Method: the NWS first evaluates Steadman's simple formula. If that averaged
86
+ * value is < 80 °F the Rothfusz regression does not apply and the simple value
87
+ * is used (this is the documented LOW-TEMPERATURE FALLBACK — below roughly
88
+ * 26.7 °C / 80 °F the heat index is essentially the air temperature). Otherwise
89
+ * the full Rothfusz regression is used, with the low-humidity (RH < 13 %) and
90
+ * high-humidity (RH > 85 %) corrections. The regression is the basis of the
91
+ * published NWS heat-index chart and is accurate to about ±1.3 °F.
92
+ *
93
+ * @param {number} tempC air temperature, °C
94
+ * @param {number} relHumidityPct relative humidity, percent 0..100
95
+ * @returns {{ heatIndexC:number, heatIndexF:number, risk:string }}
96
+ */
97
+ function heatIndex(tempC, relHumidityPct) {
98
+ num('tempC', tempC);
99
+ validateRh(relHumidityPct);
100
+
101
+ const T = cToF(tempC); // °F
102
+ const R = relHumidityPct;
103
+
104
+ // Steadman "simple" apparent temperature, averaged with dry-bulb.
105
+ const simple = 0.5 * (T + 61.0 + (T - 68.0) * 1.2 + R * 0.094);
106
+ const avg = (simple + T) / 2;
107
+
108
+ let hiF;
109
+ if (avg < 80) {
110
+ // Low-temperature fallback: Rothfusz not valid here.
111
+ hiF = avg;
112
+ } else {
113
+ hiF =
114
+ -42.379 +
115
+ 2.04901523 * T +
116
+ 10.14333127 * R -
117
+ 0.22475541 * T * R -
118
+ 0.00683783 * T * T -
119
+ 0.05481717 * R * R +
120
+ 0.00122874 * T * T * R +
121
+ 0.00085282 * T * R * R -
122
+ 0.00000199 * T * T * R * R;
123
+
124
+ if (R < 13 && T >= 80 && T <= 112) {
125
+ hiF -= ((13 - R) / 4) * Math.sqrt((17 - Math.abs(T - 95)) / 17);
126
+ } else if (R > 85 && T >= 80 && T <= 87) {
127
+ hiF += ((R - 85) / 10) * ((87 - T) / 5);
128
+ }
129
+ }
130
+
131
+ return {
132
+ heatIndexF: hiF,
133
+ heatIndexC: fToC(hiF),
134
+ risk: heatIndexRisk(hiF),
135
+ };
136
+ }
137
+
138
+ // NWS heat-index caution bands (°F thresholds):
139
+ // < 80 none (fallback region)
140
+ // 80 – 90 caution
141
+ // 90 – 103 extreme caution
142
+ // 103 – 124 danger
143
+ // >= 124 extreme danger
144
+ function heatIndexRisk(hiF) {
145
+ if (hiF < 80) return 'none';
146
+ if (hiF < 90) return 'caution';
147
+ if (hiF < 103) return 'extreme caution';
148
+ if (hiF < 124) return 'danger';
149
+ return 'extreme danger';
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // 2) HUMIDEX (Environment Canada)
154
+ // ---------------------------------------------------------------------------
155
+ /**
156
+ * Canadian humidex from air temperature and dew point.
157
+ *
158
+ * humidex = Tair + 0.5555 * (e - 10)
159
+ * e = 6.11 * exp(5417.7530 * (1/273.16 - 1/(Td + 273.15))) [hPa]
160
+ *
161
+ * Humidex is a unitless comfort index reported on the Celsius scale. If the
162
+ * dew point implies e <= 10 hPa (dry air) humidex is not defined above the air
163
+ * temperature, so we clamp to the air temperature (Environment Canada does not
164
+ * report a humidex below the air temperature).
165
+ *
166
+ * @param {number} tempC air temperature, °C
167
+ * @param {number} dewpointC dew-point temperature, °C
168
+ * @returns {{ humidex:number, risk:string }}
169
+ */
170
+ function humidex(tempC, dewpointC) {
171
+ num('tempC', tempC);
172
+ num('dewpointC', dewpointC);
173
+ if (dewpointC > tempC + 1e-9) {
174
+ throw new RangeError(
175
+ `dewpointC (${dewpointC}) cannot exceed tempC (${tempC})`
176
+ );
177
+ }
178
+
179
+ const e = vapPressureHumidex(dewpointC);
180
+ const h = tempC + 0.5555 * (e - 10);
181
+ const value = h < tempC ? tempC : h;
182
+
183
+ return { humidex: value, risk: humidexRisk(value) };
184
+ }
185
+
186
+ // Environment Canada humidex comfort bands (°C-scale):
187
+ // < 30 none / comfortable
188
+ // 30 – 39 some discomfort
189
+ // 40 – 45 great discomfort; avoid exertion
190
+ // >= 46 dangerous; heat stroke possible
191
+ function humidexRisk(h) {
192
+ if (h < 30) return 'none';
193
+ if (h < 40) return 'some discomfort';
194
+ if (h < 46) return 'great discomfort';
195
+ return 'dangerous';
196
+ }
197
+
198
+ // ---------------------------------------------------------------------------
199
+ // 3) WBGT (Wet-Bulb Globe Temperature APPROXIMATION)
200
+ // ---------------------------------------------------------------------------
201
+ /**
202
+ * Approximate Wet-Bulb Globe Temperature.
203
+ *
204
+ * INDOOR / SHADED (default) — Australian Bureau of Meteorology regression:
205
+ * WBGT = 0.567 * Ta + 0.393 * e + 3.94
206
+ * e = (RH/100) * 6.105 * exp(17.27*Ta/(237.7+Ta)) [hPa]
207
+ * This needs only air temperature and humidity; it is the standard simple
208
+ * substitute when no black-globe sensor is available and there is no direct
209
+ * sun load (indoors, or fully shaded outdoors).
210
+ *
211
+ * OUTDOOR (opts.solarWm2 given) — the shade estimate PLUS a coarse solar-load
212
+ * term. Direct sun raises the globe temperature and hence WBGT; wind ventilates
213
+ * the globe and reduces it. This term is a rough heuristic, NOT a validated
214
+ * model — see the honest-limits note in README.md. The increment is:
215
+ * dSolar = 3.2 * (solarWm2 / 1000) / sqrt(1 + windMs) [°C], capped at 4 °C
216
+ * so full midday sun (~1000 W/m^2) in light wind adds a few °C, tapering with
217
+ * wind. For real occupational decisions in the sun, measure with a globe sensor.
218
+ *
219
+ * @param {number} tempC air temperature, °C
220
+ * @param {number} relHumidityPct relative humidity, percent 0..100
221
+ * @param {object} [opts]
222
+ * @param {number} [opts.solarWm2] global solar irradiance, W/m^2 (enables outdoor)
223
+ * @param {number} [opts.windMs=1] wind speed, m/s (outdoor ventilation)
224
+ * @returns {{ wbgtC:number, flag:string, mode:'indoor'|'outdoor' }}
225
+ */
226
+ function wbgt(tempC, relHumidityPct, opts) {
227
+ num('tempC', tempC);
228
+ validateRh(relHumidityPct);
229
+ const o = opts || {};
230
+
231
+ const e = (relHumidityPct / 100) * satVapPressureBoM(tempC);
232
+ let wbgtC = 0.567 * tempC + 0.393 * e + 3.94;
233
+ let mode = 'indoor';
234
+
235
+ if (o.solarWm2 !== undefined && o.solarWm2 !== null) {
236
+ num('opts.solarWm2', o.solarWm2);
237
+ if (o.solarWm2 < 0) {
238
+ throw new RangeError(`opts.solarWm2 must be >= 0, got ${o.solarWm2}`);
239
+ }
240
+ let wind = o.windMs === undefined || o.windMs === null ? 1 : o.windMs;
241
+ num('opts.windMs', wind);
242
+ if (wind < 0) {
243
+ throw new RangeError(`opts.windMs must be >= 0, got ${wind}`);
244
+ }
245
+ let dSolar = (3.2 * (o.solarWm2 / 1000)) / Math.sqrt(1 + wind);
246
+ if (dSolar > 4) dSolar = 4; // cap the heuristic
247
+ wbgtC += dSolar;
248
+ mode = 'outdoor';
249
+ }
250
+
251
+ return { wbgtC, flag: wbgtFlag(wbgtC), mode };
252
+ }
253
+
254
+ /**
255
+ * Classify a WBGT (°C) into the flag-colour band used by the US military and
256
+ * the American College of Sports Medicine. Boundaries (°C, converted from the
257
+ * canonical °F cut-points):
258
+ *
259
+ * white WBGT < 27.8 °C (< 82.0 °F) low
260
+ * green 27.8 – 29.3 °C (82.0–84.9 °F)
261
+ * yellow 29.4 – 31.0 °C (85.0–87.9 °F)
262
+ * red 31.1 – 32.1 °C (88.0–89.9 °F)
263
+ * black >= 32.2 °C (>= 90.0 °F) highest risk
264
+ *
265
+ * @param {number} wbgtC
266
+ * @returns {string} 'white' | 'green' | 'yellow' | 'red' | 'black'
267
+ */
268
+ function wbgtFlag(wbgtC) {
269
+ num('wbgtC', wbgtC);
270
+ if (wbgtC >= 32.2) return 'black';
271
+ if (wbgtC >= 31.1) return 'red';
272
+ if (wbgtC >= 29.4) return 'yellow';
273
+ if (wbgtC >= 27.8) return 'green';
274
+ return 'white';
275
+ }
276
+
277
+ module.exports = {
278
+ heatIndex,
279
+ humidex,
280
+ wbgt,
281
+ wbgtFlag,
282
+ // low-level helpers, exported for testing / advanced use
283
+ heatIndexRisk,
284
+ humidexRisk,
285
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@verifyhash/wbgt-heat-stress",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency heat-stress indices: NWS heat index, Environment Canada humidex, and a Wet-Bulb Globe Temperature (WBGT) approximation with risk flags.",
5
+ "main": "index.js",
6
+ "type": "commonjs",
7
+ "scripts": {
8
+ "test": "node test/index.test.js"
9
+ },
10
+ "keywords": [
11
+ "wbgt",
12
+ "wet-bulb-globe-temperature",
13
+ "heat-index",
14
+ "humidex",
15
+ "heat-stress",
16
+ "apparent-temperature",
17
+ "feels-like",
18
+ "occupational-safety",
19
+ "sports-safety",
20
+ "meteorology",
21
+ "zero-dependency"
22
+ ],
23
+ "license": "MIT",
24
+ "files": [
25
+ "index.js",
26
+ "README.md"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/verifyhash/libs.git",
34
+ "directory": "wbgt-heat-stress"
35
+ },
36
+ "homepage": "https://github.com/verifyhash/libs/tree/main/wbgt-heat-stress#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/verifyhash/libs/issues"
39
+ }
40
+ }