metweave 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YaoJaro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # metweave
2
+
3
+ 把公开气象数据编织成可嵌入产品的图:机场报文(METAR / SPECI)经「解析 → 标准化 → 渲染」一条管道,变成报文卡片与地图图层。数据直连公开源(IEM currents 端点 CORS 全开,浏览器可直连);解析与渲染全部在浏览器端完成——报文不经过你自己的服务器,无需自建后端。
4
+
5
+ ### 安装
6
+
7
+ ```bash
8
+ npm install metweave @metweave/leaflet leaflet
9
+ # TypeScript 用户另装类型:npm install -D @types/leaflet
10
+ ```
11
+
12
+ ### 快速开始
13
+
14
+ 底图请在 [天地图](https://console.tianditu.gov.cn/)申请自己的**浏览器端 key**(服务条款要求按应用申请)。示例把 key 放环境变量,不写进代码:
15
+
16
+ ```ts
17
+ import * as L from "leaflet";
18
+ import "leaflet/dist/leaflet.css"; // 别漏:不引入 CSS 地图不渲染
19
+ import { getMetarReports } from "metweave/sources"; // 取数 → 解析 → 定位
20
+ import { addMetarLayer } from "@metweave/leaflet";
21
+
22
+ const TDT_KEY = import.meta.env.VITE_TIANDITU_KEY; // 你自己的天地图 key
23
+ const map = L.map("map", { center: [35.5, 105], zoom: 4 });
24
+ L.tileLayer(
25
+ `https://t{s}.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TDT_KEY}`,
26
+ { subdomains: ["0", "1", "2", "3", "4", "5", "6", "7"], attribution: "底图 © 天地图" },
27
+ ).addTo(map);
28
+ addMetarLayer(map, await getMetarReports(), { conditionColors: true });
29
+ ```
30
+
31
+ 本包再导出 `@metweave/core` / `parser` / `render`(整条管道一个入口,含 `toValues` 与机读错误类);`metweave/sources` 提供取数 helper。文档与完整示例见[主仓库](https://github.com/yaojaro/metweave)。
32
+
33
+ ## 许可
34
+
35
+ [MIT](https://github.com/yaojaro/metweave/blob/main/LICENSE) © 2026 YaoJaro——授权仅覆盖本仓库的代码与文档;随包分发的报文样本不在其列,详见主仓库 README 的「许可」说明。
package/dist/index.cjs ADDED
@@ -0,0 +1,27 @@
1
+ var _metweave_core = require("@metweave/core");
2
+ Object.keys(_metweave_core).forEach(function(k) {
3
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4
+ enumerable: true,
5
+ get: function() {
6
+ return _metweave_core[k];
7
+ }
8
+ });
9
+ });
10
+ var _metweave_parser = require("@metweave/parser");
11
+ Object.keys(_metweave_parser).forEach(function(k) {
12
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
13
+ enumerable: true,
14
+ get: function() {
15
+ return _metweave_parser[k];
16
+ }
17
+ });
18
+ });
19
+ var _metweave_render = require("@metweave/render");
20
+ Object.keys(_metweave_render).forEach(function(k) {
21
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
22
+ enumerable: true,
23
+ get: function() {
24
+ return _metweave_render[k];
25
+ }
26
+ });
27
+ });
@@ -0,0 +1,3 @@
1
+ export * from "@metweave/core";
2
+ export * from "@metweave/parser";
3
+ export * from "@metweave/render";
@@ -0,0 +1,3 @@
1
+ export * from "@metweave/core";
2
+ export * from "@metweave/parser";
3
+ export * from "@metweave/render";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "@metweave/core";
2
+ export * from "@metweave/parser";
3
+ export * from "@metweave/render";
4
+ export {};
@@ -0,0 +1,131 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _metweave_core = require("@metweave/core");
3
+ let _metweave_parser = require("@metweave/parser");
4
+ //#region src/sources.ts
5
+ const IEM_BASE = "https://mesonet.agron.iastate.edu";
6
+ /**
7
+ * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).
8
+ * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。
9
+ * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。
10
+ */
11
+ function iemCurrentsUrl(network = "CN__ASOS") {
12
+ return `${IEM_BASE}/api/1/currents.json?network=${encodeURIComponent(network)}`;
13
+ }
14
+ /** 观测记录守卫:station/raw 均须为字符串(只查键不查类型是旧洞)。 */
15
+ function isObservation(v) {
16
+ if (typeof v !== "object" || v === null) return false;
17
+ const rec = v;
18
+ return typeof rec.station === "string" && typeof rec.raw === "string";
19
+ }
20
+ /**
21
+ * Fetch IEM whole-network current observations (station/raw/coordinates passed through).
22
+ * 拉取 IEM 整网实况观测(station/raw/坐标透传)。
23
+ * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:
24
+ * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;
25
+ * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);
26
+ * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。
27
+ */
28
+ async function getMetars(network = "CN__ASOS", options = {}) {
29
+ const controller = new AbortController();
30
+ const external = options.signal;
31
+ const forward = () => {
32
+ controller.abort(external?.reason);
33
+ };
34
+ if (external !== void 0) {
35
+ if (external.aborted) forward();
36
+ else external.addEventListener("abort", forward, { once: true });
37
+ }
38
+ const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => controller.abort(new _metweave_core.MetarSourceError("timeout", network, `IEM 请求超时(>${options.timeoutMs}ms,network=${network})`)), options.timeoutMs);
39
+ try {
40
+ let res;
41
+ try {
42
+ res = await fetch(iemCurrentsUrl(network), { signal: controller.signal });
43
+ } catch (err) {
44
+ if (controller.signal.aborted) throw err;
45
+ const reason = err instanceof Error ? err.message : String(err);
46
+ throw new _metweave_core.MetarSourceError("network", network, `网络请求失败(源:IEM,network=${network}):请检查网络连通性后重试(${reason})`, { cause: err });
47
+ }
48
+ if (!res.ok) throw new _metweave_core.MetarSourceError("http-error", network, `IEM HTTP ${res.status}(network=${network})`);
49
+ let body;
50
+ try {
51
+ body = await res.json();
52
+ } catch (err) {
53
+ if (controller.signal.aborted) throw err;
54
+ const reason = err instanceof Error ? err.message : String(err);
55
+ throw new _metweave_core.MetarSourceError("bad-schema", network, `IEM 响应不是合法 JSON(可能被代理/防火墙拦截,network=${network}):${reason}`, { cause: err });
56
+ }
57
+ if (typeof body !== "object" || body === null || !("data" in body) || !Array.isArray(body.data)) throw new _metweave_core.MetarSourceError("bad-schema", network, `IEM 响应异常:缺少 data 数组(network=${network},schema 不符)`);
58
+ const rows = body.data;
59
+ if (!rows.every(isObservation)) throw new _metweave_core.MetarSourceError("bad-schema", network, `IEM 响应异常:data 存在 station/raw 非字符串的记录(network=${network},schema 不符)`);
60
+ if (rows.length === 0) throw new _metweave_core.MetarSourceError("empty-data", network, `IEM 返回空数据(network=${network})——请核对 IEM 网络名(如 CN__ASOS/RU__ASOS,参考 https://mesonet.agron.iastate.edu/sites/networks.php)`);
61
+ return rows;
62
+ } finally {
63
+ if (timer !== void 0) clearTimeout(timer);
64
+ external?.removeEventListener("abort", forward);
65
+ }
66
+ }
67
+ /**
68
+ * Fetch → parse → locate in one step: returns map-ready report items.
69
+ * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。
70
+ *
71
+ * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标
72
+ * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);
73
+ * 源站坐标为 null 且无元数据的行跳过。
74
+ * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;
75
+ * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。
76
+ */
77
+ async function getMetarReports(network = "CN__ASOS", options = {}) {
78
+ const { stations, onUnparseable, ...fetchOptions } = options;
79
+ const table = new Map((stations ?? []).map((s) => [s.icao, s]));
80
+ const observations = await getMetars(network, fetchOptions);
81
+ const items = [];
82
+ const failures = [];
83
+ for (const obs of observations) {
84
+ const station = table.get(obs.station);
85
+ const lat = station?.lat ?? obs.lat;
86
+ const lon = station?.lon ?? obs.lon;
87
+ if (lat === null || lon === null) continue;
88
+ try {
89
+ items.push({
90
+ report: (0, _metweave_parser.parse)(obs.raw),
91
+ position: [lat, lon],
92
+ title: `${obs.station}${station?.name ? ` ${station.name}` : ""}`
93
+ });
94
+ } catch (err) {
95
+ if (!(err instanceof _metweave_core.MetarParseError)) throw err;
96
+ if (onUnparseable !== void 0) {
97
+ onUnparseable({
98
+ station: obs.station,
99
+ raw: obs.raw,
100
+ code: err.code,
101
+ error: err.message
102
+ });
103
+ continue;
104
+ }
105
+ failures.push(`${obs.station}: ${err.message}`);
106
+ }
107
+ }
108
+ if (failures.length > 0) {
109
+ const summary = `network=${network}; failures=${failures.length}; ${failures.join("; ")}`;
110
+ throw new _metweave_core.MetarParseError("batch-parse-failed", summary, `报文解析失败 ${failures.length} 条(network=${network})——${failures.slice(0, 3).join(";")}${failures.length > 3 ? "……" : ""}`);
111
+ }
112
+ return items;
113
+ }
114
+ //#endregion
115
+ Object.defineProperty(exports, "MetarParseError", {
116
+ enumerable: true,
117
+ get: function() {
118
+ return _metweave_core.MetarParseError;
119
+ }
120
+ });
121
+ Object.defineProperty(exports, "MetarSourceError", {
122
+ enumerable: true,
123
+ get: function() {
124
+ return _metweave_core.MetarSourceError;
125
+ }
126
+ });
127
+ exports.getMetarReports = getMetarReports;
128
+ exports.getMetars = getMetars;
129
+ exports.iemCurrentsUrl = iemCurrentsUrl;
130
+
131
+ //# sourceMappingURL=sources.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.cjs","names":["MetarSourceError","parse","MetarParseError"],"sources":["../src/sources.ts"],"sourcesContent":["/**\n * metweave/sources — 取数 helper(伞包子路径)。\n *\n * 纪律:只做端点模板 + fetch + 取数侧整理(解析、定位联表)——缓存/质控/归档等\n * 数据管理逻辑永不进入开源包;核心包(core/parser/render)零网络代码。\n * 不静默:HTTP 失败、响应 schema 不符、报文解析失败一律 throw,绝不吞错返回空数组。\n * 错误面机读化:五路失败一律抛 MetarSourceError(code 稳定契约 + network 网络名字段;\n * message 权威文案为中文;英文用 @metweave/core 导出的 EN_MESSAGES[code] 查表,\n * 或渲染层 locale:\"en\" 整卡切换(card locale 已内置 code→英文映射))。\n */\nimport type { MetarReport } from \"@metweave/core\";\nimport type { MetarParseErrorCode } from \"@metweave/core\";\nimport { MetarParseError, MetarSourceError } from \"@metweave/core\";\nimport { parse } from \"@metweave/parser\";\n\n// 错误类复出(API 完整性):消费方从 sources 子路径 import 即可 catch 两类错误,\n// 不必再从伞包主入口双入口引入(类实体与 core 同一,instanceof 双向成立)。\nexport { MetarParseError, MetarSourceError } from \"@metweave/core\";\n\n/**\n * One fetched observation row: station, verbatim raw report, and source-provided coordinates (city-level for IEM China rows).\n * 单条取数观测行:站名、原样报文、源站坐标(IEM 中国行为城市级粗坐标)。\n */\nexport interface MetarObservation {\n /** 站名(ICAO) */\n station: string;\n /** 报文原文(IEM 通路无类型词,IEM 自带坐标为城市级粗坐标——定位请使用站点元数据) */\n raw: string;\n lat: number | null;\n lon: number | null;\n}\n\n/**\n * Options for getMetars: external abort signal and request timeout (either firing aborts).\n * getMetars 的选项:外部取消信号与请求超时(任一触发即中止)。\n */\nexport interface GetMetarsOptions {\n /** 外部取消信号(与超时组合,任一触发即中止) */\n signal?: AbortSignal;\n /** 请求超时毫秒数(触发即中止并抛出超时错误) */\n timeoutMs?: number;\n}\n\n/**\n * Station metadata for lookup joins: authoritative coordinates overriding the source's city-level ones.\n * 站点元数据(联表用):坐标为权威定位源,覆盖源站的城市级粗坐标。\n */\nexport interface StationRef {\n /** 站名(ICAO) */\n icao: string;\n /** 纬度(WGS-84) */\n lat: number;\n /** 经度(WGS-84) */\n lon: number;\n /** 站点名称(进 tooltip 标题) */\n name?: string;\n}\n\n/**\n * Per-row failure detail (the onUnparseable callback argument): one observation row failed to parse wholesale (missing station/time etc.).\n * 逐行容错的失败详情(onUnparseable 回调入参):一行观测报文解析整体失败(站名/时组缺失等)。\n * `code` is the machine-readable failure code (MetarParseError's stable contract); `error` is the narrative message.\n * code 是机读失败码(MetarParseError 的稳定契约),error 是中文 message。\n */\nexport interface UnparseableReport {\n /** 源站站名(IEM 行的 station 字段) */\n station: string;\n /** 报文原文 */\n raw: string;\n /** 机读失败码(missing-station / missing-time / invalid-time) */\n code: MetarParseErrorCode;\n /** 解析失败原因(Error.message,中文) */\n error: string;\n}\n\n/**\n * Options for getMetarReports: fetch options plus station metadata and the per-row tolerance callback.\n * getMetarReports 的选项:取数选项 + 站点联表元数据 + 逐行容错回调。\n */\nexport interface GetMetarReportsOptions extends GetMetarsOptions {\n /** 按 icao 联表的站点元数据:命中则用其精确坐标与名称,未命中回落源站坐标 */\n stations?: readonly StationRef[];\n /**\n * 逐行容错(缺省关闭):传入时解析失败的行跳过并逐条回调——失败可观测,不静默;\n * 不传时任一失败仍聚合抛出(默认行为不变,全有或全无)。\n */\n onUnparseable?: (failure: UnparseableReport) => void;\n}\n\n/**\n * A map-ready report item (structurally compatible with MetarLayerItem): the { report, position, title } triple —\n * report is the parsed IR (raw text preserved verbatim on report.raw; field values via toValues(report)),\n * position is WGS-84 [lat, lon], title is the hover title.\n * 可直接喂给地图适配器(如 addMetarLayer)的报文项(与 MetarLayerItem 结构兼容):\n * `{ report, position, title }` 三件套——report 是已解析的 IR(报文原文在 report.raw 原样保真,\n * 需要字段值走 toValues(report)),position 是 WGS-84 [lat, lon],title 是悬浮提示标题。\n */\nexport interface MetarReportItem {\n /** 已解析的报文 IR(原文经 report.raw 原样保真回溯) */\n report: MetarReport;\n /** 站点坐标(WGS-84 [lat, lon]) */\n position: [number, number];\n /** 站点提示(「ICAO 站名」;适配器缺省回落 IR 站名) */\n title: string;\n}\n\nconst IEM_BASE = \"https://mesonet.agron.iastate.edu\";\n\n/**\n * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).\n * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。\n * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。\n */\nexport function iemCurrentsUrl(network = \"CN__ASOS\"): string {\n return `${IEM_BASE}/api/1/currents.json?network=${encodeURIComponent(network)}`;\n}\n\n/** 观测记录守卫:station/raw 均须为字符串(只查键不查类型是旧洞)。 */\nfunction isObservation(v: unknown): v is MetarObservation {\n if (typeof v !== \"object\" || v === null) return false;\n const rec = v as { station?: unknown; raw?: unknown };\n return typeof rec.station === \"string\" && typeof rec.raw === \"string\";\n}\n\n/**\n * Fetch IEM whole-network current observations (station/raw/coordinates passed through).\n * 拉取 IEM 整网实况观测(station/raw/坐标透传)。\n * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:\n * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;\n * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);\n * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。\n */\nexport async function getMetars(\n network = \"CN__ASOS\",\n options: GetMetarsOptions = {},\n): Promise<MetarObservation[]> {\n // 手工组合超时与外部信号(不依赖 AbortSignal.any:兼容 Node 20 全系;setTimeout 可被测试假时钟接管)\n const controller = new AbortController();\n const external = options.signal;\n const forward = (): void => {\n controller.abort(external?.reason);\n };\n if (external !== undefined) {\n if (external.aborted) forward();\n else external.addEventListener(\"abort\", forward, { once: true });\n }\n const timer =\n options.timeoutMs === undefined\n ? undefined\n : setTimeout(\n () =>\n controller.abort(\n // 超时中止原因即最终抛出的错误:code \"timeout\"(外部 signal 取消不在此列,reason 原样透传)\n new MetarSourceError(\n \"timeout\",\n network,\n `IEM 请求超时(>${options.timeoutMs}ms,network=${network})`,\n ),\n ),\n options.timeoutMs,\n );\n try {\n let res: Response;\n try {\n res = await fetch(iemCurrentsUrl(network), { signal: controller.signal });\n } catch (err) {\n // 取消/超时引发的拒绝(controller 已中止)原样透传——保持 AbortSignal 语义\n //(超时场景透传的即上方 MetarSourceError \"timeout\");\n // 其余(断网/DNS 的 TypeError 等)包装为 MetarSourceError \"network\",原始 message 保留在文末与 cause\n if (controller.signal.aborted) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n throw new MetarSourceError(\n \"network\",\n network,\n `网络请求失败(源:IEM,network=${network}):请检查网络连通性后重试(${reason})`,\n { cause: err },\n );\n }\n if (!res.ok) {\n throw new MetarSourceError(\n \"http-error\",\n network,\n `IEM HTTP ${res.status}(network=${network})`,\n );\n }\n let body: unknown;\n try {\n body = await res.json();\n } catch (err) {\n // HTTP 200 但响应体不是 JSON(企业代理/防火墙拦截页的典型形态)——裸 SyntaxError 打穿属错误面缺口;\n // 归入 bad-schema 机读码,人话提示指向代理环境。中止引发的拒绝不在此列(保持取消语义透传)\n if (controller.signal.aborted) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应不是合法 JSON(可能被代理/防火墙拦截,network=${network}):${reason}`,\n { cause: err },\n );\n }\n if (\n typeof body !== \"object\" ||\n body === null ||\n !(\"data\" in body) ||\n !Array.isArray(body.data)\n ) {\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应异常:缺少 data 数组(network=${network},schema 不符)`,\n );\n }\n const rows = body.data;\n if (!rows.every(isObservation)) {\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应异常:data 存在 station/raw 非字符串的记录(network=${network},schema 不符)`,\n );\n }\n if (rows.length === 0) {\n // 合法网络不会返回空(整网实况端点)——空 data 几乎必然是网络名写错(IEM 对未知网络名返回 200 + 空数组)\n throw new MetarSourceError(\n \"empty-data\",\n network,\n `IEM 返回空数据(network=${network})——请核对 IEM 网络名(如 CN__ASOS/RU__ASOS,参考 https://mesonet.agron.iastate.edu/sites/networks.php)`,\n );\n }\n return rows;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", forward);\n }\n}\n\n/**\n * Fetch → parse → locate in one step: returns map-ready report items.\n * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。\n *\n * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标\n * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);\n * 源站坐标为 null 且无元数据的行跳过。\n * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;\n * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。\n */\nexport async function getMetarReports(\n network = \"CN__ASOS\",\n options: GetMetarReportsOptions = {},\n): Promise<MetarReportItem[]> {\n const { stations, onUnparseable, ...fetchOptions } = options;\n const table = new Map((stations ?? []).map((s) => [s.icao, s] as const));\n const observations = await getMetars(network, fetchOptions);\n const items: MetarReportItem[] = [];\n const failures: string[] = [];\n for (const obs of observations) {\n const station = table.get(obs.station);\n const lat = station?.lat ?? obs.lat;\n const lon = station?.lon ?? obs.lon;\n if (lat === null || lon === null) continue;\n try {\n items.push({\n report: parse(obs.raw),\n position: [lat, lon],\n title: `${obs.station}${station?.name ? ` ${station.name}` : \"\"}`,\n });\n } catch (err) {\n // parse 对字符串输入只抛 MetarParseError(code 稳定契约);其他异常属编程错误,直接上抛不降级\n if (!(err instanceof MetarParseError)) throw err;\n if (onUnparseable !== undefined) {\n onUnparseable({ station: obs.station, raw: obs.raw, code: err.code, error: err.message });\n continue;\n }\n failures.push(`${obs.station}: ${err.message}`);\n }\n }\n if (failures.length > 0) {\n // 聚合错误机读化(原裸 Error):code 'batch-parse-failed' 稳定契约;raw 字段在此承载\n // 汇总信息(网络名/条数/逐条站名与原因)而非单条报文原文——语义调整见 core/errors.ts 注释\n const summary = `network=${network}; failures=${failures.length}; ${failures.join(\"; \")}`;\n throw new MetarParseError(\n \"batch-parse-failed\",\n summary,\n `报文解析失败 ${failures.length} 条(network=${network})——${failures.slice(0, 3).join(\";\")}${failures.length > 3 ? \"……\" : \"\"}`,\n );\n }\n return items;\n}\n"],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import { MetarParseError, MetarParseErrorCode, MetarReport, MetarSourceError } from "@metweave/core";
2
+ //#region src/sources.d.ts
3
+ /**
4
+ * One fetched observation row: station, verbatim raw report, and source-provided coordinates (city-level for IEM China rows).
5
+ * 单条取数观测行:站名、原样报文、源站坐标(IEM 中国行为城市级粗坐标)。
6
+ */
7
+ export interface MetarObservation {
8
+ /** 站名(ICAO) */
9
+ station: string;
10
+ /** 报文原文(IEM 通路无类型词,IEM 自带坐标为城市级粗坐标——定位请使用站点元数据) */
11
+ raw: string;
12
+ lat: number | null;
13
+ lon: number | null;
14
+ }
15
+ /**
16
+ * Options for getMetars: external abort signal and request timeout (either firing aborts).
17
+ * getMetars 的选项:外部取消信号与请求超时(任一触发即中止)。
18
+ */
19
+ export interface GetMetarsOptions {
20
+ /** 外部取消信号(与超时组合,任一触发即中止) */
21
+ signal?: AbortSignal;
22
+ /** 请求超时毫秒数(触发即中止并抛出超时错误) */
23
+ timeoutMs?: number;
24
+ }
25
+ /**
26
+ * Station metadata for lookup joins: authoritative coordinates overriding the source's city-level ones.
27
+ * 站点元数据(联表用):坐标为权威定位源,覆盖源站的城市级粗坐标。
28
+ */
29
+ export interface StationRef {
30
+ /** 站名(ICAO) */
31
+ icao: string;
32
+ /** 纬度(WGS-84) */
33
+ lat: number;
34
+ /** 经度(WGS-84) */
35
+ lon: number;
36
+ /** 站点名称(进 tooltip 标题) */
37
+ name?: string;
38
+ }
39
+ /**
40
+ * Per-row failure detail (the onUnparseable callback argument): one observation row failed to parse wholesale (missing station/time etc.).
41
+ * 逐行容错的失败详情(onUnparseable 回调入参):一行观测报文解析整体失败(站名/时组缺失等)。
42
+ * `code` is the machine-readable failure code (MetarParseError's stable contract); `error` is the narrative message.
43
+ * code 是机读失败码(MetarParseError 的稳定契约),error 是中文 message。
44
+ */
45
+ export interface UnparseableReport {
46
+ /** 源站站名(IEM 行的 station 字段) */
47
+ station: string;
48
+ /** 报文原文 */
49
+ raw: string;
50
+ /** 机读失败码(missing-station / missing-time / invalid-time) */
51
+ code: MetarParseErrorCode;
52
+ /** 解析失败原因(Error.message,中文) */
53
+ error: string;
54
+ }
55
+ /**
56
+ * Options for getMetarReports: fetch options plus station metadata and the per-row tolerance callback.
57
+ * getMetarReports 的选项:取数选项 + 站点联表元数据 + 逐行容错回调。
58
+ */
59
+ export interface GetMetarReportsOptions extends GetMetarsOptions {
60
+ /** 按 icao 联表的站点元数据:命中则用其精确坐标与名称,未命中回落源站坐标 */
61
+ stations?: readonly StationRef[];
62
+ /**
63
+ * 逐行容错(缺省关闭):传入时解析失败的行跳过并逐条回调——失败可观测,不静默;
64
+ * 不传时任一失败仍聚合抛出(默认行为不变,全有或全无)。
65
+ */
66
+ onUnparseable?: (failure: UnparseableReport) => void;
67
+ }
68
+ /**
69
+ * A map-ready report item (structurally compatible with MetarLayerItem): the { report, position, title } triple —
70
+ * report is the parsed IR (raw text preserved verbatim on report.raw; field values via toValues(report)),
71
+ * position is WGS-84 [lat, lon], title is the hover title.
72
+ * 可直接喂给地图适配器(如 addMetarLayer)的报文项(与 MetarLayerItem 结构兼容):
73
+ * `{ report, position, title }` 三件套——report 是已解析的 IR(报文原文在 report.raw 原样保真,
74
+ * 需要字段值走 toValues(report)),position 是 WGS-84 [lat, lon],title 是悬浮提示标题。
75
+ */
76
+ export interface MetarReportItem {
77
+ /** 已解析的报文 IR(原文经 report.raw 原样保真回溯) */
78
+ report: MetarReport;
79
+ /** 站点坐标(WGS-84 [lat, lon]) */
80
+ position: [number, number];
81
+ /** 站点提示(「ICAO 站名」;适配器缺省回落 IR 站名) */
82
+ title: string;
83
+ }
84
+ /**
85
+ * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).
86
+ * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。
87
+ * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。
88
+ */
89
+ export declare function iemCurrentsUrl(network?: string): string;
90
+ /**
91
+ * Fetch IEM whole-network current observations (station/raw/coordinates passed through).
92
+ * 拉取 IEM 整网实况观测(station/raw/坐标透传)。
93
+ * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:
94
+ * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;
95
+ * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);
96
+ * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。
97
+ */
98
+ export declare function getMetars(network?: string, options?: GetMetarsOptions): Promise<MetarObservation[]>;
99
+ /**
100
+ * Fetch → parse → locate in one step: returns map-ready report items.
101
+ * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。
102
+ *
103
+ * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标
104
+ * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);
105
+ * 源站坐标为 null 且无元数据的行跳过。
106
+ * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;
107
+ * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。
108
+ */
109
+ export declare function getMetarReports(network?: string, options?: GetMetarReportsOptions): Promise<MetarReportItem[]>;
110
+ //#endregion
111
+ export { MetarParseError, MetarSourceError };
112
+ //# sourceMappingURL=sources.d.cts.map
@@ -0,0 +1,112 @@
1
+ import { MetarParseError, MetarParseErrorCode, MetarReport, MetarSourceError } from "@metweave/core";
2
+ //#region src/sources.d.ts
3
+ /**
4
+ * One fetched observation row: station, verbatim raw report, and source-provided coordinates (city-level for IEM China rows).
5
+ * 单条取数观测行:站名、原样报文、源站坐标(IEM 中国行为城市级粗坐标)。
6
+ */
7
+ export interface MetarObservation {
8
+ /** 站名(ICAO) */
9
+ station: string;
10
+ /** 报文原文(IEM 通路无类型词,IEM 自带坐标为城市级粗坐标——定位请使用站点元数据) */
11
+ raw: string;
12
+ lat: number | null;
13
+ lon: number | null;
14
+ }
15
+ /**
16
+ * Options for getMetars: external abort signal and request timeout (either firing aborts).
17
+ * getMetars 的选项:外部取消信号与请求超时(任一触发即中止)。
18
+ */
19
+ export interface GetMetarsOptions {
20
+ /** 外部取消信号(与超时组合,任一触发即中止) */
21
+ signal?: AbortSignal;
22
+ /** 请求超时毫秒数(触发即中止并抛出超时错误) */
23
+ timeoutMs?: number;
24
+ }
25
+ /**
26
+ * Station metadata for lookup joins: authoritative coordinates overriding the source's city-level ones.
27
+ * 站点元数据(联表用):坐标为权威定位源,覆盖源站的城市级粗坐标。
28
+ */
29
+ export interface StationRef {
30
+ /** 站名(ICAO) */
31
+ icao: string;
32
+ /** 纬度(WGS-84) */
33
+ lat: number;
34
+ /** 经度(WGS-84) */
35
+ lon: number;
36
+ /** 站点名称(进 tooltip 标题) */
37
+ name?: string;
38
+ }
39
+ /**
40
+ * Per-row failure detail (the onUnparseable callback argument): one observation row failed to parse wholesale (missing station/time etc.).
41
+ * 逐行容错的失败详情(onUnparseable 回调入参):一行观测报文解析整体失败(站名/时组缺失等)。
42
+ * `code` is the machine-readable failure code (MetarParseError's stable contract); `error` is the narrative message.
43
+ * code 是机读失败码(MetarParseError 的稳定契约),error 是中文 message。
44
+ */
45
+ export interface UnparseableReport {
46
+ /** 源站站名(IEM 行的 station 字段) */
47
+ station: string;
48
+ /** 报文原文 */
49
+ raw: string;
50
+ /** 机读失败码(missing-station / missing-time / invalid-time) */
51
+ code: MetarParseErrorCode;
52
+ /** 解析失败原因(Error.message,中文) */
53
+ error: string;
54
+ }
55
+ /**
56
+ * Options for getMetarReports: fetch options plus station metadata and the per-row tolerance callback.
57
+ * getMetarReports 的选项:取数选项 + 站点联表元数据 + 逐行容错回调。
58
+ */
59
+ export interface GetMetarReportsOptions extends GetMetarsOptions {
60
+ /** 按 icao 联表的站点元数据:命中则用其精确坐标与名称,未命中回落源站坐标 */
61
+ stations?: readonly StationRef[];
62
+ /**
63
+ * 逐行容错(缺省关闭):传入时解析失败的行跳过并逐条回调——失败可观测,不静默;
64
+ * 不传时任一失败仍聚合抛出(默认行为不变,全有或全无)。
65
+ */
66
+ onUnparseable?: (failure: UnparseableReport) => void;
67
+ }
68
+ /**
69
+ * A map-ready report item (structurally compatible with MetarLayerItem): the { report, position, title } triple —
70
+ * report is the parsed IR (raw text preserved verbatim on report.raw; field values via toValues(report)),
71
+ * position is WGS-84 [lat, lon], title is the hover title.
72
+ * 可直接喂给地图适配器(如 addMetarLayer)的报文项(与 MetarLayerItem 结构兼容):
73
+ * `{ report, position, title }` 三件套——report 是已解析的 IR(报文原文在 report.raw 原样保真,
74
+ * 需要字段值走 toValues(report)),position 是 WGS-84 [lat, lon],title 是悬浮提示标题。
75
+ */
76
+ export interface MetarReportItem {
77
+ /** 已解析的报文 IR(原文经 report.raw 原样保真回溯) */
78
+ report: MetarReport;
79
+ /** 站点坐标(WGS-84 [lat, lon]) */
80
+ position: [number, number];
81
+ /** 站点提示(「ICAO 站名」;适配器缺省回落 IR 站名) */
82
+ title: string;
83
+ }
84
+ /**
85
+ * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).
86
+ * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。
87
+ * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。
88
+ */
89
+ export declare function iemCurrentsUrl(network?: string): string;
90
+ /**
91
+ * Fetch IEM whole-network current observations (station/raw/coordinates passed through).
92
+ * 拉取 IEM 整网实况观测(station/raw/坐标透传)。
93
+ * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:
94
+ * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;
95
+ * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);
96
+ * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。
97
+ */
98
+ export declare function getMetars(network?: string, options?: GetMetarsOptions): Promise<MetarObservation[]>;
99
+ /**
100
+ * Fetch → parse → locate in one step: returns map-ready report items.
101
+ * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。
102
+ *
103
+ * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标
104
+ * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);
105
+ * 源站坐标为 null 且无元数据的行跳过。
106
+ * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;
107
+ * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。
108
+ */
109
+ export declare function getMetarReports(network?: string, options?: GetMetarReportsOptions): Promise<MetarReportItem[]>;
110
+ //#endregion
111
+ export { MetarParseError, MetarSourceError };
112
+ //# sourceMappingURL=sources.d.ts.map
@@ -0,0 +1,116 @@
1
+ import { MetarParseError, MetarParseError as MetarParseError$1, MetarSourceError, MetarSourceError as MetarSourceError$1 } from "@metweave/core";
2
+ import { parse } from "@metweave/parser";
3
+ //#region src/sources.ts
4
+ const IEM_BASE = "https://mesonet.agron.iastate.edu";
5
+ /**
6
+ * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).
7
+ * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。
8
+ * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。
9
+ */
10
+ function iemCurrentsUrl(network = "CN__ASOS") {
11
+ return `${IEM_BASE}/api/1/currents.json?network=${encodeURIComponent(network)}`;
12
+ }
13
+ /** 观测记录守卫:station/raw 均须为字符串(只查键不查类型是旧洞)。 */
14
+ function isObservation(v) {
15
+ if (typeof v !== "object" || v === null) return false;
16
+ const rec = v;
17
+ return typeof rec.station === "string" && typeof rec.raw === "string";
18
+ }
19
+ /**
20
+ * Fetch IEM whole-network current observations (station/raw/coordinates passed through).
21
+ * 拉取 IEM 整网实况观测(station/raw/坐标透传)。
22
+ * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:
23
+ * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;
24
+ * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);
25
+ * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。
26
+ */
27
+ async function getMetars(network = "CN__ASOS", options = {}) {
28
+ const controller = new AbortController();
29
+ const external = options.signal;
30
+ const forward = () => {
31
+ controller.abort(external?.reason);
32
+ };
33
+ if (external !== void 0) {
34
+ if (external.aborted) forward();
35
+ else external.addEventListener("abort", forward, { once: true });
36
+ }
37
+ const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => controller.abort(new MetarSourceError$1("timeout", network, `IEM 请求超时(>${options.timeoutMs}ms,network=${network})`)), options.timeoutMs);
38
+ try {
39
+ let res;
40
+ try {
41
+ res = await fetch(iemCurrentsUrl(network), { signal: controller.signal });
42
+ } catch (err) {
43
+ if (controller.signal.aborted) throw err;
44
+ const reason = err instanceof Error ? err.message : String(err);
45
+ throw new MetarSourceError$1("network", network, `网络请求失败(源:IEM,network=${network}):请检查网络连通性后重试(${reason})`, { cause: err });
46
+ }
47
+ if (!res.ok) throw new MetarSourceError$1("http-error", network, `IEM HTTP ${res.status}(network=${network})`);
48
+ let body;
49
+ try {
50
+ body = await res.json();
51
+ } catch (err) {
52
+ if (controller.signal.aborted) throw err;
53
+ const reason = err instanceof Error ? err.message : String(err);
54
+ throw new MetarSourceError$1("bad-schema", network, `IEM 响应不是合法 JSON(可能被代理/防火墙拦截,network=${network}):${reason}`, { cause: err });
55
+ }
56
+ if (typeof body !== "object" || body === null || !("data" in body) || !Array.isArray(body.data)) throw new MetarSourceError$1("bad-schema", network, `IEM 响应异常:缺少 data 数组(network=${network},schema 不符)`);
57
+ const rows = body.data;
58
+ if (!rows.every(isObservation)) throw new MetarSourceError$1("bad-schema", network, `IEM 响应异常:data 存在 station/raw 非字符串的记录(network=${network},schema 不符)`);
59
+ if (rows.length === 0) throw new MetarSourceError$1("empty-data", network, `IEM 返回空数据(network=${network})——请核对 IEM 网络名(如 CN__ASOS/RU__ASOS,参考 https://mesonet.agron.iastate.edu/sites/networks.php)`);
60
+ return rows;
61
+ } finally {
62
+ if (timer !== void 0) clearTimeout(timer);
63
+ external?.removeEventListener("abort", forward);
64
+ }
65
+ }
66
+ /**
67
+ * Fetch → parse → locate in one step: returns map-ready report items.
68
+ * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。
69
+ *
70
+ * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标
71
+ * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);
72
+ * 源站坐标为 null 且无元数据的行跳过。
73
+ * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;
74
+ * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。
75
+ */
76
+ async function getMetarReports(network = "CN__ASOS", options = {}) {
77
+ const { stations, onUnparseable, ...fetchOptions } = options;
78
+ const table = new Map((stations ?? []).map((s) => [s.icao, s]));
79
+ const observations = await getMetars(network, fetchOptions);
80
+ const items = [];
81
+ const failures = [];
82
+ for (const obs of observations) {
83
+ const station = table.get(obs.station);
84
+ const lat = station?.lat ?? obs.lat;
85
+ const lon = station?.lon ?? obs.lon;
86
+ if (lat === null || lon === null) continue;
87
+ try {
88
+ items.push({
89
+ report: parse(obs.raw),
90
+ position: [lat, lon],
91
+ title: `${obs.station}${station?.name ? ` ${station.name}` : ""}`
92
+ });
93
+ } catch (err) {
94
+ if (!(err instanceof MetarParseError$1)) throw err;
95
+ if (onUnparseable !== void 0) {
96
+ onUnparseable({
97
+ station: obs.station,
98
+ raw: obs.raw,
99
+ code: err.code,
100
+ error: err.message
101
+ });
102
+ continue;
103
+ }
104
+ failures.push(`${obs.station}: ${err.message}`);
105
+ }
106
+ }
107
+ if (failures.length > 0) {
108
+ const summary = `network=${network}; failures=${failures.length}; ${failures.join("; ")}`;
109
+ throw new MetarParseError$1("batch-parse-failed", summary, `报文解析失败 ${failures.length} 条(network=${network})——${failures.slice(0, 3).join(";")}${failures.length > 3 ? "……" : ""}`);
110
+ }
111
+ return items;
112
+ }
113
+ //#endregion
114
+ export { MetarParseError, MetarSourceError, getMetarReports, getMetars, iemCurrentsUrl };
115
+
116
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","names":["MetarSourceError","MetarParseError"],"sources":["../src/sources.ts"],"sourcesContent":["/**\n * metweave/sources — 取数 helper(伞包子路径)。\n *\n * 纪律:只做端点模板 + fetch + 取数侧整理(解析、定位联表)——缓存/质控/归档等\n * 数据管理逻辑永不进入开源包;核心包(core/parser/render)零网络代码。\n * 不静默:HTTP 失败、响应 schema 不符、报文解析失败一律 throw,绝不吞错返回空数组。\n * 错误面机读化:五路失败一律抛 MetarSourceError(code 稳定契约 + network 网络名字段;\n * message 权威文案为中文;英文用 @metweave/core 导出的 EN_MESSAGES[code] 查表,\n * 或渲染层 locale:\"en\" 整卡切换(card locale 已内置 code→英文映射))。\n */\nimport type { MetarReport } from \"@metweave/core\";\nimport type { MetarParseErrorCode } from \"@metweave/core\";\nimport { MetarParseError, MetarSourceError } from \"@metweave/core\";\nimport { parse } from \"@metweave/parser\";\n\n// 错误类复出(API 完整性):消费方从 sources 子路径 import 即可 catch 两类错误,\n// 不必再从伞包主入口双入口引入(类实体与 core 同一,instanceof 双向成立)。\nexport { MetarParseError, MetarSourceError } from \"@metweave/core\";\n\n/**\n * One fetched observation row: station, verbatim raw report, and source-provided coordinates (city-level for IEM China rows).\n * 单条取数观测行:站名、原样报文、源站坐标(IEM 中国行为城市级粗坐标)。\n */\nexport interface MetarObservation {\n /** 站名(ICAO) */\n station: string;\n /** 报文原文(IEM 通路无类型词,IEM 自带坐标为城市级粗坐标——定位请使用站点元数据) */\n raw: string;\n lat: number | null;\n lon: number | null;\n}\n\n/**\n * Options for getMetars: external abort signal and request timeout (either firing aborts).\n * getMetars 的选项:外部取消信号与请求超时(任一触发即中止)。\n */\nexport interface GetMetarsOptions {\n /** 外部取消信号(与超时组合,任一触发即中止) */\n signal?: AbortSignal;\n /** 请求超时毫秒数(触发即中止并抛出超时错误) */\n timeoutMs?: number;\n}\n\n/**\n * Station metadata for lookup joins: authoritative coordinates overriding the source's city-level ones.\n * 站点元数据(联表用):坐标为权威定位源,覆盖源站的城市级粗坐标。\n */\nexport interface StationRef {\n /** 站名(ICAO) */\n icao: string;\n /** 纬度(WGS-84) */\n lat: number;\n /** 经度(WGS-84) */\n lon: number;\n /** 站点名称(进 tooltip 标题) */\n name?: string;\n}\n\n/**\n * Per-row failure detail (the onUnparseable callback argument): one observation row failed to parse wholesale (missing station/time etc.).\n * 逐行容错的失败详情(onUnparseable 回调入参):一行观测报文解析整体失败(站名/时组缺失等)。\n * `code` is the machine-readable failure code (MetarParseError's stable contract); `error` is the narrative message.\n * code 是机读失败码(MetarParseError 的稳定契约),error 是中文 message。\n */\nexport interface UnparseableReport {\n /** 源站站名(IEM 行的 station 字段) */\n station: string;\n /** 报文原文 */\n raw: string;\n /** 机读失败码(missing-station / missing-time / invalid-time) */\n code: MetarParseErrorCode;\n /** 解析失败原因(Error.message,中文) */\n error: string;\n}\n\n/**\n * Options for getMetarReports: fetch options plus station metadata and the per-row tolerance callback.\n * getMetarReports 的选项:取数选项 + 站点联表元数据 + 逐行容错回调。\n */\nexport interface GetMetarReportsOptions extends GetMetarsOptions {\n /** 按 icao 联表的站点元数据:命中则用其精确坐标与名称,未命中回落源站坐标 */\n stations?: readonly StationRef[];\n /**\n * 逐行容错(缺省关闭):传入时解析失败的行跳过并逐条回调——失败可观测,不静默;\n * 不传时任一失败仍聚合抛出(默认行为不变,全有或全无)。\n */\n onUnparseable?: (failure: UnparseableReport) => void;\n}\n\n/**\n * A map-ready report item (structurally compatible with MetarLayerItem): the { report, position, title } triple —\n * report is the parsed IR (raw text preserved verbatim on report.raw; field values via toValues(report)),\n * position is WGS-84 [lat, lon], title is the hover title.\n * 可直接喂给地图适配器(如 addMetarLayer)的报文项(与 MetarLayerItem 结构兼容):\n * `{ report, position, title }` 三件套——report 是已解析的 IR(报文原文在 report.raw 原样保真,\n * 需要字段值走 toValues(report)),position 是 WGS-84 [lat, lon],title 是悬浮提示标题。\n */\nexport interface MetarReportItem {\n /** 已解析的报文 IR(原文经 report.raw 原样保真回溯) */\n report: MetarReport;\n /** 站点坐标(WGS-84 [lat, lon]) */\n position: [number, number];\n /** 站点提示(「ICAO 站名」;适配器缺省回落 IR 站名) */\n title: string;\n}\n\nconst IEM_BASE = \"https://mesonet.agron.iastate.edu\";\n\n/**\n * The IEM whole-network currents endpoint (CORS-open, direct browser access). Defaults to the China ASOS network (39 stations).\n * IEM 整网实况端点(CORS 全开,浏览器可直连)。缺省中国 ASOS 网(39 站)。\n * @param network - IEM network name (e.g. CN__ASOS, RU__ASOS, IA_ASOS). IEM 网络名。\n */\nexport function iemCurrentsUrl(network = \"CN__ASOS\"): string {\n return `${IEM_BASE}/api/1/currents.json?network=${encodeURIComponent(network)}`;\n}\n\n/** 观测记录守卫:station/raw 均须为字符串(只查键不查类型是旧洞)。 */\nfunction isObservation(v: unknown): v is MetarObservation {\n if (typeof v !== \"object\" || v === null) return false;\n const rec = v as { station?: unknown; raw?: unknown };\n return typeof rec.station === \"string\" && typeof rec.raw === \"string\";\n}\n\n/**\n * Fetch IEM whole-network current observations (station/raw/coordinates passed through).\n * 拉取 IEM 整网实况观测(station/raw/坐标透传)。\n * No-silent-failure discipline on the fetch side / 不静默纪律的取数面:\n * - 空数组是错误网络名的典型形态(IEM 对未知 network 返回 200 + 空 data,合法网络不会返回空)——显式 throw;\n * - fetch 网络层失败(断网/DNS)包装为中文可读错误(原始 message 保留在文末与 cause);\n * - 超时/外部 AbortSignal 触发的拒绝不包装、原样透传(保持取消语义)。\n */\nexport async function getMetars(\n network = \"CN__ASOS\",\n options: GetMetarsOptions = {},\n): Promise<MetarObservation[]> {\n // 手工组合超时与外部信号(不依赖 AbortSignal.any:兼容 Node 20 全系;setTimeout 可被测试假时钟接管)\n const controller = new AbortController();\n const external = options.signal;\n const forward = (): void => {\n controller.abort(external?.reason);\n };\n if (external !== undefined) {\n if (external.aborted) forward();\n else external.addEventListener(\"abort\", forward, { once: true });\n }\n const timer =\n options.timeoutMs === undefined\n ? undefined\n : setTimeout(\n () =>\n controller.abort(\n // 超时中止原因即最终抛出的错误:code \"timeout\"(外部 signal 取消不在此列,reason 原样透传)\n new MetarSourceError(\n \"timeout\",\n network,\n `IEM 请求超时(>${options.timeoutMs}ms,network=${network})`,\n ),\n ),\n options.timeoutMs,\n );\n try {\n let res: Response;\n try {\n res = await fetch(iemCurrentsUrl(network), { signal: controller.signal });\n } catch (err) {\n // 取消/超时引发的拒绝(controller 已中止)原样透传——保持 AbortSignal 语义\n //(超时场景透传的即上方 MetarSourceError \"timeout\");\n // 其余(断网/DNS 的 TypeError 等)包装为 MetarSourceError \"network\",原始 message 保留在文末与 cause\n if (controller.signal.aborted) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n throw new MetarSourceError(\n \"network\",\n network,\n `网络请求失败(源:IEM,network=${network}):请检查网络连通性后重试(${reason})`,\n { cause: err },\n );\n }\n if (!res.ok) {\n throw new MetarSourceError(\n \"http-error\",\n network,\n `IEM HTTP ${res.status}(network=${network})`,\n );\n }\n let body: unknown;\n try {\n body = await res.json();\n } catch (err) {\n // HTTP 200 但响应体不是 JSON(企业代理/防火墙拦截页的典型形态)——裸 SyntaxError 打穿属错误面缺口;\n // 归入 bad-schema 机读码,人话提示指向代理环境。中止引发的拒绝不在此列(保持取消语义透传)\n if (controller.signal.aborted) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应不是合法 JSON(可能被代理/防火墙拦截,network=${network}):${reason}`,\n { cause: err },\n );\n }\n if (\n typeof body !== \"object\" ||\n body === null ||\n !(\"data\" in body) ||\n !Array.isArray(body.data)\n ) {\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应异常:缺少 data 数组(network=${network},schema 不符)`,\n );\n }\n const rows = body.data;\n if (!rows.every(isObservation)) {\n throw new MetarSourceError(\n \"bad-schema\",\n network,\n `IEM 响应异常:data 存在 station/raw 非字符串的记录(network=${network},schema 不符)`,\n );\n }\n if (rows.length === 0) {\n // 合法网络不会返回空(整网实况端点)——空 data 几乎必然是网络名写错(IEM 对未知网络名返回 200 + 空数组)\n throw new MetarSourceError(\n \"empty-data\",\n network,\n `IEM 返回空数据(network=${network})——请核对 IEM 网络名(如 CN__ASOS/RU__ASOS,参考 https://mesonet.agron.iastate.edu/sites/networks.php)`,\n );\n }\n return rows;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", forward);\n }\n}\n\n/**\n * Fetch → parse → locate in one step: returns map-ready report items.\n * 取数 → 解析 → 定位一步到位:返回可直接喂给地图适配器的报文项数组。\n *\n * 定位规则:stations 命中的站用元数据精确坐标(并带站名),未命中的回落源站坐标\n * (IEM 中国站为城市级粗坐标,严肃场景请传站点元数据——见 examples 的 stations.json 用法);\n * 源站坐标为 null 且无元数据的行跳过。\n * 失败语义(不静默,两种模式):缺省任一报文解析整体失败(站名/时组缺失)即聚合抛出,绝不静默丢行;\n * 传 options.onUnparseable 则逐行容错——失败行跳过并逐条回调(可观测),好行照常返回。\n */\nexport async function getMetarReports(\n network = \"CN__ASOS\",\n options: GetMetarReportsOptions = {},\n): Promise<MetarReportItem[]> {\n const { stations, onUnparseable, ...fetchOptions } = options;\n const table = new Map((stations ?? []).map((s) => [s.icao, s] as const));\n const observations = await getMetars(network, fetchOptions);\n const items: MetarReportItem[] = [];\n const failures: string[] = [];\n for (const obs of observations) {\n const station = table.get(obs.station);\n const lat = station?.lat ?? obs.lat;\n const lon = station?.lon ?? obs.lon;\n if (lat === null || lon === null) continue;\n try {\n items.push({\n report: parse(obs.raw),\n position: [lat, lon],\n title: `${obs.station}${station?.name ? ` ${station.name}` : \"\"}`,\n });\n } catch (err) {\n // parse 对字符串输入只抛 MetarParseError(code 稳定契约);其他异常属编程错误,直接上抛不降级\n if (!(err instanceof MetarParseError)) throw err;\n if (onUnparseable !== undefined) {\n onUnparseable({ station: obs.station, raw: obs.raw, code: err.code, error: err.message });\n continue;\n }\n failures.push(`${obs.station}: ${err.message}`);\n }\n }\n if (failures.length > 0) {\n // 聚合错误机读化(原裸 Error):code 'batch-parse-failed' 稳定契约;raw 字段在此承载\n // 汇总信息(网络名/条数/逐条站名与原因)而非单条报文原文——语义调整见 core/errors.ts 注释\n const summary = `network=${network}; failures=${failures.length}; ${failures.join(\"; \")}`;\n throw new MetarParseError(\n \"batch-parse-failed\",\n summary,\n `报文解析失败 ${failures.length} 条(network=${network})——${failures.slice(0, 3).join(\";\")}${failures.length > 3 ? \"……\" : \"\"}`,\n );\n }\n return items;\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "metweave",
3
+ "version": "0.1.0",
4
+ "description": "Show live airport weather on a Leaflet map in ten lines — METAR parsing, report cards and map layers, straight from public sources.",
5
+ "keywords": [
6
+ "airport",
7
+ "aviation",
8
+ "leaflet",
9
+ "map",
10
+ "metar",
11
+ "observation",
12
+ "speci",
13
+ "visualization",
14
+ "weather"
15
+ ],
16
+ "license": "MIT",
17
+ "author": "YaoJaro <yaojaro@metweave.com>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/yaojaro/metweave.git",
21
+ "directory": "packages/metweave"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
+ }
43
+ },
44
+ "./sources": {
45
+ "import": {
46
+ "types": "./dist/sources.d.ts",
47
+ "default": "./dist/sources.js"
48
+ },
49
+ "require": {
50
+ "types": "./dist/sources.d.cts",
51
+ "default": "./dist/sources.cjs"
52
+ }
53
+ }
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "dependencies": {
59
+ "@metweave/core": "0.1.0",
60
+ "@metweave/parser": "0.1.0",
61
+ "@metweave/render": "0.1.0"
62
+ },
63
+ "engines": {
64
+ "node": ">=20"
65
+ },
66
+ "scripts": {
67
+ "build": "tsdown",
68
+ "typecheck": "tsc --noEmit"
69
+ }
70
+ }