meac-wind
v0.3.0
Published
TypeScript client for fetching and parsing public MEAC wind page data
Maintainers
Readme
meac-wind
TypeScript client for fetching and parsing public MEAC wind page data.
Data source & usage
This package fetches and parses publicly available wind measurement data from MEAC (Metrologiska Mätsystem AB), a Swedish company specializing in meteorological measurement systems.
Data credit: Wind measurement data © MEAC - Metrologiska Mätsystem AB
This is an unofficial package — not affiliated with or endorsed by MEAC. It may break if MEAC changes their website. The MIT license applies to this package's code only, not to the source data.
Allowed use
- Personal projects — learning, hobby dashboards
- Research — academic or weather analysis
- Low-frequency access — occasional fetches with caching
- Attribution — credit MEAC when displaying data
Best practices
- Rate limiting — cache responses (at least 1 minute between requests); use exponential backoff on retries
- Identification — use a transparent user-agent that identifies your app (see Best practices)
- Attribution — credit MEAC and link to https://meac.se in your UI
- Error handling — respect HTTP 429/503; don't retry indefinitely
Prohibited use
- High-frequency automated fetching (don't treat this as a real-time API)
- Commercial redistribution of MEAC's data
- Server abuse or circumventing blocks if MEAC restricts access
High-volume or commercial use
For real-time access, sub-minute updates, commercial apps, or SLAs, contact MEAC directly — they may offer API access, data feeds, or licensing.
If you represent MEAC and have concerns about this package, open an issue on the GitHub repository or contact the maintainer. Takedown or operational change requests will be honored promptly.
What it does
- Fetches the MEAC wind page HTML.
- Decodes the response as
iso-8859-1. - Under the hood, uses HTML scraping to parse the source page structure.
- Extracts:
- Last update timestamp
- Location
- Current wind strength
- Temperature
- Wind direction
- Statistics (
max,average,min) — parsed in DOM order (Max → Medel → Min) - Wind history from
javascript: alert(...)links
- Validates the final payload with Zod.
Installation
This package is ESM-only ("type": "module"). Use import in Node 18+ or a bundler that supports ESM.
npm install meac-windMain API
The public entry point is fetchWindData. It requires a location slug parameter.
import { fetchWindData } from "meac-wind";
// Get data from Hummeln Åre
const data = await fetchWindData("hummeln");
console.log(data);Documented location slugs (see KNOWN_SLUGS); any MEAC path slug string is accepted:
import { fetchWindData, KNOWN_SLUGS } from "meac-wind";
console.log(KNOWN_SLUGS); // ["hummeln", "sundsvallshamn", "helags"]import { fetchWindData } from "meac-wind";
// Get data from Sundsvalls hamn
const data = await fetchWindData("sundsvallshamn");
// Get data from Helags
const data = await fetchWindData("helags");
// Or use any other MEAC location slug
const data = await fetchWindData("your-location-slug");TypeScript Support
The package includes TypeScript definitions. Import types as needed:
import {
fetchWindData,
isKnownSlug,
KNOWN_SLUGS,
WindDataFetchError,
WindDataValidationError,
type FetchWindDataOptions,
type WindData,
type WindStatistics,
type Slug,
} from "meac-wind";
if (isKnownSlug("hummeln")) {
// slug is typed as KnownSlug
}Request options
import { fetchWindData } from "meac-wind";
// Timeout after 10 seconds (uses AbortSignal.timeout)
const data = await fetchWindData("hummeln", { timeoutMs: 10_000 });
// Or supply your own AbortSignal (timeoutMs is ignored when signal is set)
const controller = new AbortController();
const data2 = await fetchWindData("hummeln", { signal: controller.signal });Slugs must match [a-z][a-z0-9-]{0,63} (lowercase letters, digits, hyphens). Invalid slugs throw WindDataFetchError before any network request.
Returned data shape
type WindHistoryEntry = {
speed: number;
timestamp: string;
};
type WindStatistics = {
max: number;
average: number;
min: number;
};
type WindData = {
// ISO-like local timestamp from the page (no timezone offset applied)
lastUpdate: string; // YYYY-MM-DDTHH:MM:SS
location: string;
windStrength: number;
temperature: number;
windDirection: number; // 0..360
statistics: WindStatistics;
history: WindHistoryEntry[];
};Validation rules
Validation is implemented in src/schemas.ts:
lastUpdatemust matchYYYY-MM-DDTHH:MM:SS(parser output format). This is the timestamp string shown on the MEAC page, not a timezone-aware instant; invalid calendar dates are not rejected.windStrength, statistics values, and historyspeedare non-negative.windDirectionmust be between0and360.historyis an array of{ speed, timestamp }.
Statistics are read by matching .meac_label text (Max, Medel, Min) to the wind speed in the same table row. Parsed values must satisfy max >= average >= min.
Best practices for production use
1. Implement Rate Limiting
import { fetchWindData } from "meac-wind";
// Cache data and don't fetch more than once per minute
let cachedData: WindData | null = null;
let lastFetch = 0;
async function getCachedWindData(slug: string) {
const now = Date.now();
if (cachedData && now - lastFetch < 60000) {
// 1 minute cache
return cachedData;
}
cachedData = await fetchWindData(slug);
lastFetch = now;
return cachedData;
}2. Handle Errors Gracefully
fetchWindData throws WindDataFetchError (network, HTTP, or parse failures) or WindDataValidationError (schema mismatch). Both extend Error.
import {
fetchWindData,
WindDataFetchError,
WindDataValidationError,
} from "meac-wind";
try {
const data = await fetchWindData("hummeln");
console.log(data);
} catch (error) {
if (error instanceof WindDataValidationError) {
console.error("Invalid parsed data:", error.details);
} else if (error instanceof WindDataFetchError) {
console.error("Fetch or parse failed:", error.message, error.cause);
}
}3. Identify your app
The package sends a transparent user-agent (meac-wind/<version>). Forks and apps built on top should use their own identifier with a contact URL, for example:
"user-agent": "my-wind-dashboard/1.0 (+https://github.com/you/my-app)"Releases
Releases are automated with release-please on main. Typical flow:
- Work on a feature branch or
dev; open PRs intodev(or directly tomainif you prefer). - Squash-merge into
dev, then squash-mergedev→mainwhen ready. - On
main, release-please opens or updates a Release PR (version bump +CHANGELOG.md). - Merge that Release PR on
mainto create the GitHub release and publish to npm.
Commit messages
release-please reads Conventional Commits on main. For squash merges, set the squash commit title to a conventional message (often the PR title), e.g. feat: add Sundsvall slug or fix: handle empty wind history.
npm trusted publishing (one-time setup)
Publishing uses npm trusted publishing (OIDC) — no NPM_TOKEN in GitHub secrets.
On npmjs.com → package meac-wind → Settings → Trusted Publisher → GitHub Actions, set:
| Field | Value |
| --- | --- |
| Organization or user | thejoltjoker |
| Repository | meac-wind |
| Workflow filename | release-please.yml |
Enable Require two-factor authentication and Disallow tokens on the package if you want publish-only-via-OIDC.
In the repo, enable Settings → Actions → General → Allow GitHub Actions to create and approve pull requests so release-please can open Release PRs.
Development
Building
npm run buildThis compiles TypeScript files from src/ to dist/.
Testing
npm test # Run tests once
npm run test:watch # Run tests in watch modeSource Files
Current source files:
src/index.ts- public API, fetch, and Zod validation.src/parser.ts- HTML document parsing (panels, statistics, history).src/schemas.ts- Zod schemas and exported types.src/types.ts- slug types andisKnownSlug.src/slug.ts- slug format validation.src/options.ts-FetchWindDataOptionsand abort/timeout handling.src/index.test.ts- Vitest tests with mocked fetch responses.src/index.live.test.ts- optional live test (MEAC_LIVE=1 npm test; skipped by default).
The test suite uses Vitest with mocked fetch responses and checks:
- successful parsing for normal and Swedish numeric formats
- slug-based URL construction
- handling of malformed or missing HTML
- validation failures for invalid ranges and
lastUpdateformat - custom error types and edge cases in the validation error path
License & disclaimer
Code: MIT — applies to this repository's source and documentation only, not to wind data fetched from MEAC or other third parties.
Disclaimer: This package is an unofficial community tool, not a meteorological service. MEAC names and data remain the property of their respective owners; references are for identification and attribution only.
Use at your own risk. The software and any parsed data are provided "as is" without warranty of accuracy, completeness, timeliness, or continued availability. The parser may break if MEAC changes their site, and access may be restricted at any time.
Do not rely on this for safety-critical decisions (aviation, maritime, mountaineering, emergency response, construction, or similar). For authoritative or licensed data, contact MEAC directly.
You are responsible for how you use this package - including compliance with applicable laws, website terms, rate limits, and intellectual-property rights. The maintainer does not encourage excessive automated access, circumvention of blocks, or redistribution of third-party data against applicable policies.
There is no guarantee of maintenance or support. To the extent permitted by law, liability is limited as described in LICENSE.md. If you represent a rights holder with concerns, open a GitHub issue and good-faith requests will be honored promptly.
