@@ -5,99 +5,143 @@ import type { MessageSend } from "@Packages/message/types";
55import { ScriptClient } from "../service_worker/client" ;
66import { v5 as uuidv5 } from "uuid" ;
77
8+ /* ---------- Types ---------- */
9+ export type VSCodeConnectParam = { url : string ; reconnect : boolean } ;
10+
11+ /** Actions received from VSCode WebSocket */
12+ enum VSCodeAction {
13+ Hello = "hello" ,
14+ OnChange = "onchange" ,
15+ }
16+
17+ /* ---------- Main Class ---------- */
818// 在offscreen下与scriptcat-vscode建立websocket连接
919// 需要在vscode中安装scriptcat-vscode插件
1020export class VSCodeConnect {
11- logger : Logger = LoggerCore . logger ( ) . with ( { service : "VSCodeConnect" } ) ;
21+ private readonly logger : Logger = LoggerCore . logger ( ) . with ( { service : "VSCodeConnect" } ) ;
1222
13- reconnect : boolean = false ;
23+ private ws : WebSocket | undefined ;
1424
15- wsConnect : WebSocket | undefined ;
25+ private timerId : number | NodeJS . Timeout | undefined ;
1626
17- connectVSCodeTimer : any ;
18-
19- scriptClient : ScriptClient ;
27+ private readonly scriptClient : ScriptClient ;
2028
2129 constructor (
22- private group : Group ,
23- private msgSender : MessageSend
30+ vscodeConnectServer : Group ,
31+ private readonly msgSender : MessageSend
2432 ) {
2533 this . scriptClient = new ScriptClient ( this . msgSender ) ;
34+ vscodeConnectServer . on ( "connect" , ( param : VSCodeConnectParam ) => this . connect ( param ) ) ;
2635 }
2736
28- connect ( { url, reconnect } : { url : string ; reconnect : boolean } ) {
29- // 如果已经连接,断开重连
30- if ( this . wsConnect ) {
31- this . wsConnect . close ( ) ;
32- }
33- // 清理老的定时器
34- if ( this . connectVSCodeTimer ) {
35- clearInterval ( this . connectVSCodeTimer ) ;
36- this . connectVSCodeTimer = undefined ;
37- }
38- const handler = ( ) => {
39- if ( ! this . wsConnect ) {
40- return this . connectVSCode ( { url } ) ;
41- }
42- return Promise . resolve ( ) ;
37+ /* ---------- Public API ---------- */
38+ /** 启动(或重新启动)与 VSCode 的连接 */
39+ public connect ( { url, reconnect } : VSCodeConnectParam ) : Promise < void > {
40+ const doReconnect = ( ) => {
41+ // 如果已经连接,断开重连
42+ this . closeExisting ( ) ;
43+ this . clearTimer ( ) ;
44+ this . timerId = setTimeout ( connectVSCode , 100 ) ;
4345 } ;
44- if ( reconnect ) {
45- this . connectVSCodeTimer = setInterval ( ( ) => {
46- handler ( ) ;
47- } , 30 * 1000 ) ;
48- }
49- return handler ( ) ;
50- }
46+ const connectVSCode = ( ) => {
47+ if ( this . ws ) return ; // 已连接则忽略
48+ return new Promise < void > ( ( resolve , reject ) => {
49+ let ws ;
50+ try {
51+ ws = new WebSocket ( url ) ;
52+ } catch ( e : any ) {
53+ this . logger . debug ( "connect vscode faild" , Logger . E ( e ) ) ;
54+ reject ( e ) ;
55+ return ;
56+ }
57+ let connectOK = false ;
58+ ws . addEventListener ( "open" , ( ) => {
59+ ws . send ( '{"action":"hello"}' ) ;
60+ connectOK = true ;
61+ // 如重复连接,则清除之前的
62+ if ( this . ws ) {
63+ this . closeExisting ( ) ;
64+ }
65+ this . ws = ws ;
66+ resolve ( ) ;
67+ this . clearTimer ( ) ;
68+ } ) ;
69+ ws . addEventListener ( "message" , ( ev ) => {
70+ this . handleMessage ( ev ) . catch ( ( err ) => {
71+ this . logger . error ( "message handler error" , Logger . E ( err ) ) ;
72+ } ) ;
73+ } ) ;
5174
52- // 连接到vscode
53- connectVSCode ( { url } : { url : string } ) {
54- return new Promise < void > ( ( resolve , reject ) => {
55- // 如果已经连接,断开重连
56- if ( this . wsConnect ) {
57- this . wsConnect . close ( ) ;
58- }
59- try {
60- this . wsConnect = new WebSocket ( url ) ;
61- } catch ( e : any ) {
62- this . logger . debug ( "connect vscode faild" , Logger . E ( e ) ) ;
63- reject ( e ) ;
64- return ;
65- }
66- let ok = false ;
67- this . wsConnect . addEventListener ( "open" , ( ) => {
68- this . wsConnect ! . send ( '{"action":"hello"}' ) ;
69- ok = true ;
70- resolve ( ) ;
71- } ) ;
72- this . wsConnect . addEventListener ( "message" , async ( ev ) => {
73- const data = JSON . parse ( ev . data ) ;
74- switch ( data . action ) {
75- case "onchange" : {
76- // 调用安装脚本接口
77- const code = data . data . script ;
78- this . scriptClient . installByCode ( uuidv5 ( data . data . uri , uuidv5 . URL ) , code , "vscode" ) ;
79- break ;
75+ ws . addEventListener ( "error" , ( e ) => {
76+ this . ws = undefined ;
77+ this . logger . debug ( "connect vscode faild" , Logger . E ( e ) ) ;
78+ if ( ! connectOK ) {
79+ reject ( new Error ( "connect fail" ) ) ;
8080 }
81- default :
82- }
83- } ) ;
81+ if ( reconnect ) doReconnect ( ) ;
82+ } ) ;
8483
85- this . wsConnect . addEventListener ( "error" , ( e ) => {
86- this . wsConnect = undefined ;
87- this . logger . debug ( "connect vscode faild" , Logger . E ( e ) ) ;
88- if ( ! ok ) {
89- reject ( new Error ( "connect fail" ) ) ;
90- }
84+ ws . addEventListener ( "close" , ( ) => {
85+ this . ws = undefined ;
86+ this . logger . debug ( "vscode connection closed" ) ;
87+ if ( reconnect ) doReconnect ( ) ;
88+ } ) ;
89+ // 如 open, close, error 都不发生,30 秒后reject
90+ this . clearTimer ( ) ;
91+ this . timerId = setTimeout ( ( ) => {
92+ if ( ! connectOK ) {
93+ reject ( new Error ( "Timeout" ) ) ;
94+ try {
95+ ws . close ( ) ;
96+ } catch ( e ) {
97+ console . error ( e ) ;
98+ }
99+ if ( reconnect ) doReconnect ( ) ;
100+ }
101+ } , 30_000 ) ;
91102 } ) ;
103+ } ;
104+ // 如果已经连接,断开重连
105+ this . closeExisting ( ) ;
106+ // 清理老的定时器
107+ this . clearTimer ( ) ;
108+ return Promise . resolve ( ) . then ( ( ) => connectVSCode ( ) ) ;
109+ }
92110
93- this . wsConnect . addEventListener ( "close" , ( ) => {
94- this . wsConnect = undefined ;
95- this . logger . debug ( "vscode connection closed" ) ;
96- } ) ;
97- } ) ;
111+ /* ---------- Message Handling ---------- */
112+ private async handleMessage ( ev : MessageEvent ) : Promise < void > {
113+ let data : any ;
114+ try {
115+ data = JSON . parse ( ev . data as string ) ;
116+ } catch {
117+ return ; // ignore malformed JSON
118+ }
119+ switch ( data . action as VSCodeAction ) {
120+ case VSCodeAction . OnChange : {
121+ // 调用安装脚本接口
122+ const { script, uri } = data . data ;
123+ const id = uuidv5 ( uri , uuidv5 . URL ) ;
124+ await this . scriptClient . installByCode ( id , script , "vscode" ) ;
125+ break ;
126+ }
127+ default :
128+ // ignore unknown actions
129+ }
98130 }
99131
100- init ( ) {
101- this . group . on ( "connect" , this . connect . bind ( this ) ) ;
132+ /* ---------- Helpers ---------- */
133+ private closeExisting ( ) : void {
134+ try {
135+ this . ws ?. close ( ) ;
136+ } catch ( e : any ) {
137+ console . error ( e ) ;
138+ }
139+ this . ws = undefined ;
140+ }
141+ private clearTimer ( ) : void {
142+ if ( this . timerId ) {
143+ clearTimeout ( this . timerId ) ;
144+ this . timerId = undefined ;
145+ }
102146 }
103147}
0 commit comments