Guides
Browser integration
The TypeScript SDK is engineered for browser runtimes alongside Node. This guide covers running it across the major browser-class targets — vanilla web apps, Web/Service Workers, and serverless edge — plus the CORS posture each one inherits.
CORS posture
Section titled “CORS posture”| Source | Browser direct-fetch |
|---|---|
AWC live (/api/data/) | ✅ allowed |
IEM CLI (/api/1/climodat_xref.json) | ✅ allowed |
IEM ASOS (/cgi-bin/request/asos.py) | ❌ blocked (no Access-Control-Allow-Origin) |
| GHCNh PSV (NCEI) | ❌ blocked |
Polymarket Gamma (gamma-api.polymarket.com) | ❌ blocked from * origins |
Browser code (a page, a Web Worker, or a Service Worker) is bound by the same-origin policy, so the blocked endpoints can’t be fetched directly from a tab. Route those through a proxy that adds Access-Control-Allow-Origin — see Limitations.
Pattern 1 — Web / Service Worker
Section titled “Pattern 1 — Web / Service Worker”Workers run as ES modules with fetch, crypto.subtle, and IndexedDB available, so the SDK runs unchanged off the main thread. Import it and call directly — keeping the work in a worker frees the UI thread and lets IndexedDB-backed caching persist across worker restarts.
// worker.ts → bundled as an ES module, loaded via new Worker(url, { type: "module" })import { research } from "mostlyright";import { kalshiSettlementFor } from "@mostlyrightmd/markets";
self.addEventListener("message", async (event) => { const { station, fromDate, toDate } = event.data; try { const rows = await research(station, fromDate, toDate); self.postMessage({ ok: true, rows }); } catch (err) { self.postMessage({ ok: false, error: (err as Error).message }); }});The page posts a request to the worker and reads results back off the message channel — no SDK-specific wiring required, just standard postMessage.
- CSP-safe. All bundles are CSP-clean (
script-src 'self'); noeval, nonew Function(...), no remote code.ajvruns inajv-standaloneprecompiled form, never runtime. This keeps the SDK loadable under strict Content-Security-Policy headers. - Bundle size. The
@mostlyrightmd/coremain entry stays under 25 KB min+gzip. Subpath imports (@mostlyrightmd/core/temporal,/discovery,/transforms) tree-shake separately.
Pattern 2 — IIFE for vanilla web apps
Section titled “Pattern 2 — IIFE for vanilla web apps”The meta IIFE bundle (mostlyright) inlines the three scoped packages, so a single <script> tag is enough. Limited to CORS-allowed endpoints (AWC + IEM CLI).
<script src="https://unpkg.com/mostlyright/dist/index.global.js"></script><script> mostlyright.research("KNYC", "2025-01-06", "2025-01-12").then(console.log);</script>globalThis.mostlyright exposes research() and the rest of the meta surface.
Pattern 3 — Cloudflare / Bun / Deno edge
Section titled “Pattern 3 — Cloudflare / Bun / Deno edge”ES-module Workers and edge runtimes have fetch + IndexedDB shim (Workers) or full filesystem (Bun/Deno).
// worker.ts (Cloudflare Worker)import { research } from "mostlyright";import { MemoryStore } from "@mostlyrightmd/core/internal/cache";
export default { async fetch(req: Request): Promise<Response> { const url = new URL(req.url); const station = url.searchParams.get("station") ?? "KNYC"; const rows = await research(station, "2025-01-06", "2025-01-12", { cache: new MemoryStore(), // Workers — no persistent FS }); return Response.json(rows); },};For Bun + Deno, the default cache auto-detects FS support.
Cache backends per runtime
Section titled “Cache backends per runtime”| Runtime | Detected store | Notes |
|---|---|---|
| Service Worker | IndexedDB | Persistent across worker restarts |
| Browser tab / IIFE | IndexedDB | Per-origin quota (~2 GB typical) |
| Node.js | FsStore | $HOME/.mostlyright/cache-ts/ |
| Cloudflare Worker | MemoryStore | Per-isolate; lost on cold start |
| Bun / Deno | FsStore | If process.versions.node is shimmed; else memory |
Override with { cache: <YourStore> } per call — see the cache guide.
Limitations
Section titled “Limitations”ASOS + GHCNh in the browser — proxy required. The CORS-blocked sources can’t be fetched directly from a page or worker. Options:
- Use a proxy — a Cloudflare Worker, your own backend, etc. — that adds
Access-Control-Allow-Origin: *. Point the SDK at it instead of the origin. - Move the call server-side — run the SDK on the edge (see Pattern 3) or in Node, and have the browser fetch from your endpoint.
- Pre-compute with Python + serialize to JSON, ship alongside your TS bundle.
NWP forecasts in the browser — see NWP forecasts in TypeScript. Deferred to v2.0+; signature-stable stub today.
Climate gaps in the browser — see Climate gaps. Same v2.0+ deferral.
See also
Section titled “See also”- Cache — IndexedDB, FsStore, MemoryStore detection + override
- TypeScript quickstart — first-call walkthrough
- Live streaming — async generators in browser/Node