@metweave/core 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 +21 -0
- package/README.md +23 -0
- package/dist/index.cjs +86 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +563 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
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,23 @@
|
|
|
1
|
+
# @metweave/core
|
|
2
|
+
|
|
3
|
+
metweave 管道心脏处的 IR 数据模型——解析器(METAR/SPECI → IR)与渲染组件(IR → UI)之间的唯一契约。一切都是纯可序列化 JSON——零依赖、零类实例——且每个产物都携带指回原始报文的 `span`。
|
|
4
|
+
|
|
5
|
+
### 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @metweave/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### 最小示例
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { toValues } from "@metweave/core";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
每组三个显式状态(省略 ≠ 缺测 ≠ 有值)、携带 span 的机读告警码,以及 `toValues()` 两态取值视图。整报失败抛 `MetarParseError`,`code` 字段稳定——`message` 措辞后续可本地化而不构成破坏性变更。
|
|
18
|
+
|
|
19
|
+
文档与完整示例见[主仓库](https://github.com/yaojaro/metweave)。
|
|
20
|
+
|
|
21
|
+
## 许可
|
|
22
|
+
|
|
23
|
+
[MIT](https://github.com/yaojaro/metweave/blob/main/LICENSE) © 2026 YaoJaro——授权仅覆盖本仓库的代码与文档;随包分发的报文样本不在其列,详见主仓库 README 的「许可」说明。
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whole-report parse failure for a METAR/SPECI report.
|
|
5
|
+
* METAR/SPECI 报文整体解析失败。
|
|
6
|
+
* The `raw` field keeps the input verbatim (failure samples can be stored for review without extra capture).
|
|
7
|
+
* raw 字段保留输入原文(失败样本可直接落库复盘,无需额外捕获)。
|
|
8
|
+
*/
|
|
9
|
+
var MetarParseError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
/** 解析失败的输入原文(原样保真) */
|
|
12
|
+
raw;
|
|
13
|
+
constructor(code, raw, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "MetarParseError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.raw = raw;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Fetch failure (IEM and other public sources): the `network` field names the failing network for per-network branching and messaging.
|
|
22
|
+
* 取数失败(IEM 等公开源):network 字段标注出错网络名,供按网分流与提示。
|
|
23
|
+
*/
|
|
24
|
+
var MetarSourceError = class extends Error {
|
|
25
|
+
code;
|
|
26
|
+
/** 出错的 IEM 网络名(如 CN__ASOS) */
|
|
27
|
+
network;
|
|
28
|
+
constructor(code, network, message, options) {
|
|
29
|
+
super(message, options);
|
|
30
|
+
this.name = "MetarSourceError";
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.network = network;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* English messages for every error code (parse 6 + source 5), for consumers that
|
|
37
|
+
* map `code` to their own UI copy.
|
|
38
|
+
* 全部错误码的英文文案(parse 6 码 + source 5 码),供消费方按 code 映射自己的界面文案。
|
|
39
|
+
*
|
|
40
|
+
* Keyed by the stable machine-readable `code` (add-only contract); the bundled
|
|
41
|
+
* Chinese `message` on each error remains the default narrative.
|
|
42
|
+
* 键为稳定机读 `code`(只增不改);错误实体上的中文 `message` 仍是默认叙述。
|
|
43
|
+
*/
|
|
44
|
+
const EN_MESSAGES = {
|
|
45
|
+
"invalid-input": "Parse expects a METAR/SPECI report string, received a non-string value",
|
|
46
|
+
"missing-station": "Not a METAR/SPECI report: station group missing or unrecognized",
|
|
47
|
+
"missing-time": "Not a complete METAR/SPECI report: observation-time group missing",
|
|
48
|
+
"invalid-time": "Observation-time group out of range (day 01–31 / hour 00–23 / minute 00–59)",
|
|
49
|
+
"unsupported-mode": "Strict mode is not implemented in v0.1 — omit `mode` or pass 'tolerant'",
|
|
50
|
+
"batch-parse-failed": "Some reports in the batch failed to parse entirely (see the summary for per-station reasons)",
|
|
51
|
+
"http-error": "Source returned a non-2xx HTTP status",
|
|
52
|
+
"bad-schema": "Response body does not match the agreed schema",
|
|
53
|
+
"empty-data": "HTTP 200 with empty data — typically a wrong network name",
|
|
54
|
+
timeout: "Request aborted after the configured timeout",
|
|
55
|
+
network: "Network-level failure (offline, DNS, fetch refused)"
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/ir.ts
|
|
59
|
+
/**
|
|
60
|
+
* Convenience unwrap: returns the value when present; undefined for explicit missing or an omitted group.
|
|
61
|
+
* 便利取值:有值返回 value,显式缺测/组省略返回 undefined。
|
|
62
|
+
*/
|
|
63
|
+
function unwrap(observed) {
|
|
64
|
+
return observed?.kind === "value" ? observed.value : void 0;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* One projection to the all-two-state view; consume MetarReport directly only for three-state discrimination and warning spans.
|
|
68
|
+
* 一次投影得到全两态视图;需要三态判别与告警 span 的重度场景才直接消费 MetarReport。
|
|
69
|
+
*/
|
|
70
|
+
function toValues(report) {
|
|
71
|
+
return {
|
|
72
|
+
...report,
|
|
73
|
+
wind: unwrap(report.wind),
|
|
74
|
+
visibility: unwrap(report.visibility),
|
|
75
|
+
runwayVisualRange: unwrap(report.runwayVisualRange),
|
|
76
|
+
weather: unwrap(report.weather)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
exports.EN_MESSAGES = EN_MESSAGES;
|
|
81
|
+
exports.MetarParseError = MetarParseError;
|
|
82
|
+
exports.MetarSourceError = MetarSourceError;
|
|
83
|
+
exports.toValues = toValues;
|
|
84
|
+
exports.unwrap = unwrap;
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/errors.ts","../src/ir.ts"],"sourcesContent":["/**\n * @metweave/core — 机读错误面:解析整体失败与取数失败的错误类。\n *\n * 契约(与 WarningCode 同纪律):\n * 1. code 是稳定契约——命名只增不改,消费方按 code 分流(机读);\n * message 保持中文(v0.1 主受众),将来本地化 message 文案不算破坏性变更。\n * 2. 错误类挂 @metweave/core(零依赖、与 IR 同级),parser 与伞包(sources)按依赖方向引用;\n * 伞包 metweave 对 core 全量再导出,消费方 `import { MetarParseError } from \"metweave\"` 即得。\n * 3. 扩展新 code / 新字段属 additive 变更(v0.x 版本政策见根 README)。\n */\n\n/**\n * The six whole-report parse failure codes (IR contract: non-string input / missing station / missing time / out-of-range time = whole-report failure, not field-level three states).\n * parse 整体失败的六路(IR 契约:输入非字符串/无站名/无时组/时组越界 = 整体失败,不是字段级三态)。\n */\nexport type MetarParseErrorCode =\n | \"invalid-input\" /** 输入非字符串(parse(raw: string) 收到 null/数字等)——走稳定契约而非裸 Error,EN_MESSAGES 可查表 */\n | \"missing-station\" /** 首个 token 不是四字符站名组(含空输入) */\n | \"missing-time\" /** 站名后无 ddHHMMZ 时组 */\n | \"invalid-time\" /** 时组在位但数值越界(日/时/分超范围)——值不可信,等同无效时组 */\n | \"unsupported-mode\" /** mode:'strict' 在 v0.1 未实现(路线图项)——类型已预留,调用即明确报错而非静默降级 */\n /** 批量聚合解析失败(伞包 getMetarReports 缺省模式:任一行整体失败即聚合抛出)。\n * 注意 raw 字段语义在本 code 下的调整:承载汇总信息(网络名/失败条数/逐条站名与原因)而非单条报文原文——\n * 单条原文仍可经 message 与 onUnparseable 回调取得,语义差异在本注释声明。 */\n | \"batch-parse-failed\";\n\n/**\n * Whole-report parse failure for a METAR/SPECI report.\n * METAR/SPECI 报文整体解析失败。\n * The `raw` field keeps the input verbatim (failure samples can be stored for review without extra capture).\n * raw 字段保留输入原文(失败样本可直接落库复盘,无需额外捕获)。\n */\nexport class MetarParseError extends Error {\n readonly code: MetarParseErrorCode;\n /** 解析失败的输入原文(原样保真) */\n readonly raw: string;\n\n constructor(code: MetarParseErrorCode, raw: string, message: string) {\n super(message);\n this.name = \"MetarParseError\";\n this.code = code;\n this.raw = raw;\n }\n}\n\n/**\n * The five fetch failure codes for getMetars / getMetarReports.\n * getMetars / getMetarReports 的五路取数失败。\n */\nexport type MetarSourceErrorCode =\n | \"http-error\" /** 源站返回非 2xx */\n | \"bad-schema\" /** 响应体不合约定 schema(缺 data 数组 / 记录字段类型不符) */\n | \"empty-data\" /** 200 + 空 data(错误网络名的典型形态,显式判错不静默) */\n | \"timeout\" /** timeoutMs 超时中止(外部 AbortSignal 取消不在此列——取消原样透传) */\n | \"network\"; /** 网络层失败(断网 / DNS 等 fetch 拒绝) */\n\n/**\n * Fetch failure (IEM and other public sources): the `network` field names the failing network for per-network branching and messaging.\n * 取数失败(IEM 等公开源):network 字段标注出错网络名,供按网分流与提示。\n */\nexport class MetarSourceError extends Error {\n readonly code: MetarSourceErrorCode;\n /** 出错的 IEM 网络名(如 CN__ASOS) */\n readonly network: string;\n\n constructor(\n code: MetarSourceErrorCode,\n network: string,\n message: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"MetarSourceError\";\n this.code = code;\n this.network = network;\n }\n}\n\n/**\n * English messages for every error code (parse 6 + source 5), for consumers that\n * map `code` to their own UI copy.\n * 全部错误码的英文文案(parse 6 码 + source 5 码),供消费方按 code 映射自己的界面文案。\n *\n * Keyed by the stable machine-readable `code` (add-only contract); the bundled\n * Chinese `message` on each error remains the default narrative.\n * 键为稳定机读 `code`(只增不改);错误实体上的中文 `message` 仍是默认叙述。\n */\nexport const EN_MESSAGES: Record<MetarParseErrorCode | MetarSourceErrorCode, string> = {\n \"invalid-input\": \"Parse expects a METAR/SPECI report string, received a non-string value\",\n \"missing-station\": \"Not a METAR/SPECI report: station group missing or unrecognized\",\n \"missing-time\": \"Not a complete METAR/SPECI report: observation-time group missing\",\n \"invalid-time\": \"Observation-time group out of range (day 01–31 / hour 00–23 / minute 00–59)\",\n \"unsupported-mode\": \"Strict mode is not implemented in v0.1 — omit `mode` or pass 'tolerant'\",\n \"batch-parse-failed\":\n \"Some reports in the batch failed to parse entirely (see the summary for per-station reasons)\",\n \"http-error\": \"Source returned a non-2xx HTTP status\",\n \"bad-schema\": \"Response body does not match the agreed schema\",\n \"empty-data\": \"HTTP 200 with empty data — typically a wrong network name\",\n timeout: \"Request aborted after the configured timeout\",\n network: \"Network-level failure (offline, DNS, fetch refused)\",\n};\n","/**\n * @metweave/core — IR 数据模型(v0.1 契约草案,待审定)。\n *\n * 设计原则:\n * 1. IR 是解析器(格式 → IR)与渲染内核(IR → 图)之间的唯一契约,本文件即 npm 破坏性变更的边界;\n * 2. 一切产物可序列化为纯 JSON(服务端数据接口可直接返回 IR),零依赖、零类实例;\n * 3. 不静默:解析器看不懂的内容一律带 span 进 warnings[],永不丢弃;\n * 4. 三态显式建模,判据 = 电码表:只有存在「缺测电码形态」的组用 Observed(风 /////KT、能见度 ////、\n * RVR RVRNO、天气 //——全契约仅此四组)。组省略(字段 undefined)≠ 缺测(Observed missing)≠ 有值;\n * missing 覆盖台站明示缺测与解析器判定不可信的值(如 Q10054 → missing + value-out-of-range 告警,\n * 原码经 span 永可回溯)。无缺测形态的字段一律两态 plain。\n * 5. span 统一为 raw 的 UTF-16 码元半开区间 [start, end),raw.slice(start, end) 即原文片段——\n * RAW 对照视图、逐组高亮、错误定位的地基,发布后变更即破坏下游。\n * v0.1 起所有 span 字段可选:parse(raw, { spans: false }) 紧凑模式不携带任何 span\n * (批量入库/列存场景 JSON 体积约 -30%)——RAW 对照等 span 消费方将优雅降级\n * (整段高亮退化为无高亮、原码显示退化为 IR 重建,不炸)。\n *\n * 两级建模规则:组级按上述判据用 Observed 或两态 plain;组内子项一律 plain + 自带 span,\n * 子项缺测 = 值字段 | null(云高 {value:number|null,span}、云量位 null——BKN///、///025CB 两形态)。\n * 禁止 Observed 嵌套 Observed。\n *\n * 消费分层:读值用 toValues()(全两态视图,普通消费方的默认入口);需要区分「缺测 vs 组省略」、\n * 消费告警 span 与 RAW 定位的重度场景(核验/复盘/对照类)才直接进 IR。\n */\n\n// ---------------------------------------------------------------- 基元\n\n/**\n * Source span: half-open UTF-16 code-unit range [start, end) into `raw` — raw.slice(start, end) yields the original fragment.\n * 原文位置:raw 的 UTF-16 码元半开区间 [start, end),raw.slice(start, end) 即原片段。\n *\n * Multi-element groups (an array carried on an `Observed` — e.g. `runwayVisualRange`, `weather`)\n * carry a **group-level envelope span**: it runs from the first to the last element and does NOT\n * promise that everything inside belongs to the group (a stray token between two RVR groups falls\n * inside the envelope). Exact per-group text is always on the element's own `span` — RAW\n * cross-check highlighting must consume element spans, not the envelope.\n * 多元素组的组级 span 为**外包络**:自首组首至末组末,不承诺区间内皆为该组原文(两组之间\n * 夹带的无关 token 也在包络内);精确逐组原文恒在各元素自身的 span 上——RAW 对照高亮应消费\n * 元素 span,不应消费包络。\n */\nexport interface Span {\n readonly start: number;\n readonly end: number;\n}\n\n/**\n * Warning severity: info = anomaly kept as reported; warning = questionable but usable; error = seriously doubtful (cards must surface it).\n * 告警严重度:info=如实收下的反常;warning=可疑但可用;error=严重存疑(卡片需醒目提示)。\n *\n * Note: `error` is a reserved tier — the parser currently emits only info/warning (2026-09\n * audit: zero \"error\" sites). Kept in the union for the full ladder downstream; do not emit\n * without a commensurate discipline case.\n * 注:`error` 为预留档——解析器当前只产出 info/warning(2026-09 审计:解析器零 error 位)。\n * 保留在联合类型中供下游按完整阶梯处理;无相应纪律案例不得启用。\n */\nexport type WarningSeverity = \"info\" | \"warning\" | \"error\";\n\n/**\n * Machine-readable warning codes (stable keys, add-only; consumers branch on them and map `message` copy themselves).\n * 告警机读码(稳定键,只增不改;消费方据此分流,message 文案自行映射)。\n * 扩展新码属 additive 变更 / Adding a new code is an additive change.\n */\nexport type WarningCode =\n | \"unknown-token\" /** 正文里不认识的组(进 warnings 不丢弃) */\n | \"invalid-format\" /** 认出组但内容不合语法 */\n | \"value-out-of-range\" /** 数值超物理或条文范围(如 5 位数 QNH Q10054) */\n | \"missing-expected\" /** 组内必有部分缺测(如 BKN///、/////KT) */\n | \"duplicate-group\" /** 同族组重复出现(如双能见度组)——tolerant 口径以末组为准,前后值文案随告警、原文经 span/raw 回溯 */\n | \"cross-check-conflict\"; /** 双轨自洽校验失败(如 RMK T 组与正文 M 组圆整不符) */\n\n/**\n * A single parse warning: stable machine-readable `code`, severity, narrative `message`, and the source `span`.\n * 单条解析告警:稳定机读 code、严重度、叙述 message 与原文 span。\n */\nexport interface ParseWarning {\n readonly code: WarningCode;\n readonly severity: WarningSeverity;\n /** 人类可读说明(中文,卡片悬停可直接展示;i18n 需求出现时以 code 为键另行映射) */\n readonly message: string;\n readonly span?: Span;\n}\n\n/**\n * Group-level three states: kind 'value' = a value; kind 'missing' = explicitly missing — either the station's own missing code (RVRNO, ////, /////KT) or a value the parser deems untrustworthy (out of range / malformed → missing + the matching warning; a fake value never stays in `value`).\n * 组级三态:kind:'value' 有值;kind:'missing' 缺测——含台站明示缺测(RVRNO、////、/////KT)\n * 与解析器判定不可信的值(超界/非法 → missing + 对应告警,绝不把假值留在 value 里)。\n * 字段整体 undefined = 组省略(报文里压根没出现)/ The field being undefined as a whole = the group was omitted (never appeared in the report).\n */\nexport type Observed<T> =\n | { readonly kind: \"value\"; readonly value: T; readonly span?: Span }\n | { readonly kind: \"missing\"; readonly span?: Span };\n\n/**\n * Convenience unwrap: returns the value when present; undefined for explicit missing or an omitted group.\n * 便利取值:有值返回 value,显式缺测/组省略返回 undefined。\n */\nexport function unwrap<T>(observed: Observed<T> | undefined): T | undefined {\n return observed?.kind === \"value\" ? observed.value : undefined;\n}\n\n// ---------------------------------------------------------------- 度量单位(单位跟组走,禁止报文级全局单位)\n\n/** Speed unit (follows its group). 风速单位(单位跟组走)。 */\nexport type SpeedUnit = \"kt\" | \"mps\" | \"kmh\";\n/** Distance unit (follows its group). 距离单位(单位跟组走)。 */\nexport type DistanceUnit = \"m\" | \"sm\";\n/** Pressure unit (follows its group). 气压单位(单位跟组走)。 */\nexport type PressureUnit = \"hPa\" | \"inHg\";\n\n// ---------------------------------------------------------------- 报文级\n\n/**\n * Report kind marker: never inferred from the body (SPECI and METAR share the same body grammar); for IEM's stripped feeds it is injected externally via options.kind, defaulting to 'metar'.\n * 报文类型位:不从正文推断(SPECI 与 METAR 正文语法无差别);IEM 剥词场景由 options.kind 外部传入,缺省 'metar'。\n */\nexport type ReportKind = \"metar\" | \"speci\";\n\n/**\n * Observation time: day/hour/minute in UTC (two-state mandatory — no missing form; out-of-range values fail the whole report).\n * 观测时刻:日/时/分(UTC)——两态必填(无缺测形态,数值越界 = 整体失败)。\n */\nexport interface ReportTime {\n /** 日(01–31)时(00–23)分(00–59),UTC。时组为两态必填(无缺测形态)——数值越界 = 不可信时组,解析器按整体失败处理,绝不把假值留在 IR */\n readonly day: number;\n readonly hour: number;\n readonly minute: number;\n}\n\n/**\n * The wind group: direction/speed/gust plus the optional direction-variation sector (260V050).\n * 风组:风向/风速/阵风,及可选的风向变化扇区(260V050)。\n */\nexport interface WindGroup {\n /** VRB 全向风:direction 为 null 且 variable = true(风向缺测 ≠ 全向,两回事) */\n readonly variable: boolean;\n /** 度数 0–360,保留原码(正北可编 360,静风 000 编 0);VRB → null;越界(>360°)→ null + value-out-of-range 告警(子项级判缺测,值不可信)。环绕计算由消费方负责 */\n readonly direction: number | null;\n readonly speed: {\n readonly value: number;\n readonly unit: SpeedUnit;\n readonly span?: Span;\n /** 超上限前缀形态(P99KT/P49MPS,WMO 15.5.6 / AP-117 54 条):值存原码、beyond 标注超界端;三位数精确值(100–199)不带 beyond */\n readonly beyond?: \"above\";\n };\n /** 阵风 G 组(P 前缀超上限形态同速度位) */\n readonly gust?: {\n readonly value: number;\n readonly unit: SpeedUnit;\n readonly span?: Span;\n readonly beyond?: \"above\";\n };\n /** 风向变化组 260V050(扇区摆动,非瞬时波动);min/max 为顺时针起止端点,min 可大于 max(跨 0°,如 330V030),角度差须按环绕计算;任一端点越界(>360°)→ 变化组判缺测(undefined)+ value-out-of-range 告警,风组本体不受牵连 */\n readonly variation?: { readonly min: number; readonly max: number; readonly span?: Span };\n}\n\n/**\n * The visibility group: value + unit, plus threshold-coding flags (9999 / 0000 / P6SM / M-prefix are not exact readings).\n * 能见度组:数值 + 单位,及阈值性编码标志(9999 / 0000 / P6SM / M 前缀非实测值)。\n */\nexport interface VisibilityGroup {\n /** 米或英里;SM 混分数(1 1/4SM)折十进制 1.25;NDV 后缀(无方向变化)剥离丢弃、原码经 span 回溯;0000 存 0(下限编码,非实测 0 米) */\n readonly value: number;\n readonly unit: DistanceUnit;\n /** false = 阈值性编码(9999 ≥10km 上限、0000 <50m 下限、P6SM ≥6SM 美式超上限、M 前缀 <下界),true = 实测值 */\n readonly exact: boolean;\n /** 阈值方向(与 exact=false 配对出现):above = 大于/等于上界编码(9999、P 前缀),below = 小于下界(0000、M 前缀)——\n * 显示层据此选 >/< 前缀(M1/4SM → <0.25 SM、P6SM → >6 SM、9999 → ≥10 km、0000 → <50 m;\n * 0000 下限口径:AP-117 第 69 条「小于 50 米编 0000」,WMO 15.6.3 由最小档位 50 m 推出) */\n readonly beyond?: \"above\" | \"below\";\n /** 最低能见度方向组(WMO 15.6.2 VNVNVNVNDv:主导能见度后随 1200NW)——挂主导组上,非重复组不出 duplicate 告警 */\n readonly minimum?: { readonly value: number; readonly direction: string; readonly span?: Span };\n readonly span?: Span;\n}\n\n/**\n * Runway visual range (RVR): single-value or V-varying form, P/M beyond-range prefix, U/D/N trend, per-group unit (m or ft).\n * 跑道视程(RVR):单值或 V 波动形态、P/M 超界前缀、U/D/N 趋势、单位跟组走(米或英尺)。\n */\nexport interface RunwayVisualRange {\n /** 跑道号含方位后缀,如 '07L' */\n readonly runway: string;\n /** 单值形态(R07R/0150) */\n readonly value?: number;\n /** 波动形态(R07R/1800V2200) */\n readonly min?: number;\n readonly max?: number;\n /** 超界前缀:数值一律存原码(P2000 → 2000),beyondRange 标注超界端——P/M 前缀是 RVR 标准编报形态(超过最大/低于最小可编值),不作告警;V 形态仅一端超界(1400VP2000)时 beyondRange 指向该端 */\n readonly beyondRange?: \"above\" | \"below\";\n /** 单位跟组走:北美带 FT 后缀为英尺,默认米(同一套数据两种单位并存) */\n readonly unit: \"m\" | \"ft\";\n /** 趋势后缀 U/D/N */\n readonly trend?: \"up\" | \"down\" | \"no-change\";\n readonly span?: Span;\n}\n\n/**\n * Weather intensity sign: -/+ only qualify precipitation and FC/SS/DS; no sign = moderate.\n * 天气强度符:-/+ 只配降水与 FC/SS/DS;无符 = 中等。\n */\nexport type WeatherIntensity = \"-\" | \"+\";\n/** Weather descriptor code (WMO 4678: MI/PR/BC/DR/BL/SH/TS/FZ). 天气描述符电码(WMO 4678)。 */\nexport type WeatherDescriptor = \"MI\" | \"PR\" | \"BC\" | \"DR\" | \"BL\" | \"SH\" | \"TS\" | \"FZ\";\n/** Weather phenomenon code (WMO 4678 letter set). 天气现象电码(WMO 4678 字母集)。 */\nexport type WeatherPhenomenon =\n | \"DZ\"\n | \"RA\"\n | \"SN\"\n | \"SG\"\n | \"IC\"\n | \"PL\"\n | \"GR\"\n | \"GS\"\n | \"UP\"\n | \"BR\"\n | \"FG\"\n | \"FU\"\n | \"VA\"\n | \"DU\"\n | \"SA\"\n | \"HZ\"\n | \"PY\"\n | \"PO\"\n | \"SQ\"\n | \"FC\"\n | \"SS\"\n | \"DS\";\n\n/**\n * One present-weather group: optional intensity, VC proximity, optional descriptor, phenomenon list.\n * 单个现在天气组:可选强度、VC 邻近、可选描述符、现象列表。\n */\nexport interface WeatherGroup {\n readonly intensity?: WeatherIntensity;\n /** VC = 机场附近(5–10km 可见性限定),非本场上空 */\n readonly proximity: boolean;\n readonly descriptor?: WeatherDescriptor;\n readonly phenomena: readonly WeatherPhenomenon[];\n readonly span?: Span;\n}\n\n/** Cloud amount (okta classes). 云量电码(八分量档)。 */\nexport type CloudAmount = \"FEW\" | \"SCT\" | \"BKN\" | \"OVC\";\n/** Sky-clear family codes (SKC/NSC/NCD/CLR — semantics differ, original code kept). 无云家族电码(语义有别,保留原码)。 */\nexport type SkyClearCode = \"SKC\" | \"NSC\" | \"NCD\" | \"CLR\";\n/** Convective cloud type (cumulonimbus / towering cumulus). 对流云型(积雨云/浓积云)。 */\nexport type ConvectiveType = \"CB\" | \"TCU\";\n\ninterface CloudLayerBase {\n /** 高度英尺;/// 缺测 → value:null + missing-expected 告警(绝不捏造基高) */\n readonly heightFt: { readonly value: number | null; readonly span?: Span };\n readonly span?: Span;\n}\n\n/**\n * A cloud layer (amount + base height + optional convective type); amount/height slots carry null when missing.\n * 云层(云量 + 云底高 + 可选对流云型);云量/云高缺测位为 null。\n */\nexport interface CloudLayer extends CloudLayerBase {\n readonly kind: \"layer\";\n /** 云量位缺测(///025CB,15.9.1.6:探测到对流云但云量无法观测)→ null + missing-expected 告警(组内子项第二例外) */\n readonly amount: CloudAmount | null;\n readonly convective?: ConvectiveType;\n}\n\n/**\n * The VV group: sky-obscured vertical visibility replacing the whole cloud section; orthogonal to the visibility group.\n * VV 组:顶替整块云组的天空全遮蔽态,与能见度组正交。\n */\nexport interface VerticalVisibility extends CloudLayerBase {\n readonly kind: \"vertical-visibility\";\n}\n\n/** A cloud-section element: a layer or a vertical-visibility group. 云组元素:云层或垂直能见度组。 */\nexport type CloudElement = CloudLayer | VerticalVisibility;\n\n/**\n * The cloud condition: layer/vertical-visibility elements plus the sky-clear code when present.\n * 天空状况:云层/垂直能见度元素列表,及在位时的无云电码。\n */\nexport interface CloudCondition {\n readonly elements: readonly CloudElement[];\n /** 无云五兄弟:SKC(人工无云)/ NSC(有云但不显著)/ NCD(自动无探测)/ CLR(美式自动)——语义有别,保留原码 */\n readonly clear?: { readonly code: SkyClearCode; readonly span?: Span };\n}\n\n/**\n * A temperature/dewpoint reading in degrees Celsius.\n * 温度/露点读数(摄氏度)。\n */\nexport interface TemperatureReading {\n /** 摄氏度;−0.5°C 编 M00 → 0(负零符号经 RMK T 组兜回,原码经 span 回溯) */\n readonly celsius: number;\n readonly span?: Span;\n}\n\n/**\n * An altimeter setting: decoded physical value + unit (Q1008 → 1008 hPa; A3022 → 30.22 inHg).\n * 高度表设定:解码后的物理值 + 单位(Q1008 → 1008 hPa;A3022 → 30.22 inHg)。\n */\nexport interface AltimeterReading {\n /** 解码后十进制物理值:Q1008 → 1008(hPa);A3022 → 30.22(inHg,隐含小数点)——原码经 span 回溯 */\n readonly value: number;\n readonly unit: PressureUnit;\n readonly span?: Span;\n}\n\n/**\n * The trend group: kind + period + structured inner elements (see TrendElements) + raw text.\n * 趋势组:指示组种类 + 时段 + 组内要素结构化(见 TrendElements)+ 原文。\n * span/raw cover the whole trend segment (indicator + period + all element groups, e.g. \"BECMG AT0550 22015G25MPS 1000 +TSRA\");\n * span/raw 覆盖整个趋势段(指示组 + 时段 + 全部要素组,如「BECMG AT0550 22015G25MPS 1000 +TSRA」);\n * period.span covers only the period word itself; for NOSIG the span is the NOSIG word.\n * period.span 仅时段词本身;nosig 的 span 即 NOSIG 词。\n */\n/** Trend indicator kind (NOSIG / BECMG / TEMPO; 'unspecified' = worn segment led by a bare period word with the change indicator lost — IEM 归档 2026-09 实弹 128 次). 趋势指示组种类。 */\nexport type TrendKind = \"nosig\" | \"becmg\" | \"tempo\" | \"unspecified\";\n\n/**\n * Structured inner elements of one trend segment (WMO 306 FM15 §15.14.3: wind / visibility /\n * present weather / cloud families, plus the trend-only NSW code and CAVOK replacement).\n * Best-effort and silent: groups the grammar cannot recognize stay verbatim in `raw` and produce\n * no warnings (the parse-warning surface is snapshot-locked); `undefined` = no inner group was\n * recognized at all (e.g. bare NOSIG).\n * 趋势段内要素结构化(WMO 306 FM15 §15.14.3:风/能见度/天气/云四族,外加趋势专属电码 NSW 与\n * CAVOK 顶替形态)。best-effort 且静默:语法认不出的组原样保留在 raw、不产生告警(告警面受\n * 快照锁保护);undefined = 组内没有任何可识别要素(如裸 NOSIG)。\n */\nexport interface TrendElements {\n readonly wind?: WindGroup;\n readonly visibility?: VisibilityGroup;\n /** CAVOK(§15.14.3:趋势内顶替能见度/天气/云三族的形态) */\n readonly cavok?: { readonly span?: Span };\n /** 现在天气组(§15.14.3.2 w'w';恒为数组,空数组 = 无天气组被识别) */\n readonly weather: readonly WeatherGroup[];\n /** NSW(§15.14.3:趋势时段内无重要天气——趋势专属电码,正文无此组) */\n readonly nsw?: { readonly span?: Span };\n /** 天空状况(§15.14.3.3 NsNsNshshshs / VV / NSC 家族);仅当云族有组被识别时在位 */\n readonly clouds?: CloudCondition;\n}\n\n/**\n * One trend group (NOSIG / BECMG / TEMPO with optional period word; worn segments whose indicator\n * was lost in transmission enter with kind 'unspecified' plus an invalid-format warning — the\n * period word keeps leading the segment so the inner elements are still fenced out of the body);\n * inner elements are structured additively in `elements` (TrendElements).\n * 单个趋势组(NOSIG / BECMG / TEMPO,时段词可选;传输磨损丢指示组的趋势段以 kind 'unspecified'\n * 进入并附 invalid-format 告警——时段词照旧引导收段,要素组不再散落正文);组内要素经 `elements`\n * 以 additive 方式结构化(见 TrendElements)。\n */\nexport interface TrendGroup {\n readonly kind: TrendKind;\n /** FM/AT/TL 时段(如 AT0040,WMO 306 FM15 §15.14.3);v0.1 保留原词 */\n readonly period?: { readonly text: string; readonly span?: Span };\n /** 组内要素结构化(best-effort、静默;undefined = 无可识别要素;NOSIG 语义上无要素) */\n readonly elements?: TrendElements;\n readonly raw: string;\n readonly span?: Span;\n}\n\n/**\n * Braking action (WMO 306 FM15 §15.13.6.1 code table 0366, friction codes 91–95, five classes); 99 = unreliable (the friction number cannot be trusted).\n * 制动作用(WMO 306 FM15 §15.13.6.1 表 0366 摩擦电码 91–95 五档);99 = unreliable(摩擦数值不可信)。\n */\nexport type RunwayBraking =\n | \"poor\" /** 91 */\n | \"medium-poor\" /** 92 */\n | \"medium\" /** 93 */\n | \"medium-good\" /** 94 */\n | \"good\" /** 95 */\n | \"unreliable\"; /** 99:设备在积雪/雪浆中测值不可信 */\n\n/**\n * The runway state group (WMO 306 FM15 §15.13.6 — runway state codes appended to METAR; being displaced by ICAO GRF since 2020, still common on GTS in winter). Three forms share this group: the six-digit state code, the CLRD family, and SNOCLO closure.\n * 跑道状态组(WMO 306 FM15 §15.13.6——METAR 附带的跑道状态电码;ICAO 全球报告格式 GRF\n * 2020 年推广后渐被 RCR 取代,GTS 通路冬季仍常见)。三类形态共用本组:\n * ①六位状态电码:R21/490160 = 沉积物类型 1 位 + 覆盖范围 1 位 + 深度 2 位 + 摩擦 2 位;\n * ②CLRD 清除家族:R07L/CLRD//(WMO 标准,摩擦缺测)与 R06L/CLRD62(俄区惯例,摩擦 0.62);\n * ③SNOCLO 关闭:R/SNOCLO(全机场)与 R10L/SNOCLO(逐跑道)——跑道因雪/冰/清雪不可用。\n *\n * 电码表依据(WMO 306 FM15,均按 additive 契约解码):\n * - 跑道号特殊值(§15.13.6.1 注):88 = 全部跑道、99 = 重复上一份跑道状态报告;\n * - 沉积物类型(§15.13.6.2):0 干燥 / 1 潮湿 / 2 湿或积水 / 3 雾凇或霜覆盖 / 4 干雪 /\n * 5 湿雪 / 6 雪浆 / 7 冰 / 8 压实或滚压雪 / 9 冻结轮辙或脊;/ = 缺报;\n * - 覆盖范围(§15.13.6.1,表 0519):1 ≤10% / 2 11–25% / 5 26–50% / 9 51–100%;/ = 缺报;\n * 表外数字(0/3/4/6/7/8)= 非法电码 → 该位判缺测(null)+ invalid-format 告警;\n * - 深度(§15.13.6.1 表 1079):00 = <1mm 记 0;01–90 = 毫米;92–98 = 10–40cm 段记下限毫米\n * (92→100 … 98→400,98 为 40cm 以上);91 电码表未用判缺测;99 = 跑道不可用\n * (同 SNOCLO 语义 → closed=true);// = 深度操作上不显著或缺报;\n * - 摩擦(§15.13.6.1 表 0366):01–90 = 摩擦系数 0.01–0.90;91–95 = 制动作用五档(RunwayBraking);\n * 99 = 数值不可靠;// = 缺报。\n */\nexport interface RunwayStateGroup {\n /** 跑道号(含方位字母,如 '06L');'' = R/SNOCLO 全机场形态 */\n readonly runway: string;\n /** 关闭:SNOCLO(§15.13.6.1)或六位电码深度位 99(跑道不可用) */\n readonly closed?: boolean;\n /** CLRD:跑道污染已清除(后随摩擦两位或 //) */\n readonly cleared: boolean;\n /** 沉积物类型电码 0–9(§15.13.6.2 表 0919);null = /(缺报) */\n readonly deposit?: number | null;\n /** 覆盖范围电码 1/2/5/9(§15.13.6.1 表 0519——仅此四值与 /,其余数字为表外非法码);null = /(缺报)或非法码(判缺测 + invalid-format 告警,绝不留表外值) */\n readonly coverage?: number | null;\n /** 深度毫米(§15.13.6.1 表 1079;92–98 段记下限);深度位 99(跑道不可用)与 //(不显著/缺报)均记 null,由 closed 与 span 回溯区分 */\n readonly depth?: number | null;\n /** 摩擦系数 0.01–0.90(§15.13.6.1 表 0366 电码 01–90 折十进制;CLRD62 → 0.62 俄区惯例同族) */\n readonly frictionCoefficient?: number;\n /** 制动作用(§15.13.6.1 表 0366 电码 91–95 五档与 99 不可靠)——与摩擦系数互斥出现 */\n readonly brakingAction?: RunwayBraking;\n readonly span?: Span;\n}\n\n/**\n * Wind shear group (WMO 306 FM15 §15.13.3 / ICAO Annex 3 template — the `WS` body group).\n * 风切变组(WMO 306 FM15 §15.13.3 / ICAO Annex 3 模板——正文组 `WS`)。\n *\n * Flight-safety semantics: low-level wind shear on the approach/departure path is a\n * major hazard during takeoff and landing (sudden airspeed loss/gain); the group is\n * therefore rendered with danger-level highlighting.\n * 飞行安全语义:起降通道上的低空风切变是起飞/着陆阶段的重大危害(空速骤变),\n * 渲染层按危险级着色提示。\n *\n * Forms carried: the ICAO/WMO standard `WS ALL RWY` (all runways, allRunways=true) and\n * `WS RDRDR` (e.g. `WS R24`); the regional-practice variants `WS RWY02L` / `WS RWY18`\n * (literal `RWY` prefix before the designator) and `WS RWY ALL` (word order inverted)\n * are accepted equivalently — all four forms parse warning-free; multiple groups in one\n * report accumulate into one field (runways concat, span covers first-to-last group).\n * 承载形态:ICAO/WMO 标准的 `WS ALL RWY`(全部跑道,allRunways=true)与 `WS RDRDR`\n * (如 `WS R24`,IEM 归档实弹 544 次、中国区多发且为近月主流);中国区实务变体\n * `WS RWY02L` / `WS RWY18`(设计器带字面 RWY 前缀)与 `WS RWY ALL`(词序倒置)同等\n * 认组——四形态均零告警解析;同报多组累积为一个字段(runways 连接、span 覆盖首组至末组)。\n */\nexport interface WindShearGroup {\n /** Runway designators (with position letter, e.g. '02L', '18'); empty when only an ALL form is reported. 指定跑道设计器(含方位字母);仅报 ALL 形态时为空数组 */\n readonly runways: readonly string[];\n /** An all-runways form (`WS ALL RWY` standard / `WS RWY ALL` variant) was reported. 报有全跑道形态(标准 `WS ALL RWY` / 变体 `WS RWY ALL`) */\n readonly allRunways: boolean;\n readonly span?: Span;\n}\n\n/**\n * RMK 附加段:认组粒度收下(在 RMK 处切换语法状态机,不用 WMO 语法解 RMK)。\n * 已识别种类见 RemarkKind;未识别片段 kind='unknown'——RMK 本是各国外挂槽,未知 ≠ 错误,\n * 故不进 warnings(warnings 留给正文异常),防止噪音爆炸。\n * 少数正文位「认组收下」的组(维护符 $、变化能见度 VIS nVn、TAF 混入通路的 TX/TN)\n * 同走本槽(kind 各自标注)——正文里它们不是异常,不值得告警面,但需要一个 IR 落点。\n */\n/**\n * Recognized RMK group kinds (group-level recognition; unknown fragments stay kind 'unknown' and never enter warnings — RMK is a national annex slot, unknown ≠ error).\n * RMK 认组种类(认组粒度收下;未识别片段 kind 'unknown',不进 warnings——RMK 是各国附加段槽位,未知 ≠ 错误)。\n */\nexport type RemarkKind =\n | \"auto-type\" /** AO1 / AO2 */\n | \"sea-level-pressure\" /** SLP134(省略式补位规则归解析器内部) */\n | \"precise-temperature\" /** T 组(十分位,精度高于正文 M 组,同要素双写取高者) */\n | \"precip-1h\" /** P 组(英寸百分位,P0101 = 1.01 in) */\n | \"precip-window\" /** 6RRRR / 7RRRR(窗口语义由观测时刻决定) */\n | \"snow-depth\" /** 4/sss */\n | \"ice-accretion\" /** I1/I3/I6nnn */\n | \"snow-increase\" /** SNINCR 6/2(时增积雪/总积雪,英寸,两 token 一组) */\n | \"pressure-tendency\" /** 5appp(3h 变压) */\n | \"peak-wind\" /** PK WND dddff/GGgg */\n | \"phenomenon-began-ended\" /** FZRAB43E50 / FUNNEL CLOUD B2355 E06 */\n | \"surface-visibility\" /** SFC VIS n(SFC 尾串是 FC 误报大户) */\n | \"variable-visibility\" /** VIS nnnnVnnnn */\n | \"vis-no\" /** VISNO(能见度不可测) */\n | \"rvr-no\" /** RVRNO(RVR 不可用,美式报文置于备注区形态) */\n | \"temp-extrema-24h\" /** 4 组九位 40sssTTT(24h 最高/最低温度) */\n | \"temp-extrema-6h\" /** 1/2 组(6h 最高/最低温度) */\n | \"wind-shift\" /** WSHFT 1409(风向转变) */\n | \"pressure-change\" /** PRESRR / PRESFR(气压升降) */\n | \"maintenance\" /** 报尾 $(维护检查中,数据可靠性存疑) */\n | \"lightning\" /** LTG 系列 */\n | \"thunderstorm-sensor\" /** TSNO(美网高频):雷暴传感器不工作——雷暴探测不可用,非无雷暴 */\n | \"cloud-base-height\" /** QBB(俄区国家组):云底高度(米,3 位直读,与正文云组互为印证) */\n | \"aerodrome-pressure\" /** QFE(俄区国家组):场面气压——QFE749 = 749 mmHg;QFE746/0995 = 746 mmHg / 995 hPa 双单位(760 mmHg = 1013.25 hPa 标准大气互证);四位直读 hPa(QFE1003)。认组粒度收下不解码数值,原码经 span 可取 */\n | \"temperature-forecast\" /** TX/TN(TAF 温度预告组混入 METAR 通路,ICAO Annex 3 附录五):TX25/0907Z = 最高 25°C 于 09 日 07Z 到达;认组收下 raw 保真,不解码数值 */\n | \"twr-visibility\" /** TWR VIS n(塔台能见度,FMH-1 12.7.1.f) */\n | \"sectoral-visibility\" /** VIS <方位> n(分区能见度,FMH-1 12.7.1.h) */\n | \"cig-not-available\" /** CIGNO(云高计不可用,FMH-1 12.7.1.p 族) */\n | \"ceiling\" /** CIG hhh(云高,百英尺) */\n | \"ceiling-variation\" /** CIG hhhVhhh(云高波动) */\n | \"ceiling-at-location\" /** CIG hhh LOC(局地云高) */\n | \"precip-not-available\" /** PNO(降水传感器不可用,FMH-1 12.7.2.g) */\n | \"fzr-not-available\" /** FZRANO(冻雨传感器不可用,FMH-1 12.7.2.g) */\n | \"chino\" /** CHINO(天空状况传感器不可用,FMH-1 12.7.2.g) */\n | \"cloud-type-8group\" /** 8/CCC(低/中/高云型电码 0–9,X 未知,/ 缺测,FMH-1 12.7.2.b) */\n | \"snow-water-equivalent\" /** 933RRR(积雪水当量,英寸百分之一,FMH-1 12.7.2.a) */\n | \"unknown\";\n\n/**\n * One RMK group: recognized kind + verbatim raw + span.\n * 单个 RMK 组:认组 kind + 原文 raw + span。\n */\nexport interface RemarkGroup {\n readonly kind: RemarkKind;\n readonly raw: string;\n readonly span?: Span;\n}\n\n// ---------------------------------------------------------------- 报告\n\n/**\n * The parsed METAR/SPECI report — the IR root. Every product is plain serializable JSON; three-state groups (wind/visibility/RVR/weather) distinguish omitted vs missing vs value.\n * 解析后的 METAR/SPECI 报文——IR 根。产物为纯 JSON 可序列化;四个三态组(风/能见度/RVR/天气)区分省略/缺测/有值。\n */\nexport interface MetarReport {\n readonly kind: ReportKind;\n /** 原文保真(取数源 raw 字段原样,不重排不改写) */\n readonly raw: string;\n /** NIL = 台站无观测的运行凭据(站点监测场景需要区分「无观测」与「未取到」);\n * NIL 报文产出最小形态 {station, time, nil:true, raw, warnings:[]},正文组不解析(本就无正文) */\n readonly nil?: boolean;\n /** 无站名组 = 整体解析失败,不是字段级三态 */\n readonly station: string;\n /** 日时组 ddHHMMZ(UTC);同理,无时组 = 整体解析失败 */\n readonly time: ReportTime;\n /** 正交标志位:AUTO 位与 COR 位(类型位见 kind)——两两正交,禁止合并成一个「类型」字段 */\n readonly flags: {\n readonly auto: boolean;\n readonly corrected: boolean;\n };\n /** CAVOK:能见度 ≥10km + 无低云 + 无天气三关全过;此时 vis/weather/cloud 三组让位(词位见 cavokSpan) */\n readonly cavok: boolean;\n readonly cavokSpan?: Span;\n /** 组省略 = undefined;显式缺测(/////KT)= { kind:'missing' } */\n readonly wind?: Observed<WindGroup>;\n readonly visibility?: Observed<VisibilityGroup>;\n /** RVRNO(设备存在但明示不可用)= { kind:'missing' };组省略 = undefined;有值 = 数组。\n * 数组级 span 为首组至末组的外包络(契约见 Span 注释);精确逐组区间在各元素 span。 */\n readonly runwayVisualRange?: Observed<readonly RunwayVisualRange[]>;\n /** 天气组缺省 = undefined;// (自动站无法观测天气)= { kind:'missing' }。\n * 数组级 span 为首组至末组的外包络(契约见 Span 注释);精确逐组区间在各元素 span。 */\n readonly weather?: Observed<readonly WeatherGroup[]>;\n /** RE 近期天气组(15.13.2,至多三组,位于补充信息段、趋势段之前;强度恒缺省——不入 trends,无缺测形态故不套 Observed) */\n readonly recentWeather?: readonly WeatherGroup[];\n /** 天空组无整组缺测电码(子项缺测由云高/云量位兜住);CAVOK 时整组让位为 undefined */\n readonly clouds?: CloudCondition;\n readonly temperature?: TemperatureReading;\n readonly dewpoint?: TemperatureReading;\n /** RMK T 组十分位精度(高于正文 M 组,同要素双写取高者)。\n * v0.1 暂不填充——解析器把 T 组认进 remarks(kind 'precise-temperature',原文经 span 可取);\n * 数值化解计划随 strict 校验模式落地,届时填充不属破坏性变更(optional 字段)。 */\n readonly preciseTemperature?: TemperatureReading;\n /** 同 preciseTemperature:RMK T 组露点十分位。v0.1 暂不填充,原文在 remarks 可取。 */\n readonly preciseDewpoint?: TemperatureReading;\n readonly altimeter?: AltimeterReading;\n /** RMK SLP(与 A 组差 1–2 hPa 属正常姊妹关系,非脏数据)。\n * v0.1 暂不填充——原文在 remarks(kind 'sea-level-pressure')可取;数值化解计划随 strict 校验模式落地。 */\n readonly seaLevelPressure?: AltimeterReading;\n readonly trends: readonly TrendGroup[];\n /** 跑道状态组(§15.13.6 三形态:六位电码 / CLRD / SNOCLO——SNOCLO 自 v0.1 开发期的 runwayVisualRange missing 迁入,未发布故非破坏性);空数组 = 报文无跑道状态组 */\n readonly runwayStates: readonly RunwayStateGroup[];\n /** 风切变组(§15.13.3,标准 `WS ALL RWY` / `WS RDRDR`,实务变体同等认组——飞行安全重大危害项,见 WindShearGroup);undefined = 报文无风切变组 */\n readonly windShear?: WindShearGroup;\n readonly remarks: readonly RemarkGroup[];\n /** 永远存在,可为空数组 */\n readonly warnings: readonly ParseWarning[];\n}\n\n// ---------------------------------------------------------------- 解析契约与语义工具\n\n/**\n * Options for `parse`: tolerance mode (v0.1 tolerant only), external kind override, span carriage (compact mode drops all spans).\n * parse 的选项:容忍模式(v0.1 仅 tolerant)、外部类型位注入、span 携带(紧凑模式剥除全部 span)。\n */\nexport interface ParseOptions {\n /** 缺省 tolerant;strict 留给服务端校验接口(报文格式自动核对,路线图项)——\n * v0.1 尚未实现:显式传 strict 会抛 MetarParseError{code:'unsupported-mode'}(类型已预留,additive 落地不破坏契约) */\n readonly mode?: \"tolerant\" | \"strict\";\n /** 外部元数据覆盖类型位(IEM 剥词场景);缺省按正文词,无词 → 'metar' */\n readonly kind?: ReportKind;\n /** 缺省 true;false = 紧凑模式:IR 不携带任何 span(JSON 体积约 -30%,批量入库/列存场景)。\n * spans:false 时 RAW 对照等 span 消费方优雅降级(见本文件头部设计原则第 5 条) */\n readonly spans?: boolean;\n}\n\n// ---------------------------------------------------------------- 统一取值视图(普通消费方的默认入口)\n\n/**\n * The all-two-state view type: derived automatically from the IR (adding IR fields updates the view — it can never drift).\n * 全两态视图类型:由 IR 自动派生(IR 加字段视图自动跟随,永不漂移)。\n * The four three-state groups flatten to \"value | undefined\" (explicit missing and omitted share one shape); all other fields pass through unchanged.\n * 四个三态组投平为「值 | undefined」(缺测与组省略同形);其余字段原样透传。\n */\nexport type Values<R> = {\n [K in keyof R]: NonNullable<R[K]> extends Observed<infer T> ? T | undefined : R[K];\n};\n\n/** The two-state view of MetarReport (the default consumer entry). MetarReport 的两态视图(普通消费方默认入口)。 */\nexport type MetarValues = Values<MetarReport>;\n\n/**\n * One projection to the all-two-state view; consume MetarReport directly only for three-state discrimination and warning spans.\n * 一次投影得到全两态视图;需要三态判别与告警 span 的重度场景才直接消费 MetarReport。\n */\nexport function toValues(report: MetarReport): MetarValues {\n return {\n ...report,\n wind: unwrap(report.wind),\n visibility: unwrap(report.visibility),\n runwayVisualRange: unwrap(report.runwayVisualRange),\n weather: unwrap(report.weather),\n };\n}\n"],"mappings":";;;;;;;;AAgCA,IAAa,kBAAb,cAAqC,MAAM;CACzC;;CAEA;CAEA,YAAY,MAA2B,KAAa,SAAiB;EACnE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,MAAM;CACb;AACF;;;;;AAiBA,IAAa,mBAAb,cAAsC,MAAM;CAC1C;;CAEA;CAEA,YACE,MACA,SACA,SACA,SACA;EACA,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,UAAU;CACjB;AACF;;;;;;;;;;AAWA,MAAa,cAA0E;CACrF,iBAAiB;CACjB,mBAAmB;CACnB,gBAAgB;CAChB,gBAAgB;CAChB,oBAAoB;CACpB,sBACE;CACF,cAAc;CACd,cAAc;CACd,cAAc;CACd,SAAS;CACT,SAAS;AACX;;;;;;;ACJA,SAAgB,OAAU,UAAkD;CAC1E,OAAO,UAAU,SAAS,UAAU,SAAS,QAAQ,KAAA;AACvD;;;;;AAifA,SAAgB,SAAS,QAAkC;CACzD,OAAO;EACL,GAAG;EACH,MAAM,OAAO,OAAO,IAAI;EACxB,YAAY,OAAO,OAAO,UAAU;EACpC,mBAAmB,OAAO,OAAO,iBAAiB;EAClD,SAAS,OAAO,OAAO,OAAO;CAChC;AACF"}
|