mostlyright 1.5.0 → 1.5.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.
- package/dist/{chunk-OTJGBPL4.mjs → chunk-4RWHWFV3.mjs} +8 -4
- package/dist/chunk-4RWHWFV3.mjs.map +1 -0
- package/dist/{iem-asos-BPSHDERE.mjs → iem-asos-GEUKTQTM.mjs} +2 -2
- package/dist/index.bundle.mjs +76 -29
- package/dist/index.bundle.mjs.map +1 -1
- package/dist/index.global.js +81 -30
- package/dist/index.global.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-OTJGBPL4.mjs.map +0 -1
- /package/dist/{iem-asos-BPSHDERE.mjs.map → iem-asos-GEUKTQTM.mjs.map} +0 -0
package/dist/index.global.js
CHANGED
|
@@ -2917,6 +2917,11 @@ var mostlyright = (() => {
|
|
|
2917
2917
|
Number.parseInt(parts[2], 10)
|
|
2918
2918
|
];
|
|
2919
2919
|
}
|
|
2920
|
+
function addOneDay(iso) {
|
|
2921
|
+
const [y, m, d] = splitIso(iso);
|
|
2922
|
+
const next = new Date(Date.UTC(y, m - 1, d + 1));
|
|
2923
|
+
return next.toISOString().slice(0, 10);
|
|
2924
|
+
}
|
|
2920
2925
|
async function downloadIemAsos(stationCode, start, end, opts = {}) {
|
|
2921
2926
|
const reportType = opts.reportType ?? 3;
|
|
2922
2927
|
if (!VALID_REPORT_TYPES.has(reportType)) {
|
|
@@ -2926,10 +2931,9 @@ var mostlyright = (() => {
|
|
|
2926
2931
|
if (start > end) {
|
|
2927
2932
|
return [];
|
|
2928
2933
|
}
|
|
2929
|
-
const
|
|
2930
|
-
const chunks = yearlyChunksExclusiveEnd(normalizedStart, end);
|
|
2934
|
+
const chunks = opts.exactStart ? [[start, addOneDay(end)]] : yearlyChunksExclusiveEnd(`${start.slice(0, 4)}-01-01`, end);
|
|
2931
2935
|
const politenessMs = opts.politenessMs ?? IEM_POLITE_DELAY_MS;
|
|
2932
|
-
const { reportType: _rtDrop, politenessMs: _pmDrop, ...fetchOpts } = opts;
|
|
2936
|
+
const { reportType: _rtDrop, politenessMs: _pmDrop, exactStart: _esDrop, ...fetchOpts } = opts;
|
|
2933
2937
|
const out = [];
|
|
2934
2938
|
for (const [chunkStart, chunkEnd] of chunks) {
|
|
2935
2939
|
const url = buildIemUrl(stationCode, chunkStart, chunkEnd, reportType);
|
|
@@ -4075,6 +4079,26 @@ var mostlyright = (() => {
|
|
|
4075
4079
|
var SUPPORTED_MODELS = /* @__PURE__ */ new Set(["nbe", "gfs", "lav", "met", "ecm"]);
|
|
4076
4080
|
var KT_TO_MS2 = 0.5144444;
|
|
4077
4081
|
var NBE_CYCLE_CUTOVER = Date.UTC(2026, 5 - 1, 5, 0, 0, 0);
|
|
4082
|
+
var MOS_FETCH_CONCURRENCY = 8;
|
|
4083
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
4084
|
+
const results = new Array(items.length);
|
|
4085
|
+
let cursor = 0;
|
|
4086
|
+
let failed = false;
|
|
4087
|
+
async function worker() {
|
|
4088
|
+
while (cursor < items.length && !failed) {
|
|
4089
|
+
const index = cursor++;
|
|
4090
|
+
try {
|
|
4091
|
+
results[index] = await fn(items[index], index);
|
|
4092
|
+
} catch (err) {
|
|
4093
|
+
failed = true;
|
|
4094
|
+
throw err;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
const poolSize = Math.min(limit, items.length);
|
|
4099
|
+
await Promise.all(Array.from({ length: poolSize }, () => worker()));
|
|
4100
|
+
return results;
|
|
4101
|
+
}
|
|
4078
4102
|
function runtimeHoursFor(model, fromDt, toDt) {
|
|
4079
4103
|
if (model !== "nbe") return [0, 6, 12, 18];
|
|
4080
4104
|
const fromMs = fromDt.getTime();
|
|
@@ -4158,31 +4182,37 @@ var mostlyright = (() => {
|
|
|
4158
4182
|
const toDt = parseIsoDate2(toDate, true);
|
|
4159
4183
|
const hours = runtimeHoursFor(model, fromDt, toDt);
|
|
4160
4184
|
const retrievedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4161
|
-
const rows = [];
|
|
4162
4185
|
const dayMs = 864e5;
|
|
4186
|
+
const urls = [];
|
|
4163
4187
|
for (let day = fromDt.getTime(); day <= toDt.getTime(); day += dayMs) {
|
|
4164
4188
|
for (const h of hours) {
|
|
4165
4189
|
const rt = new Date(day);
|
|
4166
4190
|
rt.setUTCHours(h, 0, 0, 0);
|
|
4167
4191
|
if (rt < fromDt || rt > toDt) continue;
|
|
4168
|
-
|
|
4169
|
-
station
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
if (!resp.ok) {
|
|
4176
|
-
throw new Error(`iemMosForecasts: HTTP ${resp.status} on ${url}`);
|
|
4177
|
-
}
|
|
4178
|
-
const payload = await resp.json();
|
|
4179
|
-
for (const raw of payload.data ?? []) {
|
|
4180
|
-
const projected = parseRow(raw, station, model, retrievedAt);
|
|
4181
|
-
if (projected !== null) rows.push(projected);
|
|
4182
|
-
}
|
|
4192
|
+
urls.push(
|
|
4193
|
+
`${IEM_MOS_URL}?station=${encodeURIComponent(
|
|
4194
|
+
station
|
|
4195
|
+
)}&model=${encodeURIComponent(model.toUpperCase())}&runtime=${encodeURIComponent(
|
|
4196
|
+
rt.toISOString()
|
|
4197
|
+
)}`
|
|
4198
|
+
);
|
|
4183
4199
|
}
|
|
4184
4200
|
}
|
|
4185
|
-
|
|
4201
|
+
const perCycle = await mapWithConcurrency(urls, MOS_FETCH_CONCURRENCY, async (url) => {
|
|
4202
|
+
const resp = await fetchFn(url);
|
|
4203
|
+
if (resp.status === 404) return [];
|
|
4204
|
+
if (!resp.ok) {
|
|
4205
|
+
throw new Error(`iemMosForecasts: HTTP ${resp.status} on ${url}`);
|
|
4206
|
+
}
|
|
4207
|
+
const payload = await resp.json();
|
|
4208
|
+
const out = [];
|
|
4209
|
+
for (const raw of payload.data ?? []) {
|
|
4210
|
+
const projected = parseRow(raw, station, model, retrievedAt);
|
|
4211
|
+
if (projected !== null) out.push(projected);
|
|
4212
|
+
}
|
|
4213
|
+
return out;
|
|
4214
|
+
});
|
|
4215
|
+
return perCycle.flat();
|
|
4186
4216
|
}
|
|
4187
4217
|
|
|
4188
4218
|
// ../weather/src/forecasts/nwp-stub.ts
|
|
@@ -4461,6 +4491,15 @@ var mostlyright = (() => {
|
|
|
4461
4491
|
const n = typeof value === "number" ? value : Number(value);
|
|
4462
4492
|
return Number.isFinite(n) ? n : null;
|
|
4463
4493
|
}
|
|
4494
|
+
function maybeInt(value) {
|
|
4495
|
+
const n = maybeNumber2(value);
|
|
4496
|
+
if (n === null) return null;
|
|
4497
|
+
const floor = Math.floor(n);
|
|
4498
|
+
const diff = n - floor;
|
|
4499
|
+
if (diff < 0.5) return floor;
|
|
4500
|
+
if (diff > 0.5) return floor + 1;
|
|
4501
|
+
return floor % 2 === 0 ? floor : floor + 1;
|
|
4502
|
+
}
|
|
4464
4503
|
function pickHourlyValue(hourly, key, isPreviousRuns, idx) {
|
|
4465
4504
|
const arr = isPreviousRuns ? hourly[`${key}_previous_day1`] ?? hourly[key] : hourly[key];
|
|
4466
4505
|
if (!Array.isArray(arr) || idx >= arr.length) return null;
|
|
@@ -4488,13 +4527,16 @@ var mostlyright = (() => {
|
|
|
4488
4527
|
const params = new URLSearchParams();
|
|
4489
4528
|
params.set("latitude", String(lat));
|
|
4490
4529
|
params.set("longitude", String(lon));
|
|
4491
|
-
params.set("start_date", fromDate);
|
|
4492
|
-
params.set("end_date", toDate);
|
|
4493
4530
|
params.set("hourly", buildHourlyParam(endpoint));
|
|
4494
4531
|
params.set("models", model);
|
|
4495
4532
|
params.set("timezone", "UTC");
|
|
4496
|
-
if (endpoint === OPEN_METEO_SINGLE_RUNS_URL
|
|
4497
|
-
|
|
4533
|
+
if (endpoint === OPEN_METEO_SINGLE_RUNS_URL) {
|
|
4534
|
+
if (opts.issuedAt) {
|
|
4535
|
+
params.set("run", opts.issuedAt);
|
|
4536
|
+
}
|
|
4537
|
+
} else {
|
|
4538
|
+
params.set("start_date", fromDate);
|
|
4539
|
+
params.set("end_date", toDate);
|
|
4498
4540
|
}
|
|
4499
4541
|
const fetchFn = opts.fetchFn ?? fetch;
|
|
4500
4542
|
const url = `${endpoint}?${params.toString()}`;
|
|
@@ -4553,24 +4595,32 @@ var mostlyright = (() => {
|
|
|
4553
4595
|
dewPointC: maybeNumber2(pickHourlyValue(h, "dew_point_2m", isPrev, i)),
|
|
4554
4596
|
apparentTempC: maybeNumber2(pickHourlyValue(h, "apparent_temperature", isPrev, i)),
|
|
4555
4597
|
windSpeedMs: maybeNumber2(pickHourlyValue(h, "wind_speed_10m", isPrev, i)),
|
|
4556
|
-
windDirDeg:
|
|
4598
|
+
windDirDeg: maybeInt(pickHourlyValue(h, "wind_direction_10m", isPrev, i)),
|
|
4557
4599
|
windGustsMs: maybeNumber2(pickHourlyValue(h, "wind_gusts_10m", isPrev, i)),
|
|
4558
4600
|
precipProbability: popPct === null ? null : popPct / 100,
|
|
4559
4601
|
precipitationMm: maybeNumber2(pickHourlyValue(h, "precipitation", isPrev, i)),
|
|
4560
|
-
cloudCoverPct:
|
|
4602
|
+
cloudCoverPct: maybeInt(pickHourlyValue(h, "cloud_cover", isPrev, i)),
|
|
4561
4603
|
surfacePressureHpa: maybeNumber2(pickHourlyValue(h, "surface_pressure", isPrev, i)),
|
|
4562
4604
|
pressureMslHpa: maybeNumber2(pickHourlyValue(h, "pressure_msl", isPrev, i)),
|
|
4563
4605
|
shortwaveRadiationWm2: maybeNumber2(pickHourlyValue(h, "shortwave_radiation", isPrev, i)),
|
|
4564
4606
|
directRadiationWm2: maybeNumber2(pickHourlyValue(h, "direct_radiation", isPrev, i)),
|
|
4565
4607
|
capeJkg: maybeNumber2(pickHourlyValue(h, "cape", isPrev, i)),
|
|
4566
|
-
freezingLevelM:
|
|
4608
|
+
freezingLevelM: maybeInt(pickHourlyValue(h, "freezing_level_height", isPrev, i)),
|
|
4567
4609
|
snowDepthM: maybeNumber2(pickHourlyValue(h, "snow_depth", isPrev, i)),
|
|
4568
|
-
visibilityM:
|
|
4569
|
-
weatherCode:
|
|
4610
|
+
visibilityM: maybeInt(pickHourlyValue(h, "visibility", isPrev, i)),
|
|
4611
|
+
weatherCode: maybeInt(pickHourlyValue(h, "weather_code", isPrev, i)),
|
|
4570
4612
|
source,
|
|
4571
4613
|
retrievedAt
|
|
4572
4614
|
});
|
|
4573
4615
|
}
|
|
4616
|
+
if (source === "open_meteo.single_run" && rows.length > 0) {
|
|
4617
|
+
const loMs = Date.parse(`${fromDate}T00:00:00Z`);
|
|
4618
|
+
const hiMs = Date.parse(`${toDate}T00:00:00Z`) + 864e5;
|
|
4619
|
+
return rows.filter((r) => {
|
|
4620
|
+
const v = Date.parse(r.validAt);
|
|
4621
|
+
return v >= loMs && v < hiMs;
|
|
4622
|
+
});
|
|
4623
|
+
}
|
|
4574
4624
|
return rows;
|
|
4575
4625
|
}
|
|
4576
4626
|
|
|
@@ -6485,7 +6535,8 @@ var mostlyright = (() => {
|
|
|
6485
6535
|
if (resolvedStrategy === "exact_window") {
|
|
6486
6536
|
const chunks = await downloadIemAsos(station, fromDate, toDate, {
|
|
6487
6537
|
reportType: 3,
|
|
6488
|
-
politenessMs: 1e3
|
|
6538
|
+
politenessMs: 1e3,
|
|
6539
|
+
exactStart: true
|
|
6489
6540
|
});
|
|
6490
6541
|
for (const chunk of chunks) {
|
|
6491
6542
|
const rows = parseIemCsv(chunk.csv, { observationTypeOverride: "METAR" });
|