@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/index.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JRSoft Subway 统一WebSocket协议定义
|
|
3
3
|
* 版本: 1.0
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* 功能特性:
|
|
6
6
|
* 1. 统一消息格式定义
|
|
7
7
|
* 2. 支持设备注册、命令执行、心跳等核心功能
|
|
8
8
|
* 3. 支持 Edge 代理模式
|
|
9
|
+
*
|
|
10
|
+
* v1.6.0:单一 MessageStatus + CodeMeta + dispatchMessage(详见 code-meta.ts)
|
|
9
11
|
*/
|
|
10
12
|
|
|
13
|
+
// v1.6.0:从 code-meta 模块导入工具(用于 MessageFactory.dispatchMessage)
|
|
14
|
+
import { resolveCodeMeta, MessageStatus } from './code-meta';
|
|
15
|
+
|
|
11
16
|
// 消息类型枚举
|
|
12
17
|
export enum MessageType {
|
|
13
18
|
// 连接管理
|
|
@@ -74,14 +79,11 @@ export enum Priority {
|
|
|
74
79
|
EMERGENCY = 'EMERGENCY'
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
CANCELLED = 'CANCELLED',
|
|
83
|
-
IN_PROGRESS = 'IN_PROGRESS' // Gateway特有
|
|
84
|
-
}
|
|
82
|
+
// v1.6.0: 单一 MessageStatus enum 取代 CommandStatus / ProgressStatus
|
|
83
|
+
// 字符串值与 v1.5.0 重叠的 4 值保持一致:'IN_PROGRESS' / 'COMPLETED' / 'FAILED' / 'CANCELLED'
|
|
84
|
+
// 死代码值删除:CommandStatus.TIMEOUT / ProgressStatus.PENDING / PAUSED
|
|
85
|
+
// TIMEOUT 语义迁移:status=FAILED + report.data.error.category='TIMEOUT'
|
|
86
|
+
export { MessageStatus, type CodeMeta, type CodeSemantic, resolveCodeMeta, isKnownCode, EXPLICIT_META } from './code-meta';
|
|
85
87
|
|
|
86
88
|
// 基础消息接口
|
|
87
89
|
export interface BaseMessage {
|
|
@@ -288,7 +290,7 @@ export interface CommandResponseMessage extends BaseMessage {
|
|
|
288
290
|
type: MessageType.COMMAND_RESPONSE;
|
|
289
291
|
clientId: string; // 响应设备标识
|
|
290
292
|
requestRef: string;
|
|
291
|
-
status:
|
|
293
|
+
status: MessageStatus;
|
|
292
294
|
result?: CommandResult; // 命令执行结果
|
|
293
295
|
report?: ReportMessage;
|
|
294
296
|
executionTime?: number;
|
|
@@ -381,7 +383,7 @@ export interface ProgramResponseMessage extends BaseMessage {
|
|
|
381
383
|
type: MessageType.PROGRAM_RESPONSE;
|
|
382
384
|
clientId: string; // 响应设备标识
|
|
383
385
|
requestRef: string;
|
|
384
|
-
status:
|
|
386
|
+
status: MessageStatus; // v1.6.0 统一 status enum
|
|
385
387
|
context?: ProgramContext; // 上下文信息(可选)
|
|
386
388
|
report?: ReportMessage; // 日志信息(可选)
|
|
387
389
|
executionTime?: number; // 总执行时间(毫秒)
|
|
@@ -401,13 +403,16 @@ export interface ProgramResponseMessage extends BaseMessage {
|
|
|
401
403
|
* 不通过协议消息流转,**不**列在此 enum 中(属于 Backend 内部 audit 字符串)。
|
|
402
404
|
*/
|
|
403
405
|
export enum ProgressPhase {
|
|
404
|
-
// ---- 节目处理 pipeline
|
|
406
|
+
// ---- 节目处理 pipeline(设备端)----
|
|
407
|
+
// v1.5.0: PROGRAM 命令统一生命周期 — 加 INIT 起始 / COMPLETE 终态边界
|
|
408
|
+
PROGRAM_INIT = 'PROGRAM_INIT', // v1.5.0 新增 — 任务初始化(参数校验、资源准备、并发检查)
|
|
405
409
|
PROGRAM_FETCH = 'PROGRAM_FETCH', // 拉取节目源文件(OSS / Edge / 其他)
|
|
406
410
|
PROGRAM_EXTRACT = 'PROGRAM_EXTRACT', // 解压归档(zip/tar/rar)
|
|
407
411
|
PROGRAM_PREPROCESS = 'PROGRAM_PREPROCESS', // 图片预处理(缩放/滤镜等)
|
|
408
412
|
PROGRAM_COMPILE = 'PROGRAM_COMPILE', // 编译为显示帧
|
|
409
413
|
PROGRAM_UPLOAD = 'PROGRAM_UPLOAD', // 上传到底层显示设备
|
|
410
414
|
PROGRAM_STATS = 'PROGRAM_STATS', // 上报统计信息
|
|
415
|
+
PROGRAM_COMPLETE = 'PROGRAM_COMPLETE', // v1.5.0 新增 — 终态边界(决定 ALL_SUCCESS / PARTIAL / FAILED 等)
|
|
411
416
|
|
|
412
417
|
// ---- Edge 缓存可见性(v1.4.11 新增)----
|
|
413
418
|
EDGE_CACHE_FETCH = 'EDGE_CACHE_FETCH', // Edge 从 OSS 拉文件中
|
|
@@ -443,15 +448,9 @@ export interface DeviceOperationRecord {
|
|
|
443
448
|
result?: Record<string, any>; // 命令执行结果对象
|
|
444
449
|
}
|
|
445
450
|
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
IN_PROGRESS = 'IN_PROGRESS',
|
|
450
|
-
PAUSED = 'PAUSED',
|
|
451
|
-
COMPLETED = 'COMPLETED',
|
|
452
|
-
FAILED = 'FAILED',
|
|
453
|
-
CANCELLED = 'CANCELLED'
|
|
454
|
-
}
|
|
451
|
+
// v1.6.0:原 ProgressStatus enum 删除——所有消息的 status 字段统一类型为 MessageStatus
|
|
452
|
+
// 删除的死代码值:PENDING / PAUSED(两个值在 v1.5.0 内 0 引用,全协议历史从未真实发出)
|
|
453
|
+
// 集成方:import { MessageStatus } from '@thejrsoft/subway-protocol'
|
|
455
454
|
|
|
456
455
|
// 程序上下文信息
|
|
457
456
|
export interface ProgramContext {
|
|
@@ -471,23 +470,101 @@ export enum ReportLevel {
|
|
|
471
470
|
CRITICAL = 'CRITICAL'
|
|
472
471
|
}
|
|
473
472
|
|
|
474
|
-
// v1.
|
|
475
|
-
// - TRANSPORT:
|
|
476
|
-
// - TIMEOUT:
|
|
477
|
-
// - RESOURCE:
|
|
478
|
-
// - BUSINESS:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
473
|
+
// v1.5.0: ErrorCategory — 7 类 closed enum(v1.4.14 的 4 档扩展到 7 档)
|
|
474
|
+
// - TRANSPORT: 通讯层失败 — 网络断、CAN 总线告警、HTTP 4xx/5xx、设备离线
|
|
475
|
+
// - TIMEOUT: 等待响应超时 — HTTP 504、设备无应答、命令响应等待超时
|
|
476
|
+
// - RESOURCE: 资源不足/耗尽 — 磁盘满、内存不足、设备槽位耗尽、文件句柄耗尽
|
|
477
|
+
// - BUSINESS: 业务逻辑/状态/异常(兜底)— 业务规则违反、状态机错误、未预期异常
|
|
478
|
+
// - CONFIGURATION: 配置缺失/错误(v1.5.0 新增)— Line.config 字段缺、ParamSet 不完整
|
|
479
|
+
// - PROTOCOL: 协议层错误(v1.5.0 新增)— JSON 解析失败、字段缺失、消息格式错
|
|
480
|
+
// - AUTHORIZATION: 凭证/权限失败(v1.5.0 新增)— DAK 失效、设备未审批、命令权限不足
|
|
481
|
+
export type ErrorCategory =
|
|
482
|
+
| 'TRANSPORT'
|
|
483
|
+
| 'TIMEOUT'
|
|
484
|
+
| 'RESOURCE'
|
|
485
|
+
| 'BUSINESS'
|
|
486
|
+
| 'CONFIGURATION'
|
|
487
|
+
| 'PROTOCOL'
|
|
488
|
+
| 'AUTHORIZATION';
|
|
489
|
+
|
|
490
|
+
// v1.5.0: ErrorPhase — 错误可发生的所有 phase(命令的 ProgressPhase 子集)
|
|
491
|
+
// 与 ProgressPhase enum 完全对齐,只是命名 alias 以表达"用作错误归类"语义
|
|
492
|
+
export type ErrorPhase =
|
|
493
|
+
| 'PROGRAM_INIT'
|
|
494
|
+
| 'PROGRAM_FETCH'
|
|
495
|
+
| 'PROGRAM_EXTRACT'
|
|
496
|
+
| 'PROGRAM_PREPROCESS'
|
|
497
|
+
| 'PROGRAM_COMPILE'
|
|
498
|
+
| 'PROGRAM_UPLOAD'
|
|
499
|
+
| 'PROGRAM_STATS'
|
|
500
|
+
| 'PROGRAM_COMPLETE'
|
|
501
|
+
| 'DETECT_INIT'
|
|
502
|
+
| 'SWITCH_DETECT'
|
|
503
|
+
| 'SWITCH_CONFIG_READ'
|
|
504
|
+
| 'SYNC_DETECT'
|
|
505
|
+
| 'BARGRAPH_DETECT'
|
|
506
|
+
| 'RATER_DETECT'
|
|
507
|
+
| 'SYNC_RECOVER'
|
|
508
|
+
| 'DETECT_COMPLETE'
|
|
509
|
+
| 'SYNC_EXPORT'
|
|
510
|
+
| 'BATCH_EXECUTE';
|
|
511
|
+
|
|
512
|
+
// v1.5.0: ErrorStep — 21 个值,按 phase 归属(详见 message-validator.ts 的 step-phase 映射)
|
|
513
|
+
export type ErrorStep =
|
|
514
|
+
// PROGRAM_INIT
|
|
515
|
+
| 'ValidateParameters'
|
|
516
|
+
| 'PrepareResources'
|
|
517
|
+
| 'LoadConfig'
|
|
518
|
+
| 'CheckConcurrency'
|
|
519
|
+
// PROGRAM_FETCH
|
|
520
|
+
| 'Download'
|
|
521
|
+
| 'VerifyChecksum'
|
|
522
|
+
// PROGRAM_EXTRACT
|
|
523
|
+
| 'ExtractZip'
|
|
524
|
+
// PROGRAM_PREPROCESS
|
|
525
|
+
| 'ResizeImage'
|
|
526
|
+
| 'AdjustColor'
|
|
527
|
+
| 'GammaCorrect'
|
|
528
|
+
| 'Histogram'
|
|
529
|
+
| 'RGBCorrection'
|
|
530
|
+
// PROGRAM_COMPILE
|
|
531
|
+
| 'CompileFrame'
|
|
532
|
+
| 'MoveFrame'
|
|
533
|
+
| 'VerifyFrameCount'
|
|
534
|
+
// PROGRAM_UPLOAD
|
|
535
|
+
| 'DeviceCheck'
|
|
536
|
+
| 'DataTransfer'
|
|
537
|
+
| 'ForbiddenTable'
|
|
538
|
+
| 'StatusRecovery'
|
|
539
|
+
| 'RetryLimitExceeded'
|
|
540
|
+
// PROGRAM_STATS
|
|
541
|
+
| 'CloudReport';
|
|
542
|
+
|
|
543
|
+
// v1.5.0: 统一失败信号 schema — 嵌套在 report.data.error 下,仅 ERROR / 终态失败消息携带
|
|
544
|
+
// 4 字段全部必填,3 个 enum 字段在服务端做 strict 校验(拒收未定义值)
|
|
545
|
+
export interface ErrorInfo {
|
|
546
|
+
phase: ErrorPhase; // 错误发生在哪个 phase
|
|
547
|
+
step: ErrorStep; // 错误发生在 phase 内的哪个具体步骤(必须属于 phase)
|
|
548
|
+
category: ErrorCategory; // 协议级横切分类(7 类)
|
|
549
|
+
detail: string; // 自由文本(英文,grep 友好)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// v1.5.0: ReportData — 嵌套结构,error 仅在失败时存在;其余字段按命令业务字段平铺
|
|
553
|
+
export interface ReportData {
|
|
554
|
+
error?: ErrorInfo; // v1.5.0 新增 — 失败信号 schema(level=ERROR / status=FAILED 时必填)
|
|
555
|
+
[key: string]: any; // 业务字段平铺(programNo / fileSize / 等)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// v1.5.0: ReportMessage — 移除顶层 category 字段(下沉到 data.error.category)
|
|
559
|
+
// - data.error 仅在错误时出现(level=ERROR / status=FAILED)
|
|
560
|
+
// - 成功/进度消息:data 仍为平铺业务字段,无 error
|
|
561
|
+
// - messageEn 字段为可选,便于 i18n 兼容(默认 = message 英文文本)
|
|
485
562
|
export interface ReportMessage {
|
|
486
563
|
level: ReportLevel;
|
|
487
|
-
message: string; //
|
|
564
|
+
message: string; // 报告消息(推荐英文,作为 i18n 兜底 / 设备日志)
|
|
488
565
|
code?: string; // 标准化的消息代码
|
|
489
|
-
|
|
490
|
-
|
|
566
|
+
messageEn?: string; // v1.5.0 新增 — 英文文本(与 message 同义,兼容现有结构)
|
|
567
|
+
data?: ReportData; // v1.5.0 — 改为 ReportData 嵌套结构(含 error)
|
|
491
568
|
}
|
|
492
569
|
|
|
493
570
|
// 进度更新消息
|
|
@@ -495,7 +572,7 @@ export interface ProgressUpdateMessage extends BaseMessage {
|
|
|
495
572
|
type: MessageType.PROGRESS_UPDATE;
|
|
496
573
|
clientId: string; // 上报设备标识
|
|
497
574
|
requestRef: string;
|
|
498
|
-
status:
|
|
575
|
+
status: MessageStatus; // v1.6.0 统一 status enum(进度消息一律 IN_PROGRESS)
|
|
499
576
|
phase: ProgressPhase | string; // 当前阶段(支持自定义阶段)
|
|
500
577
|
progress: number; // 0-100 进度百分比
|
|
501
578
|
sourceType: ProgressSourceType; // 来源类型:COMMAND/SYSTEM/EDGE(v1.4.11 加 EDGE)
|
|
@@ -599,21 +676,43 @@ export function isDeviceApprovalResponseMessage(msg: any): msg is DeviceApproval
|
|
|
599
676
|
return msg && msg.type === MessageType.DEVICE_APPROVAL_RESPONSE;
|
|
600
677
|
}
|
|
601
678
|
|
|
602
|
-
// v1.
|
|
603
|
-
export const
|
|
679
|
+
// v1.5.0: ErrorCategory 类型守卫(消费方收到未知值时降级到 BUSINESS)
|
|
680
|
+
export const VALID_ERROR_CATEGORIES: readonly ErrorCategory[] = [
|
|
604
681
|
'TRANSPORT',
|
|
605
682
|
'TIMEOUT',
|
|
606
683
|
'RESOURCE',
|
|
607
684
|
'BUSINESS',
|
|
685
|
+
'CONFIGURATION',
|
|
686
|
+
'PROTOCOL',
|
|
687
|
+
'AUTHORIZATION',
|
|
608
688
|
] as const;
|
|
609
689
|
|
|
610
|
-
export function
|
|
611
|
-
return typeof value === 'string' && (
|
|
690
|
+
export function isValidErrorCategory(value: any): value is ErrorCategory {
|
|
691
|
+
return typeof value === 'string' && (VALID_ERROR_CATEGORIES as readonly string[]).includes(value);
|
|
612
692
|
}
|
|
613
693
|
|
|
614
|
-
// v1.
|
|
615
|
-
export function
|
|
616
|
-
return
|
|
694
|
+
// v1.5.0: normalizeErrorCategory — 未知值降级到 'BUSINESS'(forward-compatibility 标准做法)
|
|
695
|
+
export function normalizeErrorCategory(value: any): ErrorCategory {
|
|
696
|
+
return isValidErrorCategory(value) ? value : 'BUSINESS';
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// v1.5.0: ErrorStep ↔ ErrorPhase 归属映射(强校验:step 必须属于 phase)
|
|
700
|
+
export const ERROR_STEP_BY_PHASE: Record<string, readonly ErrorStep[]> = {
|
|
701
|
+
PROGRAM_INIT: ['ValidateParameters', 'PrepareResources', 'LoadConfig', 'CheckConcurrency'] as const,
|
|
702
|
+
PROGRAM_FETCH: ['Download', 'VerifyChecksum'] as const,
|
|
703
|
+
PROGRAM_EXTRACT: ['ExtractZip'] as const,
|
|
704
|
+
PROGRAM_PREPROCESS: ['ResizeImage', 'AdjustColor', 'GammaCorrect', 'Histogram', 'RGBCorrection'] as const,
|
|
705
|
+
PROGRAM_COMPILE: ['CompileFrame', 'MoveFrame', 'VerifyFrameCount'] as const,
|
|
706
|
+
PROGRAM_UPLOAD: ['DeviceCheck', 'DataTransfer', 'ForbiddenTable', 'StatusRecovery', 'RetryLimitExceeded'] as const,
|
|
707
|
+
PROGRAM_STATS: ['CloudReport'] as const,
|
|
708
|
+
PROGRAM_COMPLETE: [] as const,
|
|
709
|
+
// QUICKLY_DETECTION / SYNC_MONITORING_TABLE 在 v1.5.x 后续补充 step 归属
|
|
710
|
+
} as const;
|
|
711
|
+
|
|
712
|
+
export function isValidErrorStepForPhase(step: any, phase: any): boolean {
|
|
713
|
+
if (typeof step !== 'string' || typeof phase !== 'string') return false;
|
|
714
|
+
const allowed = ERROR_STEP_BY_PHASE[phase];
|
|
715
|
+
return Array.isArray(allowed) && (allowed as readonly string[]).includes(step);
|
|
617
716
|
}
|
|
618
717
|
|
|
619
718
|
|
|
@@ -785,7 +884,7 @@ export class MessageFactory {
|
|
|
785
884
|
phase: ProgressPhase | string,
|
|
786
885
|
progress: number,
|
|
787
886
|
message?: string,
|
|
788
|
-
status:
|
|
887
|
+
status: MessageStatus = MessageStatus.IN_PROGRESS,
|
|
789
888
|
options?: {
|
|
790
889
|
level?: ReportLevel;
|
|
791
890
|
code?: string;
|
|
@@ -821,7 +920,7 @@ export class MessageFactory {
|
|
|
821
920
|
static createCommandResponseMessage(
|
|
822
921
|
clientId: string, // 添加:响应设备标识
|
|
823
922
|
requestRef: string,
|
|
824
|
-
status:
|
|
923
|
+
status: MessageStatus,
|
|
825
924
|
result: any,
|
|
826
925
|
message?: string,
|
|
827
926
|
options?: {
|
|
@@ -857,7 +956,7 @@ export class MessageFactory {
|
|
|
857
956
|
static createProgramResponseMessage(
|
|
858
957
|
clientId: string, // 添加:响应设备标识
|
|
859
958
|
requestRef: string,
|
|
860
|
-
status:
|
|
959
|
+
status: MessageStatus,
|
|
861
960
|
result: any,
|
|
862
961
|
message?: string,
|
|
863
962
|
options?: {
|
|
@@ -1067,6 +1166,122 @@ export class MessageFactory {
|
|
|
1067
1166
|
version: '1.0'
|
|
1068
1167
|
};
|
|
1069
1168
|
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* v1.6.0: 统一消息派发接口
|
|
1172
|
+
*
|
|
1173
|
+
* 设备端发送终态消息(COMMAND_RESPONSE / PROGRAM_RESPONSE)或进度消息(PROGRESS_UPDATE)时,
|
|
1174
|
+
* 只需要提供 code 和业务字段,协议层通过 CodeMeta 自动推导 status / level / 消息类型。
|
|
1175
|
+
*
|
|
1176
|
+
* 替代旧的 createCommandResponseMessage / createProgramResponseMessage / createProgressUpdateMessage。
|
|
1177
|
+
*
|
|
1178
|
+
* @param params dispatch 参数
|
|
1179
|
+
* @returns 完整的协议消息(具体类型由 code 派生的 isTerminal 决定)
|
|
1180
|
+
* @throws Error 当 code 要求 data.error 但 params.error 缺失时
|
|
1181
|
+
*/
|
|
1182
|
+
static dispatchMessage(params: {
|
|
1183
|
+
clientId: string;
|
|
1184
|
+
requestRef: string;
|
|
1185
|
+
code: string;
|
|
1186
|
+
message: string;
|
|
1187
|
+
/** 终态消息:result 块;进度消息:业务平铺字段 */
|
|
1188
|
+
data?: Record<string, any>;
|
|
1189
|
+
/** v1.5.0 失败 schema — 当 code 要求时必填 */
|
|
1190
|
+
error?: {
|
|
1191
|
+
phase: string;
|
|
1192
|
+
step: string;
|
|
1193
|
+
category: string;
|
|
1194
|
+
detail: string;
|
|
1195
|
+
};
|
|
1196
|
+
/** 仅进度消息:phase 与 progress */
|
|
1197
|
+
phase?: ProgressPhase | string;
|
|
1198
|
+
progress?: number;
|
|
1199
|
+
/** 进度消息 sourceType(默认 COMMAND)*/
|
|
1200
|
+
sourceType?: ProgressSourceType;
|
|
1201
|
+
/** 终态消息 result 块(COMMAND_RESPONSE)*/
|
|
1202
|
+
result?: any;
|
|
1203
|
+
/** 终态消息 context(PROGRAM_RESPONSE)*/
|
|
1204
|
+
context?: ProgramContext;
|
|
1205
|
+
/** 终态消息 executionTime */
|
|
1206
|
+
executionTime?: number;
|
|
1207
|
+
}): CommandResponseMessage | ProgramResponseMessage | ProgressUpdateMessage {
|
|
1208
|
+
const meta = resolveCodeMeta(params.code);
|
|
1209
|
+
|
|
1210
|
+
// 强校验:requiresErrorBlock 时 data.error 必填
|
|
1211
|
+
if (meta.requiresErrorBlock && !params.error) {
|
|
1212
|
+
throw new Error(
|
|
1213
|
+
`dispatchMessage: code "${params.code}" requires data.error (4 fields: phase/step/category/detail)`,
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
const reportData: Record<string, any> = { ...(params.data || {}) };
|
|
1218
|
+
if (params.error) {
|
|
1219
|
+
reportData.error = params.error;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const report: ReportMessage = {
|
|
1223
|
+
level: meta.level,
|
|
1224
|
+
message: params.message,
|
|
1225
|
+
code: params.code,
|
|
1226
|
+
data: reportData,
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
const timestamp = new Date().toISOString();
|
|
1230
|
+
const version = '1.0';
|
|
1231
|
+
|
|
1232
|
+
if (meta.isTerminal) {
|
|
1233
|
+
// 通过 code 前缀决定终态消息类型:PROGRAM_* → PROGRAM_RESPONSE,其余 → COMMAND_RESPONSE
|
|
1234
|
+
const isProgramTerminal =
|
|
1235
|
+
params.code.startsWith('PROGRAM_') && !params.code.startsWith('PROGRAM_FETCH') &&
|
|
1236
|
+
!params.code.startsWith('PROGRAM_EXTRACT') && !params.code.startsWith('PROGRAM_PREPROCESS') &&
|
|
1237
|
+
!params.code.startsWith('PROGRAM_COMPILE') && !params.code.startsWith('PROGRAM_STATS') &&
|
|
1238
|
+
!params.code.startsWith('PROGRAM_INIT');
|
|
1239
|
+
|
|
1240
|
+
if (isProgramTerminal) {
|
|
1241
|
+
const msg: ProgramResponseMessage = {
|
|
1242
|
+
type: MessageType.PROGRAM_RESPONSE,
|
|
1243
|
+
clientId: params.clientId,
|
|
1244
|
+
requestRef: params.requestRef,
|
|
1245
|
+
status: meta.status as MessageStatus,
|
|
1246
|
+
context: params.context,
|
|
1247
|
+
report,
|
|
1248
|
+
executionTime: params.executionTime,
|
|
1249
|
+
timestamp,
|
|
1250
|
+
version,
|
|
1251
|
+
};
|
|
1252
|
+
return msg;
|
|
1253
|
+
} else {
|
|
1254
|
+
const msg: CommandResponseMessage = {
|
|
1255
|
+
type: MessageType.COMMAND_RESPONSE,
|
|
1256
|
+
clientId: params.clientId,
|
|
1257
|
+
requestRef: params.requestRef,
|
|
1258
|
+
status: meta.status as MessageStatus,
|
|
1259
|
+
result: params.result,
|
|
1260
|
+
report,
|
|
1261
|
+
executionTime: params.executionTime,
|
|
1262
|
+
timestamp,
|
|
1263
|
+
version,
|
|
1264
|
+
};
|
|
1265
|
+
return msg;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// 进度消息
|
|
1270
|
+
const progressMsg: ProgressUpdateMessage = {
|
|
1271
|
+
type: MessageType.PROGRESS_UPDATE,
|
|
1272
|
+
clientId: params.clientId,
|
|
1273
|
+
requestRef: params.requestRef,
|
|
1274
|
+
status: meta.status as MessageStatus,
|
|
1275
|
+
phase: params.phase ?? '',
|
|
1276
|
+
progress: params.progress ?? 0,
|
|
1277
|
+
sourceType: params.sourceType ?? 'COMMAND',
|
|
1278
|
+
context: params.context,
|
|
1279
|
+
report,
|
|
1280
|
+
timestamp,
|
|
1281
|
+
version,
|
|
1282
|
+
};
|
|
1283
|
+
return progressMsg;
|
|
1284
|
+
}
|
|
1070
1285
|
}
|
|
1071
1286
|
|
|
1072
1287
|
|
|
@@ -1093,7 +1308,7 @@ export type AnyMessage =
|
|
|
1093
1308
|
| ErrorMessage;
|
|
1094
1309
|
|
|
1095
1310
|
// 导出常量
|
|
1096
|
-
export const PROTOCOL_VERSION = '1.
|
|
1311
|
+
export const PROTOCOL_VERSION = '1.6.0';
|
|
1097
1312
|
export const DEFAULT_TIMEOUT = 10000;
|
|
1098
1313
|
export const DEFAULT_PRIORITY = Priority.NORMAL;
|
|
1099
1314
|
|
package/src/message-validator.ts
CHANGED
|
@@ -6,10 +6,9 @@ import {
|
|
|
6
6
|
MessageType,
|
|
7
7
|
ClientType,
|
|
8
8
|
Priority,
|
|
9
|
-
|
|
9
|
+
MessageStatus,
|
|
10
10
|
OperationType,
|
|
11
11
|
CommandType,
|
|
12
|
-
ProgressStatus,
|
|
13
12
|
ProgressPhase,
|
|
14
13
|
ReportLevel,
|
|
15
14
|
ProgramType,
|
|
@@ -336,7 +335,7 @@ export class MessageValidator {
|
|
|
336
335
|
}
|
|
337
336
|
if (!message.status) {
|
|
338
337
|
errors.push('status is required');
|
|
339
|
-
} else if (!this.
|
|
338
|
+
} else if (!this.isValidMessageStatus(message.status)) {
|
|
340
339
|
errors.push(`Invalid status: ${message.status}`);
|
|
341
340
|
}
|
|
342
341
|
if (!message.phase) {
|
|
@@ -536,8 +535,8 @@ export class MessageValidator {
|
|
|
536
535
|
/**
|
|
537
536
|
* 验证命令状态
|
|
538
537
|
*/
|
|
539
|
-
static
|
|
540
|
-
return Object.values(
|
|
538
|
+
static isValidMessageStatus(status: string): boolean {
|
|
539
|
+
return Object.values(MessageStatus).includes(status as MessageStatus);
|
|
541
540
|
}
|
|
542
541
|
|
|
543
542
|
/**
|
|
@@ -554,13 +553,6 @@ export class MessageValidator {
|
|
|
554
553
|
return Object.values(CommandType).includes(commandType as CommandType);
|
|
555
554
|
}
|
|
556
555
|
|
|
557
|
-
/**
|
|
558
|
-
* 验证进度状态
|
|
559
|
-
*/
|
|
560
|
-
static isValidProgressStatus(status: string): boolean {
|
|
561
|
-
return Object.values(ProgressStatus).includes(status as ProgressStatus);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
556
|
/**
|
|
565
557
|
* 验证进度阶段
|
|
566
558
|
*/
|
package/src/protocol-utils.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
MessageStatus,
|
|
7
7
|
Priority,
|
|
8
8
|
ClientType,
|
|
9
9
|
MessageType,
|
|
@@ -26,26 +26,26 @@ import {
|
|
|
26
26
|
*/
|
|
27
27
|
export class ProtocolUtils {
|
|
28
28
|
/**
|
|
29
|
-
* 将
|
|
29
|
+
* 将 MessageStatus 枚举转换为字符串
|
|
30
30
|
*/
|
|
31
|
-
static statusToString(status:
|
|
31
|
+
static statusToString(status: MessageStatus): string {
|
|
32
32
|
return status.toString();
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* 将字符串转换为
|
|
36
|
+
* 将字符串转换为 MessageStatus 枚举
|
|
37
37
|
* @throws {Error} 如果字符串不是有效的状态值
|
|
38
38
|
*/
|
|
39
|
-
static stringToStatus(status: string):
|
|
39
|
+
static stringToStatus(status: string): MessageStatus {
|
|
40
40
|
// 处理大小写和空格
|
|
41
41
|
const normalizedStatus = status.trim().toUpperCase();
|
|
42
42
|
|
|
43
43
|
// 检查是否是有效的枚举值
|
|
44
|
-
if (!Object.values(
|
|
45
|
-
throw new Error(`Invalid command status: ${status}. Valid values are: ${Object.values(
|
|
44
|
+
if (!Object.values(MessageStatus).includes(normalizedStatus as MessageStatus)) {
|
|
45
|
+
throw new Error(`Invalid command status: ${status}. Valid values are: ${Object.values(MessageStatus).join(', ')}`);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
return normalizedStatus as
|
|
48
|
+
return normalizedStatus as MessageStatus;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -225,7 +225,7 @@ export class ProtocolUtils {
|
|
|
225
225
|
* 安全的枚举转换(不抛出异常)
|
|
226
226
|
* @returns 枚举值或 null
|
|
227
227
|
*/
|
|
228
|
-
static tryParseStatus(status: string):
|
|
228
|
+
static tryParseStatus(status: string): MessageStatus | null {
|
|
229
229
|
try {
|
|
230
230
|
return this.stringToStatus(status);
|
|
231
231
|
} catch {
|