@mostlyrightmd/core 0.1.0-rc.7

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/discovery/index.cjs +1646 -0
  4. package/dist/discovery/index.cjs.map +1 -0
  5. package/dist/discovery/index.d.cts +313 -0
  6. package/dist/discovery/index.d.ts +313 -0
  7. package/dist/discovery/index.mjs +1609 -0
  8. package/dist/discovery/index.mjs.map +1 -0
  9. package/dist/formats/index.cjs +498 -0
  10. package/dist/formats/index.cjs.map +1 -0
  11. package/dist/formats/index.d.cts +97 -0
  12. package/dist/formats/index.d.ts +97 -0
  13. package/dist/formats/index.mjs +465 -0
  14. package/dist/formats/index.mjs.map +1 -0
  15. package/dist/index.cjs +1624 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +559 -0
  18. package/dist/index.d.ts +559 -0
  19. package/dist/index.global.js +1582 -0
  20. package/dist/index.global.js.map +1 -0
  21. package/dist/index.mjs +1557 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/internal/bounds.cjs +125 -0
  24. package/dist/internal/bounds.cjs.map +1 -0
  25. package/dist/internal/bounds.d.cts +36 -0
  26. package/dist/internal/bounds.d.ts +36 -0
  27. package/dist/internal/bounds.mjs +81 -0
  28. package/dist/internal/bounds.mjs.map +1 -0
  29. package/dist/internal/cache/fs.cjs +217 -0
  30. package/dist/internal/cache/fs.cjs.map +1 -0
  31. package/dist/internal/cache/fs.d.cts +57 -0
  32. package/dist/internal/cache/fs.d.ts +57 -0
  33. package/dist/internal/cache/fs.mjs +179 -0
  34. package/dist/internal/cache/fs.mjs.map +1 -0
  35. package/dist/internal/cache/index.browser.cjs +1184 -0
  36. package/dist/internal/cache/index.browser.cjs.map +1 -0
  37. package/dist/internal/cache/index.browser.d.cts +20 -0
  38. package/dist/internal/cache/index.browser.d.ts +20 -0
  39. package/dist/internal/cache/index.browser.mjs +36 -0
  40. package/dist/internal/cache/index.browser.mjs.map +1 -0
  41. package/dist/internal/cache/index.cjs +1389 -0
  42. package/dist/internal/cache/index.cjs.map +1 -0
  43. package/dist/internal/cache/index.d.cts +16 -0
  44. package/dist/internal/cache/index.d.ts +16 -0
  45. package/dist/internal/cache/index.mjs +40 -0
  46. package/dist/internal/cache/index.mjs.map +1 -0
  47. package/dist/internal/chunk-PKJXHY27.mjs +1137 -0
  48. package/dist/internal/chunk-PKJXHY27.mjs.map +1 -0
  49. package/dist/internal/convert.cjs +161 -0
  50. package/dist/internal/convert.cjs.map +1 -0
  51. package/dist/internal/convert.d.cts +44 -0
  52. package/dist/internal/convert.d.ts +44 -0
  53. package/dist/internal/convert.mjs +117 -0
  54. package/dist/internal/convert.mjs.map +1 -0
  55. package/dist/internal/fs-O6XR4WWW.mjs +183 -0
  56. package/dist/internal/fs-O6XR4WWW.mjs.map +1 -0
  57. package/dist/internal/keys-B7C8C88N.d.cts +191 -0
  58. package/dist/internal/keys-B7C8C88N.d.ts +191 -0
  59. package/dist/internal/merge/index.cjs +75 -0
  60. package/dist/internal/merge/index.cjs.map +1 -0
  61. package/dist/internal/merge/index.d.cts +74 -0
  62. package/dist/internal/merge/index.d.ts +74 -0
  63. package/dist/internal/merge/index.mjs +46 -0
  64. package/dist/internal/merge/index.mjs.map +1 -0
  65. package/dist/internal/pairs.cjs +328 -0
  66. package/dist/internal/pairs.cjs.map +1 -0
  67. package/dist/internal/pairs.d.cts +105 -0
  68. package/dist/internal/pairs.d.ts +105 -0
  69. package/dist/internal/pairs.mjs +298 -0
  70. package/dist/internal/pairs.mjs.map +1 -0
  71. package/dist/qc/index.cjs +247 -0
  72. package/dist/qc/index.cjs.map +1 -0
  73. package/dist/qc/index.d.cts +140 -0
  74. package/dist/qc/index.d.ts +140 -0
  75. package/dist/qc/index.mjs +212 -0
  76. package/dist/qc/index.mjs.map +1 -0
  77. package/dist/temporal/index.cjs +504 -0
  78. package/dist/temporal/index.cjs.map +1 -0
  79. package/dist/temporal/index.d.cts +121 -0
  80. package/dist/temporal/index.d.ts +121 -0
  81. package/dist/temporal/index.mjs +474 -0
  82. package/dist/temporal/index.mjs.map +1 -0
  83. package/dist/transforms/index.cjs +399 -0
  84. package/dist/transforms/index.cjs.map +1 -0
  85. package/dist/transforms/index.d.cts +193 -0
  86. package/dist/transforms/index.d.ts +193 -0
  87. package/dist/transforms/index.mjs +362 -0
  88. package/dist/transforms/index.mjs.map +1 -0
  89. package/dist/validator.cjs +1870 -0
  90. package/dist/validator.cjs.map +1 -0
  91. package/dist/validator.d.cts +30 -0
  92. package/dist/validator.d.ts +30 -0
  93. package/dist/validator.mjs +1843 -0
  94. package/dist/validator.mjs.map +1 -0
  95. package/package.json +115 -0
