@thejrsoft/subway-protocol 1.4.14 → 1.6.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/CHANGELOG.md +212 -0
- package/dist/asyncapi-sync.js +3 -3
- package/dist/code-meta.d.ts +57 -0
- package/dist/code-meta.d.ts.map +1 -0
- package/dist/code-meta.js +268 -0
- package/dist/code-meta.js.map +1 -0
- package/dist/index.d.ts +71 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +132 -32
- package/dist/index.js.map +1 -1
- package/dist/message-validator.d.ts +1 -5
- package/dist/message-validator.d.ts.map +1 -1
- package/dist/message-validator.js +3 -9
- package/dist/message-validator.js.map +1 -1
- package/dist/protocol-utils.d.ts +6 -6
- package/dist/protocol-utils.js +4 -4
- package/package.json +2 -2
- package/src/__tests__/report-category.test.ts +131 -56
- package/src/asyncapi-sync.ts +4 -4
- package/src/code-meta.ts +322 -0
- package/src/index.ts +262 -47
- package/src/message-validator.ts +4 -12
- package/src/protocol-utils.ts +9 -9
- package/src/__tests__/protocol.test.ts +0 -297
package/src/code-meta.ts
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeMeta — v1.6.0 协议核心
|
|
3
|
+
*
|
|
4
|
+
* report.code 是业务命运的单一信息源;status / level / semantic 全部由此派生。
|
|
5
|
+
*
|
|
6
|
+
* 设备端调用 dispatchMessage(code, data) 时,协议层自动按 code 推导 status / level,
|
|
7
|
+
* 集成方接收消息后调用 resolveCodeMeta(code) 查表得到 semantic / requiresErrorBlock 等。
|
|
8
|
+
*
|
|
9
|
+
* 设计依据:claude-docs/protocol-upgrade-proposal-v1.6.0.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// v1.6.0: 使用 type-only import 避免与 index.ts 的循环依赖
|
|
13
|
+
// index.ts 在 MessageFactory.dispatchMessage 中 import code-meta,
|
|
14
|
+
// code-meta 本文件如果运行时 import ./index 会触发 TDZ。
|
|
15
|
+
// 使用 type import 仅在编译期解析,运行时不产生 cyclic require。
|
|
16
|
+
import type { ReportLevel } from './index';
|
|
17
|
+
|
|
18
|
+
/** v1.6.0:单一 status enum,合并 v1.5.0 的 CommandStatus + ProgressStatus */
|
|
19
|
+
export enum MessageStatus {
|
|
20
|
+
IN_PROGRESS = 'IN_PROGRESS',
|
|
21
|
+
COMPLETED = 'COMPLETED',
|
|
22
|
+
FAILED = 'FAILED',
|
|
23
|
+
CANCELLED = 'CANCELLED',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 业务语义分类——终态语义 + 进度语义 */
|
|
27
|
+
export type CodeSemantic =
|
|
28
|
+
// 终态语义(仅在 isTerminal=true 时出现)
|
|
29
|
+
| 'success'
|
|
30
|
+
| 'partial'
|
|
31
|
+
| 'failure'
|
|
32
|
+
| 'no-op'
|
|
33
|
+
| 'cancel'
|
|
34
|
+
| 'busy'
|
|
35
|
+
// 进度语义(isTerminal=false)
|
|
36
|
+
| 'phase_start'
|
|
37
|
+
| 'phase_end'
|
|
38
|
+
| 'phase_failed'
|
|
39
|
+
| 'step_success'
|
|
40
|
+
| 'step_skipped'
|
|
41
|
+
| 'step_progress'
|
|
42
|
+
| 'retry'
|
|
43
|
+
| 'retry_pending'
|
|
44
|
+
| 'warning';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 单一 code 的元数据描述符
|
|
48
|
+
*
|
|
49
|
+
* 派生字段(status / level)会序列化进消息;
|
|
50
|
+
* 客户端字段(semantic / requiresErrorBlock / isTerminal)仅供查表,不进消息。
|
|
51
|
+
*/
|
|
52
|
+
export interface CodeMeta {
|
|
53
|
+
isTerminal: boolean;
|
|
54
|
+
status: MessageStatus;
|
|
55
|
+
level: ReportLevel;
|
|
56
|
+
semantic: CodeSemantic;
|
|
57
|
+
requiresErrorBlock: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 终态 code 显式 META 表
|
|
62
|
+
*
|
|
63
|
+
* 这些 code 是业务命运决断点,每个语义都是独特的,**不能用后缀规则推导**。
|
|
64
|
+
* 设备端发送终态消息时,dispatchMessage 在此表查询,自动填充消息字段。
|
|
65
|
+
*/
|
|
66
|
+
export const EXPLICIT_META: Record<string, CodeMeta> = {
|
|
67
|
+
// ═══════════ PROGRAM 节目部署 ═══════════
|
|
68
|
+
PROGRAM_COMPLETED: {
|
|
69
|
+
isTerminal: true,
|
|
70
|
+
status: MessageStatus.COMPLETED,
|
|
71
|
+
level: 'INFO' as ReportLevel,
|
|
72
|
+
semantic: 'success',
|
|
73
|
+
requiresErrorBlock: false,
|
|
74
|
+
},
|
|
75
|
+
PROGRAM_PARTIALLY_SUCCEEDED: {
|
|
76
|
+
isTerminal: true,
|
|
77
|
+
status: MessageStatus.COMPLETED,
|
|
78
|
+
level: 'WARNING' as ReportLevel,
|
|
79
|
+
semantic: 'partial',
|
|
80
|
+
requiresErrorBlock: false,
|
|
81
|
+
},
|
|
82
|
+
PROGRAM_ALL_FAILED: {
|
|
83
|
+
isTerminal: true,
|
|
84
|
+
status: MessageStatus.FAILED,
|
|
85
|
+
level: 'ERROR' as ReportLevel,
|
|
86
|
+
semantic: 'failure',
|
|
87
|
+
requiresErrorBlock: true,
|
|
88
|
+
},
|
|
89
|
+
PROGRAM_NO_PROGRAMS: {
|
|
90
|
+
isTerminal: true,
|
|
91
|
+
status: MessageStatus.COMPLETED,
|
|
92
|
+
level: 'INFO' as ReportLevel,
|
|
93
|
+
semantic: 'no-op',
|
|
94
|
+
requiresErrorBlock: false,
|
|
95
|
+
},
|
|
96
|
+
PROGRAM_UPLOAD_FAILED: {
|
|
97
|
+
isTerminal: true,
|
|
98
|
+
status: MessageStatus.FAILED,
|
|
99
|
+
level: 'ERROR' as ReportLevel,
|
|
100
|
+
semantic: 'failure',
|
|
101
|
+
requiresErrorBlock: true,
|
|
102
|
+
},
|
|
103
|
+
PROGRAM_UPLOAD_BUSY: {
|
|
104
|
+
isTerminal: true,
|
|
105
|
+
status: MessageStatus.FAILED,
|
|
106
|
+
level: 'WARNING' as ReportLevel,
|
|
107
|
+
semantic: 'busy',
|
|
108
|
+
requiresErrorBlock: true,
|
|
109
|
+
},
|
|
110
|
+
PROGRAM_UPLOAD_CANCELLED: {
|
|
111
|
+
isTerminal: true,
|
|
112
|
+
status: MessageStatus.CANCELLED,
|
|
113
|
+
level: 'WARNING' as ReportLevel,
|
|
114
|
+
semantic: 'cancel',
|
|
115
|
+
requiresErrorBlock: true,
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// ═══════════ QUICKLY_DETECTION 一键检测 ═══════════
|
|
119
|
+
QUICK_DETECTION_ALL_ONLINE: {
|
|
120
|
+
isTerminal: true,
|
|
121
|
+
status: MessageStatus.COMPLETED,
|
|
122
|
+
level: 'INFO' as ReportLevel,
|
|
123
|
+
semantic: 'success',
|
|
124
|
+
requiresErrorBlock: false,
|
|
125
|
+
},
|
|
126
|
+
QUICK_DETECTION_PARTIAL_ONLINE: {
|
|
127
|
+
isTerminal: true,
|
|
128
|
+
status: MessageStatus.COMPLETED,
|
|
129
|
+
level: 'WARNING' as ReportLevel,
|
|
130
|
+
semantic: 'partial',
|
|
131
|
+
requiresErrorBlock: false,
|
|
132
|
+
},
|
|
133
|
+
QUICK_DETECTION_ALL_OFFLINE: {
|
|
134
|
+
isTerminal: true,
|
|
135
|
+
status: MessageStatus.COMPLETED,
|
|
136
|
+
level: 'ERROR' as ReportLevel,
|
|
137
|
+
semantic: 'failure',
|
|
138
|
+
requiresErrorBlock: true,
|
|
139
|
+
},
|
|
140
|
+
QUICK_DETECTION_FAILED: {
|
|
141
|
+
isTerminal: true,
|
|
142
|
+
status: MessageStatus.FAILED,
|
|
143
|
+
level: 'ERROR' as ReportLevel,
|
|
144
|
+
semantic: 'failure',
|
|
145
|
+
requiresErrorBlock: true,
|
|
146
|
+
},
|
|
147
|
+
QUICK_DETECTION_BUSY: {
|
|
148
|
+
isTerminal: true,
|
|
149
|
+
status: MessageStatus.FAILED,
|
|
150
|
+
level: 'WARNING' as ReportLevel,
|
|
151
|
+
semantic: 'busy',
|
|
152
|
+
requiresErrorBlock: true,
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
// ═══════════ SYNC_MONITORING_TABLE 监播导出 ═══════════
|
|
156
|
+
SYNC_MONITORING_TABLE_READ_SUCCESS: {
|
|
157
|
+
isTerminal: true,
|
|
158
|
+
status: MessageStatus.COMPLETED,
|
|
159
|
+
level: 'INFO' as ReportLevel,
|
|
160
|
+
semantic: 'success',
|
|
161
|
+
requiresErrorBlock: false,
|
|
162
|
+
},
|
|
163
|
+
SYNC_MONITORING_TABLE_READ_FAILED: {
|
|
164
|
+
isTerminal: true,
|
|
165
|
+
status: MessageStatus.FAILED,
|
|
166
|
+
level: 'ERROR' as ReportLevel,
|
|
167
|
+
semantic: 'failure',
|
|
168
|
+
requiresErrorBlock: true,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 进度 code 后缀规则
|
|
174
|
+
*
|
|
175
|
+
* 进度消息有数百个 code(按 PROGRAM_FETCH_DOWNLOAD_PROGRESS 这种命名规范),
|
|
176
|
+
* 不可能逐一罗列 META,但 level / status / semantic 完全可从后缀派生。
|
|
177
|
+
*
|
|
178
|
+
* 优先级从高到低,首次命中即返回。
|
|
179
|
+
*/
|
|
180
|
+
interface SuffixRule {
|
|
181
|
+
/** 后缀匹配(endsWith)*/
|
|
182
|
+
suffix?: string;
|
|
183
|
+
/** 中缀匹配(用于 _WARN_ 这种夹中间的情况)*/
|
|
184
|
+
infix?: string;
|
|
185
|
+
meta: Omit<CodeMeta, 'isTerminal'>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const SUFFIX_RULES: SuffixRule[] = [
|
|
189
|
+
{
|
|
190
|
+
suffix: '_FAILED',
|
|
191
|
+
meta: {
|
|
192
|
+
status: MessageStatus.IN_PROGRESS,
|
|
193
|
+
level: 'ERROR' as ReportLevel,
|
|
194
|
+
semantic: 'phase_failed',
|
|
195
|
+
requiresErrorBlock: true,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
suffix: '_RETRY_PENDING',
|
|
200
|
+
meta: {
|
|
201
|
+
status: MessageStatus.IN_PROGRESS,
|
|
202
|
+
level: 'WARNING' as ReportLevel,
|
|
203
|
+
semantic: 'retry_pending',
|
|
204
|
+
requiresErrorBlock: false,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
suffix: '_RETRY',
|
|
209
|
+
meta: {
|
|
210
|
+
status: MessageStatus.IN_PROGRESS,
|
|
211
|
+
level: 'WARNING' as ReportLevel,
|
|
212
|
+
semantic: 'retry',
|
|
213
|
+
requiresErrorBlock: false,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
// 后缀或中缀都命中:例 PROGRAM_UPLOAD_WARN_CAN_BUS_ALARM
|
|
218
|
+
infix: '_WARN',
|
|
219
|
+
meta: {
|
|
220
|
+
status: MessageStatus.IN_PROGRESS,
|
|
221
|
+
level: 'WARNING' as ReportLevel,
|
|
222
|
+
semantic: 'warning',
|
|
223
|
+
requiresErrorBlock: false,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
suffix: '_SKIPPED',
|
|
228
|
+
meta: {
|
|
229
|
+
status: MessageStatus.IN_PROGRESS,
|
|
230
|
+
level: 'INFO' as ReportLevel,
|
|
231
|
+
semantic: 'step_skipped',
|
|
232
|
+
requiresErrorBlock: false,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
suffix: '_COMPLETED',
|
|
237
|
+
meta: {
|
|
238
|
+
status: MessageStatus.IN_PROGRESS,
|
|
239
|
+
level: 'INFO' as ReportLevel,
|
|
240
|
+
semantic: 'phase_end',
|
|
241
|
+
requiresErrorBlock: false,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
suffix: '_START',
|
|
246
|
+
meta: {
|
|
247
|
+
status: MessageStatus.IN_PROGRESS,
|
|
248
|
+
level: 'INFO' as ReportLevel,
|
|
249
|
+
semantic: 'phase_start',
|
|
250
|
+
requiresErrorBlock: false,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
suffix: '_PROGRESS',
|
|
255
|
+
meta: {
|
|
256
|
+
status: MessageStatus.IN_PROGRESS,
|
|
257
|
+
level: 'INFO' as ReportLevel,
|
|
258
|
+
semantic: 'step_progress',
|
|
259
|
+
requiresErrorBlock: false,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
suffix: '_SUCCESS',
|
|
264
|
+
meta: {
|
|
265
|
+
status: MessageStatus.IN_PROGRESS,
|
|
266
|
+
level: 'INFO' as ReportLevel,
|
|
267
|
+
semantic: 'step_success',
|
|
268
|
+
requiresErrorBlock: false,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 兜底 META(未知 code 时返回)
|
|
275
|
+
*
|
|
276
|
+
* 接收方收到未知 code 时降级到 step_progress,避免阻塞协议处理。
|
|
277
|
+
*/
|
|
278
|
+
const FALLBACK_META: CodeMeta = {
|
|
279
|
+
isTerminal: false,
|
|
280
|
+
status: MessageStatus.IN_PROGRESS,
|
|
281
|
+
level: 'INFO' as ReportLevel,
|
|
282
|
+
semantic: 'step_progress',
|
|
283
|
+
requiresErrorBlock: false,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 单一 code 解析入口
|
|
288
|
+
*
|
|
289
|
+
* 解析顺序:
|
|
290
|
+
* 1. 终态显式表(EXPLICIT_META) — 30 个 closed enum
|
|
291
|
+
* 2. 后缀规则(SUFFIX_RULES) — 9 条规则覆盖数百个进度 code
|
|
292
|
+
* 3. 兜底(FALLBACK_META) — 未知 code 降级
|
|
293
|
+
*
|
|
294
|
+
* @param code report.code 字符串
|
|
295
|
+
* @returns CodeMeta — status / level / semantic / requiresErrorBlock / isTerminal
|
|
296
|
+
*/
|
|
297
|
+
export function resolveCodeMeta(code: string): CodeMeta {
|
|
298
|
+
if (EXPLICIT_META[code]) {
|
|
299
|
+
return EXPLICIT_META[code];
|
|
300
|
+
}
|
|
301
|
+
for (const rule of SUFFIX_RULES) {
|
|
302
|
+
if (rule.suffix && code.endsWith(rule.suffix)) {
|
|
303
|
+
return { isTerminal: false, ...rule.meta };
|
|
304
|
+
}
|
|
305
|
+
if (rule.infix && code.includes(rule.infix)) {
|
|
306
|
+
return { isTerminal: false, ...rule.meta };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return FALLBACK_META;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* 判断 code 是否已知(命中显式表或后缀规则,不走兜底)
|
|
314
|
+
*/
|
|
315
|
+
export function isKnownCode(code: string): boolean {
|
|
316
|
+
if (EXPLICIT_META[code]) return true;
|
|
317
|
+
for (const rule of SUFFIX_RULES) {
|
|
318
|
+
if (rule.suffix && code.endsWith(rule.suffix)) return true;
|
|
319
|
+
if (rule.infix && code.includes(rule.infix)) return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|