@mostlyrightmd/core 1.1.3 → 1.4.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.
- package/README.md +1 -1
- package/dist/discovery/index.cjs +471 -169
- package/dist/discovery/index.cjs.map +1 -1
- package/dist/discovery/index.d.cts +39 -11
- package/dist/discovery/index.d.ts +39 -11
- package/dist/discovery/index.mjs +471 -169
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.cjs +618 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +326 -29
- package/dist/index.d.ts +326 -29
- package/dist/index.global.js +610 -173
- package/dist/index.global.js.map +1 -1
- package/dist/index.mjs +610 -173
- package/dist/index.mjs.map +1 -1
- package/dist/internal/cache/index.browser.cjs +501 -163
- package/dist/internal/cache/index.browser.cjs.map +1 -1
- package/dist/internal/cache/index.browser.d.cts +8 -2
- package/dist/internal/cache/index.browser.d.ts +8 -2
- package/dist/internal/cache/index.browser.mjs +10 -5
- package/dist/internal/cache/index.browser.mjs.map +1 -1
- package/dist/internal/cache/index.cjs +503 -161
- package/dist/internal/cache/index.cjs.map +1 -1
- package/dist/internal/cache/index.d.cts +12 -6
- package/dist/internal/cache/index.d.ts +12 -6
- package/dist/internal/cache/index.mjs +12 -3
- package/dist/internal/cache/index.mjs.map +1 -1
- package/dist/internal/{chunk-PKJXHY27.mjs → chunk-QDQSYUFW.mjs} +494 -160
- package/dist/internal/chunk-QDQSYUFW.mjs.map +1 -0
- package/dist/internal/{keys-B7C8C88N.d.cts → versionedCacheStore-DyHDqFIC.d.cts} +23 -1
- package/dist/internal/{keys-B7C8C88N.d.ts → versionedCacheStore-DyHDqFIC.d.ts} +23 -1
- package/dist/preprocessing/index.cjs +150 -0
- package/dist/preprocessing/index.cjs.map +1 -0
- package/dist/preprocessing/index.d.cts +111 -0
- package/dist/preprocessing/index.d.ts +111 -0
- package/dist/preprocessing/index.mjs +121 -0
- package/dist/preprocessing/index.mjs.map +1 -0
- package/dist/temporal/index.cjs +56 -4
- package/dist/temporal/index.cjs.map +1 -1
- package/dist/temporal/index.d.cts +24 -1
- package/dist/temporal/index.d.ts +24 -1
- package/dist/temporal/index.mjs +55 -4
- package/dist/temporal/index.mjs.map +1 -1
- package/dist/validator.cjs +694 -109
- package/dist/validator.cjs.map +1 -1
- package/dist/validator.mjs +694 -109
- package/dist/validator.mjs.map +1 -1
- package/package.json +25 -2
- package/dist/internal/chunk-PKJXHY27.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/internal/cache/fs.ts","../../../src/internal/cache/index.ts","../../../src/internal/cache/types.ts","../../../src/internal/cache/memory.ts","../../../src/internal/cache/indexeddb.ts","../../../src/internal/cache/default.ts","../../../src/data/generated/stations.ts","../../../src/snapshot.ts","../../../src/internal/cache/skip-rules.ts","../../../src/internal/cache/keys.ts"],"sourcesContent":["// FsStore — node:fs/promises + proper-lockfile CacheStore for Node runtimes.\n//\n// Path layout under the configured root:\n//\n// <root>/<sanitized-key>.json\n//\n// Where `<root>` defaults to\n// `$MOSTLYRIGHT_CACHE_DIR ?? $TRADEWINDS_CACHE_DIR (legacy + warn) ??\n// $HOME/.mostlyright/cache-ts` (per TS-CACHE-02 — distinct from Python's\n// `.mostlyright/cache` so the JSON envelopes here can't shadow Python's\n// parquet files). Phase 12 W4 + review-iter2: mirrors the Python back-compat\n// shim semantics — canonical → legacy + DeprecationWarning → default.\n//\n// Atomic write: payload is written to `<path>.tmp` then renamed onto\n// `<path>` (POSIX-atomic; Windows-safe via `fs.rename`).\n//\n// withLock uses proper-lockfile against a `<path>.lock` sidecar so two\n// concurrent writers serialize per-key. The lock is taken with\n// `realpath: false` so we can lock a path whose target may not yet\n// exist.\n//\n// Divergence from Python: the Python cache is parquet + per-month station\n// files keyed by (station, year, month); TS is JSON + per-key files keyed\n// by the caller's opaque string. Station-aware key generation lives in\n// `keys.ts` (plan 03).\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, readdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport * as properLockfile from \"proper-lockfile\";\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\n\n/**\n * Resolve the cache root on each call (not cached at module load) so tests\n * can `vi.stubEnv(\"MOSTLYRIGHT_CACHE_DIR\", ...)` between cases without a\n * module reload.\n *\n * Resolution order (Phase 12 W4 + review-iter2 — mirrors Python shim):\n * 1. `MOSTLYRIGHT_CACHE_DIR` env var (canonical, post-Phase-12).\n * 2. `TRADEWINDS_CACHE_DIR` env var (legacy; emits a one-time deprecation\n * `console.warn`; scheduled for removal in vts-0.3).\n * 3. `~/.mostlyright/cache-ts` (per TS-CACHE-02 — DISTINCT from Python's\n * `~/.mostlyright/cache` so JSON envelopes here can't shadow Python's\n * parquet files).\n */\nlet _legacyCacheDirWarned = false;\n\nexport function defaultFsRoot(): string {\n const canonical = process.env.MOSTLYRIGHT_CACHE_DIR;\n if (canonical !== undefined && canonical.length > 0) return canonical;\n const legacy = process.env.TRADEWINDS_CACHE_DIR;\n if (legacy !== undefined && legacy.length > 0) {\n if (!_legacyCacheDirWarned) {\n console.warn(\n \"TRADEWINDS_CACHE_DIR is deprecated; use MOSTLYRIGHT_CACHE_DIR. \" +\n \"Support will be removed in vts-0.3. \" +\n \"Run: mv ~/.tradewinds ~/.mostlyright\",\n );\n _legacyCacheDirWarned = true;\n }\n return legacy;\n }\n return join(homedir(), \".mostlyright\", \"cache-ts\");\n}\n\n/** Reset the one-time TRADEWINDS_CACHE_DIR deprecation latch — TEST USE ONLY. */\nexport function _resetLegacyCacheDirWarn(): void {\n _legacyCacheDirWarned = false;\n}\n\nexport interface FsStoreOptions {\n /** Override root directory. Defaults to {@link defaultFsRoot}. */\n readonly root?: string;\n}\n\n/**\n * Node-side CacheStore. Each key maps to one JSON file under the root.\n */\nexport class FsStore implements CacheStore {\n readonly #root: string;\n // In-process per-key promise chain. proper-lockfile guarantees\n // cross-process exclusion but its retry-based contention resolution\n // is order-non-deterministic — two in-process callers racing on\n // `lock()` may acquire in either order. Layering an in-process chain\n // ensures strict FIFO for callers within the same Node process (and\n // serves as a cheap fast-path: only one of N in-process callers ever\n // actually contends with proper-lockfile).\n readonly #chain = new Map<string, Promise<unknown>>();\n\n constructor(opts: FsStoreOptions = {}) {\n this.#root = opts.root ?? defaultFsRoot();\n }\n\n /**\n * Path resolver — `key` is sanitized via `encodeURIComponent` so that\n *\n * 1. `:` `/` `\\` cannot escape the root (all percent-encoded), and\n * 2. the key → file mapping is INJECTIVE: distinct keys always map to\n * distinct files.\n *\n * Iter-13 C16 fix: the previous implementation collapsed `:` / `/` / `\\`\n * to the literal substring `\"__\"`, which is a lossy mapping — `\"a:b\"`,\n * `\"a/b\"`, and the literal `\"a__b\"` all hashed to `a__b.json`, so one\n * key's write would silently overwrite (and corrupt subsequent reads of)\n * another key. `encodeURIComponent` is bijective on string inputs and\n * filesystem-safe on every platform we ship (POSIX + Windows): the\n * characters it leaves unescaped (alphanumerics, `-._~!*'()`) are all\n * legal in NTFS, APFS, and ext4 filenames, and `%` is itself legal.\n *\n * BREAKING in v0.1.0: on-disk cache files written by any prior\n * pre-release of this package use the old `__`-replacement scheme and\n * are unreadable after upgrade. This is acceptable for a local-first\n * cache: entries are regenerated on demand from live data, and the\n * cache directory can be safely deleted by the user.\n */\n #pathFor(key: string): string {\n const safe = encodeURIComponent(key);\n return join(this.#root, `${safe}.json`);\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const p = this.#pathFor(key);\n let raw: string;\n try {\n raw = await readFile(p, \"utf8\");\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return null;\n throw e;\n }\n let entry: CacheEntry<T>;\n try {\n entry = JSON.parse(raw) as CacheEntry<T>;\n } catch {\n // Corrupt cache entry — treat as miss; do NOT throw. Caller\n // re-fetches and overwrites.\n return null;\n }\n if (entry.expiresAt !== undefined && Date.now() >= entry.expiresAt) {\n // Lazy-evict: best-effort unlink, ignore failures.\n try {\n await rm(p, { force: true });\n } catch {\n // ignore\n }\n return null;\n }\n return entry.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const p = this.#pathFor(key);\n await mkdir(dirname(p), { recursive: true });\n const entry: CacheEntry<T> =\n opts?.ttlMs !== undefined ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };\n // Codex iter-2 C6: use a UNIQUE temp filename per write. Two concurrent\n // `set(\"same-key\", ...)` calls would otherwise race on the shared\n // `<path>.tmp`: writer A creates `<path>.tmp` and renames it to\n // `<path>`; writer B's subsequent rename then fails with ENOENT\n // because A's rename moved B's-in-progress temp away. With a unique\n // per-write suffix, each writer owns its own temp file; rename-into-\n // place stays atomic on POSIX (last-rename-wins semantics — any of\n // the N concurrent writers' value will be the final cache contents,\n // documented at the test that covers this).\n const tmp = `${p}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, JSON.stringify(entry), \"utf8\");\n await rename(tmp, p);\n } catch (e) {\n // Best-effort cleanup if rename failed (e.g. permissions). Don't\n // let a stale unique-temp file leak.\n try {\n await rm(tmp, { force: true });\n } catch {\n // ignore\n }\n throw e;\n }\n }\n\n async delete(key: string): Promise<void> {\n const p = this.#pathFor(key);\n try {\n await rm(p, { force: true });\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return;\n throw e;\n }\n }\n\n /**\n * Enumerate keys whose stored files exist under the cache root and whose\n * decoded form starts with `prefix`.\n *\n * Returns an empty list if the root directory does not exist (cold cache).\n *\n * TS-W6 Wave 1: used by `availability()` to count observation months and\n * climate years for a station. The file→key mapping is the inverse of\n * `#pathFor` (encodeURIComponent → strip `.json` → decodeURIComponent).\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n let entries: string[];\n try {\n entries = await readdir(this.#root);\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return Object.freeze([]);\n throw e;\n }\n const out: string[] = [];\n for (const name of entries) {\n if (!name.endsWith(\".json\")) continue;\n // Ignore the proper-lockfile lock sidecars and our own in-flight\n // unique-temp files (`<key>.json.<uuid>.tmp`) — they end with `.tmp`\n // not `.json`, so the suffix filter above already excludes them.\n // The lock directories proper-lockfile creates end with `.json.lock`\n // (a directory entry), also excluded by the `.json` suffix check.\n const encoded = name.slice(0, -\".json\".length);\n let decoded: string;\n try {\n decoded = decodeURIComponent(encoded);\n } catch {\n // Defensive: skip files whose names don't decode (manual placements,\n // partial writes, etc).\n continue;\n }\n if (decoded.startsWith(prefix)) {\n out.push(decoded);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const p = this.#pathFor(key);\n // Chain in-process callers FIFO. Cross-process exclusion is layered\n // on top via proper-lockfile inside `run()`.\n const prev = this.#chain.get(key) ?? Promise.resolve();\n const run = async (): Promise<T> => {\n await mkdir(dirname(p), { recursive: true });\n const release = await properLockfile.lock(p, {\n realpath: false,\n retries: { retries: 5, minTimeout: 20, maxTimeout: 200 },\n });\n try {\n return await fn();\n } finally {\n await release();\n }\n };\n const next = prev.then(run, run);\n // Absorber tail so the chain doesn't leak unhandled rejections.\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) this.#chain.delete(key);\n });\n return next;\n }\n}\n","// Barrel for @mostlyrightmd/core/internal/cache.\n//\n// Plan 01: types + MemoryStore + FsStore.\n// Plan 02: IndexedDBStore + defaultCacheStore (runtime auto-detect).\n// Plan 03: skip-rule predicates + key generators.\n//\n// Iter-2 H5: `FsStore` / `defaultFsRoot` / `FsStoreOptions` are NOT\n// re-exported here. tsup hoisted them into a sibling chunk that the\n// subbundle top-level-imported (transitively pulling `node:fs/promises`,\n// `node:os`, `node:path`, `proper-lockfile`, `node:crypto` into MV3\n// service-worker bundles), even though `defaultCacheStore` itself uses\n// a dynamic `import('./fs.js')` per iter-1 H3. Node-only consumers\n// (FsStore unit tests + downstream Node users) MUST import from the\n// dedicated subpath `@mostlyrightmd/core/internal/cache/fs`.\n\nexport type { CacheStore, CacheSetOptions, CacheEntry } from \"./types.js\";\nexport { lockKeyFor } from \"./types.js\";\nexport { MemoryStore } from \"./memory.js\";\nexport { IndexedDBStore, DB_NAME as INDEXEDDB_DB_NAME } from \"./indexeddb.js\";\nexport type { IndexedDBStoreOptions } from \"./indexeddb.js\";\nexport { defaultCacheStore } from \"./default.js\";\nexport {\n shouldSkipCacheForCurrentLstMonth,\n shouldSkipCacheForCurrentLstYear,\n isLiveSource,\n isWithinVolatileWindow,\n isWritableMonth,\n isWritableYear,\n} from \"./skip-rules.js\";\nexport { cacheKeyForObservations, cacheKeyForClimate } from \"./keys.js\";\n","// CacheStore — pluggable key/value contract for the @mostlyrightmd/core cache\n// layer. Three concrete implementations land in TS-W3:\n// - MemoryStore — Map-backed, no persistence (Cloudflare Workers default).\n// - FsStore — node:fs/promises + proper-lockfile (Node default).\n// - IndexedDBStore — idb + Web Locks API (browser; plan 02).\n//\n// `defaultCacheStore()` (plan 02) auto-detects at runtime per\n// TS-SDK-DESIGN §5.4.\n\n/** Cache entry envelope with optional TTL. */\nexport interface CacheEntry<T = unknown> {\n readonly value: T;\n /** Epoch ms when the entry expires. Absence = no expiry. */\n readonly expiresAt?: number;\n}\n\n/** Optional setters for cache writes. */\nexport interface CacheSetOptions {\n /** Time-to-live in milliseconds. Implementations may honor or ignore. */\n readonly ttlMs?: number;\n}\n\n/**\n * Pluggable key/value cache contract used throughout the SDK.\n *\n * All methods are async — concrete implementations may resolve immediately\n * (MemoryStore) or do I/O (FsStore / IndexedDBStore).\n *\n * Semantic contract:\n * - `get<T>(key)` returns the stored value or `null` on miss. NEVER throws\n * on miss.\n * - `set<T>(key, value, opts?)` overwrites. ttlMs is implementation-honored\n * (MemoryStore + IndexedDBStore honor it; FsStore ignores in v0.1).\n * - `delete(key)` is a no-op on miss; returns void.\n * - `withLock<T>(key, fn)` runs `fn` under a key-scoped exclusive lock and\n * releases on settle (resolve OR throw). Nested calls to the same key\n * serialize; calls to different keys MAY run in parallel.\n */\nexport interface CacheStore {\n get<T = unknown>(key: string): Promise<T | null>;\n set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void>;\n delete(key: string): Promise<void>;\n withLock<T>(key: string, fn: () => Promise<T>): Promise<T>;\n}\n\n/**\n * Canonical lock identifier for a given cache key.\n *\n * Pure — same key → same lock id. Used by FsStore (proper-lockfile sidecar)\n * and IndexedDBStore (`navigator.locks.request(...)` name).\n */\nexport function lockKeyFor(key: string): string {\n return `mostlyright:cache:lock:${key}`;\n}\n","// MemoryStore — Map-backed CacheStore for ephemeral runtimes (Cloudflare\n// Workers, jsdom test envs without persistence). NOT shared across\n// processes — per-instance state.\n//\n// Value isolation via `structuredClone`: callers can mutate stored objects\n// after `.set()` without leaking changes back. Honors ttlMs with lazy\n// eviction at `.get()` time.\n//\n// withLock uses a per-key promise chain — pending lock-acquisitions queue\n// behind the current holder and run in FIFO order on settle.\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\n\n/**\n * In-memory cache. Per-instance state; two MemoryStore instances do NOT\n * share state.\n *\n * - Values cloned via `structuredClone` so post-`set` mutation can't leak.\n * - ttlMs honored with lazy eviction on `get`.\n * - withLock serializes nested calls via a per-key promise chain.\n */\nexport class MemoryStore implements CacheStore {\n readonly #entries = new Map<string, CacheEntry<unknown>>();\n readonly #chain = new Map<string, Promise<unknown>>();\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const e = this.#entries.get(key);\n if (e === undefined) return null;\n if (e.expiresAt !== undefined && Date.now() >= e.expiresAt) {\n this.#entries.delete(key);\n return null;\n }\n // Defensive clone on read too — callers can't mutate stored value via\n // the returned reference either.\n return structuredClone(e.value) as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const cloned = structuredClone(value);\n const entry: CacheEntry<unknown> =\n opts?.ttlMs !== undefined\n ? { value: cloned, expiresAt: Date.now() + opts.ttlMs }\n : { value: cloned };\n this.#entries.set(key, entry);\n }\n\n async delete(key: string): Promise<void> {\n this.#entries.delete(key);\n }\n\n /**\n * Enumerate live (non-expired) keys with the given prefix.\n *\n * TS-W6 Wave 1: `availability()` uses this to count cached observation\n * months and climate years per station. Expired entries are evicted as a\n * side effect (same lazy-eviction policy as `.get`).\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n const now = Date.now();\n const out: string[] = [];\n for (const [key, entry] of this.#entries) {\n if (entry.expiresAt !== undefined && now >= entry.expiresAt) {\n this.#entries.delete(key);\n continue;\n }\n if (key.startsWith(prefix)) {\n out.push(key);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.#chain.get(key) ?? Promise.resolve();\n // Chain `fn` after `prev` regardless of whether `prev` resolved or\n // rejected — the lock holder's failure shouldn't poison the queue.\n const next = prev.then(\n () => fn(),\n () => fn(),\n );\n // Store an absorber as the new tail so a later prev.then() handles\n // both branches without producing an unhandled-rejection warning. The\n // caller still receives the original `next` promise (including any\n // rejection from `fn`).\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) {\n this.#chain.delete(key);\n }\n });\n return next;\n }\n}\n","// IndexedDBStore — idb + Web Locks API CacheStore for browsers.\n//\n// Per TS-CACHE-02, the canonical IndexedDB DB name is `mostlyright-cache-v1`.\n// Object store: `entries`. Schema: key = string, value = CacheEntry<T>.\n//\n// withLock prefers `navigator.locks.request(name, ...)` (Web Locks API,\n// Chrome 69+, Firefox 96+, Safari 15.4+; all production-browser baselines).\n// When `navigator.locks` is unavailable (jsdom default), falls back to the\n// same per-key in-process promise chain used by MemoryStore / FsStore —\n// edge runtimes (Workers without web-locks polyfills) get the in-process\n// guarantee at least.\n//\n// ttlMs is honored via lazy eviction at `get` time (matches FsStore).\n\nimport { type IDBPDatabase, openDB } from \"idb\";\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\nimport { lockKeyFor } from \"./types.js\";\n\n/** Canonical DB name. Re-exported via the cache barrel. */\nexport const DB_NAME = \"mostlyright-cache-v1\";\n\nconst STORE_NAME = \"entries\";\nconst SCHEMA_VERSION = 1;\n\nexport interface IndexedDBStoreOptions {\n /** Override the DB name. Tests pass unique values per case so they don't pollute each other. */\n readonly dbName?: string;\n}\n\ninterface WebLocksApi {\n request: <T>(\n name: string,\n options: { mode: \"exclusive\" },\n fn: () => Promise<T> | T,\n ) => Promise<T>;\n}\n\nfunction getWebLocks(): WebLocksApi | null {\n if (typeof navigator === \"undefined\") return null;\n const nav = navigator as unknown as { locks?: WebLocksApi };\n return nav.locks ?? null;\n}\n\n/**\n * Browser CacheStore backed by IndexedDB (via idb) + Web Locks API.\n *\n * When `navigator.locks` is unavailable (jsdom, edge runtimes without\n * Web Locks), falls back to a per-key in-process promise chain.\n */\nexport class IndexedDBStore implements CacheStore {\n readonly #dbName: string;\n readonly #dbPromise: Promise<IDBPDatabase>;\n readonly #chain = new Map<string, Promise<unknown>>();\n\n constructor(opts: IndexedDBStoreOptions = {}) {\n this.#dbName = opts.dbName ?? DB_NAME;\n this.#dbPromise = openDB(this.#dbName, SCHEMA_VERSION, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME);\n }\n },\n });\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const db = await this.#dbPromise;\n const entry = (await db.get(STORE_NAME, key)) as CacheEntry<T> | undefined;\n if (entry === undefined) return null;\n if (entry.expiresAt !== undefined && Date.now() >= entry.expiresAt) {\n // Lazy-evict — best-effort; ignore failures.\n try {\n await db.delete(STORE_NAME, key);\n } catch {\n // ignore\n }\n return null;\n }\n return entry.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const db = await this.#dbPromise;\n const entry: CacheEntry<T> =\n opts?.ttlMs !== undefined ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };\n await db.put(STORE_NAME, entry, key);\n }\n\n async delete(key: string): Promise<void> {\n const db = await this.#dbPromise;\n await db.delete(STORE_NAME, key);\n }\n\n /**\n * Enumerate keys with the given prefix using IndexedDB's bounded range\n * query. Live keys only — expired entries are lazy-evicted on read by\n * `get()`, so a stale-but-not-yet-evicted entry can appear here; callers\n * who care about expiration should `get()` to confirm.\n *\n * TS-W6 Wave 1: `availability()` uses this to count observation months and\n * climate years for a station.\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n const db = await this.#dbPromise;\n // Build an inclusive lower bound and an exclusive upper bound using the\n // next-codepoint trick. IndexedDB's IDBKeyRange handles string ordering\n // by UTF-16 code units, which matches JS string comparison; we use the\n // Unicode max-codepoint as the upper sentinel so any key starting with\n // `prefix` lands inside the range.\n const range = IDBKeyRange.bound(prefix, `${prefix}`, false, false);\n const keys = (await db.getAllKeys(STORE_NAME, range)) as IDBValidKey[];\n const out: string[] = [];\n for (const k of keys) {\n if (typeof k === \"string\" && k.startsWith(prefix)) {\n out.push(k);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const locks = getWebLocks();\n if (locks !== null) {\n // Web Locks API — production-browser path. Cross-tab safe.\n return locks.request<T>(lockKeyFor(key), { mode: \"exclusive\" }, () => fn());\n }\n // Fallback for jsdom / edge runtimes without navigator.locks: in-process\n // per-key promise chain (FIFO; matches MemoryStore semantics).\n const prev = this.#chain.get(key) ?? Promise.resolve();\n const next = prev.then(\n () => fn(),\n () => fn(),\n );\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) this.#chain.delete(key);\n });\n return next;\n }\n}\n","// defaultCacheStore — runtime auto-detection per TS-SDK-DESIGN §5.4.\n//\n// Priority order (fixed, deterministic):\n// 1. `typeof indexedDB !== \"undefined\"` → IndexedDBStore (browser)\n// 2. `typeof process !== \"undefined\" && process.versions?.node` → FsStore\n// (Node, loaded via `await import('./fs.js')`)\n// 3. else → MemoryStore (Workers / edge / unknown)\n//\n// Iter-1 H3: `FsStore` is NO longer statically imported here. It\n// top-level-imports `node:fs/promises`, `node:os`, `node:path`, and\n// `proper-lockfile`, which would pull all four into any MV3 / browser /\n// edge bundle that touches `@mostlyrightmd/core/internal/cache` — even via\n// the IndexedDB code path. The dynamic `await import('./fs.js')` behind\n// a `process.versions?.node` runtime feature-detect ensures bundlers\n// can statically prove the FsStore subgraph is unreachable from the\n// browser entry, eliminating the Node-only-deps edge.\n//\n// Function is async because dynamic import returns a Promise. Callers\n// (research() and friends) already operate inside async code paths.\n\nimport { IndexedDBStore } from \"./indexeddb.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport type { CacheStore } from \"./types.js\";\n\n/**\n * Auto-detect the best CacheStore for the current runtime.\n *\n * Returns a NEW instance per call.\n *\n * @returns a Promise resolving to a fresh CacheStore. The Node-only\n * `FsStore` is loaded via dynamic import behind a runtime feature\n * detect, so browser / MV3 / edge bundles never pull `node:fs/promises`\n * et al. (iter-1 H3 fix).\n */\nexport async function defaultCacheStore(): Promise<CacheStore> {\n if (typeof indexedDB !== \"undefined\") return new IndexedDBStore();\n if (\n typeof process !== \"undefined\" &&\n typeof process.versions === \"object\" &&\n process.versions !== null &&\n typeof process.versions.node === \"string\"\n ) {\n // Dynamic import keeps `./fs.js` (and its `node:fs/promises`,\n // `node:os`, `node:path`, `proper-lockfile` chain) out of the\n // browser bundle. Bundlers that support code-splitting will emit\n // a separate chunk; bundlers targeting `browser` resolution skip\n // the chunk entirely when the feature-detect short-circuits.\n const { FsStore } = await import(\"./fs.js\");\n return new FsStore();\n }\n return new MemoryStore();\n}\n","// AUTO-GENERATED by @mostlyrightmd/codegen from schemas/stations.json.\n// DO NOT EDIT — regenerate with: pnpm codegen\n// Last manifest SHA recorded in schemas/EXPORT_MANIFEST.json\n\nexport interface StationInfo {\n code: string | null;\n ghcnh_id: string | null;\n icao: string;\n name: string | null;\n tz: string;\n latitude: number | null;\n longitude: number | null;\n country: string | null;\n}\n\nexport const STATIONS: ReadonlyArray<StationInfo> = [\n {\n code: \"EDDB\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDB\",\n latitude: 52.3667,\n longitude: 13.5033,\n name: \"Berlin Brandenburg\",\n tz: \"Europe/Berlin\",\n },\n {\n code: \"EDDF\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDF\",\n latitude: 50.0379,\n longitude: 8.5622,\n name: \"Frankfurt am Main\",\n tz: \"Europe/Berlin\",\n },\n {\n code: \"EDDM\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDM\",\n latitude: 48.3538,\n longitude: 11.7861,\n name: \"Munich Franz Josef Strauss\",\n tz: \"Europe/Berlin\",\n },\n {\n code: \"EFHK\",\n country: \"FI\",\n ghcnh_id: null,\n icao: \"EFHK\",\n latitude: 60.3172,\n longitude: 24.9633,\n name: \"Helsinki-Vantaa\",\n tz: \"Europe/Helsinki\",\n },\n {\n code: \"EGKK\",\n country: \"GB\",\n ghcnh_id: null,\n icao: \"EGKK\",\n latitude: 51.1481,\n longitude: -0.1903,\n name: \"London Gatwick\",\n tz: \"Europe/London\",\n },\n {\n code: \"EGLL\",\n country: \"GB\",\n ghcnh_id: null,\n icao: \"EGLL\",\n latitude: 51.4706,\n longitude: -0.4619,\n name: \"London Heathrow\",\n tz: \"Europe/London\",\n },\n {\n code: \"EHAM\",\n country: \"NL\",\n ghcnh_id: null,\n icao: \"EHAM\",\n latitude: 52.3086,\n longitude: 4.7639,\n name: \"Amsterdam Schiphol\",\n tz: \"Europe/Amsterdam\",\n },\n {\n code: \"EKCH\",\n country: \"DK\",\n ghcnh_id: null,\n icao: \"EKCH\",\n latitude: 55.6181,\n longitude: 12.6561,\n name: \"Copenhagen Kastrup\",\n tz: \"Europe/Copenhagen\",\n },\n {\n code: \"EPWA\",\n country: \"PL\",\n ghcnh_id: null,\n icao: \"EPWA\",\n latitude: 52.1657,\n longitude: 20.9671,\n name: \"Warsaw Chopin\",\n tz: \"Europe/Warsaw\",\n },\n {\n code: \"ESSA\",\n country: \"SE\",\n ghcnh_id: null,\n icao: \"ESSA\",\n latitude: 59.6519,\n longitude: 17.9186,\n name: \"Stockholm Arlanda\",\n tz: \"Europe/Stockholm\",\n },\n {\n code: \"ATL\",\n country: \"US\",\n ghcnh_id: \"USW00013874\",\n icao: \"KATL\",\n latitude: 33.6407,\n longitude: -84.4277,\n name: \"Hartsfield-Jackson Atlanta International\",\n tz: \"America/New_York\",\n },\n {\n code: \"AUS\",\n country: \"US\",\n ghcnh_id: \"USW00013904\",\n icao: \"KAUS\",\n latitude: 30.1975,\n longitude: -97.6664,\n name: \"Austin-Bergstrom International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"BOS\",\n country: \"US\",\n ghcnh_id: \"USW00014739\",\n icao: \"KBOS\",\n latitude: 42.3656,\n longitude: -71.0096,\n name: \"Boston Logan International\",\n tz: \"America/New_York\",\n },\n {\n code: \"DCA\",\n country: \"US\",\n ghcnh_id: \"USW00013743\",\n icao: \"KDCA\",\n latitude: 38.8512,\n longitude: -77.0402,\n name: \"Washington Reagan National\",\n tz: \"America/New_York\",\n },\n {\n code: \"DEN\",\n country: \"US\",\n ghcnh_id: \"USW00003017\",\n icao: \"KDEN\",\n latitude: 39.8561,\n longitude: -104.6737,\n name: \"Denver International\",\n tz: \"America/Denver\",\n },\n {\n code: \"DFW\",\n country: \"US\",\n ghcnh_id: \"USW00003927\",\n icao: \"KDFW\",\n latitude: 32.8998,\n longitude: -97.0403,\n name: \"Dallas-Fort Worth International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"HOU\",\n country: \"US\",\n ghcnh_id: \"USW00012918\",\n icao: \"KHOU\",\n latitude: 29.6454,\n longitude: -95.2789,\n name: \"Houston Hobby\",\n tz: \"America/Chicago\",\n },\n {\n code: \"LAS\",\n country: \"US\",\n ghcnh_id: \"USW00023169\",\n icao: \"KLAS\",\n latitude: 36.084,\n longitude: -115.1537,\n name: \"Harry Reid (McCarran) International\",\n tz: \"America/Los_Angeles\",\n },\n {\n code: \"LAX\",\n country: \"US\",\n ghcnh_id: \"USW00023174\",\n icao: \"KLAX\",\n latitude: 33.9425,\n longitude: -118.4081,\n name: \"Los Angeles International\",\n tz: \"America/Los_Angeles\",\n },\n {\n code: \"MDW\",\n country: \"US\",\n ghcnh_id: \"USW00014819\",\n icao: \"KMDW\",\n latitude: 41.7868,\n longitude: -87.7522,\n name: \"Chicago Midway International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"MIA\",\n country: \"US\",\n ghcnh_id: \"USW00012839\",\n icao: \"KMIA\",\n latitude: 25.7959,\n longitude: -80.287,\n name: \"Miami International\",\n tz: \"America/New_York\",\n },\n {\n code: \"MSP\",\n country: \"US\",\n ghcnh_id: \"USW00014922\",\n icao: \"KMSP\",\n latitude: 44.8848,\n longitude: -93.2223,\n name: \"Minneapolis-St Paul International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"MSY\",\n country: \"US\",\n ghcnh_id: \"USW00012916\",\n icao: \"KMSY\",\n latitude: 29.9934,\n longitude: -90.258,\n name: \"New Orleans Louis Armstrong International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"NYC\",\n country: \"US\",\n ghcnh_id: \"USW00094728\",\n icao: \"KNYC\",\n latitude: 40.7789,\n longitude: -73.9692,\n name: \"Central Park, New York\",\n tz: \"America/New_York\",\n },\n {\n code: \"OKC\",\n country: \"US\",\n ghcnh_id: \"USW00013967\",\n icao: \"KOKC\",\n latitude: 35.3931,\n longitude: -97.6007,\n name: \"Oklahoma City Will Rogers World\",\n tz: \"America/Chicago\",\n },\n {\n code: \"PHL\",\n country: \"US\",\n ghcnh_id: \"USW00013739\",\n icao: \"KPHL\",\n latitude: 39.8721,\n longitude: -75.2411,\n name: \"Philadelphia International\",\n tz: \"America/New_York\",\n },\n {\n code: \"PHX\",\n country: \"US\",\n ghcnh_id: \"USW00023183\",\n icao: \"KPHX\",\n latitude: 33.4373,\n longitude: -112.0078,\n name: \"Phoenix Sky Harbor International\",\n tz: \"America/Phoenix\",\n },\n {\n code: \"SAT\",\n country: \"US\",\n ghcnh_id: \"USW00012921\",\n icao: \"KSAT\",\n latitude: 29.5337,\n longitude: -98.4698,\n name: \"San Antonio International\",\n tz: \"America/Chicago\",\n },\n {\n code: \"SEA\",\n country: \"US\",\n ghcnh_id: \"USW00024233\",\n icao: \"KSEA\",\n latitude: 47.4502,\n longitude: -122.3088,\n name: \"Seattle-Tacoma International\",\n tz: \"America/Los_Angeles\",\n },\n {\n code: \"SFO\",\n country: \"US\",\n ghcnh_id: \"USW00023234\",\n icao: \"KSFO\",\n latitude: 37.6213,\n longitude: -122.379,\n name: \"San Francisco International\",\n tz: \"America/Los_Angeles\",\n },\n {\n code: \"LEBL\",\n country: \"ES\",\n ghcnh_id: null,\n icao: \"LEBL\",\n latitude: 41.2974,\n longitude: 2.0833,\n name: \"Barcelona El Prat\",\n tz: \"Europe/Madrid\",\n },\n {\n code: \"LEMD\",\n country: \"ES\",\n ghcnh_id: null,\n icao: \"LEMD\",\n latitude: 40.4719,\n longitude: -3.5626,\n name: \"Madrid Barajas\",\n tz: \"Europe/Madrid\",\n },\n {\n code: \"LFPB\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPB\",\n latitude: 48.9694,\n longitude: 2.4414,\n name: \"Paris Le Bourget\",\n tz: \"Europe/Paris\",\n },\n {\n code: \"LFPG\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPG\",\n latitude: 49.0097,\n longitude: 2.5479,\n name: \"Paris Charles de Gaulle\",\n tz: \"Europe/Paris\",\n },\n {\n code: \"LFPO\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPO\",\n latitude: 48.7233,\n longitude: 2.3794,\n name: \"Paris Orly\",\n tz: \"Europe/Paris\",\n },\n {\n code: \"LIMC\",\n country: \"IT\",\n ghcnh_id: null,\n icao: \"LIMC\",\n latitude: 45.6306,\n longitude: 8.7281,\n name: \"Milan Malpensa\",\n tz: \"Europe/Rome\",\n },\n {\n code: \"LIRF\",\n country: \"IT\",\n ghcnh_id: null,\n icao: \"LIRF\",\n latitude: 41.8003,\n longitude: 12.2389,\n name: \"Rome Fiumicino\",\n tz: \"Europe/Rome\",\n },\n {\n code: \"LOWW\",\n country: \"AT\",\n ghcnh_id: null,\n icao: \"LOWW\",\n latitude: 48.1103,\n longitude: 16.5697,\n name: \"Vienna International\",\n tz: \"Europe/Vienna\",\n },\n {\n code: \"LSZH\",\n country: \"CH\",\n ghcnh_id: null,\n icao: \"LSZH\",\n latitude: 47.4647,\n longitude: 8.5492,\n name: \"Zurich\",\n tz: \"Europe/Zurich\",\n },\n {\n code: \"NZAA\",\n country: \"NZ\",\n ghcnh_id: null,\n icao: \"NZAA\",\n latitude: -37.0081,\n longitude: 174.7917,\n name: \"Auckland\",\n tz: \"Pacific/Auckland\",\n },\n {\n code: \"NZWN\",\n country: \"NZ\",\n ghcnh_id: null,\n icao: \"NZWN\",\n latitude: -41.3272,\n longitude: 174.8053,\n name: \"Wellington\",\n tz: \"Pacific/Auckland\",\n },\n {\n code: \"OERK\",\n country: \"SA\",\n ghcnh_id: null,\n icao: \"OERK\",\n latitude: 24.9576,\n longitude: 46.6988,\n name: \"Riyadh King Khalid International\",\n tz: \"Asia/Riyadh\",\n },\n {\n code: \"OMDB\",\n country: \"AE\",\n ghcnh_id: null,\n icao: \"OMDB\",\n latitude: 25.2532,\n longitude: 55.3657,\n name: \"Dubai International\",\n tz: \"Asia/Dubai\",\n },\n {\n code: \"OTHH\",\n country: \"QA\",\n ghcnh_id: null,\n icao: \"OTHH\",\n latitude: 25.2731,\n longitude: 51.608,\n name: \"Doha Hamad International\",\n tz: \"Asia/Qatar\",\n },\n {\n code: \"RCTP\",\n country: \"TW\",\n ghcnh_id: null,\n icao: \"RCTP\",\n latitude: 25.0777,\n longitude: 121.2328,\n name: \"Taipei Taoyuan\",\n tz: \"Asia/Taipei\",\n },\n {\n code: \"RJAA\",\n country: \"JP\",\n ghcnh_id: null,\n icao: \"RJAA\",\n latitude: 35.7647,\n longitude: 140.3864,\n name: \"Tokyo Narita\",\n tz: \"Asia/Tokyo\",\n },\n {\n code: \"RJTT\",\n country: \"JP\",\n ghcnh_id: null,\n icao: \"RJTT\",\n latitude: 35.5522,\n longitude: 139.78,\n name: \"Tokyo Haneda\",\n tz: \"Asia/Tokyo\",\n },\n {\n code: \"RKSI\",\n country: \"KR\",\n ghcnh_id: null,\n icao: \"RKSI\",\n latitude: 37.4691,\n longitude: 126.4505,\n name: \"Seoul Incheon\",\n tz: \"Asia/Seoul\",\n },\n {\n code: \"SAEZ\",\n country: \"AR\",\n ghcnh_id: null,\n icao: \"SAEZ\",\n latitude: -34.8222,\n longitude: -58.5358,\n name: \"Buenos Aires Ezeiza\",\n tz: \"America/Argentina/Buenos_Aires\",\n },\n {\n code: \"SBGR\",\n country: \"BR\",\n ghcnh_id: null,\n icao: \"SBGR\",\n latitude: -23.4356,\n longitude: -46.4731,\n name: \"São Paulo Guarulhos\",\n tz: \"America/Sao_Paulo\",\n },\n {\n code: \"UUEE\",\n country: \"RU\",\n ghcnh_id: null,\n icao: \"UUEE\",\n latitude: 55.9728,\n longitude: 37.4147,\n name: \"Moscow Sheremetyevo\",\n tz: \"Europe/Moscow\",\n },\n {\n code: \"VABB\",\n country: \"IN\",\n ghcnh_id: null,\n icao: \"VABB\",\n latitude: 19.0887,\n longitude: 72.8679,\n name: \"Mumbai Chhatrapati Shivaji\",\n tz: \"Asia/Kolkata\",\n },\n {\n code: \"VHHH\",\n country: \"HK\",\n ghcnh_id: null,\n icao: \"VHHH\",\n latitude: 22.308,\n longitude: 113.9185,\n name: \"Hong Kong International\",\n tz: \"Asia/Hong_Kong\",\n },\n {\n code: \"VIDP\",\n country: \"IN\",\n ghcnh_id: null,\n icao: \"VIDP\",\n latitude: 28.5562,\n longitude: 77.1,\n name: \"Delhi Indira Gandhi\",\n tz: \"Asia/Kolkata\",\n },\n {\n code: \"VTBS\",\n country: \"TH\",\n ghcnh_id: null,\n icao: \"VTBS\",\n latitude: 13.69,\n longitude: 100.7501,\n name: \"Bangkok Suvarnabhumi\",\n tz: \"Asia/Bangkok\",\n },\n {\n code: \"WSSS\",\n country: \"SG\",\n ghcnh_id: null,\n icao: \"WSSS\",\n latitude: 1.3644,\n longitude: 103.9915,\n name: \"Singapore Changi\",\n tz: \"Asia/Singapore\",\n },\n {\n code: \"YBBN\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YBBN\",\n latitude: -27.3842,\n longitude: 153.1175,\n name: \"Brisbane\",\n tz: \"Australia/Brisbane\",\n },\n {\n code: \"YMML\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YMML\",\n latitude: -37.6733,\n longitude: 144.8433,\n name: \"Melbourne Tullamarine\",\n tz: \"Australia/Melbourne\",\n },\n {\n code: \"YSSY\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YSSY\",\n latitude: -33.9461,\n longitude: 151.1772,\n name: \"Sydney Kingsford Smith\",\n tz: \"Australia/Sydney\",\n },\n {\n code: \"ZBAA\",\n country: \"CN\",\n ghcnh_id: null,\n icao: \"ZBAA\",\n latitude: 40.0801,\n longitude: 116.5846,\n name: \"Beijing Capital\",\n tz: \"Asia/Shanghai\",\n },\n {\n code: \"ZSPD\",\n country: \"CN\",\n ghcnh_id: null,\n icao: \"ZSPD\",\n latitude: 31.1443,\n longitude: 121.8083,\n name: \"Shanghai Pudong\",\n tz: \"Asia/Shanghai\",\n },\n] as const;\n\nexport const STATION_BY_CODE: ReadonlyMap<string, StationInfo> = new Map<string, StationInfo>([\n [\"ATL\", STATIONS[10]!],\n [\"AUS\", STATIONS[11]!],\n [\"BOS\", STATIONS[12]!],\n [\"DCA\", STATIONS[13]!],\n [\"DEN\", STATIONS[14]!],\n [\"DFW\", STATIONS[15]!],\n [\"EDDB\", STATIONS[0]!],\n [\"EDDF\", STATIONS[1]!],\n [\"EDDM\", STATIONS[2]!],\n [\"EFHK\", STATIONS[3]!],\n [\"EGKK\", STATIONS[4]!],\n [\"EGLL\", STATIONS[5]!],\n [\"EHAM\", STATIONS[6]!],\n [\"EKCH\", STATIONS[7]!],\n [\"EPWA\", STATIONS[8]!],\n [\"ESSA\", STATIONS[9]!],\n [\"HOU\", STATIONS[16]!],\n [\"LAS\", STATIONS[17]!],\n [\"LAX\", STATIONS[18]!],\n [\"LEBL\", STATIONS[30]!],\n [\"LEMD\", STATIONS[31]!],\n [\"LFPB\", STATIONS[32]!],\n [\"LFPG\", STATIONS[33]!],\n [\"LFPO\", STATIONS[34]!],\n [\"LIMC\", STATIONS[35]!],\n [\"LIRF\", STATIONS[36]!],\n [\"LOWW\", STATIONS[37]!],\n [\"LSZH\", STATIONS[38]!],\n [\"MDW\", STATIONS[19]!],\n [\"MIA\", STATIONS[20]!],\n [\"MSP\", STATIONS[21]!],\n [\"MSY\", STATIONS[22]!],\n [\"NYC\", STATIONS[23]!],\n [\"NZAA\", STATIONS[39]!],\n [\"NZWN\", STATIONS[40]!],\n [\"OERK\", STATIONS[41]!],\n [\"OKC\", STATIONS[24]!],\n [\"OMDB\", STATIONS[42]!],\n [\"OTHH\", STATIONS[43]!],\n [\"PHL\", STATIONS[25]!],\n [\"PHX\", STATIONS[26]!],\n [\"RCTP\", STATIONS[44]!],\n [\"RJAA\", STATIONS[45]!],\n [\"RJTT\", STATIONS[46]!],\n [\"RKSI\", STATIONS[47]!],\n [\"SAEZ\", STATIONS[48]!],\n [\"SAT\", STATIONS[27]!],\n [\"SBGR\", STATIONS[49]!],\n [\"SEA\", STATIONS[28]!],\n [\"SFO\", STATIONS[29]!],\n [\"UUEE\", STATIONS[50]!],\n [\"VABB\", STATIONS[51]!],\n [\"VHHH\", STATIONS[52]!],\n [\"VIDP\", STATIONS[53]!],\n [\"VTBS\", STATIONS[54]!],\n [\"WSSS\", STATIONS[55]!],\n [\"YBBN\", STATIONS[56]!],\n [\"YMML\", STATIONS[57]!],\n [\"YSSY\", STATIONS[58]!],\n [\"ZBAA\", STATIONS[59]!],\n [\"ZSPD\", STATIONS[60]!],\n]);\n\nexport const STATION_BY_ICAO: ReadonlyMap<string, StationInfo> = new Map<string, StationInfo>([\n [\"EDDB\", STATIONS[0]!],\n [\"EDDF\", STATIONS[1]!],\n [\"EDDM\", STATIONS[2]!],\n [\"EFHK\", STATIONS[3]!],\n [\"EGKK\", STATIONS[4]!],\n [\"EGLL\", STATIONS[5]!],\n [\"EHAM\", STATIONS[6]!],\n [\"EKCH\", STATIONS[7]!],\n [\"EPWA\", STATIONS[8]!],\n [\"ESSA\", STATIONS[9]!],\n [\"KATL\", STATIONS[10]!],\n [\"KAUS\", STATIONS[11]!],\n [\"KBOS\", STATIONS[12]!],\n [\"KDCA\", STATIONS[13]!],\n [\"KDEN\", STATIONS[14]!],\n [\"KDFW\", STATIONS[15]!],\n [\"KHOU\", STATIONS[16]!],\n [\"KLAS\", STATIONS[17]!],\n [\"KLAX\", STATIONS[18]!],\n [\"KMDW\", STATIONS[19]!],\n [\"KMIA\", STATIONS[20]!],\n [\"KMSP\", STATIONS[21]!],\n [\"KMSY\", STATIONS[22]!],\n [\"KNYC\", STATIONS[23]!],\n [\"KOKC\", STATIONS[24]!],\n [\"KPHL\", STATIONS[25]!],\n [\"KPHX\", STATIONS[26]!],\n [\"KSAT\", STATIONS[27]!],\n [\"KSEA\", STATIONS[28]!],\n [\"KSFO\", STATIONS[29]!],\n [\"LEBL\", STATIONS[30]!],\n [\"LEMD\", STATIONS[31]!],\n [\"LFPB\", STATIONS[32]!],\n [\"LFPG\", STATIONS[33]!],\n [\"LFPO\", STATIONS[34]!],\n [\"LIMC\", STATIONS[35]!],\n [\"LIRF\", STATIONS[36]!],\n [\"LOWW\", STATIONS[37]!],\n [\"LSZH\", STATIONS[38]!],\n [\"NZAA\", STATIONS[39]!],\n [\"NZWN\", STATIONS[40]!],\n [\"OERK\", STATIONS[41]!],\n [\"OMDB\", STATIONS[42]!],\n [\"OTHH\", STATIONS[43]!],\n [\"RCTP\", STATIONS[44]!],\n [\"RJAA\", STATIONS[45]!],\n [\"RJTT\", STATIONS[46]!],\n [\"RKSI\", STATIONS[47]!],\n [\"SAEZ\", STATIONS[48]!],\n [\"SBGR\", STATIONS[49]!],\n [\"UUEE\", STATIONS[50]!],\n [\"VABB\", STATIONS[51]!],\n [\"VHHH\", STATIONS[52]!],\n [\"VIDP\", STATIONS[53]!],\n [\"VTBS\", STATIONS[54]!],\n [\"WSSS\", STATIONS[55]!],\n [\"YBBN\", STATIONS[56]!],\n [\"YMML\", STATIONS[57]!],\n [\"YSSY\", STATIONS[58]!],\n [\"ZBAA\", STATIONS[59]!],\n [\"ZSPD\", STATIONS[60]!],\n]);\n","// Snapshot math — settlement-window and market-close arithmetic.\n//\n// Ported from `packages/core/src/mostlyright/snapshot.py` and\n// `packages/core/src/mostlyright/_internal/_pairs.py:market_close_utc`.\n//\n// Key concepts:\n// - LOCAL STANDARD TIME (LST): station's standard UTC offset, DST ignored.\n// Kalshi NHIGH/NLOW contracts define the settlement window in LST.\n// - Settlement window: midnight-midnight LST for a given date.\n// During US daylight saving the clock window is 1:00 AM–1:00 AM next day\n// (EDT), but the UTC bounds are the same year-round.\n// - CLI publication delay: NWS issues the overnight final CLI ~04:00–10:00\n// UTC the day after observation. Default: 10 h after midnight LST.\n\n// ---------------------------------------------------------------------------\n// Station → IANA timezone database\n// ---------------------------------------------------------------------------\n//\n// Used to extract the LOCAL STANDARD TIME UTC offset via a January reference\n// moment. Ported from `mostlyright.snapshot._STATION_TZ`.\n\nexport const _STATION_TZ: Readonly<Record<string, string>> = Object.freeze({\n // Eastern (UTC-5 standard / UTC-4 DST)\n NYC: \"America/New_York\",\n JFK: \"America/New_York\",\n LGA: \"America/New_York\",\n EWR: \"America/New_York\",\n ATL: \"America/New_York\",\n BOS: \"America/New_York\",\n PHL: \"America/New_York\",\n DCA: \"America/New_York\",\n IAD: \"America/New_York\",\n BWI: \"America/New_York\",\n MIA: \"America/New_York\",\n MCO: \"America/New_York\",\n TPA: \"America/New_York\",\n CLT: \"America/New_York\",\n RDU: \"America/New_York\",\n CLE: \"America/New_York\",\n PIT: \"America/New_York\",\n BUF: \"America/New_York\",\n DTW: \"America/Detroit\",\n IND: \"America/Indiana/Indianapolis\",\n CVG: \"America/New_York\",\n CMH: \"America/New_York\",\n SYR: \"America/New_York\",\n ALB: \"America/New_York\",\n BTV: \"America/New_York\",\n ORF: \"America/New_York\",\n RIC: \"America/New_York\",\n GSO: \"America/New_York\",\n CHS: \"America/New_York\",\n SAV: \"America/New_York\",\n JAX: \"America/New_York\",\n RSW: \"America/New_York\",\n PBI: \"America/New_York\",\n FLL: \"America/New_York\",\n // Central (UTC-6 standard / UTC-5 DST)\n ORD: \"America/Chicago\",\n MDW: \"America/Chicago\",\n DFW: \"America/Chicago\",\n DAL: \"America/Chicago\",\n IAH: \"America/Chicago\",\n HOU: \"America/Chicago\",\n MSP: \"America/Chicago\",\n STL: \"America/Chicago\",\n MCI: \"America/Chicago\",\n OMA: \"America/Chicago\",\n MKE: \"America/Chicago\",\n MSY: \"America/Chicago\",\n MEM: \"America/Chicago\",\n BNA: \"America/Chicago\",\n OKC: \"America/Chicago\",\n SAT: \"America/Chicago\",\n AUS: \"America/Chicago\",\n DSM: \"America/Chicago\",\n TUL: \"America/Chicago\",\n LIT: \"America/Chicago\",\n BIR: \"America/Chicago\",\n SDF: \"America/Chicago\",\n HSV: \"America/Chicago\",\n BHM: \"America/Chicago\",\n MOB: \"America/Chicago\",\n BTR: \"America/Chicago\",\n SHV: \"America/Chicago\",\n // Mountain (UTC-7 standard / UTC-6 DST)\n DEN: \"America/Denver\",\n SLC: \"America/Denver\",\n ABQ: \"America/Denver\",\n BOI: \"America/Boise\",\n BZN: \"America/Denver\",\n GJT: \"America/Denver\",\n // Arizona: no DST (UTC-7 always)\n PHX: \"America/Phoenix\",\n TUS: \"America/Phoenix\",\n // Pacific (UTC-8 standard / UTC-7 DST)\n LAX: \"America/Los_Angeles\",\n SFO: \"America/Los_Angeles\",\n SEA: \"America/Los_Angeles\",\n PDX: \"America/Los_Angeles\",\n LAS: \"America/Los_Angeles\",\n SAN: \"America/Los_Angeles\",\n OAK: \"America/Los_Angeles\",\n SJC: \"America/Los_Angeles\",\n SMF: \"America/Los_Angeles\",\n RNO: \"America/Los_Angeles\",\n FAT: \"America/Los_Angeles\",\n SNA: \"America/Los_Angeles\",\n ONT: \"America/Los_Angeles\",\n BUR: \"America/Los_Angeles\",\n // Alaska (UTC-9 standard / UTC-8 DST)\n ANC: \"America/Anchorage\",\n FAI: \"America/Anchorage\",\n JNU: \"America/Juneau\",\n // Hawaii (UTC-10, no DST)\n HNL: \"Pacific/Honolulu\",\n OGG: \"Pacific/Honolulu\",\n KOA: \"Pacific/Honolulu\",\n // International (iter-6 H12): minimal set required to un-skip the\n // case-5 RJTT year-wrap cache behavior test. Python's\n // `mostlyright.snapshot._resolve_tz` falls back to the broader STATIONS\n // registry for intl ICAOs; the TS port hasn't ported that fallback\n // yet (tracked as TS-W6 — exhaustive intl-station tz coverage). This\n // entry closes H12 cleanly without pulling the whole STATIONS map in.\n // ICAO key (RJTT) — international stations have no 3-letter NWS code.\n // Tokyo Haneda — UTC+9 LST, no DST.\n RJTT: \"Asia/Tokyo\",\n});\n\n/** Reference UTC moment in January (no DST in Northern Hemisphere US). */\nexport const _JAN_REF = new Date(Date.UTC(2024, 0, 15, 12, 0, 0));\n\n/** NWS CLI typical publication delay: 10 h after midnight LST. */\nexport const _CLI_PUBLICATION_DELAY_HOURS = 10.0;\n\n/** Kalshi market typical close time (LST). */\nexport const _MARKET_CLOSE_HOUR_LST = 16;\nexport const _MARKET_CLOSE_MINUTE_LST = 30;\n\n// ---------------------------------------------------------------------------\n// LST offset extraction\n// ---------------------------------------------------------------------------\n\nconst _OFFSET_CACHE = new Map<string, number>();\n\n/**\n * Return the LOCAL STANDARD TIME UTC offset (in hours) for an IANA tz,\n * sampled from January 15 2024 12:00 UTC so the result is never affected\n * by DST in the Northern Hemisphere.\n *\n * Implementation: format `_JAN_REF` in the target tz via Intl.DateTimeFormat\n * and diff against the UTC formatted view to recover the offset.\n */\nexport function _lstOffsetHours(stationTz: string): number {\n const cached = _OFFSET_CACHE.get(stationTz);\n if (cached !== undefined) return cached;\n\n // We compute: localComponents(stationTz, _JAN_REF) − utcComponents(_JAN_REF).\n // The difference gives the tz offset in (hours). Negative for west of UTC.\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: stationTz,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n const parts = fmt.formatToParts(_JAN_REF);\n const get = (type: string): number => {\n const part = parts.find((p) => p.type === type);\n if (!part) {\n throw new Error(`Intl.DateTimeFormat missing ${type} for tz=${stationTz}`);\n }\n return Number(part.value);\n };\n\n const year = get(\"year\");\n const month = get(\"month\");\n const day = get(\"day\");\n let hour = get(\"hour\");\n const minute = get(\"minute\");\n const second = get(\"second\");\n // Some locales return hour \"24\" instead of \"00\" for midnight; normalize.\n if (hour === 24) hour = 0;\n\n // Compute the timezone's wall-clock for _JAN_REF treated as UTC.\n const localAsUtc = Date.UTC(year, month - 1, day, hour, minute, second);\n const offsetMs = localAsUtc - _JAN_REF.getTime();\n const offsetHours = offsetMs / 3_600_000;\n _OFFSET_CACHE.set(stationTz, offsetHours);\n return offsetHours;\n}\n\n// ---------------------------------------------------------------------------\n// Station code normalization + tz lookup\n// ---------------------------------------------------------------------------\n\nfunction _stationCodeNormalized(station: string): string {\n const s = station.trim().toUpperCase();\n if (s.length === 4 && s.startsWith(\"K\")) {\n return s.substring(1);\n }\n return s;\n}\n\n/**\n * Resolve a station code (NWS 3-letter, ICAO 4-letter) to an IANA tz string.\n * Honors `tzOverride` first, then the built-in `_STATION_TZ` map.\n * Throws if no tz can be resolved.\n */\nexport function _resolveStationTz(station: string, tzOverride?: string): string {\n if (tzOverride) return tzOverride;\n const code = _stationCodeNormalized(station);\n const tz = _STATION_TZ[code];\n if (tz) return tz;\n throw new Error(\n `Unknown station timezone: ${JSON.stringify(code)}. Add it to _STATION_TZ or pass tzOverride=\"America/...\".`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// as_of parsing\n// ---------------------------------------------------------------------------\n\nfunction _parseAsOf(asOf: Date | string): Date {\n if (asOf instanceof Date) {\n if (Number.isNaN(asOf.getTime())) {\n throw new Error(\"Invalid Date passed as asOf\");\n }\n return asOf;\n }\n let s = asOf.trim();\n // Python: bare ISO without tz → assume UTC.\n if (s.endsWith(\"Z\")) {\n // Date.parse handles \"Z\" natively.\n } else if (!/[+-]\\d{2}:?\\d{2}$/.test(s)) {\n // No timezone suffix — treat as UTC.\n s = `${s}Z`;\n }\n const ms = Date.parse(s);\n if (!Number.isFinite(ms)) {\n throw new Error(`Invalid as_of string: ${JSON.stringify(asOf)}`);\n }\n return new Date(ms);\n}\n\n// ---------------------------------------------------------------------------\n// Public surface\n// ---------------------------------------------------------------------------\n\nfunction _pad2(n: number): string {\n return n < 10 ? `0${n}` : `${n}`;\n}\n\nfunction _isoDate(year: number, month: number, day: number): string {\n return `${year}-${_pad2(month)}-${_pad2(day)}`;\n}\n\n/**\n * Return the Kalshi settlement date (YYYY-MM-DD LST) for a UTC moment.\n *\n * Kalshi NHIGH/NLOW contracts cover midnight–midnight LOCAL STANDARD TIME.\n * DST is ignored: the window is always fixed to the standard UTC offset.\n */\nexport function settlementDateFor(\n asOf: Date | string,\n station: string,\n tzOverride?: string,\n): string {\n const utcDt = _parseAsOf(asOf);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n // offsetHours is negative for US stations → lstMs < utcMs.\n const lstMs = utcDt.getTime() + offsetHours * 3_600_000;\n const lst = new Date(lstMs);\n // Use getUTC* because we already shifted the epoch by the LST offset.\n return _isoDate(lst.getUTCFullYear(), lst.getUTCMonth() + 1, lst.getUTCDate());\n}\n\n/**\n * Return UTC start/end of the Kalshi settlement window for a date.\n * The window is midnight-midnight LST, expressed in UTC.\n */\nexport function settlementWindowUtc(\n dateStr: string,\n station: string,\n tzOverride?: string,\n): [Date, Date] {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for settlement window: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n // midnight LST = 00:00 LST = (00:00 UTC) − offset (offset is negative)\n // Example: UTC-5 → midnight LST = 05:00 UTC.\n const midnightLstAsUtcMs = Date.UTC(year, month - 1, day, 0, 0, 0);\n const startMs = midnightLstAsUtcMs - offsetHours * 3_600_000;\n const start = new Date(startMs);\n const end = new Date(startMs + 24 * 3_600_000);\n return [start, end];\n}\n\n/**\n * Return the UTC time at which the NWS CLI for a date is expected to be\n * available. Default delay is 10 h after midnight LST on the next day.\n */\nexport function cliAvailableAt(\n dateStr: string,\n station: string,\n delayHours: number = _CLI_PUBLICATION_DELAY_HOURS,\n tzOverride?: string,\n): Date {\n const [, windowEnd] = settlementWindowUtc(dateStr, station, tzOverride);\n return new Date(windowEnd.getTime() + delayHours * 3_600_000);\n}\n\n/**\n * Return the UTC time of the Kalshi market close for a settlement date.\n * Kalshi NHIGH/NLOW markets close at 4:30 PM LST on the day of settlement.\n */\nexport function marketCloseUtc(dateStr: string, station: string, tzOverride?: string): Date {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for market close: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n const marketCloseAsUtcMs = Date.UTC(\n year,\n month - 1,\n day,\n _MARKET_CLOSE_HOUR_LST,\n _MARKET_CLOSE_MINUTE_LST,\n 0,\n );\n return new Date(marketCloseAsUtcMs - offsetHours * 3_600_000);\n}\n","// Cache-skip rule predicates — pure functions over inputs.\n//\n// Mirrors `packages/weather/src/mostlyright/weather/cache.py`:\n// - `_is_current_lst_month` / `_is_current_lst_year`\n// - `_is_live_source`\n//\n// Plus one TS-NEW addition required by TS-CACHE-02:\n// - `isWithinVolatileWindow` (30-day volatile-window check for archive\n// endpoints). Python's `cache.py` predates this rule; back-porting to\n// Python is tracked as a CROSS-SDK-SYNC parity ticket.\n//\n// All functions accept an optional `now: Date` test seam — production\n// callers pass `new Date()` once at the call site (plan 06).\n\nimport { STATION_BY_CODE, STATION_BY_ICAO } from \"../../data/generated/stations.js\";\nimport { _lstOffsetHours } from \"../../snapshot.js\";\n\n/** Resolve a station identifier (3-letter code OR 4-letter ICAO) to LST offset hours. */\nfunction _lstOffsetHoursFor(station: string): number {\n const upper = station.trim().toUpperCase();\n const byCode = STATION_BY_CODE.get(upper);\n if (byCode !== undefined) return _lstOffsetHours(byCode.tz);\n const byIcao = STATION_BY_ICAO.get(upper);\n if (byIcao !== undefined) return _lstOffsetHours(byIcao.tz);\n if (upper.length === 4 && upper.startsWith(\"K\")) {\n const stripped = upper.slice(1);\n const retry = STATION_BY_CODE.get(stripped);\n if (retry !== undefined) return _lstOffsetHours(retry.tz);\n }\n throw new RangeError(`unknown station: ${JSON.stringify(station)}`);\n}\n\n/**\n * Compute the station's current LST wall-clock as a UTC Date offset by the\n * LST hour shift. Use `getUTC*` to read fields (we already shifted the epoch).\n */\nfunction _nowLst(station: string, now: Date = new Date()): Date {\n const offsetHours = _lstOffsetHoursFor(station);\n return new Date(now.getTime() + offsetHours * 3_600_000);\n}\n\n/**\n * True iff `(year, month)` is the current LST month for `station`.\n *\n * Mirrors Python `_is_current_lst_month`. The current month is mutable\n * (observations still arriving) — caching it would serve stale data.\n */\nexport function shouldSkipCacheForCurrentLstMonth(\n station: string,\n year: number,\n month: number,\n now?: Date,\n): boolean {\n const lst = _nowLst(station, now);\n return lst.getUTCFullYear() === year && lst.getUTCMonth() + 1 === month;\n}\n\n/**\n * True iff `year` is the current LST year for `station`. Annual analog of\n * the monthly variant — gates the climate cache.\n */\nexport function shouldSkipCacheForCurrentLstYear(\n station: string,\n year: number,\n now?: Date,\n): boolean {\n const lst = _nowLst(station, now);\n return lst.getUTCFullYear() === year;\n}\n\n/**\n * True iff `(year, month)` is a **strictly past** UTC month relative to\n * `now` — i.e. cacheable on the strictest possible temporal axis.\n *\n * iter-12 C14: `shouldSkipCacheForCurrentLstMonth` and `isMonthVolatile`\n * (lives in `meta/src/research.ts`) only catch the *current* LST month\n * and the immediate post-month volatile tail. Both predicates return\n * false for months that lie in the FUTURE relative to `now`, or for the\n * current UTC month when the station's LST is still in the prior UTC\n * month (negative tz offsets near UTC midnight). An empty / partial\n * fetch for such a month would be persisted and later served as\n * \"complete.\" `isWritableMonth` is a stricter additional gate: it\n * requires the (year, month) to be lexicographically less than the\n * UTC current month, so neither future months nor the partial current\n * UTC month are ever cacheable — regardless of any station's LST.\n *\n * Mirrors Python `cache.py:_is_current_lst_month`'s implicit invariant\n * (Python paths use parquet-on-disk which can't be written for future\n * dates because the cache root never spawns those years). TS callers\n * MUST gate cache reads AND writes on this predicate before applying\n * the LST / volatile-window gates.\n */\nexport function isWritableMonth(year: number, month: number, now: Date): boolean {\n const nowYear = now.getUTCFullYear();\n const nowMonth = now.getUTCMonth() + 1; // 1-12\n if (year < nowYear) return true;\n if (year > nowYear) return false;\n return month < nowMonth;\n}\n\n/**\n * True iff `year` is a **strictly past** UTC year relative to `now` —\n * the annual analog of `isWritableMonth`.\n *\n * iter-12 C15: `shouldSkipCacheForCurrentLstYear` only catches the\n * current LST year. It misses (a) future years, which would silently\n * cache empty/incomplete data, and (b) the UTC Jan-1 boundary window\n * where the station's LST is still in the prior calendar year (negative\n * tz offsets) but the UTC year has already rolled over — without this\n * gate the new UTC year, which is mutable, could be written. Stricter\n * additional gate: require `year < now.getUTCFullYear()`. TS callers\n * MUST gate cache reads AND writes on this predicate before applying\n * the LST / volatile-window gates.\n */\nexport function isWritableYear(year: number, now: Date): boolean {\n return year < now.getUTCFullYear();\n}\n\n/**\n * True iff `source` ends with `.live`.\n *\n * Mirrors Python `_is_live_source` byte-equivalently — accepts null /\n * undefined / empty (returns false in all three cases).\n */\nexport function isLiveSource(source: string | null | undefined): boolean {\n return typeof source === \"string\" && source.length > 0 && source.endsWith(\".live\");\n}\n\n/**\n * **TS-NEW** addition per TS-CACHE-02: archive endpoints within `days` days\n * of `archiveAsOf` are treated as volatile (some sources amend their\n * published data for ~30 days post-event). NOT a Python port today — file\n * a CROSS-SDK-SYNC parity ticket if Python adopts it.\n *\n * Returns true iff `eventDate` falls within `[archiveAsOf - days, archiveAsOf]`\n * (inclusive at both endpoints — an event exactly `days` days before\n * `archiveAsOf` is still volatile and MUST be re-fetched).\n *\n * Events AFTER `archiveAsOf` are never volatile by this rule (deltaDays < 0).\n */\nexport function isWithinVolatileWindow(eventDate: string, archiveAsOf: string, days = 30): boolean {\n const e = Date.parse(`${eventDate}T00:00:00Z`);\n const a = Date.parse(`${archiveAsOf}T00:00:00Z`);\n if (!Number.isFinite(e) || !Number.isFinite(a)) {\n throw new RangeError(\n `invalid YYYY-MM-DD: eventDate=${JSON.stringify(eventDate)} archiveAsOf=${JSON.stringify(archiveAsOf)}`,\n );\n }\n const deltaDays = (a - e) / 86_400_000;\n return deltaDays >= 0 && deltaDays <= days;\n}\n","// Cache-key generators — pure functions producing the canonical key strings\n// `cacheKeyForObservations(station, year, month)` and\n// `cacheKeyForClimate(station, year)`. Matches the Python file-path zero-\n// padded month/year scheme (Python: `01.parquet`, TS: `:01`).\n//\n// Station is upper-cased but NOT validated here (validation is the\n// orchestrator's job; this module stays pure).\n//\n// iter-7 H13 / H14: an optional `source` segment was added to\n// `cacheKeyForObservations` so the multi-source observations cache\n// (IEM ASOS + GHCNh) does not collide on the same `(station, year, month)`\n// triplet. Python writes one parquet per month containing the merged\n// observations from all sources; the TS orchestrator caches per-source\n// chunks pre-merge (because the fetchers are independent paths) and\n// disambiguates with the source segment. Omitting `source` preserves the\n// legacy 3-arg key shape — useful for tests that don't care about source\n// (sentinel preloads, fixture replays) and matches the canonical\n// Python-parity contract for callers that have already pre-merged sources.\n\nconst MIN_YEAR = 1900;\nconst MAX_YEAR = 2100;\nconst SOURCE_RE = /^[a-z0-9_-]+$/;\n\n/**\n * Build the canonical observations cache key.\n *\n * Examples:\n * `cacheKeyForObservations(\"KNYC\", 2025, 1)` →\n * `\"mostlyright:v1:observations:KNYC:2025:01\"`.\n * `cacheKeyForObservations(\"KNYC\", 2025, 1, \"iem\")` →\n * `\"mostlyright:v1:observations:KNYC:2025:01:iem\"`.\n *\n * The `source` segment (optional, lowercase alphanumeric / hyphen /\n * underscore) namespaces per-source pre-merge chunks so IEM ASOS and\n * GHCNh writes for the same `(station, year, month)` do not collide.\n * Omit for back-compat (sentinel preloads, fixture replays).\n */\nexport function cacheKeyForObservations(\n station: string,\n year: number,\n month: number,\n source?: string,\n): string {\n if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {\n throw new RangeError(`year out of range: ${year}`);\n }\n if (!Number.isInteger(month) || month < 1 || month > 12) {\n throw new RangeError(`month out of range: ${month}`);\n }\n const yyyy = String(year).padStart(4, \"0\");\n const mm = String(month).padStart(2, \"0\");\n const base = `mostlyright:v1:observations:${station.toUpperCase()}:${yyyy}:${mm}`;\n if (source === undefined) return base;\n if (typeof source !== \"string\" || !SOURCE_RE.test(source)) {\n throw new RangeError(\n `source must match ${SOURCE_RE.source} (lowercase alnum / hyphen / underscore); got ${JSON.stringify(source)}`,\n );\n }\n return `${base}:${source}`;\n}\n\n/**\n * Build the canonical climate cache key (annual).\n *\n * Example: `cacheKeyForClimate(\"KNYC\", 2025)` →\n * `\"mostlyright:v1:climate:KNYC:2025\"`.\n */\nexport function cacheKeyForClimate(station: string, year: number): string {\n if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {\n throw new RangeError(`year out of range: ${year}`);\n }\n const yyyy = String(year).padStart(4, \"0\");\n return `mostlyright:v1:climate:${station.toUpperCase()}:${yyyy}`;\n}\n"],"mappings":"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AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AACxB,CAAC;AAEM,IAAM,kBAAoD,oBAAI,IAAyB;AAAA,EAC5F,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,C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","names":["FsStore"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/internal/cache/fs.ts","../../../src/internal/cache/index.ts","../../../src/internal/cache/types.ts","../../../src/internal/cache/memory.ts","../../../src/internal/cache/indexeddb.ts","../../../src/internal/cache/versionedCacheStore.ts","../../../src/internal/cache/default.ts","../../../src/data/generated/stations.ts","../../../src/snapshot.ts","../../../src/internal/cache/skip-rules.ts","../../../src/internal/cache/keys.ts"],"sourcesContent":["// FsStore — node:fs/promises + proper-lockfile CacheStore for Node runtimes.\n//\n// Path layout under the configured root:\n//\n// <root>/<sanitized-key>.json\n//\n// Where `<root>` defaults to\n// `$MOSTLYRIGHT_CACHE_DIR ?? $TRADEWINDS_CACHE_DIR (legacy + warn) ??\n// $HOME/.mostlyright/cache-ts` (per TS-CACHE-02 — distinct from Python's\n// `.mostlyright/cache` so the JSON envelopes here can't shadow Python's\n// parquet files). Phase 12 W4 + review-iter2: mirrors the Python back-compat\n// shim semantics — canonical → legacy + DeprecationWarning → default.\n//\n// Atomic write: payload is written to `<path>.tmp` then renamed onto\n// `<path>` (POSIX-atomic; Windows-safe via `fs.rename`).\n//\n// withLock uses proper-lockfile against a `<path>.lock` sidecar so two\n// concurrent writers serialize per-key. The lock is taken with\n// `realpath: false` so we can lock a path whose target may not yet\n// exist.\n//\n// Divergence from Python: the Python cache is parquet + per-month station\n// files keyed by (station, year, month); TS is JSON + per-key files keyed\n// by the caller's opaque string. Station-aware key generation lives in\n// `keys.ts` (plan 03).\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, readdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport * as properLockfile from \"proper-lockfile\";\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\n\n/**\n * Resolve the cache root on each call (not cached at module load) so tests\n * can `vi.stubEnv(\"MOSTLYRIGHT_CACHE_DIR\", ...)` between cases without a\n * module reload.\n *\n * Resolution order (Phase 12 W4 + review-iter2 — mirrors Python shim):\n * 1. `MOSTLYRIGHT_CACHE_DIR` env var (canonical, post-Phase-12).\n * 2. `TRADEWINDS_CACHE_DIR` env var (legacy; emits a one-time deprecation\n * `console.warn`; scheduled for removal in vts-0.3).\n * 3. `~/.mostlyright/cache-ts` (per TS-CACHE-02 — DISTINCT from Python's\n * `~/.mostlyright/cache` so JSON envelopes here can't shadow Python's\n * parquet files).\n */\nlet _legacyCacheDirWarned = false;\n\nexport function defaultFsRoot(): string {\n const canonical = process.env.MOSTLYRIGHT_CACHE_DIR;\n if (canonical !== undefined && canonical.length > 0) return canonical;\n const legacy = process.env.TRADEWINDS_CACHE_DIR;\n if (legacy !== undefined && legacy.length > 0) {\n if (!_legacyCacheDirWarned) {\n console.warn(\n \"TRADEWINDS_CACHE_DIR is deprecated; use MOSTLYRIGHT_CACHE_DIR. \" +\n \"Support will be removed in vts-0.3. \" +\n \"Run: mv ~/.tradewinds ~/.mostlyright\",\n );\n _legacyCacheDirWarned = true;\n }\n return legacy;\n }\n return join(homedir(), \".mostlyright\", \"cache-ts\");\n}\n\n/** Reset the one-time TRADEWINDS_CACHE_DIR deprecation latch — TEST USE ONLY. */\nexport function _resetLegacyCacheDirWarn(): void {\n _legacyCacheDirWarned = false;\n}\n\nexport interface FsStoreOptions {\n /** Override root directory. Defaults to {@link defaultFsRoot}. */\n readonly root?: string;\n}\n\n/**\n * Node-side CacheStore. Each key maps to one JSON file under the root.\n */\nexport class FsStore implements CacheStore {\n readonly #root: string;\n // In-process per-key promise chain. proper-lockfile guarantees\n // cross-process exclusion but its retry-based contention resolution\n // is order-non-deterministic — two in-process callers racing on\n // `lock()` may acquire in either order. Layering an in-process chain\n // ensures strict FIFO for callers within the same Node process (and\n // serves as a cheap fast-path: only one of N in-process callers ever\n // actually contends with proper-lockfile).\n readonly #chain = new Map<string, Promise<unknown>>();\n\n constructor(opts: FsStoreOptions = {}) {\n this.#root = opts.root ?? defaultFsRoot();\n }\n\n /**\n * Path resolver — `key` is sanitized via `encodeURIComponent` so that\n *\n * 1. `:` `/` `\\` cannot escape the root (all percent-encoded), and\n * 2. the key → file mapping is INJECTIVE: distinct keys always map to\n * distinct files.\n *\n * Iter-13 C16 fix: the previous implementation collapsed `:` / `/` / `\\`\n * to the literal substring `\"__\"`, which is a lossy mapping — `\"a:b\"`,\n * `\"a/b\"`, and the literal `\"a__b\"` all hashed to `a__b.json`, so one\n * key's write would silently overwrite (and corrupt subsequent reads of)\n * another key. `encodeURIComponent` is bijective on string inputs and\n * filesystem-safe on every platform we ship (POSIX + Windows): the\n * characters it leaves unescaped (alphanumerics, `-._~!*'()`) are all\n * legal in NTFS, APFS, and ext4 filenames, and `%` is itself legal.\n *\n * BREAKING in v0.1.0: on-disk cache files written by any prior\n * pre-release of this package use the old `__`-replacement scheme and\n * are unreadable after upgrade. This is acceptable for a local-first\n * cache: entries are regenerated on demand from live data, and the\n * cache directory can be safely deleted by the user.\n */\n #pathFor(key: string): string {\n const safe = encodeURIComponent(key);\n return join(this.#root, `${safe}.json`);\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const p = this.#pathFor(key);\n let raw: string;\n try {\n raw = await readFile(p, \"utf8\");\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return null;\n throw e;\n }\n let entry: CacheEntry<T>;\n try {\n entry = JSON.parse(raw) as CacheEntry<T>;\n } catch {\n // Corrupt cache entry — treat as miss; do NOT throw. Caller\n // re-fetches and overwrites.\n return null;\n }\n if (entry.expiresAt !== undefined && Date.now() >= entry.expiresAt) {\n // Lazy-evict: best-effort unlink, ignore failures.\n try {\n await rm(p, { force: true });\n } catch {\n // ignore\n }\n return null;\n }\n return entry.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const p = this.#pathFor(key);\n await mkdir(dirname(p), { recursive: true });\n const entry: CacheEntry<T> =\n opts?.ttlMs !== undefined ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };\n // Codex iter-2 C6: use a UNIQUE temp filename per write. Two concurrent\n // `set(\"same-key\", ...)` calls would otherwise race on the shared\n // `<path>.tmp`: writer A creates `<path>.tmp` and renames it to\n // `<path>`; writer B's subsequent rename then fails with ENOENT\n // because A's rename moved B's-in-progress temp away. With a unique\n // per-write suffix, each writer owns its own temp file; rename-into-\n // place stays atomic on POSIX (last-rename-wins semantics — any of\n // the N concurrent writers' value will be the final cache contents,\n // documented at the test that covers this).\n const tmp = `${p}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, JSON.stringify(entry), \"utf8\");\n await rename(tmp, p);\n } catch (e) {\n // Best-effort cleanup if rename failed (e.g. permissions). Don't\n // let a stale unique-temp file leak.\n try {\n await rm(tmp, { force: true });\n } catch {\n // ignore\n }\n throw e;\n }\n }\n\n async delete(key: string): Promise<void> {\n const p = this.#pathFor(key);\n try {\n await rm(p, { force: true });\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return;\n throw e;\n }\n }\n\n /**\n * Enumerate keys whose stored files exist under the cache root and whose\n * decoded form starts with `prefix`.\n *\n * Returns an empty list if the root directory does not exist (cold cache).\n *\n * TS-W6 Wave 1: used by `availability()` to count observation months and\n * climate years for a station. The file→key mapping is the inverse of\n * `#pathFor` (encodeURIComponent → strip `.json` → decodeURIComponent).\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n let entries: string[];\n try {\n entries = await readdir(this.#root);\n } catch (e: unknown) {\n const code = (e as { code?: string }).code;\n if (code === \"ENOENT\") return Object.freeze([]);\n throw e;\n }\n const out: string[] = [];\n for (const name of entries) {\n if (!name.endsWith(\".json\")) continue;\n // Ignore the proper-lockfile lock sidecars and our own in-flight\n // unique-temp files (`<key>.json.<uuid>.tmp`) — they end with `.tmp`\n // not `.json`, so the suffix filter above already excludes them.\n // The lock directories proper-lockfile creates end with `.json.lock`\n // (a directory entry), also excluded by the `.json` suffix check.\n const encoded = name.slice(0, -\".json\".length);\n let decoded: string;\n try {\n decoded = decodeURIComponent(encoded);\n } catch {\n // Defensive: skip files whose names don't decode (manual placements,\n // partial writes, etc).\n continue;\n }\n if (decoded.startsWith(prefix)) {\n out.push(decoded);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const p = this.#pathFor(key);\n // Chain in-process callers FIFO. Cross-process exclusion is layered\n // on top via proper-lockfile inside `run()`.\n const prev = this.#chain.get(key) ?? Promise.resolve();\n const run = async (): Promise<T> => {\n await mkdir(dirname(p), { recursive: true });\n const release = await properLockfile.lock(p, {\n realpath: false,\n retries: { retries: 5, minTimeout: 20, maxTimeout: 200 },\n });\n try {\n return await fn();\n } finally {\n await release();\n }\n };\n const next = prev.then(run, run);\n // Absorber tail so the chain doesn't leak unhandled rejections.\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) this.#chain.delete(key);\n });\n return next;\n }\n}\n","// Barrel for @mostlyrightmd/core/internal/cache.\n//\n// Plan 01: types + MemoryStore + FsStore.\n// Plan 02: IndexedDBStore + defaultCacheStore (runtime auto-detect).\n// Plan 03: skip-rule predicates + key generators.\n//\n// Iter-2 H5: `FsStore` / `defaultFsRoot` / `FsStoreOptions` are NOT\n// re-exported here. tsup hoisted them into a sibling chunk that the\n// subbundle top-level-imported (transitively pulling `node:fs/promises`,\n// `node:os`, `node:path`, `proper-lockfile`, `node:crypto` into MV3\n// service-worker bundles), even though `defaultCacheStore` itself uses\n// a dynamic `import('./fs.js')` per iter-1 H3. Node-only consumers\n// (FsStore unit tests + downstream Node users) MUST import from the\n// dedicated subpath `@mostlyrightmd/core/internal/cache/fs`.\n\nexport type { CacheStore, CacheSetOptions, CacheEntry } from \"./types.js\";\nexport { lockKeyFor } from \"./types.js\";\nexport { MemoryStore } from \"./memory.js\";\nexport { IndexedDBStore, DB_NAME as INDEXEDDB_DB_NAME } from \"./indexeddb.js\";\nexport type { IndexedDBStoreOptions } from \"./indexeddb.js\";\nexport { defaultCacheStore } from \"./default.js\";\nexport {\n shouldSkipCacheForCurrentLstMonth,\n shouldSkipCacheForCurrentLstYear,\n isLiveSource,\n isWithinVolatileWindow,\n isWritableMonth,\n isWritableYear,\n} from \"./skip-rules.js\";\nexport { cacheKeyForObservations, cacheKeyForClimate } from \"./keys.js\";\n// Phase 21 21-03: schema-version invalidation adapter. Wired into\n// `defaultCacheStore()` so existing callers transparently get versioning.\nexport {\n versionedCacheStore,\n CACHE_SCHEMA_VERSION as VERSIONED_CACHE_SCHEMA_VERSION,\n} from \"./versionedCacheStore.js\";\nexport { CACHE_SCHEMA_VERSION } from \"./types.js\";\n","// CacheStore — pluggable key/value contract for the @mostlyrightmd/core cache\n// layer. Three concrete implementations land in TS-W3:\n// - MemoryStore — Map-backed, no persistence (Cloudflare Workers default).\n// - FsStore — node:fs/promises + proper-lockfile (Node default).\n// - IndexedDBStore — idb + Web Locks API (browser; plan 02).\n//\n// `defaultCacheStore()` (plan 02) auto-detects at runtime per\n// TS-SDK-DESIGN §5.4.\n\n/** Cache entry envelope with optional TTL. */\nexport interface CacheEntry<T = unknown> {\n readonly value: T;\n /** Epoch ms when the entry expires. Absence = no expiry. */\n readonly expiresAt?: number;\n}\n\n/** Optional setters for cache writes. */\nexport interface CacheSetOptions {\n /** Time-to-live in milliseconds. Implementations may honor or ignore. */\n readonly ttlMs?: number;\n}\n\n/**\n * Pluggable key/value cache contract used throughout the SDK.\n *\n * All methods are async — concrete implementations may resolve immediately\n * (MemoryStore) or do I/O (FsStore / IndexedDBStore).\n *\n * Semantic contract:\n * - `get<T>(key)` returns the stored value or `null` on miss. NEVER throws\n * on miss.\n * - `set<T>(key, value, opts?)` overwrites. ttlMs is implementation-honored\n * (MemoryStore + IndexedDBStore honor it; FsStore ignores in v0.1).\n * - `delete(key)` is a no-op on miss; returns void.\n * - `withLock<T>(key, fn)` runs `fn` under a key-scoped exclusive lock and\n * releases on settle (resolve OR throw). Nested calls to the same key\n * serialize; calls to different keys MAY run in parallel.\n */\nexport interface CacheStore {\n get<T = unknown>(key: string): Promise<T | null>;\n set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void>;\n delete(key: string): Promise<void>;\n withLock<T>(key: string, fn: () => Promise<T>): Promise<T>;\n}\n\n/**\n * Canonical lock identifier for a given cache key.\n *\n * Pure — same key → same lock id. Used by FsStore (proper-lockfile sidecar)\n * and IndexedDBStore (`navigator.locks.request(...)` name).\n */\nexport function lockKeyFor(key: string): string {\n return `mostlyright:cache:lock:${key}`;\n}\n\n/**\n * Canonical cache schema version stamp (Phase 21 21-03).\n *\n * Must match Python `mostlyright.weather.cache._cache_schema_version`\n * (set in Phase 18 18-08 to \"v2-phase18-integer-f\" after the ASOS\n * integer-°F precision fix). Embedded by `versionedCacheStore` into\n * every cache write so pre-bump entries silently re-fetch.\n *\n * Bump this string when a cache-shape change ships; existing cached\n * values invalidate on next read with no operator action required.\n */\nexport const CACHE_SCHEMA_VERSION = \"v2-phase18-integer-f\";\n","// MemoryStore — Map-backed CacheStore for ephemeral runtimes (Cloudflare\n// Workers, jsdom test envs without persistence). NOT shared across\n// processes — per-instance state.\n//\n// Value isolation via `structuredClone`: callers can mutate stored objects\n// after `.set()` without leaking changes back. Honors ttlMs with lazy\n// eviction at `.get()` time.\n//\n// withLock uses a per-key promise chain — pending lock-acquisitions queue\n// behind the current holder and run in FIFO order on settle.\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\n\n/**\n * In-memory cache. Per-instance state; two MemoryStore instances do NOT\n * share state.\n *\n * - Values cloned via `structuredClone` so post-`set` mutation can't leak.\n * - ttlMs honored with lazy eviction on `get`.\n * - withLock serializes nested calls via a per-key promise chain.\n */\nexport class MemoryStore implements CacheStore {\n readonly #entries = new Map<string, CacheEntry<unknown>>();\n readonly #chain = new Map<string, Promise<unknown>>();\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const e = this.#entries.get(key);\n if (e === undefined) return null;\n if (e.expiresAt !== undefined && Date.now() >= e.expiresAt) {\n this.#entries.delete(key);\n return null;\n }\n // Defensive clone on read too — callers can't mutate stored value via\n // the returned reference either.\n return structuredClone(e.value) as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const cloned = structuredClone(value);\n const entry: CacheEntry<unknown> =\n opts?.ttlMs !== undefined\n ? { value: cloned, expiresAt: Date.now() + opts.ttlMs }\n : { value: cloned };\n this.#entries.set(key, entry);\n }\n\n async delete(key: string): Promise<void> {\n this.#entries.delete(key);\n }\n\n /**\n * Enumerate live (non-expired) keys with the given prefix.\n *\n * TS-W6 Wave 1: `availability()` uses this to count cached observation\n * months and climate years per station. Expired entries are evicted as a\n * side effect (same lazy-eviction policy as `.get`).\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n const now = Date.now();\n const out: string[] = [];\n for (const [key, entry] of this.#entries) {\n if (entry.expiresAt !== undefined && now >= entry.expiresAt) {\n this.#entries.delete(key);\n continue;\n }\n if (key.startsWith(prefix)) {\n out.push(key);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.#chain.get(key) ?? Promise.resolve();\n // Chain `fn` after `prev` regardless of whether `prev` resolved or\n // rejected — the lock holder's failure shouldn't poison the queue.\n const next = prev.then(\n () => fn(),\n () => fn(),\n );\n // Store an absorber as the new tail so a later prev.then() handles\n // both branches without producing an unhandled-rejection warning. The\n // caller still receives the original `next` promise (including any\n // rejection from `fn`).\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) {\n this.#chain.delete(key);\n }\n });\n return next;\n }\n}\n","// IndexedDBStore — idb + Web Locks API CacheStore for browsers.\n//\n// Per TS-CACHE-02, the canonical IndexedDB DB name is `mostlyright-cache-v1`.\n// Object store: `entries`. Schema: key = string, value = CacheEntry<T>.\n//\n// withLock prefers `navigator.locks.request(name, ...)` (Web Locks API,\n// Chrome 69+, Firefox 96+, Safari 15.4+; all production-browser baselines).\n// When `navigator.locks` is unavailable (jsdom default), falls back to the\n// same per-key in-process promise chain used by MemoryStore / FsStore —\n// edge runtimes (Workers without web-locks polyfills) get the in-process\n// guarantee at least.\n//\n// ttlMs is honored via lazy eviction at `get` time (matches FsStore).\n\nimport { type IDBPDatabase, openDB } from \"idb\";\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\nimport { lockKeyFor } from \"./types.js\";\n\n/** Canonical DB name. Re-exported via the cache barrel. */\nexport const DB_NAME = \"mostlyright-cache-v1\";\n\nconst STORE_NAME = \"entries\";\nconst SCHEMA_VERSION = 1;\n\nexport interface IndexedDBStoreOptions {\n /** Override the DB name. Tests pass unique values per case so they don't pollute each other. */\n readonly dbName?: string;\n}\n\ninterface WebLocksApi {\n request: <T>(\n name: string,\n options: { mode: \"exclusive\" },\n fn: () => Promise<T> | T,\n ) => Promise<T>;\n}\n\nfunction getWebLocks(): WebLocksApi | null {\n if (typeof navigator === \"undefined\") return null;\n const nav = navigator as unknown as { locks?: WebLocksApi };\n return nav.locks ?? null;\n}\n\n/**\n * Browser CacheStore backed by IndexedDB (via idb) + Web Locks API.\n *\n * When `navigator.locks` is unavailable (jsdom, edge runtimes without\n * Web Locks), falls back to a per-key in-process promise chain.\n */\nexport class IndexedDBStore implements CacheStore {\n readonly #dbName: string;\n readonly #dbPromise: Promise<IDBPDatabase>;\n readonly #chain = new Map<string, Promise<unknown>>();\n\n constructor(opts: IndexedDBStoreOptions = {}) {\n this.#dbName = opts.dbName ?? DB_NAME;\n this.#dbPromise = openDB(this.#dbName, SCHEMA_VERSION, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME);\n }\n },\n });\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const db = await this.#dbPromise;\n const entry = (await db.get(STORE_NAME, key)) as CacheEntry<T> | undefined;\n if (entry === undefined) return null;\n if (entry.expiresAt !== undefined && Date.now() >= entry.expiresAt) {\n // Lazy-evict — best-effort; ignore failures.\n try {\n await db.delete(STORE_NAME, key);\n } catch {\n // ignore\n }\n return null;\n }\n return entry.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const db = await this.#dbPromise;\n const entry: CacheEntry<T> =\n opts?.ttlMs !== undefined ? { value, expiresAt: Date.now() + opts.ttlMs } : { value };\n await db.put(STORE_NAME, entry, key);\n }\n\n async delete(key: string): Promise<void> {\n const db = await this.#dbPromise;\n await db.delete(STORE_NAME, key);\n }\n\n /**\n * Enumerate keys with the given prefix using IndexedDB's bounded range\n * query. Live keys only — expired entries are lazy-evicted on read by\n * `get()`, so a stale-but-not-yet-evicted entry can appear here; callers\n * who care about expiration should `get()` to confirm.\n *\n * TS-W6 Wave 1: `availability()` uses this to count observation months and\n * climate years for a station.\n */\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n const db = await this.#dbPromise;\n // Build an inclusive lower bound and an exclusive upper bound using the\n // next-codepoint trick. IndexedDB's IDBKeyRange handles string ordering\n // by UTF-16 code units, which matches JS string comparison; we use the\n // Unicode max-codepoint as the upper sentinel so any key starting with\n // `prefix` lands inside the range.\n const range = IDBKeyRange.bound(prefix, `${prefix}`, false, false);\n const keys = (await db.getAllKeys(STORE_NAME, range)) as IDBValidKey[];\n const out: string[] = [];\n for (const k of keys) {\n if (typeof k === \"string\" && k.startsWith(prefix)) {\n out.push(k);\n }\n }\n return Object.freeze(out);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const locks = getWebLocks();\n if (locks !== null) {\n // Web Locks API — production-browser path. Cross-tab safe.\n return locks.request<T>(lockKeyFor(key), { mode: \"exclusive\" }, () => fn());\n }\n // Fallback for jsdom / edge runtimes without navigator.locks: in-process\n // per-key promise chain (FIFO; matches MemoryStore semantics).\n const prev = this.#chain.get(key) ?? Promise.resolve();\n const next = prev.then(\n () => fn(),\n () => fn(),\n );\n const absorbed = next.then(\n () => undefined,\n () => undefined,\n );\n this.#chain.set(key, absorbed);\n absorbed.finally(() => {\n if (this.#chain.get(key) === absorbed) this.#chain.delete(key);\n });\n return next;\n }\n}\n","// versionedCacheStore — Phase 21 21-03.\n//\n// Schema-version invariant for the TS cache, matching Python's Phase 18\n// 18-08 invariant. Wraps any underlying CacheStore so reads with a stale\n// `_cache_schema_version` field return null (cache miss → re-fetch),\n// matching the parquet-metadata-based invariant on the Python side.\n//\n// Before: TS cache was generic key/value with no version stamping. After\n// Phase 18 lifted the ASOS integer-°F precision fix, pre-Phase-18 user\n// caches (with 0.06°F float values) would silently return stale values\n// on next call instead of re-fetching. Python embeds the version into\n// parquet metadata (pyarrow `kv_metadata`); TS embeds it as a sidecar\n// field in the stored JSON value.\n//\n// Wire-up: `defaultCacheStore()` wraps each concrete store\n// (IndexedDBStore / FsStore / MemoryStore) via this adapter so consumers\n// see the same `CacheStore` interface. The version wrap/unwrap is\n// invisible from the caller's perspective.\n//\n// Bump `CACHE_SCHEMA_VERSION` in `./types.ts` when the next cache-shape\n// change ships; existing cached values silently invalidate.\n\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"./types.js\";\n\n/** Sentinel field name embedded in every cached value. */\nconst VERSION_FIELD = \"_cache_schema_version\" as const;\n\n/** Wrapper shape stored under each key. */\nexport interface VersionedEntry<T = unknown> {\n readonly value: T;\n readonly _cache_schema_version: string;\n}\n\nfunction isVersionedEntry(v: unknown): v is VersionedEntry<unknown> {\n if (v === null || typeof v !== \"object\") return false;\n if (!(VERSION_FIELD in (v as Record<string, unknown>))) return false;\n return typeof (v as Record<string, unknown>)[VERSION_FIELD] === \"string\";\n}\n\n// Optional extension surface — some concrete stores (MemoryStore,\n// IndexedDBStore) expose `listKeys(prefix)` beyond the CacheStore\n// contract. The adapter forwards it transparently when present so\n// `availability()` and friends keep working.\ninterface ListKeysCapable {\n listKeys(prefix: string): Promise<ReadonlyArray<string>>;\n}\n\nfunction hasListKeys(s: CacheStore): s is CacheStore & ListKeysCapable {\n return typeof (s as Partial<ListKeysCapable>).listKeys === \"function\";\n}\n\nclass VersionedCacheStore implements CacheStore, ListKeysCapable {\n readonly #inner: CacheStore;\n readonly #version: string;\n\n constructor(inner: CacheStore, version: string) {\n if (typeof version !== \"string\" || version.length === 0) {\n throw new TypeError(\"versionedCacheStore: version must be a non-empty string\");\n }\n this.#inner = inner;\n this.#version = version;\n }\n\n /**\n * Test/diagnostics seam: return the underlying store so tests can assert\n * which concrete backend `defaultCacheStore()` selected. NOT a production\n * API — production code MUST use the wrapped store so version\n * invalidation fires on stale reads.\n *\n * @internal\n */\n __peekInner(): CacheStore {\n return this.#inner;\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await this.#inner.get<unknown>(key);\n if (raw === null) return null;\n if (!isVersionedEntry(raw)) {\n // Pre-21-03 cache entry (no version wrapper). Treat as miss; caller\n // will re-fetch with the new wrapper on the next set.\n return null;\n }\n if (raw._cache_schema_version !== this.#version) {\n // Mismatched schema version — stale. Treat as miss.\n return null;\n }\n return raw.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, opts?: CacheSetOptions): Promise<void> {\n const wrapped: VersionedEntry<T> = {\n value,\n [VERSION_FIELD]: this.#version,\n } as VersionedEntry<T>;\n await this.#inner.set(key, wrapped, opts);\n }\n\n async delete(key: string): Promise<void> {\n await this.#inner.delete(key);\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n return this.#inner.withLock(key, fn);\n }\n\n async listKeys(prefix: string): Promise<ReadonlyArray<string>> {\n if (hasListKeys(this.#inner)) {\n return this.#inner.listKeys(prefix);\n }\n return Object.freeze([]);\n }\n}\n\n/**\n * Wrap any CacheStore so reads validate `_cache_schema_version` and\n * writes embed it. The wrapper is transparent — callers continue to use\n * the same `CacheStore` interface.\n *\n * @param inner underlying store (MemoryStore / IndexedDBStore / FsStore)\n * @param version the schema version to embed; non-matching reads miss\n */\nexport function versionedCacheStore(inner: CacheStore, version: string): CacheStore {\n return new VersionedCacheStore(inner, version);\n}\n\n// Re-export the canonical version constant alongside the adapter — the\n// constant lives in `./types.ts` (single source of truth) but is\n// surfaced here too so callers writing\n// `versionedCacheStore(inner, CACHE_SCHEMA_VERSION)` import both from\n// the same module.\nexport { CACHE_SCHEMA_VERSION } from \"./types.js\";\n\n/**\n * Internal helper for tests + diagnostics: expose the raw envelope shape\n * so callers can pre-seed an underlying store with the wrapped shape\n * without round-tripping through this adapter.\n */\nexport function wrapForCache<T>(value: T, version: string): VersionedEntry<T> {\n return { value, [VERSION_FIELD]: version } as VersionedEntry<T>;\n}\n","// defaultCacheStore — runtime auto-detection per TS-SDK-DESIGN §5.4.\n//\n// Priority order (fixed, deterministic):\n// 1. `typeof indexedDB !== \"undefined\"` → IndexedDBStore (browser)\n// 2. `typeof process !== \"undefined\" && process.versions?.node` → FsStore\n// (Node, loaded via `await import('./fs.js')`)\n// 3. else → MemoryStore (Workers / edge / unknown)\n//\n// Iter-1 H3: `FsStore` is NO longer statically imported here. It\n// top-level-imports `node:fs/promises`, `node:os`, `node:path`, and\n// `proper-lockfile`, which would pull all four into any MV3 / browser /\n// edge bundle that touches `@mostlyrightmd/core/internal/cache` — even via\n// the IndexedDB code path. The dynamic `await import('./fs.js')` behind\n// a `process.versions?.node` runtime feature-detect ensures bundlers\n// can statically prove the FsStore subgraph is unreachable from the\n// browser entry, eliminating the Node-only-deps edge.\n//\n// Function is async because dynamic import returns a Promise. Callers\n// (research() and friends) already operate inside async code paths.\n\nimport { IndexedDBStore } from \"./indexeddb.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport { CACHE_SCHEMA_VERSION, type CacheStore } from \"./types.js\";\nimport { versionedCacheStore } from \"./versionedCacheStore.js\";\n\n/**\n * Auto-detect the best CacheStore for the current runtime.\n *\n * Returns a NEW instance per call.\n *\n * Phase 21 21-03 (iter-1 fix per codex + ts-architect CRITICAL): the\n * concrete store is wrapped in `versionedCacheStore(CACHE_SCHEMA_VERSION)`\n * so pre-Phase-18 cache entries (no version sidecar, or wrong version)\n * silently miss instead of returning stale `0.06°F`-precision rows. The\n * wrap is transparent — callers see the same `CacheStore` interface.\n *\n * @returns a Promise resolving to a fresh CacheStore (wrapped). The\n * Node-only `FsStore` is loaded via dynamic import behind a runtime\n * feature detect, so browser / MV3 / edge bundles never pull\n * `node:fs/promises` et al. (iter-1 H3 fix).\n */\nexport async function defaultCacheStore(): Promise<CacheStore> {\n const inner = await pickConcreteStore();\n return versionedCacheStore(inner, CACHE_SCHEMA_VERSION);\n}\n\nasync function pickConcreteStore(): Promise<CacheStore> {\n if (typeof indexedDB !== \"undefined\") return new IndexedDBStore();\n if (\n typeof process !== \"undefined\" &&\n typeof process.versions === \"object\" &&\n process.versions !== null &&\n typeof process.versions.node === \"string\"\n ) {\n // Dynamic import keeps `./fs.js` (and its `node:fs/promises`,\n // `node:os`, `node:path`, `proper-lockfile` chain) out of the\n // browser bundle. Bundlers that support code-splitting will emit\n // a separate chunk; bundlers targeting `browser` resolution skip\n // the chunk entirely when the feature-detect short-circuits.\n const { FsStore } = await import(\"./fs.js\");\n return new FsStore();\n }\n return new MemoryStore();\n}\n","// AUTO-GENERATED by @mostlyrightmd/codegen from schemas/stations.json.\n// DO NOT EDIT — regenerate with: pnpm codegen\n// Last manifest SHA recorded in schemas/EXPORT_MANIFEST.json\n\nexport interface StationInfo {\n code: string | null;\n ghcnh_id: string | null;\n icao: string;\n name: string | null;\n tz: string;\n latitude: number | null;\n longitude: number | null;\n country: string | null;\n venues: ReadonlyArray<string>;\n}\n\nexport const STATIONS: ReadonlyArray<StationInfo> = [\n {\n code: \"EDDB\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDB\",\n latitude: 52.3667,\n longitude: 13.5033,\n name: \"Berlin Brandenburg\",\n tz: \"Europe/Berlin\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EDDF\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDF\",\n latitude: 50.0379,\n longitude: 8.5622,\n name: \"Frankfurt am Main\",\n tz: \"Europe/Berlin\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EDDM\",\n country: \"DE\",\n ghcnh_id: null,\n icao: \"EDDM\",\n latitude: 48.3538,\n longitude: 11.7861,\n name: \"Munich Franz Josef Strauss\",\n tz: \"Europe/Berlin\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EFHK\",\n country: \"FI\",\n ghcnh_id: null,\n icao: \"EFHK\",\n latitude: 60.3172,\n longitude: 24.9633,\n name: \"Helsinki-Vantaa\",\n tz: \"Europe/Helsinki\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EGKK\",\n country: \"GB\",\n ghcnh_id: null,\n icao: \"EGKK\",\n latitude: 51.1481,\n longitude: -0.1903,\n name: \"London Gatwick\",\n tz: \"Europe/London\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EGLL\",\n country: \"GB\",\n ghcnh_id: null,\n icao: \"EGLL\",\n latitude: 51.4706,\n longitude: -0.4619,\n name: \"London Heathrow\",\n tz: \"Europe/London\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EHAM\",\n country: \"NL\",\n ghcnh_id: null,\n icao: \"EHAM\",\n latitude: 52.3086,\n longitude: 4.7639,\n name: \"Amsterdam Schiphol\",\n tz: \"Europe/Amsterdam\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EKCH\",\n country: \"DK\",\n ghcnh_id: null,\n icao: \"EKCH\",\n latitude: 55.6181,\n longitude: 12.6561,\n name: \"Copenhagen Kastrup\",\n tz: \"Europe/Copenhagen\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"EPWA\",\n country: \"PL\",\n ghcnh_id: null,\n icao: \"EPWA\",\n latitude: 52.1657,\n longitude: 20.9671,\n name: \"Warsaw Chopin\",\n tz: \"Europe/Warsaw\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"ESSA\",\n country: \"SE\",\n ghcnh_id: null,\n icao: \"ESSA\",\n latitude: 59.6519,\n longitude: 17.9186,\n name: \"Stockholm Arlanda\",\n tz: \"Europe/Stockholm\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"ATL\",\n country: \"US\",\n ghcnh_id: \"USW00013874\",\n icao: \"KATL\",\n latitude: 33.6407,\n longitude: -84.4277,\n name: \"Hartsfield-Jackson Atlanta International\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"AUS\",\n country: \"US\",\n ghcnh_id: \"USW00013904\",\n icao: \"KAUS\",\n latitude: 30.1975,\n longitude: -97.6664,\n name: \"Austin-Bergstrom International\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"BNA\",\n country: \"US\",\n ghcnh_id: \"USW00013897\",\n icao: \"KBNA\",\n latitude: 36.1245,\n longitude: -86.6782,\n name: \"Nashville International\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"BOS\",\n country: \"US\",\n ghcnh_id: \"USW00014739\",\n icao: \"KBOS\",\n latitude: 42.3656,\n longitude: -71.0096,\n name: \"Boston Logan International\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"CVG\",\n country: \"US\",\n ghcnh_id: \"USW00093814\",\n icao: \"KCVG\",\n latitude: 39.0488,\n longitude: -84.6678,\n name: \"Cincinnati/Northern Kentucky International\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"DCA\",\n country: \"US\",\n ghcnh_id: \"USW00013743\",\n icao: \"KDCA\",\n latitude: 38.8512,\n longitude: -77.0402,\n name: \"Washington Reagan National\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"DEN\",\n country: \"US\",\n ghcnh_id: \"USW00003017\",\n icao: \"KDEN\",\n latitude: 39.8561,\n longitude: -104.6737,\n name: \"Denver International\",\n tz: \"America/Denver\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"DFW\",\n country: \"US\",\n ghcnh_id: \"USW00003927\",\n icao: \"KDFW\",\n latitude: 32.8998,\n longitude: -97.0403,\n name: \"Dallas-Fort Worth International\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"DTW\",\n country: \"US\",\n ghcnh_id: \"USW00094847\",\n icao: \"KDTW\",\n latitude: 42.2124,\n longitude: -83.3534,\n name: \"Detroit Metropolitan Wayne County\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"HOU\",\n country: \"US\",\n ghcnh_id: \"USW00012918\",\n icao: \"KHOU\",\n latitude: 29.6454,\n longitude: -95.2789,\n name: \"Houston Hobby\",\n tz: \"America/Chicago\",\n venues: [],\n },\n {\n code: \"IAH\",\n country: \"US\",\n ghcnh_id: \"USW00012960\",\n icao: \"KIAH\",\n latitude: 29.9844,\n longitude: -95.3414,\n name: \"Houston George Bush Intercontinental\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"LAS\",\n country: \"US\",\n ghcnh_id: \"USW00023169\",\n icao: \"KLAS\",\n latitude: 36.084,\n longitude: -115.1537,\n name: \"Harry Reid (McCarran) International\",\n tz: \"America/Los_Angeles\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"LAX\",\n country: \"US\",\n ghcnh_id: \"USW00023174\",\n icao: \"KLAX\",\n latitude: 33.9425,\n longitude: -118.4081,\n name: \"Los Angeles International\",\n tz: \"America/Los_Angeles\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"MDW\",\n country: \"US\",\n ghcnh_id: \"USW00014819\",\n icao: \"KMDW\",\n latitude: 41.7868,\n longitude: -87.7522,\n name: \"Chicago Midway International\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"MIA\",\n country: \"US\",\n ghcnh_id: \"USW00012839\",\n icao: \"KMIA\",\n latitude: 25.7959,\n longitude: -80.287,\n name: \"Miami International\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"MSP\",\n country: \"US\",\n ghcnh_id: \"USW00014922\",\n icao: \"KMSP\",\n latitude: 44.8848,\n longitude: -93.2223,\n name: \"Minneapolis-St Paul International\",\n tz: \"America/Chicago\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"MSY\",\n country: \"US\",\n ghcnh_id: \"USW00012916\",\n icao: \"KMSY\",\n latitude: 29.9934,\n longitude: -90.258,\n name: \"New Orleans Louis Armstrong International\",\n tz: \"America/Chicago\",\n venues: [],\n },\n {\n code: \"NYC\",\n country: \"US\",\n ghcnh_id: \"USW00094728\",\n icao: \"KNYC\",\n latitude: 40.7789,\n longitude: -73.9692,\n name: \"Central Park, New York\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"OKC\",\n country: \"US\",\n ghcnh_id: \"USW00013967\",\n icao: \"KOKC\",\n latitude: 35.3931,\n longitude: -97.6007,\n name: \"Oklahoma City Will Rogers World\",\n tz: \"America/Chicago\",\n venues: [],\n },\n {\n code: \"PHL\",\n country: \"US\",\n ghcnh_id: \"USW00013739\",\n icao: \"KPHL\",\n latitude: 39.8721,\n longitude: -75.2411,\n name: \"Philadelphia International\",\n tz: \"America/New_York\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"PHX\",\n country: \"US\",\n ghcnh_id: \"USW00023183\",\n icao: \"KPHX\",\n latitude: 33.4373,\n longitude: -112.0078,\n name: \"Phoenix Sky Harbor International\",\n tz: \"America/Phoenix\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"SAT\",\n country: \"US\",\n ghcnh_id: \"USW00012921\",\n icao: \"KSAT\",\n latitude: 29.5337,\n longitude: -98.4698,\n name: \"San Antonio International\",\n tz: \"America/Chicago\",\n venues: [],\n },\n {\n code: \"SEA\",\n country: \"US\",\n ghcnh_id: \"USW00024233\",\n icao: \"KSEA\",\n latitude: 47.4502,\n longitude: -122.3088,\n name: \"Seattle-Tacoma International\",\n tz: \"America/Los_Angeles\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"SFO\",\n country: \"US\",\n ghcnh_id: \"USW00023234\",\n icao: \"KSFO\",\n latitude: 37.6213,\n longitude: -122.379,\n name: \"San Francisco International\",\n tz: \"America/Los_Angeles\",\n venues: [\n \"kalshi\",\n \"polymarket\",\n ],\n },\n {\n code: \"SLC\",\n country: \"US\",\n ghcnh_id: \"USW00024127\",\n icao: \"KSLC\",\n latitude: 40.7884,\n longitude: -111.9778,\n name: \"Salt Lake City International\",\n tz: \"America/Denver\",\n venues: [\n \"kalshi\",\n ],\n },\n {\n code: \"LEBL\",\n country: \"ES\",\n ghcnh_id: null,\n icao: \"LEBL\",\n latitude: 41.2974,\n longitude: 2.0833,\n name: \"Barcelona El Prat\",\n tz: \"Europe/Madrid\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LEMD\",\n country: \"ES\",\n ghcnh_id: null,\n icao: \"LEMD\",\n latitude: 40.4719,\n longitude: -3.5626,\n name: \"Madrid Barajas\",\n tz: \"Europe/Madrid\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LFPB\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPB\",\n latitude: 48.9694,\n longitude: 2.4414,\n name: \"Paris Le Bourget\",\n tz: \"Europe/Paris\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LFPG\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPG\",\n latitude: 49.0097,\n longitude: 2.5479,\n name: \"Paris Charles de Gaulle\",\n tz: \"Europe/Paris\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LFPO\",\n country: \"FR\",\n ghcnh_id: null,\n icao: \"LFPO\",\n latitude: 48.7233,\n longitude: 2.3794,\n name: \"Paris Orly\",\n tz: \"Europe/Paris\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LIMC\",\n country: \"IT\",\n ghcnh_id: null,\n icao: \"LIMC\",\n latitude: 45.6306,\n longitude: 8.7281,\n name: \"Milan Malpensa\",\n tz: \"Europe/Rome\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LIRF\",\n country: \"IT\",\n ghcnh_id: null,\n icao: \"LIRF\",\n latitude: 41.8003,\n longitude: 12.2389,\n name: \"Rome Fiumicino\",\n tz: \"Europe/Rome\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LOWW\",\n country: \"AT\",\n ghcnh_id: null,\n icao: \"LOWW\",\n latitude: 48.1103,\n longitude: 16.5697,\n name: \"Vienna International\",\n tz: \"Europe/Vienna\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"LSZH\",\n country: \"CH\",\n ghcnh_id: null,\n icao: \"LSZH\",\n latitude: 47.4647,\n longitude: 8.5492,\n name: \"Zurich\",\n tz: \"Europe/Zurich\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"NZAA\",\n country: \"NZ\",\n ghcnh_id: null,\n icao: \"NZAA\",\n latitude: -37.0081,\n longitude: 174.7917,\n name: \"Auckland\",\n tz: \"Pacific/Auckland\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"NZWN\",\n country: \"NZ\",\n ghcnh_id: null,\n icao: \"NZWN\",\n latitude: -41.3272,\n longitude: 174.8053,\n name: \"Wellington\",\n tz: \"Pacific/Auckland\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"OERK\",\n country: \"SA\",\n ghcnh_id: null,\n icao: \"OERK\",\n latitude: 24.9576,\n longitude: 46.6988,\n name: \"Riyadh King Khalid International\",\n tz: \"Asia/Riyadh\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"OMDB\",\n country: \"AE\",\n ghcnh_id: null,\n icao: \"OMDB\",\n latitude: 25.2532,\n longitude: 55.3657,\n name: \"Dubai International\",\n tz: \"Asia/Dubai\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"OTHH\",\n country: \"QA\",\n ghcnh_id: null,\n icao: \"OTHH\",\n latitude: 25.2731,\n longitude: 51.608,\n name: \"Doha Hamad International\",\n tz: \"Asia/Qatar\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"RCTP\",\n country: \"TW\",\n ghcnh_id: null,\n icao: \"RCTP\",\n latitude: 25.0777,\n longitude: 121.2328,\n name: \"Taipei Taoyuan\",\n tz: \"Asia/Taipei\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"RJAA\",\n country: \"JP\",\n ghcnh_id: null,\n icao: \"RJAA\",\n latitude: 35.7647,\n longitude: 140.3864,\n name: \"Tokyo Narita\",\n tz: \"Asia/Tokyo\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"RJTT\",\n country: \"JP\",\n ghcnh_id: null,\n icao: \"RJTT\",\n latitude: 35.5522,\n longitude: 139.78,\n name: \"Tokyo Haneda\",\n tz: \"Asia/Tokyo\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"RKSI\",\n country: \"KR\",\n ghcnh_id: null,\n icao: \"RKSI\",\n latitude: 37.4691,\n longitude: 126.4505,\n name: \"Seoul Incheon\",\n tz: \"Asia/Seoul\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"SAEZ\",\n country: \"AR\",\n ghcnh_id: null,\n icao: \"SAEZ\",\n latitude: -34.8222,\n longitude: -58.5358,\n name: \"Buenos Aires Ezeiza\",\n tz: \"America/Argentina/Buenos_Aires\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"SBGR\",\n country: \"BR\",\n ghcnh_id: null,\n icao: \"SBGR\",\n latitude: -23.4356,\n longitude: -46.4731,\n name: \"São Paulo Guarulhos\",\n tz: \"America/Sao_Paulo\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"UUEE\",\n country: \"RU\",\n ghcnh_id: null,\n icao: \"UUEE\",\n latitude: 55.9728,\n longitude: 37.4147,\n name: \"Moscow Sheremetyevo\",\n tz: \"Europe/Moscow\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"VABB\",\n country: \"IN\",\n ghcnh_id: null,\n icao: \"VABB\",\n latitude: 19.0887,\n longitude: 72.8679,\n name: \"Mumbai Chhatrapati Shivaji\",\n tz: \"Asia/Kolkata\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"VHHH\",\n country: \"HK\",\n ghcnh_id: null,\n icao: \"VHHH\",\n latitude: 22.308,\n longitude: 113.9185,\n name: \"Hong Kong International\",\n tz: \"Asia/Hong_Kong\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"VIDP\",\n country: \"IN\",\n ghcnh_id: null,\n icao: \"VIDP\",\n latitude: 28.5562,\n longitude: 77.1,\n name: \"Delhi Indira Gandhi\",\n tz: \"Asia/Kolkata\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"VTBS\",\n country: \"TH\",\n ghcnh_id: null,\n icao: \"VTBS\",\n latitude: 13.69,\n longitude: 100.7501,\n name: \"Bangkok Suvarnabhumi\",\n tz: \"Asia/Bangkok\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"WSSS\",\n country: \"SG\",\n ghcnh_id: null,\n icao: \"WSSS\",\n latitude: 1.3644,\n longitude: 103.9915,\n name: \"Singapore Changi\",\n tz: \"Asia/Singapore\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"YBBN\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YBBN\",\n latitude: -27.3842,\n longitude: 153.1175,\n name: \"Brisbane\",\n tz: \"Australia/Brisbane\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"YMML\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YMML\",\n latitude: -37.6733,\n longitude: 144.8433,\n name: \"Melbourne Tullamarine\",\n tz: \"Australia/Melbourne\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"YSSY\",\n country: \"AU\",\n ghcnh_id: null,\n icao: \"YSSY\",\n latitude: -33.9461,\n longitude: 151.1772,\n name: \"Sydney Kingsford Smith\",\n tz: \"Australia/Sydney\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"ZBAA\",\n country: \"CN\",\n ghcnh_id: null,\n icao: \"ZBAA\",\n latitude: 40.0801,\n longitude: 116.5846,\n name: \"Beijing Capital\",\n tz: \"Asia/Shanghai\",\n venues: [\n \"polymarket\",\n ],\n },\n {\n code: \"ZSPD\",\n country: \"CN\",\n ghcnh_id: null,\n icao: \"ZSPD\",\n latitude: 31.1443,\n longitude: 121.8083,\n name: \"Shanghai Pudong\",\n tz: \"Asia/Shanghai\",\n venues: [\n \"polymarket\",\n ],\n },\n] as const;\n\nexport const STATION_BY_CODE: ReadonlyMap<string, StationInfo> = new Map<string, StationInfo>([\n [\"ATL\", STATIONS[10]!],\n [\"AUS\", STATIONS[11]!],\n [\"BNA\", STATIONS[12]!],\n [\"BOS\", STATIONS[13]!],\n [\"CVG\", STATIONS[14]!],\n [\"DCA\", STATIONS[15]!],\n [\"DEN\", STATIONS[16]!],\n [\"DFW\", STATIONS[17]!],\n [\"DTW\", STATIONS[18]!],\n [\"EDDB\", STATIONS[0]!],\n [\"EDDF\", STATIONS[1]!],\n [\"EDDM\", STATIONS[2]!],\n [\"EFHK\", STATIONS[3]!],\n [\"EGKK\", STATIONS[4]!],\n [\"EGLL\", STATIONS[5]!],\n [\"EHAM\", STATIONS[6]!],\n [\"EKCH\", STATIONS[7]!],\n [\"EPWA\", STATIONS[8]!],\n [\"ESSA\", STATIONS[9]!],\n [\"HOU\", STATIONS[19]!],\n [\"IAH\", STATIONS[20]!],\n [\"LAS\", STATIONS[21]!],\n [\"LAX\", STATIONS[22]!],\n [\"LEBL\", STATIONS[35]!],\n [\"LEMD\", STATIONS[36]!],\n [\"LFPB\", STATIONS[37]!],\n [\"LFPG\", STATIONS[38]!],\n [\"LFPO\", STATIONS[39]!],\n [\"LIMC\", STATIONS[40]!],\n [\"LIRF\", STATIONS[41]!],\n [\"LOWW\", STATIONS[42]!],\n [\"LSZH\", STATIONS[43]!],\n [\"MDW\", STATIONS[23]!],\n [\"MIA\", STATIONS[24]!],\n [\"MSP\", STATIONS[25]!],\n [\"MSY\", STATIONS[26]!],\n [\"NYC\", STATIONS[27]!],\n [\"NZAA\", STATIONS[44]!],\n [\"NZWN\", STATIONS[45]!],\n [\"OERK\", STATIONS[46]!],\n [\"OKC\", STATIONS[28]!],\n [\"OMDB\", STATIONS[47]!],\n [\"OTHH\", STATIONS[48]!],\n [\"PHL\", STATIONS[29]!],\n [\"PHX\", STATIONS[30]!],\n [\"RCTP\", STATIONS[49]!],\n [\"RJAA\", STATIONS[50]!],\n [\"RJTT\", STATIONS[51]!],\n [\"RKSI\", STATIONS[52]!],\n [\"SAEZ\", STATIONS[53]!],\n [\"SAT\", STATIONS[31]!],\n [\"SBGR\", STATIONS[54]!],\n [\"SEA\", STATIONS[32]!],\n [\"SFO\", STATIONS[33]!],\n [\"SLC\", STATIONS[34]!],\n [\"UUEE\", STATIONS[55]!],\n [\"VABB\", STATIONS[56]!],\n [\"VHHH\", STATIONS[57]!],\n [\"VIDP\", STATIONS[58]!],\n [\"VTBS\", STATIONS[59]!],\n [\"WSSS\", STATIONS[60]!],\n [\"YBBN\", STATIONS[61]!],\n [\"YMML\", STATIONS[62]!],\n [\"YSSY\", STATIONS[63]!],\n [\"ZBAA\", STATIONS[64]!],\n [\"ZSPD\", STATIONS[65]!],\n]);\n\nexport const STATION_BY_ICAO: ReadonlyMap<string, StationInfo> = new Map<string, StationInfo>([\n [\"EDDB\", STATIONS[0]!],\n [\"EDDF\", STATIONS[1]!],\n [\"EDDM\", STATIONS[2]!],\n [\"EFHK\", STATIONS[3]!],\n [\"EGKK\", STATIONS[4]!],\n [\"EGLL\", STATIONS[5]!],\n [\"EHAM\", STATIONS[6]!],\n [\"EKCH\", STATIONS[7]!],\n [\"EPWA\", STATIONS[8]!],\n [\"ESSA\", STATIONS[9]!],\n [\"KATL\", STATIONS[10]!],\n [\"KAUS\", STATIONS[11]!],\n [\"KBNA\", STATIONS[12]!],\n [\"KBOS\", STATIONS[13]!],\n [\"KCVG\", STATIONS[14]!],\n [\"KDCA\", STATIONS[15]!],\n [\"KDEN\", STATIONS[16]!],\n [\"KDFW\", STATIONS[17]!],\n [\"KDTW\", STATIONS[18]!],\n [\"KHOU\", STATIONS[19]!],\n [\"KIAH\", STATIONS[20]!],\n [\"KLAS\", STATIONS[21]!],\n [\"KLAX\", STATIONS[22]!],\n [\"KMDW\", STATIONS[23]!],\n [\"KMIA\", STATIONS[24]!],\n [\"KMSP\", STATIONS[25]!],\n [\"KMSY\", STATIONS[26]!],\n [\"KNYC\", STATIONS[27]!],\n [\"KOKC\", STATIONS[28]!],\n [\"KPHL\", STATIONS[29]!],\n [\"KPHX\", STATIONS[30]!],\n [\"KSAT\", STATIONS[31]!],\n [\"KSEA\", STATIONS[32]!],\n [\"KSFO\", STATIONS[33]!],\n [\"KSLC\", STATIONS[34]!],\n [\"LEBL\", STATIONS[35]!],\n [\"LEMD\", STATIONS[36]!],\n [\"LFPB\", STATIONS[37]!],\n [\"LFPG\", STATIONS[38]!],\n [\"LFPO\", STATIONS[39]!],\n [\"LIMC\", STATIONS[40]!],\n [\"LIRF\", STATIONS[41]!],\n [\"LOWW\", STATIONS[42]!],\n [\"LSZH\", STATIONS[43]!],\n [\"NZAA\", STATIONS[44]!],\n [\"NZWN\", STATIONS[45]!],\n [\"OERK\", STATIONS[46]!],\n [\"OMDB\", STATIONS[47]!],\n [\"OTHH\", STATIONS[48]!],\n [\"RCTP\", STATIONS[49]!],\n [\"RJAA\", STATIONS[50]!],\n [\"RJTT\", STATIONS[51]!],\n [\"RKSI\", STATIONS[52]!],\n [\"SAEZ\", STATIONS[53]!],\n [\"SBGR\", STATIONS[54]!],\n [\"UUEE\", STATIONS[55]!],\n [\"VABB\", STATIONS[56]!],\n [\"VHHH\", STATIONS[57]!],\n [\"VIDP\", STATIONS[58]!],\n [\"VTBS\", STATIONS[59]!],\n [\"WSSS\", STATIONS[60]!],\n [\"YBBN\", STATIONS[61]!],\n [\"YMML\", STATIONS[62]!],\n [\"YSSY\", STATIONS[63]!],\n [\"ZBAA\", STATIONS[64]!],\n [\"ZSPD\", STATIONS[65]!],\n]);\n","// Snapshot math — settlement-window and market-close arithmetic.\n//\n// Ported from `packages/core/src/mostlyright/snapshot.py` and\n// `packages/core/src/mostlyright/_internal/_pairs.py:market_close_utc`.\n//\n// Key concepts:\n// - LOCAL STANDARD TIME (LST): station's standard UTC offset, DST ignored.\n// Kalshi NHIGH/NLOW contracts define the settlement window in LST.\n// - Settlement window: midnight-midnight LST for a given date.\n// During US daylight saving the clock window is 1:00 AM–1:00 AM next day\n// (EDT), but the UTC bounds are the same year-round.\n// - CLI publication delay: NWS issues the overnight final CLI ~04:00–10:00\n// UTC the day after observation. Default: 10 h after midnight LST.\n\n// ---------------------------------------------------------------------------\n// Station → IANA timezone database\n// ---------------------------------------------------------------------------\n//\n// Used to extract the LOCAL STANDARD TIME UTC offset via a January reference\n// moment. Ported from `mostlyright.snapshot._STATION_TZ`.\n\nexport const _STATION_TZ: Readonly<Record<string, string>> = Object.freeze({\n // Eastern (UTC-5 standard / UTC-4 DST)\n NYC: \"America/New_York\",\n JFK: \"America/New_York\",\n LGA: \"America/New_York\",\n EWR: \"America/New_York\",\n ATL: \"America/New_York\",\n BOS: \"America/New_York\",\n PHL: \"America/New_York\",\n DCA: \"America/New_York\",\n IAD: \"America/New_York\",\n BWI: \"America/New_York\",\n MIA: \"America/New_York\",\n MCO: \"America/New_York\",\n TPA: \"America/New_York\",\n CLT: \"America/New_York\",\n RDU: \"America/New_York\",\n CLE: \"America/New_York\",\n PIT: \"America/New_York\",\n BUF: \"America/New_York\",\n DTW: \"America/Detroit\",\n IND: \"America/Indiana/Indianapolis\",\n CVG: \"America/New_York\",\n CMH: \"America/New_York\",\n SYR: \"America/New_York\",\n ALB: \"America/New_York\",\n BTV: \"America/New_York\",\n ORF: \"America/New_York\",\n RIC: \"America/New_York\",\n GSO: \"America/New_York\",\n CHS: \"America/New_York\",\n SAV: \"America/New_York\",\n JAX: \"America/New_York\",\n RSW: \"America/New_York\",\n PBI: \"America/New_York\",\n FLL: \"America/New_York\",\n // Central (UTC-6 standard / UTC-5 DST)\n ORD: \"America/Chicago\",\n MDW: \"America/Chicago\",\n DFW: \"America/Chicago\",\n DAL: \"America/Chicago\",\n IAH: \"America/Chicago\",\n HOU: \"America/Chicago\",\n MSP: \"America/Chicago\",\n STL: \"America/Chicago\",\n MCI: \"America/Chicago\",\n OMA: \"America/Chicago\",\n MKE: \"America/Chicago\",\n MSY: \"America/Chicago\",\n MEM: \"America/Chicago\",\n BNA: \"America/Chicago\",\n OKC: \"America/Chicago\",\n SAT: \"America/Chicago\",\n AUS: \"America/Chicago\",\n DSM: \"America/Chicago\",\n TUL: \"America/Chicago\",\n LIT: \"America/Chicago\",\n BIR: \"America/Chicago\",\n SDF: \"America/Chicago\",\n HSV: \"America/Chicago\",\n BHM: \"America/Chicago\",\n MOB: \"America/Chicago\",\n BTR: \"America/Chicago\",\n SHV: \"America/Chicago\",\n // Mountain (UTC-7 standard / UTC-6 DST)\n DEN: \"America/Denver\",\n SLC: \"America/Denver\",\n ABQ: \"America/Denver\",\n BOI: \"America/Boise\",\n BZN: \"America/Denver\",\n GJT: \"America/Denver\",\n // Arizona: no DST (UTC-7 always)\n PHX: \"America/Phoenix\",\n TUS: \"America/Phoenix\",\n // Pacific (UTC-8 standard / UTC-7 DST)\n LAX: \"America/Los_Angeles\",\n SFO: \"America/Los_Angeles\",\n SEA: \"America/Los_Angeles\",\n PDX: \"America/Los_Angeles\",\n LAS: \"America/Los_Angeles\",\n SAN: \"America/Los_Angeles\",\n OAK: \"America/Los_Angeles\",\n SJC: \"America/Los_Angeles\",\n SMF: \"America/Los_Angeles\",\n RNO: \"America/Los_Angeles\",\n FAT: \"America/Los_Angeles\",\n SNA: \"America/Los_Angeles\",\n ONT: \"America/Los_Angeles\",\n BUR: \"America/Los_Angeles\",\n // Alaska (UTC-9 standard / UTC-8 DST)\n ANC: \"America/Anchorage\",\n FAI: \"America/Anchorage\",\n JNU: \"America/Juneau\",\n // Hawaii (UTC-10, no DST)\n HNL: \"Pacific/Honolulu\",\n OGG: \"Pacific/Honolulu\",\n KOA: \"Pacific/Honolulu\",\n // International (iter-6 H12): minimal set required to un-skip the\n // case-5 RJTT year-wrap cache behavior test. Python's\n // `mostlyright.snapshot._resolve_tz` falls back to the broader STATIONS\n // registry for intl ICAOs; the TS port hasn't ported that fallback\n // yet (tracked as TS-W6 — exhaustive intl-station tz coverage). This\n // entry closes H12 cleanly without pulling the whole STATIONS map in.\n // ICAO key (RJTT) — international stations have no 3-letter NWS code.\n // Tokyo Haneda — UTC+9 LST, no DST.\n RJTT: \"Asia/Tokyo\",\n});\n\n/** Reference UTC moment in January (no DST in Northern Hemisphere US). */\nexport const _JAN_REF = new Date(Date.UTC(2024, 0, 15, 12, 0, 0));\n\n/** NWS CLI typical publication delay: 10 h after midnight LST. */\nexport const _CLI_PUBLICATION_DELAY_HOURS = 10.0;\n\n/** Kalshi market typical close time (LST). */\nexport const _MARKET_CLOSE_HOUR_LST = 16;\nexport const _MARKET_CLOSE_MINUTE_LST = 30;\n\n// ---------------------------------------------------------------------------\n// LST offset extraction\n// ---------------------------------------------------------------------------\n\nconst _OFFSET_CACHE = new Map<string, number>();\n\n/**\n * Return the LOCAL STANDARD TIME UTC offset (in hours) for an IANA tz,\n * sampled from January 15 2024 12:00 UTC so the result is never affected\n * by DST in the Northern Hemisphere.\n *\n * Implementation: format `_JAN_REF` in the target tz via Intl.DateTimeFormat\n * and diff against the UTC formatted view to recover the offset.\n */\nexport function _lstOffsetHours(stationTz: string): number {\n const cached = _OFFSET_CACHE.get(stationTz);\n if (cached !== undefined) return cached;\n\n // We compute: localComponents(stationTz, _JAN_REF) − utcComponents(_JAN_REF).\n // The difference gives the tz offset in (hours). Negative for west of UTC.\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: stationTz,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n const parts = fmt.formatToParts(_JAN_REF);\n const get = (type: string): number => {\n const part = parts.find((p) => p.type === type);\n if (!part) {\n throw new Error(`Intl.DateTimeFormat missing ${type} for tz=${stationTz}`);\n }\n return Number(part.value);\n };\n\n const year = get(\"year\");\n const month = get(\"month\");\n const day = get(\"day\");\n let hour = get(\"hour\");\n const minute = get(\"minute\");\n const second = get(\"second\");\n // Some locales return hour \"24\" instead of \"00\" for midnight; normalize.\n if (hour === 24) hour = 0;\n\n // Compute the timezone's wall-clock for _JAN_REF treated as UTC.\n const localAsUtc = Date.UTC(year, month - 1, day, hour, minute, second);\n const offsetMs = localAsUtc - _JAN_REF.getTime();\n const offsetHours = offsetMs / 3_600_000;\n _OFFSET_CACHE.set(stationTz, offsetHours);\n return offsetHours;\n}\n\n// ---------------------------------------------------------------------------\n// Station code normalization + tz lookup\n// ---------------------------------------------------------------------------\n\nfunction _stationCodeNormalized(station: string): string {\n const s = station.trim().toUpperCase();\n if (s.length === 4 && s.startsWith(\"K\")) {\n return s.substring(1);\n }\n return s;\n}\n\n/**\n * Resolve a station code (NWS 3-letter, ICAO 4-letter) to an IANA tz string.\n * Honors `tzOverride` first, then the built-in `_STATION_TZ` map.\n * Throws if no tz can be resolved.\n */\nexport function _resolveStationTz(station: string, tzOverride?: string): string {\n if (tzOverride) return tzOverride;\n const code = _stationCodeNormalized(station);\n const tz = _STATION_TZ[code];\n if (tz) return tz;\n throw new Error(\n `Unknown station timezone: ${JSON.stringify(code)}. Add it to _STATION_TZ or pass tzOverride=\"America/...\".`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// as_of parsing\n// ---------------------------------------------------------------------------\n\nfunction _parseAsOf(asOf: Date | string): Date {\n if (asOf instanceof Date) {\n if (Number.isNaN(asOf.getTime())) {\n throw new Error(\"Invalid Date passed as asOf\");\n }\n return asOf;\n }\n let s = asOf.trim();\n // Python: bare ISO without tz → assume UTC.\n if (s.endsWith(\"Z\")) {\n // Date.parse handles \"Z\" natively.\n } else if (!/[+-]\\d{2}:?\\d{2}$/.test(s)) {\n // No timezone suffix — treat as UTC.\n s = `${s}Z`;\n }\n const ms = Date.parse(s);\n if (!Number.isFinite(ms)) {\n throw new Error(`Invalid as_of string: ${JSON.stringify(asOf)}`);\n }\n return new Date(ms);\n}\n\n// ---------------------------------------------------------------------------\n// Public surface\n// ---------------------------------------------------------------------------\n\nfunction _pad2(n: number): string {\n return n < 10 ? `0${n}` : `${n}`;\n}\n\nfunction _isoDate(year: number, month: number, day: number): string {\n return `${year}-${_pad2(month)}-${_pad2(day)}`;\n}\n\n/**\n * Return the Kalshi settlement date (YYYY-MM-DD LST) for a UTC moment.\n *\n * Kalshi NHIGH/NLOW contracts cover midnight–midnight LOCAL STANDARD TIME.\n * DST is ignored: the window is always fixed to the standard UTC offset.\n */\nexport function settlementDateFor(\n asOf: Date | string,\n station: string,\n tzOverride?: string,\n): string {\n const utcDt = _parseAsOf(asOf);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n // offsetHours is negative for US stations → lstMs < utcMs.\n const lstMs = utcDt.getTime() + offsetHours * 3_600_000;\n const lst = new Date(lstMs);\n // Use getUTC* because we already shifted the epoch by the LST offset.\n return _isoDate(lst.getUTCFullYear(), lst.getUTCMonth() + 1, lst.getUTCDate());\n}\n\n/**\n * Return UTC start/end of the Kalshi settlement window for a date.\n * The window is midnight-midnight LST, expressed in UTC.\n */\nexport function settlementWindowUtc(\n dateStr: string,\n station: string,\n tzOverride?: string,\n): [Date, Date] {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for settlement window: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n // midnight LST = 00:00 LST = (00:00 UTC) − offset (offset is negative)\n // Example: UTC-5 → midnight LST = 05:00 UTC.\n const midnightLstAsUtcMs = Date.UTC(year, month - 1, day, 0, 0, 0);\n const startMs = midnightLstAsUtcMs - offsetHours * 3_600_000;\n const start = new Date(startMs);\n const end = new Date(startMs + 24 * 3_600_000);\n return [start, end];\n}\n\n/**\n * Return the UTC time at which the NWS CLI for a date is expected to be\n * available. Default delay is 10 h after midnight LST on the next day.\n */\nexport function cliAvailableAt(\n dateStr: string,\n station: string,\n delayHours: number = _CLI_PUBLICATION_DELAY_HOURS,\n tzOverride?: string,\n): Date {\n const [, windowEnd] = settlementWindowUtc(dateStr, station, tzOverride);\n return new Date(windowEnd.getTime() + delayHours * 3_600_000);\n}\n\n/**\n * Return the UTC time of the Kalshi market close for a settlement date.\n * Kalshi NHIGH/NLOW markets close at 4:30 PM LST on the day of settlement.\n */\nexport function marketCloseUtc(dateStr: string, station: string, tzOverride?: string): Date {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for market close: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n const marketCloseAsUtcMs = Date.UTC(\n year,\n month - 1,\n day,\n _MARKET_CLOSE_HOUR_LST,\n _MARKET_CLOSE_MINUTE_LST,\n 0,\n );\n return new Date(marketCloseAsUtcMs - offsetHours * 3_600_000);\n}\n","// Cache-skip rule predicates — pure functions over inputs.\n//\n// Mirrors `packages/weather/src/mostlyright/weather/cache.py`:\n// - `_is_current_lst_month` / `_is_current_lst_year`\n// - `_is_live_source`\n//\n// Plus one TS-NEW addition required by TS-CACHE-02:\n// - `isWithinVolatileWindow` (30-day volatile-window check for archive\n// endpoints). Python's `cache.py` predates this rule; back-porting to\n// Python is tracked as a CROSS-SDK-SYNC parity ticket.\n//\n// All functions accept an optional `now: Date` test seam — production\n// callers pass `new Date()` once at the call site (plan 06).\n\nimport { STATION_BY_CODE, STATION_BY_ICAO } from \"../../data/generated/stations.js\";\nimport { _lstOffsetHours } from \"../../snapshot.js\";\n\n/** Resolve a station identifier (3-letter code OR 4-letter ICAO) to LST offset hours. */\nfunction _lstOffsetHoursFor(station: string): number {\n const upper = station.trim().toUpperCase();\n const byCode = STATION_BY_CODE.get(upper);\n if (byCode !== undefined) return _lstOffsetHours(byCode.tz);\n const byIcao = STATION_BY_ICAO.get(upper);\n if (byIcao !== undefined) return _lstOffsetHours(byIcao.tz);\n if (upper.length === 4 && upper.startsWith(\"K\")) {\n const stripped = upper.slice(1);\n const retry = STATION_BY_CODE.get(stripped);\n if (retry !== undefined) return _lstOffsetHours(retry.tz);\n }\n throw new RangeError(`unknown station: ${JSON.stringify(station)}`);\n}\n\n/**\n * Compute the station's current LST wall-clock as a UTC Date offset by the\n * LST hour shift. Use `getUTC*` to read fields (we already shifted the epoch).\n */\nfunction _nowLst(station: string, now: Date = new Date()): Date {\n const offsetHours = _lstOffsetHoursFor(station);\n return new Date(now.getTime() + offsetHours * 3_600_000);\n}\n\n/**\n * True iff `(year, month)` is the current LST month for `station`.\n *\n * Mirrors Python `_is_current_lst_month`. The current month is mutable\n * (observations still arriving) — caching it would serve stale data.\n */\nexport function shouldSkipCacheForCurrentLstMonth(\n station: string,\n year: number,\n month: number,\n now?: Date,\n): boolean {\n const lst = _nowLst(station, now);\n return lst.getUTCFullYear() === year && lst.getUTCMonth() + 1 === month;\n}\n\n/**\n * True iff `year` is the current LST year for `station`. Annual analog of\n * the monthly variant — gates the climate cache.\n */\nexport function shouldSkipCacheForCurrentLstYear(\n station: string,\n year: number,\n now?: Date,\n): boolean {\n const lst = _nowLst(station, now);\n return lst.getUTCFullYear() === year;\n}\n\n/**\n * True iff `(year, month)` is a **strictly past** UTC month relative to\n * `now` — i.e. cacheable on the strictest possible temporal axis.\n *\n * iter-12 C14: `shouldSkipCacheForCurrentLstMonth` and `isMonthVolatile`\n * (lives in `meta/src/research.ts`) only catch the *current* LST month\n * and the immediate post-month volatile tail. Both predicates return\n * false for months that lie in the FUTURE relative to `now`, or for the\n * current UTC month when the station's LST is still in the prior UTC\n * month (negative tz offsets near UTC midnight). An empty / partial\n * fetch for such a month would be persisted and later served as\n * \"complete.\" `isWritableMonth` is a stricter additional gate: it\n * requires the (year, month) to be lexicographically less than the\n * UTC current month, so neither future months nor the partial current\n * UTC month are ever cacheable — regardless of any station's LST.\n *\n * Mirrors Python `cache.py:_is_current_lst_month`'s implicit invariant\n * (Python paths use parquet-on-disk which can't be written for future\n * dates because the cache root never spawns those years). TS callers\n * MUST gate cache reads AND writes on this predicate before applying\n * the LST / volatile-window gates.\n */\nexport function isWritableMonth(year: number, month: number, now: Date): boolean {\n const nowYear = now.getUTCFullYear();\n const nowMonth = now.getUTCMonth() + 1; // 1-12\n if (year < nowYear) return true;\n if (year > nowYear) return false;\n return month < nowMonth;\n}\n\n/**\n * True iff `year` is a **strictly past** UTC year relative to `now` —\n * the annual analog of `isWritableMonth`.\n *\n * iter-12 C15: `shouldSkipCacheForCurrentLstYear` only catches the\n * current LST year. It misses (a) future years, which would silently\n * cache empty/incomplete data, and (b) the UTC Jan-1 boundary window\n * where the station's LST is still in the prior calendar year (negative\n * tz offsets) but the UTC year has already rolled over — without this\n * gate the new UTC year, which is mutable, could be written. Stricter\n * additional gate: require `year < now.getUTCFullYear()`. TS callers\n * MUST gate cache reads AND writes on this predicate before applying\n * the LST / volatile-window gates.\n */\nexport function isWritableYear(year: number, now: Date): boolean {\n return year < now.getUTCFullYear();\n}\n\n/**\n * True iff `source` ends with `.live`.\n *\n * Mirrors Python `_is_live_source` byte-equivalently — accepts null /\n * undefined / empty (returns false in all three cases).\n */\nexport function isLiveSource(source: string | null | undefined): boolean {\n return typeof source === \"string\" && source.length > 0 && source.endsWith(\".live\");\n}\n\n/**\n * **TS-NEW** addition per TS-CACHE-02: archive endpoints within `days` days\n * of `archiveAsOf` are treated as volatile (some sources amend their\n * published data for ~30 days post-event). NOT a Python port today — file\n * a CROSS-SDK-SYNC parity ticket if Python adopts it.\n *\n * Returns true iff `eventDate` falls within `[archiveAsOf - days, archiveAsOf]`\n * (inclusive at both endpoints — an event exactly `days` days before\n * `archiveAsOf` is still volatile and MUST be re-fetched).\n *\n * Events AFTER `archiveAsOf` are never volatile by this rule (deltaDays < 0).\n */\nexport function isWithinVolatileWindow(eventDate: string, archiveAsOf: string, days = 30): boolean {\n const e = Date.parse(`${eventDate}T00:00:00Z`);\n const a = Date.parse(`${archiveAsOf}T00:00:00Z`);\n if (!Number.isFinite(e) || !Number.isFinite(a)) {\n throw new RangeError(\n `invalid YYYY-MM-DD: eventDate=${JSON.stringify(eventDate)} archiveAsOf=${JSON.stringify(archiveAsOf)}`,\n );\n }\n const deltaDays = (a - e) / 86_400_000;\n return deltaDays >= 0 && deltaDays <= days;\n}\n","// Cache-key generators — pure functions producing the canonical key strings\n// `cacheKeyForObservations(station, year, month)` and\n// `cacheKeyForClimate(station, year)`. Matches the Python file-path zero-\n// padded month/year scheme (Python: `01.parquet`, TS: `:01`).\n//\n// Station is upper-cased but NOT validated here (validation is the\n// orchestrator's job; this module stays pure).\n//\n// iter-7 H13 / H14: an optional `source` segment was added to\n// `cacheKeyForObservations` so the multi-source observations cache\n// (IEM ASOS + GHCNh) does not collide on the same `(station, year, month)`\n// triplet. Python writes one parquet per month containing the merged\n// observations from all sources; the TS orchestrator caches per-source\n// chunks pre-merge (because the fetchers are independent paths) and\n// disambiguates with the source segment. Omitting `source` preserves the\n// legacy 3-arg key shape — useful for tests that don't care about source\n// (sentinel preloads, fixture replays) and matches the canonical\n// Python-parity contract for callers that have already pre-merged sources.\n\nconst MIN_YEAR = 1900;\nconst MAX_YEAR = 2100;\nconst SOURCE_RE = /^[a-z0-9_-]+$/;\n\n/**\n * Build the canonical observations cache key.\n *\n * Examples:\n * `cacheKeyForObservations(\"KNYC\", 2025, 1)` →\n * `\"mostlyright:v1:observations:KNYC:2025:01\"`.\n * `cacheKeyForObservations(\"KNYC\", 2025, 1, \"iem\")` →\n * `\"mostlyright:v1:observations:KNYC:2025:01:iem\"`.\n *\n * The `source` segment (optional, lowercase alphanumeric / hyphen /\n * underscore) namespaces per-source pre-merge chunks so IEM ASOS and\n * GHCNh writes for the same `(station, year, month)` do not collide.\n * Omit for back-compat (sentinel preloads, fixture replays).\n */\nexport function cacheKeyForObservations(\n station: string,\n year: number,\n month: number,\n source?: string,\n): string {\n if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {\n throw new RangeError(`year out of range: ${year}`);\n }\n if (!Number.isInteger(month) || month < 1 || month > 12) {\n throw new RangeError(`month out of range: ${month}`);\n }\n const yyyy = String(year).padStart(4, \"0\");\n const mm = String(month).padStart(2, \"0\");\n const base = `mostlyright:v1:observations:${station.toUpperCase()}:${yyyy}:${mm}`;\n if (source === undefined) return base;\n if (typeof source !== \"string\" || !SOURCE_RE.test(source)) {\n throw new RangeError(\n `source must match ${SOURCE_RE.source} (lowercase alnum / hyphen / underscore); got ${JSON.stringify(source)}`,\n );\n }\n return `${base}:${source}`;\n}\n\n/**\n * Build the canonical climate cache key (annual).\n *\n * Example: `cacheKeyForClimate(\"KNYC\", 2025)` →\n * `\"mostlyright:v1:climate:KNYC:2025\"`.\n */\nexport function cacheKeyForClimate(station: string, year: number): string {\n if (!Number.isInteger(year) || year < MIN_YEAR || year > MAX_YEAR) {\n throw new RangeError(`year out of range: ${year}`);\n }\n const yyyy = String(year).padStart(4, \"0\");\n return `mostlyright:v1:climate:${station.toUpperCase()}:${yyyy}`;\n}\n"],"mappings":"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AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,OAAO,SAAS,EAAE,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AACxB,CAAC;AAEM,IAAM,kBAAoD,oBAAI,IAAyB;AAAA,EAC5F,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,CAAE;AAAA,EACtB,CAAC,QAAQ,SAAS,EAAE,C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","names":["FsStore"]}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { C as
|
|
1
|
+
import { c as CacheStore } from '../versionedCacheStore-DyHDqFIC.cjs';
|
|
2
|
+
export { C as CACHE_SCHEMA_VERSION, a as CacheEntry, b as CacheSetOptions, D as INDEXEDDB_DB_NAME, I as IndexedDBStore, d as IndexedDBStoreOptions, M as MemoryStore, C as VERSIONED_CACHE_SCHEMA_VERSION, e as cacheKeyForClimate, f as cacheKeyForObservations, i as isLiveSource, g as isWithinVolatileWindow, h as isWritableMonth, j as isWritableYear, l as lockKeyFor, s as shouldSkipCacheForCurrentLstMonth, k as shouldSkipCacheForCurrentLstYear, v as versionedCacheStore } from '../versionedCacheStore-DyHDqFIC.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Auto-detect the best CacheStore for the current runtime.
|
|
6
6
|
*
|
|
7
7
|
* Returns a NEW instance per call.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Phase 21 21-03 (iter-1 fix per codex + ts-architect CRITICAL): the
|
|
10
|
+
* concrete store is wrapped in `versionedCacheStore(CACHE_SCHEMA_VERSION)`
|
|
11
|
+
* so pre-Phase-18 cache entries (no version sidecar, or wrong version)
|
|
12
|
+
* silently miss instead of returning stale `0.06°F`-precision rows. The
|
|
13
|
+
* wrap is transparent — callers see the same `CacheStore` interface.
|
|
14
|
+
*
|
|
15
|
+
* @returns a Promise resolving to a fresh CacheStore (wrapped). The
|
|
16
|
+
* Node-only `FsStore` is loaded via dynamic import behind a runtime
|
|
17
|
+
* feature detect, so browser / MV3 / edge bundles never pull
|
|
18
|
+
* `node:fs/promises` et al. (iter-1 H3 fix).
|
|
13
19
|
*/
|
|
14
20
|
declare function defaultCacheStore(): Promise<CacheStore>;
|
|
15
21
|
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { C as
|
|
1
|
+
import { c as CacheStore } from '../versionedCacheStore-DyHDqFIC.js';
|
|
2
|
+
export { C as CACHE_SCHEMA_VERSION, a as CacheEntry, b as CacheSetOptions, D as INDEXEDDB_DB_NAME, I as IndexedDBStore, d as IndexedDBStoreOptions, M as MemoryStore, C as VERSIONED_CACHE_SCHEMA_VERSION, e as cacheKeyForClimate, f as cacheKeyForObservations, i as isLiveSource, g as isWithinVolatileWindow, h as isWritableMonth, j as isWritableYear, l as lockKeyFor, s as shouldSkipCacheForCurrentLstMonth, k as shouldSkipCacheForCurrentLstYear, v as versionedCacheStore } from '../versionedCacheStore-DyHDqFIC.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Auto-detect the best CacheStore for the current runtime.
|
|
6
6
|
*
|
|
7
7
|
* Returns a NEW instance per call.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Phase 21 21-03 (iter-1 fix per codex + ts-architect CRITICAL): the
|
|
10
|
+
* concrete store is wrapped in `versionedCacheStore(CACHE_SCHEMA_VERSION)`
|
|
11
|
+
* so pre-Phase-18 cache entries (no version sidecar, or wrong version)
|
|
12
|
+
* silently miss instead of returning stale `0.06°F`-precision rows. The
|
|
13
|
+
* wrap is transparent — callers see the same `CacheStore` interface.
|
|
14
|
+
*
|
|
15
|
+
* @returns a Promise resolving to a fresh CacheStore (wrapped). The
|
|
16
|
+
* Node-only `FsStore` is loaded via dynamic import behind a runtime
|
|
17
|
+
* feature detect, so browser / MV3 / edge bundles never pull
|
|
18
|
+
* `node:fs/promises` et al. (iter-1 H3 fix).
|
|
13
19
|
*/
|
|
14
20
|
declare function defaultCacheStore(): Promise<CacheStore>;
|
|
15
21
|
|