@@ -0,0 +1,1389 @@
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 __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/internal/cache/fs.ts
34
+ var fs_exports = {};
35
+ __export(fs_exports, {
36
+ FsStore: () => FsStore,
37
+ _resetLegacyCacheDirWarn: () => _resetLegacyCacheDirWarn,
38
+ defaultFsRoot: () => defaultFsRoot
39
+ });
40
+ function defaultFsRoot() {
41
+ const canonical = process.env.MOSTLYRIGHT_CACHE_DIR;
42
+ if (canonical !== void 0 && canonical.length > 0) return canonical;
43
+ const legacy = process.env.TRADEWINDS_CACHE_DIR;
44
+ if (legacy !== void 0 && legacy.length > 0) {
45
+ if (!_legacyCacheDirWarned) {
46
+ console.warn(
47
+ "TRADEWINDS_CACHE_DIR is deprecated; use MOSTLYRIGHT_CACHE_DIR. Support will be removed in vts-0.3. Run: mv ~/.tradewinds ~/.mostlyright"
48
+ );
49
+ _legacyCacheDirWarned = true;
50
+ }
51
+ return legacy;
52
+ }
53
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".mostlyright", "cache-ts");
54
+ }
55
+ function _resetLegacyCacheDirWarn() {
56
+ _legacyCacheDirWarned = false;
57
+ }
58
+ var import_node_crypto, import_promises, import_node_os, import_node_path, properLockfile, _legacyCacheDirWarned, FsStore;
59
+ var init_fs = __esm({
60
+ "src/internal/cache/fs.ts"() {
61
+ "use strict";
62
+ import_node_crypto = require("crypto");
63
+ import_promises = require("fs/promises");
64
+ import_node_os = require("os");
65
+ import_node_path = require("path");
66
+ properLockfile = __toESM(require("proper-lockfile"), 1);
67
+ _legacyCacheDirWarned = false;
68
+ FsStore = class {
69
+ #root;
70
+ // In-process per-key promise chain. proper-lockfile guarantees
71
+ // cross-process exclusion but its retry-based contention resolution
72
+ // is order-non-deterministic — two in-process callers racing on
73
+ // `lock()` may acquire in either order. Layering an in-process chain
74
+ // ensures strict FIFO for callers within the same Node process (and
75
+ // serves as a cheap fast-path: only one of N in-process callers ever
76
+ // actually contends with proper-lockfile).
77
+ #chain = /* @__PURE__ */ new Map();
78
+ constructor(opts = {}) {
79
+ this.#root = opts.root ?? defaultFsRoot();
80
+ }
81
+ /**
82
+ * Path resolver — `key` is sanitized via `encodeURIComponent` so that
83
+ *
84
+ * 1. `:` `/` `\` cannot escape the root (all percent-encoded), and
85
+ * 2. the key → file mapping is INJECTIVE: distinct keys always map to
86
+ * distinct files.
87
+ *
88
+ * Iter-13 C16 fix: the previous implementation collapsed `:` / `/` / `\`
89
+ * to the literal substring `"__"`, which is a lossy mapping — `"a:b"`,
90
+ * `"a/b"`, and the literal `"a__b"` all hashed to `a__b.json`, so one
91
+ * key's write would silently overwrite (and corrupt subsequent reads of)
92
+ * another key. `encodeURIComponent` is bijective on string inputs and
93
+ * filesystem-safe on every platform we ship (POSIX + Windows): the
94
+ * characters it leaves unescaped (alphanumerics, `-._~!*'()`) are all
95
+ * legal in NTFS, APFS, and ext4 filenames, and `%` is itself legal.
96
+ *
97
+ * BREAKING in v0.1.0: on-disk cache files written by any prior
98
+ * pre-release of this package use the old `__`-replacement scheme and
99
+ * are unreadable after upgrade. This is acceptable for a local-first
100
+ * cache: entries are regenerated on demand from live data, and the
101
+ * cache directory can be safely deleted by the user.
102
+ */
103
+ #pathFor(key) {
104
+ const safe = encodeURIComponent(key);
105
+ return (0, import_node_path.join)(this.#root, `${safe}.json`);
106
+ }
107
+ async get(key) {
108
+ const p = this.#pathFor(key);
109
+ let raw;
110
+ try {
111
+ raw = await (0, import_promises.readFile)(p, "utf8");
112
+ } catch (e) {
113
+ const code = e.code;
114
+ if (code === "ENOENT") return null;
115
+ throw e;
116
+ }
117
+ let entry;
118
+ try {
119
+ entry = JSON.parse(raw);
120
+ } catch {
121
+ return null;
122
+ }
123
+ if (entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt) {
124
+ try {
125
+ await (0, import_promises.rm)(p, { force: true });
126
+ } catch {
127
+ }
128
+ return null;
129
+ }
130
+ return entry.value;
131
+ }
132
+ async set(key, value, opts) {
133
+ const p = this.#pathFor(key);
134
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(p), { recursive: true });
135
+ const entry = opts?.ttlMs !== void 0 ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };
136
+ const tmp = `${p}.${(0, import_node_crypto.randomUUID)()}.tmp`;
137
+ try {
138
+ await (0, import_promises.writeFile)(tmp, JSON.stringify(entry), "utf8");
139
+ await (0, import_promises.rename)(tmp, p);
140
+ } catch (e) {
141
+ try {
142
+ await (0, import_promises.rm)(tmp, { force: true });
143
+ } catch {
144
+ }
145
+ throw e;
146
+ }
147
+ }
148
+ async delete(key) {
149
+ const p = this.#pathFor(key);
150
+ try {
151
+ await (0, import_promises.rm)(p, { force: true });
152
+ } catch (e) {
153
+ const code = e.code;
154
+ if (code === "ENOENT") return;
155
+ throw e;
156
+ }
157
+ }
158
+ /**
159
+ * Enumerate keys whose stored files exist under the cache root and whose
160
+ * decoded form starts with `prefix`.
161
+ *
162
+ * Returns an empty list if the root directory does not exist (cold cache).
163
+ *
164
+ * TS-W6 Wave 1: used by `availability()` to count observation months and
165
+ * climate years for a station. The file→key mapping is the inverse of
166
+ * `#pathFor` (encodeURIComponent → strip `.json` → decodeURIComponent).
167
+ */
168
+ async listKeys(prefix) {
169
+ let entries;
170
+ try {
171
+ entries = await (0, import_promises.readdir)(this.#root);
172
+ } catch (e) {
173
+ const code = e.code;
174
+ if (code === "ENOENT") return Object.freeze([]);
175
+ throw e;
176
+ }
177
+ const out = [];
178
+ for (const name of entries) {
179
+ if (!name.endsWith(".json")) continue;
180
+ const encoded = name.slice(0, -".json".length);
181
+ let decoded;
182
+ try {
183
+ decoded = decodeURIComponent(encoded);
184
+ } catch {
185
+ continue;
186
+ }
187
+ if (decoded.startsWith(prefix)) {
188
+ out.push(decoded);
189
+ }
190
+ }
191
+ return Object.freeze(out);
192
+ }
193
+ async withLock(key, fn) {
194
+ const p = this.#pathFor(key);
195
+ const prev = this.#chain.get(key) ?? Promise.resolve();
196
+ const run = async () => {
197
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(p), { recursive: true });
198
+ const release = await properLockfile.lock(p, {
199
+ realpath: false,
200
+ retries: { retries: 5, minTimeout: 20, maxTimeout: 200 }
201
+ });
202
+ try {
203
+ return await fn();
204
+ } finally {
205
+ await release();
206
+ }
207
+ };
208
+ const next = prev.then(run, run);
209
+ const absorbed = next.then(
210
+ () => void 0,
211
+ () => void 0
212
+ );
213
+ this.#chain.set(key, absorbed);
214
+ absorbed.finally(() => {
215
+ if (this.#chain.get(key) === absorbed) this.#chain.delete(key);
216
+ });
217
+ return next;
218
+ }
219
+ };
220
+ }
221
+ });
222
+
223
+ // src/internal/cache/index.ts
224
+ var cache_exports = {};
225
+ __export(cache_exports, {
226
+ INDEXEDDB_DB_NAME: () => DB_NAME,
227
+ IndexedDBStore: () => IndexedDBStore,
228
+ MemoryStore: () => MemoryStore,
229
+ cacheKeyForClimate: () => cacheKeyForClimate,
230
+ cacheKeyForObservations: () => cacheKeyForObservations,
231
+ defaultCacheStore: () => defaultCacheStore,
232
+ isLiveSource: () => isLiveSource,
233
+ isWithinVolatileWindow: () => isWithinVolatileWindow,
234
+ isWritableMonth: () => isWritableMonth,
235
+ isWritableYear: () => isWritableYear,
236
+ lockKeyFor: () => lockKeyFor,
237
+ shouldSkipCacheForCurrentLstMonth: () => shouldSkipCacheForCurrentLstMonth,
238
+ shouldSkipCacheForCurrentLstYear: () => shouldSkipCacheForCurrentLstYear
239
+ });
240
+ module.exports = __toCommonJS(cache_exports);
241
+
242
+ // src/internal/cache/types.ts
243
+ function lockKeyFor(key) {
244
+ return `mostlyright:cache:lock:${key}`;
245
+ }
246
+
247
+ // src/internal/cache/memory.ts
248
+ var MemoryStore = class {
249
+ #entries = /* @__PURE__ */ new Map();
250
+ #chain = /* @__PURE__ */ new Map();
251
+ async get(key) {
252
+ const e = this.#entries.get(key);
253
+ if (e === void 0) return null;
254
+ if (e.expiresAt !== void 0 && Date.now() >= e.expiresAt) {
255
+ this.#entries.delete(key);
256
+ return null;
257
+ }
258
+ return structuredClone(e.value);
259
+ }
260
+ async set(key, value, opts) {
261
+ const cloned = structuredClone(value);
262
+ const entry = opts?.ttlMs !== void 0 ? { value: cloned, expiresAt: Date.now() + opts.ttlMs } : { value: cloned };
263
+ this.#entries.set(key, entry);
264
+ }
265
+ async delete(key) {
266
+ this.#entries.delete(key);
267
+ }
268
+ /**
269
+ * Enumerate live (non-expired) keys with the given prefix.
270
+ *
271
+ * TS-W6 Wave 1: `availability()` uses this to count cached observation
272
+ * months and climate years per station. Expired entries are evicted as a
273
+ * side effect (same lazy-eviction policy as `.get`).
274
+ */
275
+ async listKeys(prefix) {
276
+ const now = Date.now();
277
+ const out = [];
278
+ for (const [key, entry] of this.#entries) {
279
+ if (entry.expiresAt !== void 0 && now >= entry.expiresAt) {
280
+ this.#entries.delete(key);
281
+ continue;
282
+ }
283
+ if (key.startsWith(prefix)) {
284
+ out.push(key);
285
+ }
286
+ }
287
+ return Object.freeze(out);
288
+ }
289
+ async withLock(key, fn) {
290
+ const prev = this.#chain.get(key) ?? Promise.resolve();
291
+ const next = prev.then(
292
+ () => fn(),
293
+ () => fn()
294
+ );
295
+ const absorbed = next.then(
296
+ () => void 0,
297
+ () => void 0
298
+ );
299
+ this.#chain.set(key, absorbed);
300
+ absorbed.finally(() => {
301
+ if (this.#chain.get(key) === absorbed) {
302
+ this.#chain.delete(key);
303
+ }
304
+ });
305
+ return next;
306
+ }
307
+ };
308
+
309
+ // src/internal/cache/indexeddb.ts
310
+ var import_idb = require("idb");
311
+ var DB_NAME = "mostlyright-cache-v1";
312
+ var STORE_NAME = "entries";
313
+ var SCHEMA_VERSION = 1;
314
+ function getWebLocks() {
315
+ if (typeof navigator === "undefined") return null;
316
+ const nav = navigator;
317
+ return nav.locks ?? null;
318
+ }
319
+ var IndexedDBStore = class {
320
+ #dbName;
321
+ #dbPromise;
322
+ #chain = /* @__PURE__ */ new Map();
323
+ constructor(opts = {}) {
324
+ this.#dbName = opts.dbName ?? DB_NAME;
325
+ this.#dbPromise = (0, import_idb.openDB)(this.#dbName, SCHEMA_VERSION, {
326
+ upgrade(db) {
327
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
328
+ db.createObjectStore(STORE_NAME);
329
+ }
330
+ }
331
+ });
332
+ }
333
+ async get(key) {
334
+ const db = await this.#dbPromise;
335
+ const entry = await db.get(STORE_NAME, key);
336
+ if (entry === void 0) return null;
337
+ if (entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt) {
338
+ try {
339
+ await db.delete(STORE_NAME, key);
340
+ } catch {
341
+ }
342
+ return null;
343
+ }
344
+ return entry.value;
345
+ }
346
+ async set(key, value, opts) {
347
+ const db = await this.#dbPromise;
348
+ const entry = opts?.ttlMs !== void 0 ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };
349
+ await db.put(STORE_NAME, entry, key);
350
+ }
351
+ async delete(key) {
352
+ const db = await this.#dbPromise;
353
+ await db.delete(STORE_NAME, key);
354
+ }
355
+ /**
356
+ * Enumerate keys with the given prefix using IndexedDB's bounded range
357
+ * query. Live keys only — expired entries are lazy-evicted on read by
358
+ * `get()`, so a stale-but-not-yet-evicted entry can appear here; callers
359
+ * who care about expiration should `get()` to confirm.
360
+ *
361
+ * TS-W6 Wave 1: `availability()` uses this to count observation months and
362
+ * climate years for a station.
363
+ */
364
+ async listKeys(prefix) {
365
+ const db = await this.#dbPromise;
366
+ const range = IDBKeyRange.bound(prefix, `${prefix}\uFFFF`, false, false);
367
+ const keys = await db.getAllKeys(STORE_NAME, range);
368
+ const out = [];
369
+ for (const k of keys) {
370
+ if (typeof k === "string" && k.startsWith(prefix)) {
371
+ out.push(k);
372
+ }
373
+ }
374
+ return Object.freeze(out);
375
+ }
376
+ async withLock(key, fn) {
377
+ const locks = getWebLocks();
378
+ if (locks !== null) {
379
+ return locks.request(lockKeyFor(key), { mode: "exclusive" }, () => fn());
380
+ }
381
+ const prev = this.#chain.get(key) ?? Promise.resolve();
382
+ const next = prev.then(
383
+ () => fn(),
384
+ () => fn()
385
+ );
386
+ const absorbed = next.then(
387
+ () => void 0,
388
+ () => void 0
389
+ );
390
+ this.#chain.set(key, absorbed);
391
+ absorbed.finally(() => {
392
+ if (this.#chain.get(key) === absorbed) this.#chain.delete(key);
393
+ });
394
+ return next;
395
+ }
396
+ };
397
+
398
+ // src/internal/cache/default.ts
399
+ async function defaultCacheStore() {
400
+ if (typeof indexedDB !== "undefined") return new IndexedDBStore();
401
+ if (typeof process !== "undefined" && typeof process.versions === "object" && process.versions !== null && typeof process.versions.node === "string") {
402
+ const { FsStore: FsStore2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
403
+ return new FsStore2();
404
+ }
405
+ return new MemoryStore();
406
+ }
407
+
408
+ // src/data/generated/stations.ts
409
+ var STATIONS = [
410
+ {
411
+ code: "EDDB",
412
+ country: "DE",
413
+ ghcnh_id: null,
414
+ icao: "EDDB",
415
+ latitude: 52.3667,
416
+ longitude: 13.5033,
417
+ name: "Berlin Brandenburg",
418
+ tz: "Europe/Berlin"
419
+ },
420
+ {
421
+ code: "EDDF",
422
+ country: "DE",
423
+ ghcnh_id: null,
424
+ icao: "EDDF",
425
+ latitude: 50.0379,
426
+ longitude: 8.5622,
427
+ name: "Frankfurt am Main",
428
+ tz: "Europe/Berlin"
429
+ },
430
+ {
431
+ code: "EDDM",
432
+ country: "DE",
433
+ ghcnh_id: null,
434
+ icao: "EDDM",
435
+ latitude: 48.3538,
436
+ longitude: 11.7861,
437
+ name: "Munich Franz Josef Strauss",
438
+ tz: "Europe/Berlin"
439
+ },
440
+ {
441
+ code: "EFHK",
442
+ country: "FI",
443
+ ghcnh_id: null,
444
+ icao: "EFHK",
445
+ latitude: 60.3172,
446
+ longitude: 24.9633,
447
+ name: "Helsinki-Vantaa",
448
+ tz: "Europe/Helsinki"
449
+ },
450
+ {
451
+ code: "EGKK",
452
+ country: "GB",
453
+ ghcnh_id: null,
454
+ icao: "EGKK",
455
+ latitude: 51.1481,
456
+ longitude: -0.1903,
457
+ name: "London Gatwick",
458
+ tz: "Europe/London"
459
+ },
460
+ {
461
+ code: "EGLL",
462
+ country: "GB",
463
+ ghcnh_id: null,
464
+ icao: "EGLL",
465
+ latitude: 51.4706,
466
+ longitude: -0.4619,
467
+ name: "London Heathrow",
468
+ tz: "Europe/London"
469
+ },
470
+ {
471
+ code: "EHAM",
472
+ country: "NL",
473
+ ghcnh_id: null,
474
+ icao: "EHAM",
475
+ latitude: 52.3086,
476
+ longitude: 4.7639,
477
+ name: "Amsterdam Schiphol",
478
+ tz: "Europe/Amsterdam"
479
+ },
480
+ {
481
+ code: "EKCH",
482
+ country: "DK",
483
+ ghcnh_id: null,
484
+ icao: "EKCH",
485
+ latitude: 55.6181,
486
+ longitude: 12.6561,
487
+ name: "Copenhagen Kastrup",
488
+ tz: "Europe/Copenhagen"
489
+ },
490
+ {
491
+ code: "EPWA",
492
+ country: "PL",
493
+ ghcnh_id: null,
494
+ icao: "EPWA",
495
+ latitude: 52.1657,
496
+ longitude: 20.9671,
497
+ name: "Warsaw Chopin",
498
+ tz: "Europe/Warsaw"
499
+ },
500
+ {
501
+ code: "ESSA",
502
+ country: "SE",
503
+ ghcnh_id: null,
504
+ icao: "ESSA",
505
+ latitude: 59.6519,
506
+ longitude: 17.9186,
507
+ name: "Stockholm Arlanda",
508
+ tz: "Europe/Stockholm"
509
+ },
510
+ {
511
+ code: "ATL",
512
+ country: "US",
513
+ ghcnh_id: "USW00013874",
514
+ icao: "KATL",
515
+ latitude: 33.6407,
516
+ longitude: -84.4277,
517
+ name: "Hartsfield-Jackson Atlanta International",
518
+ tz: "America/New_York"
519
+ },
520
+ {
521
+ code: "AUS",
522
+ country: "US",
523
+ ghcnh_id: "USW00013904",
524
+ icao: "KAUS",
525
+ latitude: 30.1975,
526
+ longitude: -97.6664,
527
+ name: "Austin-Bergstrom International",
528
+ tz: "America/Chicago"
529
+ },
530
+ {
531
+ code: "BOS",
532
+ country: "US",
533
+ ghcnh_id: "USW00014739",
534
+ icao: "KBOS",
535
+ latitude: 42.3656,
536
+ longitude: -71.0096,
537
+ name: "Boston Logan International",
538
+ tz: "America/New_York"
539
+ },
540
+ {
541
+ code: "DCA",
542
+ country: "US",
543
+ ghcnh_id: "USW00013743",
544
+ icao: "KDCA",
545
+ latitude: 38.8512,
546
+ longitude: -77.0402,
547
+ name: "Washington Reagan National",
548
+ tz: "America/New_York"
549
+ },
550
+ {
551
+ code: "DEN",
552
+ country: "US",
553
+ ghcnh_id: "USW00003017",
554
+ icao: "KDEN",
555
+ latitude: 39.8561,
556
+ longitude: -104.6737,
557
+ name: "Denver International",
558
+ tz: "America/Denver"
559
+ },
560
+ {
561
+ code: "DFW",
562
+ country: "US",
563
+ ghcnh_id: "USW00003927",
564
+ icao: "KDFW",
565
+ latitude: 32.8998,
566
+ longitude: -97.0403,
567
+ name: "Dallas-Fort Worth International",
568
+ tz: "America/Chicago"
569
+ },
570
+ {
571
+ code: "HOU",
572
+ country: "US",
573
+ ghcnh_id: "USW00012918",
574
+ icao: "KHOU",
575
+ latitude: 29.6454,
576
+ longitude: -95.2789,
577
+ name: "Houston Hobby",
578
+ tz: "America/Chicago"
579
+ },
580
+ {
581
+ code: "LAS",
582
+ country: "US",
583
+ ghcnh_id: "USW00023169",
584
+ icao: "KLAS",
585
+ latitude: 36.084,
586
+ longitude: -115.1537,
587
+ name: "Harry Reid (McCarran) International",
588
+ tz: "America/Los_Angeles"
589
+ },
590
+ {
591
+ code: "LAX",
592
+ country: "US",
593
+ ghcnh_id: "USW00023174",
594
+ icao: "KLAX",
595
+ latitude: 33.9425,
596
+ longitude: -118.4081,
597
+ name: "Los Angeles International",
598
+ tz: "America/Los_Angeles"
599
+ },
600
+ {
601
+ code: "MDW",
602
+ country: "US",
603
+ ghcnh_id: "USW00014819",
604
+ icao: "KMDW",
605
+ latitude: 41.7868,
606
+ longitude: -87.7522,
607
+ name: "Chicago Midway International",
608
+ tz: "America/Chicago"
609
+ },
610
+ {
611
+ code: "MIA",
612
+ country: "US",
613
+ ghcnh_id: "USW00012839",
614
+ icao: "KMIA",
615
+ latitude: 25.7959,
616
+ longitude: -80.287,
617
+ name: "Miami International",
618
+ tz: "America/New_York"
619
+ },
620
+ {
621
+ code: "MSP",
622
+ country: "US",
623
+ ghcnh_id: "USW00014922",
624
+ icao: "KMSP",
625
+ latitude: 44.8848,
626
+ longitude: -93.2223,
627
+ name: "Minneapolis-St Paul International",
628
+ tz: "America/Chicago"
629
+ },
630
+ {
631
+ code: "MSY",
632
+ country: "US",
633
+ ghcnh_id: "USW00012916",
634
+ icao: "KMSY",
635
+ latitude: 29.9934,
636
+ longitude: -90.258,
637
+ name: "New Orleans Louis Armstrong International",
638
+ tz: "America/Chicago"
639
+ },
640
+ {
641
+ code: "NYC",
642
+ country: "US",
643
+ ghcnh_id: "USW00094728",
644
+ icao: "KNYC",
645
+ latitude: 40.7789,
646
+ longitude: -73.9692,
647
+ name: "Central Park, New York",
648
+ tz: "America/New_York"
649
+ },
650
+ {
651
+ code: "OKC",
652
+ country: "US",
653
+ ghcnh_id: "USW00013967",
654
+ icao: "KOKC",
655
+ latitude: 35.3931,
656
+ longitude: -97.6007,
657
+ name: "Oklahoma City Will Rogers World",
658
+ tz: "America/Chicago"
659
+ },
660
+ {
661
+ code: "PHL",
662
+ country: "US",
663
+ ghcnh_id: "USW00013739",
664
+ icao: "KPHL",
665
+ latitude: 39.8721,
666
+ longitude: -75.2411,
667
+ name: "Philadelphia International",
668
+ tz: "America/New_York"
669
+ },
670
+ {
671
+ code: "PHX",
672
+ country: "US",
673
+ ghcnh_id: "USW00023183",
674
+ icao: "KPHX",
675
+ latitude: 33.4373,
676
+ longitude: -112.0078,
677
+ name: "Phoenix Sky Harbor International",
678
+ tz: "America/Phoenix"
679
+ },
680
+ {
681
+ code: "SAT",
682
+ country: "US",
683
+ ghcnh_id: "USW00012921",
684
+ icao: "KSAT",
685
+ latitude: 29.5337,
686
+ longitude: -98.4698,
687
+ name: "San Antonio International",
688
+ tz: "America/Chicago"
689
+ },
690
+ {
691
+ code: "SEA",
692
+ country: "US",
693
+ ghcnh_id: "USW00024233",
694
+ icao: "KSEA",
695
+ latitude: 47.4502,
696
+ longitude: -122.3088,
697
+ name: "Seattle-Tacoma International",
698
+ tz: "America/Los_Angeles"
699
+ },
700
+ {
701
+ code: "SFO",
702
+ country: "US",
703
+ ghcnh_id: "USW00023234",
704
+ icao: "KSFO",
705
+ latitude: 37.6213,
706
+ longitude: -122.379,
707
+ name: "San Francisco International",
708
+ tz: "America/Los_Angeles"
709
+ },
710
+ {
711
+ code: "LEBL",
712
+ country: "ES",
713
+ ghcnh_id: null,
714
+ icao: "LEBL",
715
+ latitude: 41.2974,
716
+ longitude: 2.0833,
717
+ name: "Barcelona El Prat",
718
+ tz: "Europe/Madrid"
719
+ },
720
+ {
721
+ code: "LEMD",
722
+ country: "ES",
723
+ ghcnh_id: null,
724
+ icao: "LEMD",
725
+ latitude: 40.4719,
726
+ longitude: -3.5626,
727
+ name: "Madrid Barajas",
728
+ tz: "Europe/Madrid"
729
+ },
730
+ {
731
+ code: "LFPB",
732
+ country: "FR",
733
+ ghcnh_id: null,
734
+ icao: "LFPB",
735
+ latitude: 48.9694,
736
+ longitude: 2.4414,
737
+ name: "Paris Le Bourget",
738
+ tz: "Europe/Paris"
739
+ },
740
+ {
741
+ code: "LFPG",
742
+ country: "FR",
743
+ ghcnh_id: null,
744
+ icao: "LFPG",
745
+ latitude: 49.0097,
746
+ longitude: 2.5479,
747
+ name: "Paris Charles de Gaulle",
748
+ tz: "Europe/Paris"
749
+ },
750
+ {
751
+ code: "LFPO",
752
+ country: "FR",
753
+ ghcnh_id: null,
754
+ icao: "LFPO",
755
+ latitude: 48.7233,
756
+ longitude: 2.3794,
757
+ name: "Paris Orly",
758
+ tz: "Europe/Paris"
759
+ },
760
+ {
761
+ code: "LIMC",
762
+ country: "IT",
763
+ ghcnh_id: null,
764
+ icao: "LIMC",
765
+ latitude: 45.6306,
766
+ longitude: 8.7281,
767
+ name: "Milan Malpensa",
768
+ tz: "Europe/Rome"
769
+ },
770
+ {
771
+ code: "LIRF",
772
+ country: "IT",
773
+ ghcnh_id: null,
774
+ icao: "LIRF",
775
+ latitude: 41.8003,
776
+ longitude: 12.2389,
777
+ name: "Rome Fiumicino",
778
+ tz: "Europe/Rome"
779
+ },
780
+ {
781
+ code: "LOWW",
782
+ country: "AT",
783
+ ghcnh_id: null,
784
+ icao: "LOWW",
785
+ latitude: 48.1103,
786
+ longitude: 16.5697,
787
+ name: "Vienna International",
788
+ tz: "Europe/Vienna"
789
+ },
790
+ {
791
+ code: "LSZH",
792
+ country: "CH",
793
+ ghcnh_id: null,
794
+ icao: "LSZH",
795
+ latitude: 47.4647,
796
+ longitude: 8.5492,
797
+ name: "Zurich",
798
+ tz: "Europe/Zurich"
799
+ },
800
+ {
801
+ code: "NZAA",
802
+ country: "NZ",
803
+ ghcnh_id: null,
804
+ icao: "NZAA",
805
+ latitude: -37.0081,
806
+ longitude: 174.7917,
807
+ name: "Auckland",
808
+ tz: "Pacific/Auckland"
809
+ },
810
+ {
811
+ code: "NZWN",
812
+ country: "NZ",
813
+ ghcnh_id: null,
814
+ icao: "NZWN",
815
+ latitude: -41.3272,
816
+ longitude: 174.8053,
817
+ name: "Wellington",
818
+ tz: "Pacific/Auckland"
819
+ },
820
+ {
821
+ code: "OERK",
822
+ country: "SA",
823
+ ghcnh_id: null,
824
+ icao: "OERK",
825
+ latitude: 24.9576,
826
+ longitude: 46.6988,
827
+ name: "Riyadh King Khalid International",
828
+ tz: "Asia/Riyadh"
829
+ },
830
+ {
831
+ code: "OMDB",
832
+ country: "AE",
833
+ ghcnh_id: null,
834
+ icao: "OMDB",
835
+ latitude: 25.2532,
836
+ longitude: 55.3657,
837
+ name: "Dubai International",
838
+ tz: "Asia/Dubai"
839
+ },
840
+ {
841
+ code: "OTHH",
842
+ country: "QA",
843
+ ghcnh_id: null,
844
+ icao: "OTHH",
845
+ latitude: 25.2731,
846
+ longitude: 51.608,
847
+ name: "Doha Hamad International",
848
+ tz: "Asia/Qatar"
849
+ },
850
+ {
851
+ code: "RCTP",
852
+ country: "TW",
853
+ ghcnh_id: null,
854
+ icao: "RCTP",
855
+ latitude: 25.0777,
856
+ longitude: 121.2328,
857
+ name: "Taipei Taoyuan",
858
+ tz: "Asia/Taipei"
859
+ },
860
+ {
861
+ code: "RJAA",
862
+ country: "JP",
863
+ ghcnh_id: null,
864
+ icao: "RJAA",
865
+ latitude: 35.7647,
866
+ longitude: 140.3864,
867
+ name: "Tokyo Narita",
868
+ tz: "Asia/Tokyo"
869
+ },
870
+ {
871
+ code: "RJTT",
872
+ country: "JP",
873
+ ghcnh_id: null,
874
+ icao: "RJTT",
875
+ latitude: 35.5522,
876
+ longitude: 139.78,
877
+ name: "Tokyo Haneda",
878
+ tz: "Asia/Tokyo"
879
+ },
880
+ {
881
+ code: "RKSI",
882
+ country: "KR",
883
+ ghcnh_id: null,
884
+ icao: "RKSI",
885
+ latitude: 37.4691,
886
+ longitude: 126.4505,
887
+ name: "Seoul Incheon",
888
+ tz: "Asia/Seoul"
889
+ },
890
+ {
891
+ code: "SAEZ",
892
+ country: "AR",
893
+ ghcnh_id: null,
894
+ icao: "SAEZ",
895
+ latitude: -34.8222,
896
+ longitude: -58.5358,
897
+ name: "Buenos Aires Ezeiza",
898
+ tz: "America/Argentina/Buenos_Aires"
899
+ },
900
+ {
901
+ code: "SBGR",
902
+ country: "BR",
903
+ ghcnh_id: null,
904
+ icao: "SBGR",
905
+ latitude: -23.4356,
906
+ longitude: -46.4731,
907
+ name: "S\xE3o Paulo Guarulhos",
908
+ tz: "America/Sao_Paulo"
909
+ },
910
+ {
911
+ code: "UUEE",
912
+ country: "RU",
913
+ ghcnh_id: null,
914
+ icao: "UUEE",
915
+ latitude: 55.9728,
916
+ longitude: 37.4147,
917
+ name: "Moscow Sheremetyevo",
918
+ tz: "Europe/Moscow"
919
+ },
920
+ {
921
+ code: "VABB",
922
+ country: "IN",
923
+ ghcnh_id: null,
924
+ icao: "VABB",
925
+ latitude: 19.0887,
926
+ longitude: 72.8679,
927
+ name: "Mumbai Chhatrapati Shivaji",
928
+ tz: "Asia/Kolkata"
929
+ },
930
+ {
931
+ code: "VHHH",
932
+ country: "HK",
933
+ ghcnh_id: null,
934
+ icao: "VHHH",
935
+ latitude: 22.308,
936
+ longitude: 113.9185,
937
+ name: "Hong Kong International",
938
+ tz: "Asia/Hong_Kong"
939
+ },
940
+ {
941
+ code: "VIDP",
942
+ country: "IN",
943
+ ghcnh_id: null,
944
+ icao: "VIDP",
945
+ latitude: 28.5562,
946
+ longitude: 77.1,
947
+ name: "Delhi Indira Gandhi",
948
+ tz: "Asia/Kolkata"
949
+ },
950
+ {
951
+ code: "VTBS",
952
+ country: "TH",
953
+ ghcnh_id: null,
954
+ icao: "VTBS",
955
+ latitude: 13.69,
956
+ longitude: 100.7501,
957
+ name: "Bangkok Suvarnabhumi",
958
+ tz: "Asia/Bangkok"
959
+ },
960
+ {
961
+ code: "WSSS",
962
+ country: "SG",
963
+ ghcnh_id: null,
964
+ icao: "WSSS",
965
+ latitude: 1.3644,
966
+ longitude: 103.9915,
967
+ name: "Singapore Changi",
968
+ tz: "Asia/Singapore"
969
+ },
970
+ {
971
+ code: "YBBN",
972
+ country: "AU",
973
+ ghcnh_id: null,
974
+ icao: "YBBN",
975
+ latitude: -27.3842,
976
+ longitude: 153.1175,
977
+ name: "Brisbane",
978
+ tz: "Australia/Brisbane"
979
+ },
980
+ {
981
+ code: "YMML",
982
+ country: "AU",
983
+ ghcnh_id: null,
984
+ icao: "YMML",
985
+ latitude: -37.6733,
986
+ longitude: 144.8433,
987
+ name: "Melbourne Tullamarine",
988
+ tz: "Australia/Melbourne"
989
+ },
990
+ {
991
+ code: "YSSY",
992
+ country: "AU",
993
+ ghcnh_id: null,
994
+ icao: "YSSY",
995
+ latitude: -33.9461,
996
+ longitude: 151.1772,
997
+ name: "Sydney Kingsford Smith",
998
+ tz: "Australia/Sydney"
999
+ },
1000
+ {
1001
+ code: "ZBAA",
1002
+ country: "CN",
1003
+ ghcnh_id: null,
1004
+ icao: "ZBAA",
1005
+ latitude: 40.0801,
1006
+ longitude: 116.5846,
1007
+ name: "Beijing Capital",
1008
+ tz: "Asia/Shanghai"
1009
+ },
1010
+ {
1011
+ code: "ZSPD",
1012
+ country: "CN",
1013
+ ghcnh_id: null,
1014
+ icao: "ZSPD",
1015
+ latitude: 31.1443,
1016
+ longitude: 121.8083,
1017
+ name: "Shanghai Pudong",
1018
+ tz: "Asia/Shanghai"
1019
+ }
1020
+ ];
1021
+ var STATION_BY_CODE = /* @__PURE__ */ new Map([
1022
+ ["ATL", STATIONS[10]],
1023
+ ["AUS", STATIONS[11]],
1024
+ ["BOS", STATIONS[12]],
1025
+ ["DCA", STATIONS[13]],
1026
+ ["DEN", STATIONS[14]],
1027
+ ["DFW", STATIONS[15]],
1028
+ ["EDDB", STATIONS[0]],
1029
+ ["EDDF", STATIONS[1]],
1030
+ ["EDDM", STATIONS[2]],
1031
+ ["EFHK", STATIONS[3]],
1032
+ ["EGKK", STATIONS[4]],
1033
+ ["EGLL", STATIONS[5]],
1034
+ ["EHAM", STATIONS[6]],
1035
+ ["EKCH", STATIONS[7]],
1036
+ ["EPWA", STATIONS[8]],
1037
+ ["ESSA", STATIONS[9]],
1038
+ ["HOU", STATIONS[16]],
1039
+ ["LAS", STATIONS[17]],
1040
+ ["LAX", STATIONS[18]],
1041
+ ["LEBL", STATIONS[30]],
1042
+ ["LEMD", STATIONS[31]],
1043
+ ["LFPB", STATIONS[32]],
1044
+ ["LFPG", STATIONS[33]],
1045
+ ["LFPO", STATIONS[34]],
1046
+ ["LIMC", STATIONS[35]],
1047
+ ["LIRF", STATIONS[36]],
1048
+ ["LOWW", STATIONS[37]],
1049
+ ["LSZH", STATIONS[38]],
1050
+ ["MDW", STATIONS[19]],
1051
+ ["MIA", STATIONS[20]],
1052
+ ["MSP", STATIONS[21]],
1053
+ ["MSY", STATIONS[22]],
1054
+ ["NYC", STATIONS[23]],
1055
+ ["NZAA", STATIONS[39]],
1056
+ ["NZWN", STATIONS[40]],
1057
+ ["OERK", STATIONS[41]],
1058
+ ["OKC", STATIONS[24]],
1059
+ ["OMDB", STATIONS[42]],
1060
+ ["OTHH", STATIONS[43]],
1061
+ ["PHL", STATIONS[25]],
1062
+ ["PHX", STATIONS[26]],
1063
+ ["RCTP", STATIONS[44]],
1064
+ ["RJAA", STATIONS[45]],
1065
+ ["RJTT", STATIONS[46]],
1066
+ ["RKSI", STATIONS[47]],
1067
+ ["SAEZ", STATIONS[48]],
1068
+ ["SAT", STATIONS[27]],
1069
+ ["SBGR", STATIONS[49]],
1070
+ ["SEA", STATIONS[28]],
1071
+ ["SFO", STATIONS[29]],
1072
+ ["UUEE", STATIONS[50]],
1073
+ ["VABB", STATIONS[51]],
1074
+ ["VHHH", STATIONS[52]],
1075
+ ["VIDP", STATIONS[53]],
1076
+ ["VTBS", STATIONS[54]],
1077
+ ["WSSS", STATIONS[55]],
1078
+ ["YBBN", STATIONS[56]],
1079
+ ["YMML", STATIONS[57]],
1080
+ ["YSSY", STATIONS[58]],
1081
+ ["ZBAA", STATIONS[59]],
1082
+ ["ZSPD", STATIONS[60]]
1083
+ ]);
1084
+ var STATION_BY_ICAO = /* @__PURE__ */ new Map([
1085
+ ["EDDB", STATIONS[0]],
1086
+ ["EDDF", STATIONS[1]],
1087
+ ["EDDM", STATIONS[2]],
1088
+ ["EFHK", STATIONS[3]],
1089
+ ["EGKK", STATIONS[4]],
1090
+ ["EGLL", STATIONS[5]],
1091
+ ["EHAM", STATIONS[6]],
1092
+ ["EKCH", STATIONS[7]],
1093
+ ["EPWA", STATIONS[8]],
1094
+ ["ESSA", STATIONS[9]],
1095
+ ["KATL", STATIONS[10]],
1096
+ ["KAUS", STATIONS[11]],
1097
+ ["KBOS", STATIONS[12]],
1098
+ ["KDCA", STATIONS[13]],
1099
+ ["KDEN", STATIONS[14]],
1100
+ ["KDFW", STATIONS[15]],
1101
+ ["KHOU", STATIONS[16]],
1102
+ ["KLAS", STATIONS[17]],
1103
+ ["KLAX", STATIONS[18]],
1104
+ ["KMDW", STATIONS[19]],
1105
+ ["KMIA", STATIONS[20]],
1106
+ ["KMSP", STATIONS[21]],
1107
+ ["KMSY", STATIONS[22]],
1108
+ ["KNYC", STATIONS[23]],
1109
+ ["KOKC", STATIONS[24]],
1110
+ ["KPHL", STATIONS[25]],
1111
+ ["KPHX", STATIONS[26]],
1112
+ ["KSAT", STATIONS[27]],
1113
+ ["KSEA", STATIONS[28]],
1114
+ ["KSFO", STATIONS[29]],
1115
+ ["LEBL", STATIONS[30]],
1116
+ ["LEMD", STATIONS[31]],
1117
+ ["LFPB", STATIONS[32]],
1118
+ ["LFPG", STATIONS[33]],
1119
+ ["LFPO", STATIONS[34]],
1120
+ ["LIMC", STATIONS[35]],
1121
+ ["LIRF", STATIONS[36]],
1122
+ ["LOWW", STATIONS[37]],
1123
+ ["LSZH", STATIONS[38]],
1124
+ ["NZAA", STATIONS[39]],
1125
+ ["NZWN", STATIONS[40]],
1126
+ ["OERK", STATIONS[41]],
1127
+ ["OMDB", STATIONS[42]],
1128
+ ["OTHH", STATIONS[43]],
1129
+ ["RCTP", STATIONS[44]],
1130
+ ["RJAA", STATIONS[45]],
1131
+ ["RJTT", STATIONS[46]],
1132
+ ["RKSI", STATIONS[47]],
1133
+ ["SAEZ", STATIONS[48]],
1134
+ ["SBGR", STATIONS[49]],
1135
+ ["UUEE", STATIONS[50]],
1136
+ ["VABB", STATIONS[51]],
1137
+ ["VHHH", STATIONS[52]],
1138
+ ["VIDP", STATIONS[53]],
1139
+ ["VTBS", STATIONS[54]],
1140
+ ["WSSS", STATIONS[55]],
1141
+ ["YBBN", STATIONS[56]],
1142
+ ["YMML", STATIONS[57]],
1143
+ ["YSSY", STATIONS[58]],
1144
+ ["ZBAA", STATIONS[59]],
1145
+ ["ZSPD", STATIONS[60]]
1146
+ ]);
1147
+
1148
+ // src/snapshot.ts
1149
+ var _STATION_TZ = Object.freeze({
1150
+ // Eastern (UTC-5 standard / UTC-4 DST)
1151
+ NYC: "America/New_York",
1152
+ JFK: "America/New_York",
1153
+ LGA: "America/New_York",
1154
+ EWR: "America/New_York",
1155
+ ATL: "America/New_York",
1156
+ BOS: "America/New_York",
1157
+ PHL: "America/New_York",
1158
+ DCA: "America/New_York",
1159
+ IAD: "America/New_York",
1160
+ BWI: "America/New_York",
1161
+ MIA: "America/New_York",
1162
+ MCO: "America/New_York",
1163
+ TPA: "America/New_York",
1164
+ CLT: "America/New_York",
1165
+ RDU: "America/New_York",
1166
+ CLE: "America/New_York",
1167
+ PIT: "America/New_York",
1168
+ BUF: "America/New_York",
1169
+ DTW: "America/Detroit",
1170
+ IND: "America/Indiana/Indianapolis",
1171
+ CVG: "America/New_York",
1172
+ CMH: "America/New_York",
1173
+ SYR: "America/New_York",
1174
+ ALB: "America/New_York",
1175
+ BTV: "America/New_York",
1176
+ ORF: "America/New_York",
1177
+ RIC: "America/New_York",
1178
+ GSO: "America/New_York",
1179
+ CHS: "America/New_York",
1180
+ SAV: "America/New_York",
1181
+ JAX: "America/New_York",
1182
+ RSW: "America/New_York",
1183
+ PBI: "America/New_York",
1184
+ FLL: "America/New_York",
1185
+ // Central (UTC-6 standard / UTC-5 DST)
1186
+ ORD: "America/Chicago",
1187
+ MDW: "America/Chicago",
1188
+ DFW: "America/Chicago",
1189
+ DAL: "America/Chicago",
1190
+ IAH: "America/Chicago",
1191
+ HOU: "America/Chicago",
1192
+ MSP: "America/Chicago",
1193
+ STL: "America/Chicago",
1194
+ MCI: "America/Chicago",
1195
+ OMA: "America/Chicago",
1196
+ MKE: "America/Chicago",
1197
+ MSY: "America/Chicago",
1198
+ MEM: "America/Chicago",
1199
+ BNA: "America/Chicago",
1200
+ OKC: "America/Chicago",
1201
+ SAT: "America/Chicago",
1202
+ AUS: "America/Chicago",
1203
+ DSM: "America/Chicago",
1204
+ TUL: "America/Chicago",
1205
+ LIT: "America/Chicago",
1206
+ BIR: "America/Chicago",
1207
+ SDF: "America/Chicago",
1208
+ HSV: "America/Chicago",
1209
+ BHM: "America/Chicago",
1210
+ MOB: "America/Chicago",
1211
+ BTR: "America/Chicago",
1212
+ SHV: "America/Chicago",
1213
+ // Mountain (UTC-7 standard / UTC-6 DST)
1214
+ DEN: "America/Denver",
1215
+ SLC: "America/Denver",
1216
+ ABQ: "America/Denver",
1217
+ BOI: "America/Boise",
1218
+ BZN: "America/Denver",
1219
+ GJT: "America/Denver",
1220
+ // Arizona: no DST (UTC-7 always)
1221
+ PHX: "America/Phoenix",
1222
+ TUS: "America/Phoenix",
1223
+ // Pacific (UTC-8 standard / UTC-7 DST)
1224
+ LAX: "America/Los_Angeles",
1225
+ SFO: "America/Los_Angeles",
1226
+ SEA: "America/Los_Angeles",
1227
+ PDX: "America/Los_Angeles",
1228
+ LAS: "America/Los_Angeles",
1229
+ SAN: "America/Los_Angeles",
1230
+ OAK: "America/Los_Angeles",
1231
+ SJC: "America/Los_Angeles",
1232
+ SMF: "America/Los_Angeles",
1233
+ RNO: "America/Los_Angeles",
1234
+ FAT: "America/Los_Angeles",
1235
+ SNA: "America/Los_Angeles",
1236
+ ONT: "America/Los_Angeles",
1237
+ BUR: "America/Los_Angeles",
1238
+ // Alaska (UTC-9 standard / UTC-8 DST)
1239
+ ANC: "America/Anchorage",
1240
+ FAI: "America/Anchorage",
1241
+ JNU: "America/Juneau",
1242
+ // Hawaii (UTC-10, no DST)
1243
+ HNL: "Pacific/Honolulu",
1244
+ OGG: "Pacific/Honolulu",
1245
+ KOA: "Pacific/Honolulu",
1246
+ // International (iter-6 H12): minimal set required to un-skip the
1247
+ // case-5 RJTT year-wrap cache behavior test. Python's
1248
+ // `mostlyright.snapshot._resolve_tz` falls back to the broader STATIONS
1249
+ // registry for intl ICAOs; the TS port hasn't ported that fallback
1250
+ // yet (tracked as TS-W6 — exhaustive intl-station tz coverage). This
1251
+ // entry closes H12 cleanly without pulling the whole STATIONS map in.
1252
+ // ICAO key (RJTT) — international stations have no 3-letter NWS code.
1253
+ // Tokyo Haneda — UTC+9 LST, no DST.
1254
+ RJTT: "Asia/Tokyo"
1255
+ });
1256
+ var _JAN_REF = new Date(Date.UTC(2024, 0, 15, 12, 0, 0));
1257
+ var _OFFSET_CACHE = /* @__PURE__ */ new Map();
1258
+ function _lstOffsetHours(stationTz) {
1259
+ const cached = _OFFSET_CACHE.get(stationTz);
1260
+ if (cached !== void 0) return cached;
1261
+ const fmt = new Intl.DateTimeFormat("en-US", {
1262
+ timeZone: stationTz,
1263
+ hour12: false,
1264
+ year: "numeric",
1265
+ month: "2-digit",
1266
+ day: "2-digit",
1267
+ hour: "2-digit",
1268
+ minute: "2-digit",
1269
+ second: "2-digit"
1270
+ });
1271
+ const parts = fmt.formatToParts(_JAN_REF);
1272
+ const get = (type) => {
1273
+ const part = parts.find((p) => p.type === type);
1274
+ if (!part) {
1275
+ throw new Error(`Intl.DateTimeFormat missing ${type} for tz=${stationTz}`);
1276
+ }
1277
+ return Number(part.value);
1278
+ };
1279
+ const year = get("year");
1280
+ const month = get("month");
1281
+ const day = get("day");
1282
+ let hour = get("hour");
1283
+ const minute = get("minute");
1284
+ const second = get("second");
1285
+ if (hour === 24) hour = 0;
1286
+ const localAsUtc = Date.UTC(year, month - 1, day, hour, minute, second);
1287
+ const offsetMs = localAsUtc - _JAN_REF.getTime();
1288
+ const offsetHours = offsetMs / 36e5;
1289
+ _OFFSET_CACHE.set(stationTz, offsetHours);
1290
+ return offsetHours;
1291
+ }
1292
+
1293
+ // src/internal/cache/skip-rules.ts
1294
+ function _lstOffsetHoursFor(station) {
1295
+ const upper = station.trim().toUpperCase();
1296
+ const byCode = STATION_BY_CODE.get(upper);
1297
+ if (byCode !== void 0) return _lstOffsetHours(byCode.tz);
1298
+ const byIcao = STATION_BY_ICAO.get(upper);
1299
+ if (byIcao !== void 0) return _lstOffsetHours(byIcao.tz);
1300
+ if (upper.length === 4 && upper.startsWith("K")) {
1301
+ const stripped = upper.slice(1);
1302
+ const retry = STATION_BY_CODE.get(stripped);
1303
+ if (retry !== void 0) return _lstOffsetHours(retry.tz);
1304
+ }
1305
+ throw new RangeError(`unknown station: ${JSON.stringify(station)}`);
1306
+ }
1307
+ function _nowLst(station, now = /* @__PURE__ */ new Date()) {
1308
+ const offsetHours = _lstOffsetHoursFor(station);
1309
+ return new Date(now.getTime() + offsetHours * 36e5);
1310
+ }
1311
+ function shouldSkipCacheForCurrentLstMonth(station, year, month, now) {
1312
+ const lst = _nowLst(station, now);
1313
+ return lst.getUTCFullYear() === year && lst.getUTCMonth() + 1 === month;
1314
+ }
1315
+ function shouldSkipCacheForCurrentLstYear(station, year, now) {
1316
+ const lst = _nowLst(station, now);
1317
+ return lst.getUTCFullYear() === year;
1318
+ }
1319
+ function isWritableMonth(year, month, now) {
1320
+ const nowYear = now.getUTCFullYear();
1321
+ const nowMonth = now.getUTCMonth() + 1;
1322
+ if (year < nowYear) return true;
1323
+ if (year > nowYear) return false;
1324
+ return month < nowMonth;
1325
+ }
1326
+ function isWritableYear(year, now) {
1327
+ return year < now.getUTCFullYear();
1328
+ }
1329
+ function isLiveSource(source) {
1330
+ return typeof source === "string" && source.length > 0 && source.endsWith(".live");
1331
+ }
1332
+ function isWithinVolatileWindow(eventDate, archiveAsOf, days = 30) {
1333
+ const e = Date.parse(`${eventDate}T00:00:00Z`);
1334
+ const a = Date.parse(`${archiveAsOf}T00:00:00Z`);
1335
+ if (!Number.isFinite(e) || !Number.isFinite(a)) {
1336
+ throw new RangeError(
1337
+ `invalid YYYY-MM-DD: eventDate=${JSON.stringify(eventDate)} archiveAsOf=${JSON.stringify(archiveAsOf)}`
1338
+ );
1339
+ }
1340
+ const deltaDays = (a - e) / 864e5;
1341
+ return deltaDays >= 0 && deltaDays <= days;
1342
+ }
1343
+
1344
+ // src/internal/cache/keys.ts
1345
+ var MIN_YEAR = 1900;
1346
+ var MAX_YEAR = 2100;
1347
+ var SOURCE_RE = /^[a-z0-9_-]+$/;
1348
+ function cacheKeyForObservations(station, year, month, source) {
1349
+ if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {
1350
+ throw new RangeError(`year out of range: ${year}`);
1351
+ }
1352
+ if (!Number.isInteger(month) || month < 1 || month > 12) {
1353
+ throw new RangeError(`month out of range: ${month}`);
1354
+ }
1355
+ const yyyy = String(year).padStart(4, "0");
1356
+ const mm = String(month).padStart(2, "0");
1357
+ const base = `mostlyright:v1:observations:${station.toUpperCase()}:${yyyy}:${mm}`;
1358
+ if (source === void 0) return base;
1359
+ if (typeof source !== "string" || !SOURCE_RE.test(source)) {
1360
+ throw new RangeError(
1361
+ `source must match ${SOURCE_RE.source} (lowercase alnum / hyphen / underscore); got ${JSON.stringify(source)}`
1362
+ );
1363
+ }
1364
+ return `${base}:${source}`;
1365
+ }
1366
+ function cacheKeyForClimate(station, year) {
1367
+ if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {
1368
+ throw new RangeError(`year out of range: ${year}`);
1369
+ }
1370
+ const yyyy = String(year).padStart(4, "0");
1371
+ return `mostlyright:v1:climate:${station.toUpperCase()}:${yyyy}`;
1372
+ }
1373
+ // Annotate the CommonJS export names for ESM import in node:
1374
+ 0 && (module.exports = {
1375
+ INDEXEDDB_DB_NAME,
1376
+ IndexedDBStore,
1377
+ MemoryStore,
1378
+ cacheKeyForClimate,
1379
+ cacheKeyForObservations,
1380
+ defaultCacheStore,
1381
+ isLiveSource,
1382
+ isWithinVolatileWindow,
1383
+ isWritableMonth,
1384
+ isWritableYear,
1385
+ lockKeyFor,
1386
+ shouldSkipCacheForCurrentLstMonth,
1387
+ shouldSkipCacheForCurrentLstYear
1388
+ });
1389
+ //# sourceMappingURL=index.cjs.map