xshell 1.0.43 → 1.0.45
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/file.js +16 -6
- package/net.browser.d.ts +36 -21
- package/net.browser.js +53 -42
- package/net.d.ts +41 -21
- package/net.js +53 -42
- package/package.json +18 -18
- package/process.d.ts +1 -0
- package/process.js +4 -1
- package/repl.d.ts +1 -1
- package/repl.js +14 -14
- package/server.js +1 -1
- package/utils.d.ts +1 -1
- package/utils.js +11 -8
- package/xshell.js +1 -1
package/file.js
CHANGED
|
@@ -234,11 +234,11 @@ export async function fmove(src, dst, { overwrite = false, print = true } = {})
|
|
|
234
234
|
- overwrite?: `true` 默认覆盖(不检查效率更高) better performance without check */
|
|
235
235
|
export async function frename(fp, fp_, { fpd, print = true, overwrite = true } = {}) {
|
|
236
236
|
if (fpd) {
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
assert(fpd.isdir);
|
|
238
|
+
fp = fpd + fp;
|
|
239
|
+
fp_ = fpd + fp_;
|
|
239
240
|
}
|
|
240
|
-
|
|
241
|
-
throw new Error(t('fp 和 fp_ 必须是绝对路径'));
|
|
241
|
+
assert(path.isAbsolute(fp) && path.isAbsolute(fp_), t('fp 和 fp_ 必须是绝对路径'));
|
|
242
242
|
if (print)
|
|
243
243
|
console.log(t('重命名'), fp, '→', fp_);
|
|
244
244
|
if (!overwrite && fexists(fp_))
|
|
@@ -256,8 +256,18 @@ export async function frename(fp, fp_, { fpd, print = true, overwrite = true } =
|
|
|
256
256
|
export async function fmkdir(fpd, { print = true, mode, } = {}) {
|
|
257
257
|
assert(path.isAbsolute(fpd), t('fpd 必须是绝对路径: ') + fpd);
|
|
258
258
|
assert(fpd.isdir, t('fpd 必须以 / 结尾: ') + fpd);
|
|
259
|
-
//
|
|
260
|
-
|
|
259
|
+
// 类似 D:/ 这样的根路径,调用 fsp.mkdir 会报错无权限
|
|
260
|
+
if (fpd.length === 3 &&
|
|
261
|
+
'A' <= fpd[0] && fpd[0] <= 'Z' && fpd[1] === ':' && fpd[2] === '/')
|
|
262
|
+
if (fexists(fpd)) {
|
|
263
|
+
if (print)
|
|
264
|
+
console.log(t('已存在文件夹'), fpd);
|
|
265
|
+
return fpd;
|
|
266
|
+
}
|
|
267
|
+
else
|
|
268
|
+
throw new Error(t('不存在且无法创建文件夹 {{fpd}}', { fpd }));
|
|
269
|
+
// Calling fs.promises.mkdir() when path is a directory that exists results in a rejection only when recursive is false.
|
|
270
|
+
const fpd_ = (await fsp.mkdir(fpd, { recursive: true, mode }))?.fpd;
|
|
261
271
|
if (fpd_) {
|
|
262
272
|
if (print)
|
|
263
273
|
console.log(t('已创建文件夹'), fpd);
|
package/net.browser.d.ts
CHANGED
|
@@ -125,10 +125,13 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
125
125
|
bins?: number[];
|
|
126
126
|
}
|
|
127
127
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
129
|
+
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
130
|
+
使用 remote.call 进行一元 rpc
|
|
131
|
+
使用 remote.send 结合 message.id 进行复杂 rpc
|
|
132
|
+
创建后等到首个 remote.call 或 remote.send 时自动建立实际 websocket 连接
|
|
133
|
+
rpc 状态与底层连接的状态无关,如果是传入 url 创建的 remote 实例,remote.send 时检测到断线会自动建立新的 websocket 连接
|
|
134
|
+
|
|
132
135
|
@example
|
|
133
136
|
// Zero 继承自 Remote 并通过 call 实现了一些方法
|
|
134
137
|
let zero = new Zero({ local: true })
|
|
@@ -145,42 +148,54 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
145
148
|
|
|
146
149
|
zero.send({ id, func: 'subscribe_stdio' }) */
|
|
147
150
|
export declare class Remote {
|
|
148
|
-
/**
|
|
151
|
+
/** 在构造 Remote 时, this.initiator = Boolean(url || websocket)
|
|
152
|
+
- true: 作为 websocket 连接发起方
|
|
153
|
+
- false: 作为 websocket 连接接收方 */
|
|
149
154
|
initiator: boolean;
|
|
150
|
-
/** websocket url */
|
|
155
|
+
/** 作为 websocket 连接发起方,对端的 url 地址 */
|
|
151
156
|
url?: string;
|
|
152
|
-
/**
|
|
157
|
+
/** 作为 websocket 连接发起方有 websocket lock */
|
|
153
158
|
lwebsocket?: Lock<WebSocket>;
|
|
154
|
-
/**
|
|
159
|
+
/** websocket 连接发起方,接收方,都能被对端通过 (rpc message).func 调用的 rpc 函数 */
|
|
155
160
|
funcs: Record<string, MessageHandler>;
|
|
156
|
-
/** map<id, message handler>: 通过 rpc message.id 找到对应的 handler
|
|
161
|
+
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
162
|
+
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
157
163
|
handlers: Map<number, MessageHandler>;
|
|
164
|
+
/** `false` 打印所有交互的 rpc messages */
|
|
158
165
|
print: boolean;
|
|
159
|
-
pconnect: Promise<any>;
|
|
160
166
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
161
167
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
162
168
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
163
|
-
/** 作为
|
|
164
|
-
作为
|
|
165
|
-
constructor({ url, funcs, websocket, on_error }?: {
|
|
169
|
+
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
170
|
+
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
171
|
+
constructor({ url, funcs, print, websocket, on_error }?: {
|
|
166
172
|
url?: string;
|
|
167
173
|
funcs?: Remote['funcs'];
|
|
174
|
+
print?: boolean;
|
|
168
175
|
websocket?: WebSocket;
|
|
169
176
|
on_error?(error: WebSocketConnectionError, websocket: WebSocket): void;
|
|
170
177
|
});
|
|
171
178
|
on_error?(error: WebSocketConnectionError, websocket: WebSocket): void;
|
|
172
|
-
/** 幂等,保证 websocket
|
|
173
|
-
|
|
179
|
+
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
180
|
+
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
181
|
+
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
182
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
183
|
+
作为 websocket 连接接收方,需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
174
184
|
connect(websocket?: WebSocket): Promise<void>;
|
|
185
|
+
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
175
186
|
disconnect(): void;
|
|
176
|
-
/**
|
|
187
|
+
/** 发送 message 到对端 remote
|
|
188
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
189
|
+
作为 websocket 连接接收方,必传 websocket 参数
|
|
177
190
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
178
191
|
send(message: Message, websocket?: WebSocket): Promise<void>;
|
|
179
|
-
/** 处理接收到的 websocket message 并解析, 根据 id
|
|
180
|
-
如果 message.done == true
|
|
181
|
-
如果 handler
|
|
192
|
+
/** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理
|
|
193
|
+
如果 message.done == true 则对端指示当前 remote 可以清理 handler
|
|
194
|
+
如果 handler 返回了值 (一定是数组),则包装为 message 发送
|
|
182
195
|
使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
183
196
|
handle(data: Uint8Array, websocket: WebSocket): Promise<void>;
|
|
184
|
-
/**
|
|
185
|
-
|
|
197
|
+
/** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应)
|
|
198
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
199
|
+
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
200
|
+
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
186
201
|
}
|
package/net.browser.js
CHANGED
|
@@ -235,10 +235,13 @@ export async function connect_websocket(url, { protocols, on_message, on_error,
|
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
237
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
239
|
+
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
240
|
+
使用 remote.call 进行一元 rpc
|
|
241
|
+
使用 remote.send 结合 message.id 进行复杂 rpc
|
|
242
|
+
创建后等到首个 remote.call 或 remote.send 时自动建立实际 websocket 连接
|
|
243
|
+
rpc 状态与底层连接的状态无关,如果是传入 url 创建的 remote 实例,remote.send 时检测到断线会自动建立新的 websocket 连接
|
|
244
|
+
|
|
242
245
|
@example
|
|
243
246
|
// Zero 继承自 Remote 并通过 call 实现了一些方法
|
|
244
247
|
let zero = new Zero({ local: true })
|
|
@@ -255,18 +258,21 @@ export async function connect_websocket(url, { protocols, on_message, on_error,
|
|
|
255
258
|
|
|
256
259
|
zero.send({ id, func: 'subscribe_stdio' }) */
|
|
257
260
|
export class Remote {
|
|
258
|
-
/**
|
|
261
|
+
/** 在构造 Remote 时, this.initiator = Boolean(url || websocket)
|
|
262
|
+
- true: 作为 websocket 连接发起方
|
|
263
|
+
- false: 作为 websocket 连接接收方 */
|
|
259
264
|
initiator;
|
|
260
|
-
/** websocket url */
|
|
265
|
+
/** 作为 websocket 连接发起方,对端的 url 地址 */
|
|
261
266
|
url;
|
|
262
|
-
/**
|
|
267
|
+
/** 作为 websocket 连接发起方有 websocket lock */
|
|
263
268
|
lwebsocket;
|
|
264
|
-
/**
|
|
269
|
+
/** websocket 连接发起方,接收方,都能被对端通过 (rpc message).func 调用的 rpc 函数 */
|
|
265
270
|
funcs;
|
|
266
|
-
/** map<id, message handler>: 通过 rpc message.id 找到对应的 handler
|
|
271
|
+
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
272
|
+
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
267
273
|
handlers = new Map();
|
|
274
|
+
/** `false` 打印所有交互的 rpc messages */
|
|
268
275
|
print = false;
|
|
269
|
-
pconnect;
|
|
270
276
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
271
277
|
static parse(buffer) {
|
|
272
278
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -287,6 +293,7 @@ export class Remote {
|
|
|
287
293
|
return message;
|
|
288
294
|
}
|
|
289
295
|
static pack({ id, func, data = [], done, error }) {
|
|
296
|
+
assert('length' in data, t('message.data 必须是数组'));
|
|
290
297
|
let data_ = new Array(data.length);
|
|
291
298
|
let bins = [];
|
|
292
299
|
let bufs = [];
|
|
@@ -300,7 +307,6 @@ export class Remote {
|
|
|
300
307
|
else
|
|
301
308
|
data_[i] = item;
|
|
302
309
|
}
|
|
303
|
-
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
304
310
|
const data_json = {
|
|
305
311
|
id,
|
|
306
312
|
...func ? { func } : {},
|
|
@@ -309,27 +315,36 @@ export class Remote {
|
|
|
309
315
|
...data_.length ? { data: data_ } : {},
|
|
310
316
|
...bins.length ? { bins } : {},
|
|
311
317
|
};
|
|
318
|
+
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
312
319
|
const str_json = encoder.encode(JSON.stringify(data_json));
|
|
313
320
|
let dv = new DataView(new ArrayBuffer(4));
|
|
314
321
|
dv.setUint32(0, str_json.length, true);
|
|
315
322
|
return concat([dv, str_json, ...bufs]);
|
|
316
323
|
}
|
|
317
|
-
/** 作为
|
|
318
|
-
作为
|
|
319
|
-
constructor({ url, funcs
|
|
324
|
+
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
325
|
+
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
326
|
+
constructor({ url, funcs, print, websocket, on_error } = {}) {
|
|
320
327
|
assert(!(url && websocket), t('构建 Remote 时 url 和 websocket 最多只能传一个'));
|
|
321
328
|
this.initiator = Boolean(url || websocket);
|
|
322
|
-
|
|
323
|
-
|
|
329
|
+
if (url)
|
|
330
|
+
this.url = url;
|
|
331
|
+
if (funcs)
|
|
332
|
+
this.funcs = funcs;
|
|
333
|
+
if (print !== undefined)
|
|
334
|
+
this.print = print;
|
|
324
335
|
if (on_error)
|
|
325
336
|
this.on_error = on_error;
|
|
326
|
-
this.
|
|
337
|
+
if (this.initiator)
|
|
338
|
+
this.lwebsocket = new Lock(websocket || null);
|
|
327
339
|
}
|
|
328
340
|
on_error(error, websocket) {
|
|
329
341
|
throw error;
|
|
330
342
|
}
|
|
331
|
-
/** 幂等,保证 websocket
|
|
332
|
-
|
|
343
|
+
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
344
|
+
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
345
|
+
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
346
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
347
|
+
作为 websocket 连接接收方,需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
333
348
|
async connect(websocket) {
|
|
334
349
|
if (this.initiator)
|
|
335
350
|
if (this.lwebsocket.resource?.readyState === WebSocket.OPEN)
|
|
@@ -344,8 +359,6 @@ export class Remote {
|
|
|
344
359
|
// 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
|
|
345
360
|
if (websocket?.readyState === WebSocket.OPEN)
|
|
346
361
|
return;
|
|
347
|
-
else if (!this.url)
|
|
348
|
-
throw new Error(t('创建 Remote 时传入的 websocket 连接已断开'));
|
|
349
362
|
else // 重连
|
|
350
363
|
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
351
364
|
on_message: (data, websocket) => {
|
|
@@ -354,29 +367,25 @@ export class Remote {
|
|
|
354
367
|
on_error: this.on_error.bind(this)
|
|
355
368
|
});
|
|
356
369
|
});
|
|
357
|
-
else if (websocket.readyState
|
|
358
|
-
return;
|
|
359
|
-
else
|
|
370
|
+
else if (websocket.readyState !== WebSocket.OPEN)
|
|
360
371
|
throw new Error(t('传入的 websocket 连接已断开'));
|
|
361
372
|
}
|
|
373
|
+
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
362
374
|
disconnect() {
|
|
363
375
|
this.lwebsocket.resource?.close(1000);
|
|
364
376
|
}
|
|
365
|
-
/**
|
|
377
|
+
/** 发送 message 到对端 remote
|
|
378
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
379
|
+
作为 websocket 连接接收方,必传 websocket 参数
|
|
366
380
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
367
381
|
async send(message, websocket) {
|
|
382
|
+
if (this.print)
|
|
383
|
+
console.log('remote.send:', message);
|
|
368
384
|
try {
|
|
369
|
-
|
|
370
|
-
assert(!websocket || websocket === this.lwebsocket.resource);
|
|
371
|
-
await this.connect();
|
|
372
|
-
websocket = this.lwebsocket.resource;
|
|
373
|
-
}
|
|
374
|
-
else
|
|
375
|
-
await this.connect(websocket);
|
|
385
|
+
await this.connect(websocket);
|
|
376
386
|
if (!message.id)
|
|
377
387
|
message.id = genid();
|
|
378
|
-
|
|
379
|
-
websocket.send(Remote.pack(message));
|
|
388
|
+
(websocket || this.lwebsocket.resource).send(Remote.pack(message));
|
|
380
389
|
}
|
|
381
390
|
catch (error) {
|
|
382
391
|
if (message.id)
|
|
@@ -384,21 +393,21 @@ export class Remote {
|
|
|
384
393
|
throw error;
|
|
385
394
|
}
|
|
386
395
|
}
|
|
387
|
-
/** 处理接收到的 websocket message 并解析, 根据 id
|
|
388
|
-
如果 message.done == true
|
|
389
|
-
如果 handler
|
|
396
|
+
/** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理
|
|
397
|
+
如果 message.done == true 则对端指示当前 remote 可以清理 handler
|
|
398
|
+
如果 handler 返回了值 (一定是数组),则包装为 message 发送
|
|
390
399
|
使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
391
400
|
async handle(data, websocket) {
|
|
392
401
|
const message = Remote.parse(data);
|
|
393
402
|
const { id, func, done } = message;
|
|
394
403
|
if (this.print)
|
|
395
|
-
console.log(message);
|
|
404
|
+
console.log('remote.handle:', message);
|
|
396
405
|
let handler;
|
|
397
406
|
if (func)
|
|
398
407
|
handler = this.funcs[func];
|
|
399
408
|
else {
|
|
400
409
|
handler = this.handlers.get(id);
|
|
401
|
-
if (done)
|
|
410
|
+
if (done && handler)
|
|
402
411
|
this.handlers.delete(id);
|
|
403
412
|
}
|
|
404
413
|
try {
|
|
@@ -421,8 +430,10 @@ export class Remote {
|
|
|
421
430
|
throw error;
|
|
422
431
|
}
|
|
423
432
|
}
|
|
424
|
-
/**
|
|
425
|
-
|
|
433
|
+
/** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应)
|
|
434
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
435
|
+
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
436
|
+
async call(func, args, websocket) {
|
|
426
437
|
return new Promise(async (resolve, reject) => {
|
|
427
438
|
const id = genid();
|
|
428
439
|
this.handlers.set(id, (message) => {
|
|
@@ -434,7 +445,7 @@ export class Remote {
|
|
|
434
445
|
this.handlers.delete(id);
|
|
435
446
|
});
|
|
436
447
|
try {
|
|
437
|
-
await this.send({ id, func, data: args }); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
|
|
448
|
+
await this.send({ id, func, data: args }, websocket); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
|
|
438
449
|
}
|
|
439
450
|
catch (error) {
|
|
440
451
|
reject(error);
|
package/net.d.ts
CHANGED
|
@@ -173,10 +173,13 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
173
173
|
bins?: number[];
|
|
174
174
|
}
|
|
175
175
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
177
|
+
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
178
|
+
使用 remote.call 进行一元 rpc
|
|
179
|
+
使用 remote.send 结合 message.id 进行复杂 rpc
|
|
180
|
+
创建后等到首个 remote.call 或 remote.send 时自动建立实际 websocket 连接
|
|
181
|
+
rpc 状态与底层连接的状态无关,如果是传入 url 创建的 remote 实例,remote.send 时检测到断线会自动建立新的 websocket 连接
|
|
182
|
+
|
|
180
183
|
@example
|
|
181
184
|
// Zero 继承自 Remote 并通过 call 实现了一些方法
|
|
182
185
|
let zero = new Zero({ local: true })
|
|
@@ -193,42 +196,59 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
193
196
|
|
|
194
197
|
zero.send({ id, func: 'subscribe_stdio' }) */
|
|
195
198
|
export declare class Remote {
|
|
196
|
-
/**
|
|
199
|
+
/** 在构造 Remote 时, this.initiator = Boolean(url || websocket)
|
|
200
|
+
- true: 作为 websocket 连接发起方
|
|
201
|
+
- false: 作为 websocket 连接接收方 */
|
|
197
202
|
initiator: boolean;
|
|
198
|
-
/** websocket url */
|
|
203
|
+
/** 作为 websocket 连接发起方,对端的 url 地址 */
|
|
199
204
|
url?: string;
|
|
200
|
-
/**
|
|
205
|
+
/** 作为 websocket 连接发起方有 websocket lock */
|
|
201
206
|
lwebsocket?: Lock<WebSocket>;
|
|
202
|
-
/**
|
|
207
|
+
/** websocket 连接发起方,接收方,都能被对端通过 (rpc message).func 调用的 rpc 函数 */
|
|
203
208
|
funcs: Record<string, MessageHandler>;
|
|
204
|
-
/** map<id, message handler>: 通过 rpc message.id 找到对应的 handler
|
|
209
|
+
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
210
|
+
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
205
211
|
handlers: Map<number, MessageHandler>;
|
|
212
|
+
/** `false` 打印所有交互的 rpc messages */
|
|
206
213
|
print: boolean;
|
|
207
|
-
pconnect: Promise<any>;
|
|
208
214
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
209
215
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
210
216
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
211
|
-
/** 作为
|
|
212
|
-
作为
|
|
213
|
-
constructor({ url, funcs, websocket, on_error }?: {
|
|
217
|
+
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
218
|
+
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
219
|
+
constructor({ url, funcs, print, websocket, on_error }?: {
|
|
214
220
|
url?: string;
|
|
215
221
|
funcs?: Remote['funcs'];
|
|
222
|
+
print?: boolean;
|
|
216
223
|
websocket?: WebSocket;
|
|
217
224
|
on_error?(error: WebSocketConnectionError, websocket: WebSocket): void;
|
|
218
225
|
});
|
|
219
226
|
on_error?(error: WebSocketConnectionError, websocket: WebSocket): void;
|
|
220
|
-
/** 幂等,保证 websocket
|
|
221
|
-
|
|
227
|
+
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
228
|
+
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
229
|
+
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
230
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
231
|
+
作为 websocket 连接接收方,需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
222
232
|
connect(websocket?: WebSocket): Promise<void>;
|
|
233
|
+
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
223
234
|
disconnect(): void;
|
|
224
|
-
/**
|
|
235
|
+
/** 发送 message 到对端 remote
|
|
236
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
237
|
+
作为 websocket 连接接收方,必传 websocket 参数
|
|
225
238
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
226
239
|
send(message: Message, websocket?: WebSocket): Promise<void>;
|
|
227
|
-
/** 处理接收到的 websocket message 并解析, 根据 id
|
|
228
|
-
如果 message.done == true
|
|
229
|
-
如果 handler
|
|
240
|
+
/** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理
|
|
241
|
+
如果 message.done == true 则对端指示当前 remote 可以清理 handler
|
|
242
|
+
如果 handler 返回了值 (一定是数组),则包装为 message 发送
|
|
230
243
|
使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
231
244
|
handle(data: Uint8Array, websocket: WebSocket): Promise<void>;
|
|
232
|
-
/**
|
|
233
|
-
|
|
245
|
+
/** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应)
|
|
246
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
247
|
+
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
248
|
+
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
249
|
+
}
|
|
250
|
+
/** 通常将连接到 server 的 client 抽象为下面的结构 */
|
|
251
|
+
export interface RemoteClient {
|
|
252
|
+
remote: Remote;
|
|
253
|
+
websocket: WebSocket;
|
|
234
254
|
}
|
package/net.js
CHANGED
|
@@ -388,10 +388,13 @@ on_message, on_error, on_close }) {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
391
|
+
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
392
|
+
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
393
|
+
使用 remote.call 进行一元 rpc
|
|
394
|
+
使用 remote.send 结合 message.id 进行复杂 rpc
|
|
395
|
+
创建后等到首个 remote.call 或 remote.send 时自动建立实际 websocket 连接
|
|
396
|
+
rpc 状态与底层连接的状态无关,如果是传入 url 创建的 remote 实例,remote.send 时检测到断线会自动建立新的 websocket 连接
|
|
397
|
+
|
|
395
398
|
@example
|
|
396
399
|
// Zero 继承自 Remote 并通过 call 实现了一些方法
|
|
397
400
|
let zero = new Zero({ local: true })
|
|
@@ -408,18 +411,21 @@ on_message, on_error, on_close }) {
|
|
|
408
411
|
|
|
409
412
|
zero.send({ id, func: 'subscribe_stdio' }) */
|
|
410
413
|
export class Remote {
|
|
411
|
-
/**
|
|
414
|
+
/** 在构造 Remote 时, this.initiator = Boolean(url || websocket)
|
|
415
|
+
- true: 作为 websocket 连接发起方
|
|
416
|
+
- false: 作为 websocket 连接接收方 */
|
|
412
417
|
initiator;
|
|
413
|
-
/** websocket url */
|
|
418
|
+
/** 作为 websocket 连接发起方,对端的 url 地址 */
|
|
414
419
|
url;
|
|
415
|
-
/**
|
|
420
|
+
/** 作为 websocket 连接发起方有 websocket lock */
|
|
416
421
|
lwebsocket;
|
|
417
|
-
/**
|
|
422
|
+
/** websocket 连接发起方,接收方,都能被对端通过 (rpc message).func 调用的 rpc 函数 */
|
|
418
423
|
funcs;
|
|
419
|
-
/** map<id, message handler>: 通过 rpc message.id 找到对应的 handler
|
|
424
|
+
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
425
|
+
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
420
426
|
handlers = new Map();
|
|
427
|
+
/** `false` 打印所有交互的 rpc messages */
|
|
421
428
|
print = false;
|
|
422
|
-
pconnect;
|
|
423
429
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
424
430
|
static parse(buffer) {
|
|
425
431
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -440,6 +446,7 @@ export class Remote {
|
|
|
440
446
|
return message;
|
|
441
447
|
}
|
|
442
448
|
static pack({ id, func, data = [], done, error }) {
|
|
449
|
+
assert('length' in data, t('message.data 必须是数组'));
|
|
443
450
|
let data_ = new Array(data.length);
|
|
444
451
|
let bins = [];
|
|
445
452
|
let bufs = [];
|
|
@@ -453,7 +460,6 @@ export class Remote {
|
|
|
453
460
|
else
|
|
454
461
|
data_[i] = item;
|
|
455
462
|
}
|
|
456
|
-
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
457
463
|
const data_json = {
|
|
458
464
|
id,
|
|
459
465
|
...func ? { func } : {},
|
|
@@ -462,27 +468,36 @@ export class Remote {
|
|
|
462
468
|
...data_.length ? { data: data_ } : {},
|
|
463
469
|
...bins.length ? { bins } : {},
|
|
464
470
|
};
|
|
471
|
+
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
465
472
|
const str_json = encoder.encode(JSON.stringify(data_json));
|
|
466
473
|
let dv = new DataView(new ArrayBuffer(4));
|
|
467
474
|
dv.setUint32(0, str_json.length, true);
|
|
468
475
|
return concat([dv, str_json, ...bufs]);
|
|
469
476
|
}
|
|
470
|
-
/** 作为
|
|
471
|
-
作为
|
|
472
|
-
constructor({ url, funcs
|
|
477
|
+
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
478
|
+
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
479
|
+
constructor({ url, funcs, print, websocket, on_error } = {}) {
|
|
473
480
|
assert(!(url && websocket), t('构建 Remote 时 url 和 websocket 最多只能传一个'));
|
|
474
481
|
this.initiator = Boolean(url || websocket);
|
|
475
|
-
|
|
476
|
-
|
|
482
|
+
if (url)
|
|
483
|
+
this.url = url;
|
|
484
|
+
if (funcs)
|
|
485
|
+
this.funcs = funcs;
|
|
486
|
+
if (print !== undefined)
|
|
487
|
+
this.print = print;
|
|
477
488
|
if (on_error)
|
|
478
489
|
this.on_error = on_error;
|
|
479
|
-
this.
|
|
490
|
+
if (this.initiator)
|
|
491
|
+
this.lwebsocket = new Lock(websocket || null);
|
|
480
492
|
}
|
|
481
493
|
on_error(error, websocket) {
|
|
482
494
|
throw error;
|
|
483
495
|
}
|
|
484
|
-
/** 幂等,保证 websocket
|
|
485
|
-
|
|
496
|
+
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
497
|
+
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
498
|
+
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
499
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
500
|
+
作为 websocket 连接接收方,需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
486
501
|
async connect(websocket) {
|
|
487
502
|
if (this.initiator)
|
|
488
503
|
if (this.lwebsocket.resource?.readyState === WebSocket.OPEN)
|
|
@@ -497,8 +512,6 @@ export class Remote {
|
|
|
497
512
|
// 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
|
|
498
513
|
if (websocket?.readyState === WebSocket.OPEN)
|
|
499
514
|
return;
|
|
500
|
-
else if (!this.url)
|
|
501
|
-
throw new Error(t('创建 Remote 时传入的 websocket 连接已断开'));
|
|
502
515
|
else // 重连
|
|
503
516
|
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
504
517
|
on_message: (data, websocket) => {
|
|
@@ -507,29 +520,25 @@ export class Remote {
|
|
|
507
520
|
on_error: this.on_error.bind(this)
|
|
508
521
|
});
|
|
509
522
|
});
|
|
510
|
-
else if (websocket.readyState
|
|
511
|
-
return;
|
|
512
|
-
else
|
|
523
|
+
else if (websocket.readyState !== WebSocket.OPEN)
|
|
513
524
|
throw new Error(t('传入的 websocket 连接已断开'));
|
|
514
525
|
}
|
|
526
|
+
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
515
527
|
disconnect() {
|
|
516
528
|
this.lwebsocket.resource?.close(1000);
|
|
517
529
|
}
|
|
518
|
-
/**
|
|
530
|
+
/** 发送 message 到对端 remote
|
|
531
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
532
|
+
作为 websocket 连接接收方,必传 websocket 参数
|
|
519
533
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
520
534
|
async send(message, websocket) {
|
|
535
|
+
if (this.print)
|
|
536
|
+
console.log('remote.send:', message);
|
|
521
537
|
try {
|
|
522
|
-
|
|
523
|
-
assert(!websocket || websocket === this.lwebsocket.resource);
|
|
524
|
-
await this.connect();
|
|
525
|
-
websocket = this.lwebsocket.resource;
|
|
526
|
-
}
|
|
527
|
-
else
|
|
528
|
-
await this.connect(websocket);
|
|
538
|
+
await this.connect(websocket);
|
|
529
539
|
if (!message.id)
|
|
530
540
|
message.id = genid();
|
|
531
|
-
|
|
532
|
-
websocket.send(Remote.pack(message));
|
|
541
|
+
(websocket || this.lwebsocket.resource).send(Remote.pack(message));
|
|
533
542
|
}
|
|
534
543
|
catch (error) {
|
|
535
544
|
if (message.id)
|
|
@@ -537,21 +546,21 @@ export class Remote {
|
|
|
537
546
|
throw error;
|
|
538
547
|
}
|
|
539
548
|
}
|
|
540
|
-
/** 处理接收到的 websocket message 并解析, 根据 id
|
|
541
|
-
如果 message.done == true
|
|
542
|
-
如果 handler
|
|
549
|
+
/** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理
|
|
550
|
+
如果 message.done == true 则对端指示当前 remote 可以清理 handler
|
|
551
|
+
如果 handler 返回了值 (一定是数组),则包装为 message 发送
|
|
543
552
|
使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
544
553
|
async handle(data, websocket) {
|
|
545
554
|
const message = Remote.parse(data);
|
|
546
555
|
const { id, func, done } = message;
|
|
547
556
|
if (this.print)
|
|
548
|
-
console.log(message);
|
|
557
|
+
console.log('remote.handle:', message);
|
|
549
558
|
let handler;
|
|
550
559
|
if (func)
|
|
551
560
|
handler = this.funcs[func];
|
|
552
561
|
else {
|
|
553
562
|
handler = this.handlers.get(id);
|
|
554
|
-
if (done)
|
|
563
|
+
if (done && handler)
|
|
555
564
|
this.handlers.delete(id);
|
|
556
565
|
}
|
|
557
566
|
try {
|
|
@@ -574,8 +583,10 @@ export class Remote {
|
|
|
574
583
|
throw error;
|
|
575
584
|
}
|
|
576
585
|
}
|
|
577
|
-
/**
|
|
578
|
-
|
|
586
|
+
/** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应)
|
|
587
|
+
作为 websocket 连接发起方,不需要传入 websocket
|
|
588
|
+
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
589
|
+
async call(func, args, websocket) {
|
|
579
590
|
return new Promise(async (resolve, reject) => {
|
|
580
591
|
const id = genid();
|
|
581
592
|
this.handlers.set(id, (message) => {
|
|
@@ -587,7 +598,7 @@ export class Remote {
|
|
|
587
598
|
this.handlers.delete(id);
|
|
588
599
|
});
|
|
589
600
|
try {
|
|
590
|
-
await this.send({ id, func, data: args }); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
|
|
601
|
+
await this.send({ id, func, data: args }, websocket); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
|
|
591
602
|
}
|
|
592
603
|
catch (error) {
|
|
593
604
|
reject(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@babel/core": "^7.22.
|
|
49
|
-
"@babel/parser": "^7.22.
|
|
50
|
-
"@babel/traverse": "^7.22.
|
|
48
|
+
"@babel/core": "^7.22.10",
|
|
49
|
+
"@babel/parser": "^7.22.10",
|
|
50
|
+
"@babel/traverse": "^7.22.10",
|
|
51
51
|
"@koa/cors": "^4.0.0",
|
|
52
52
|
"@types/ws": "^8.5.5",
|
|
53
53
|
"byte-size": "^8.1.1",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"fs-extra": "^11.1.1",
|
|
63
63
|
"gulp-sort": "^2.0.0",
|
|
64
64
|
"hash-string": "^1.0.0",
|
|
65
|
-
"i18next": "^23.2
|
|
65
|
+
"i18next": "^23.4.2",
|
|
66
66
|
"i18next-scanner": "^4.3.0",
|
|
67
67
|
"js-cookie": "^3.0.5",
|
|
68
68
|
"koa": "^2.14.2",
|
|
@@ -71,44 +71,44 @@
|
|
|
71
71
|
"lodash": "^4.17.21",
|
|
72
72
|
"map-stream": "0.0.7",
|
|
73
73
|
"mime-types": "^2.1.35",
|
|
74
|
-
"ora": "^
|
|
74
|
+
"ora": "^7.0.1",
|
|
75
75
|
"qs": "^6.11.2",
|
|
76
76
|
"react": "^18.2.0",
|
|
77
|
-
"react-i18next": "^13.0.
|
|
77
|
+
"react-i18next": "^13.0.3",
|
|
78
78
|
"resolve-path": "^1.4.0",
|
|
79
79
|
"strip-ansi": "^7.1.0",
|
|
80
80
|
"through2": "^4.0.2",
|
|
81
81
|
"tough-cookie": "^4.1.3",
|
|
82
82
|
"tslib": "^2.6.1",
|
|
83
83
|
"typescript": "^5.1.6",
|
|
84
|
-
"undici": "^5.
|
|
84
|
+
"undici": "^5.23.0",
|
|
85
85
|
"vinyl": "^3.0.0",
|
|
86
86
|
"vinyl-fs": "^4.0.0",
|
|
87
87
|
"ws": "^8.13.0"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@babel/types": "^7.22.
|
|
90
|
+
"@babel/types": "^7.22.10",
|
|
91
91
|
"@types/babel__traverse": "^7.20.1",
|
|
92
92
|
"@types/byte-size": "^8.1.0",
|
|
93
93
|
"@types/chardet": "^0.8.1",
|
|
94
94
|
"@types/fs-extra": "^11.0.1",
|
|
95
95
|
"@types/gulp-sort": "2.0.1",
|
|
96
96
|
"@types/js-cookie": "^3.0.3",
|
|
97
|
-
"@types/koa": "^2.13.
|
|
97
|
+
"@types/koa": "^2.13.8",
|
|
98
98
|
"@types/koa-compress": "^4.0.3",
|
|
99
|
-
"@types/lodash": "^4.14.
|
|
99
|
+
"@types/lodash": "^4.14.196",
|
|
100
100
|
"@types/mime-types": "^2.1.1",
|
|
101
|
-
"@types/node": "^20.4.
|
|
101
|
+
"@types/node": "^20.4.8",
|
|
102
102
|
"@types/qs": "^6.9.7",
|
|
103
|
-
"@types/react": "^18.2.
|
|
103
|
+
"@types/react": "^18.2.18",
|
|
104
104
|
"@types/through2": "^2.0.38",
|
|
105
105
|
"@types/tough-cookie": "^4.0.2",
|
|
106
106
|
"@types/vinyl-fs": "^3.0.2",
|
|
107
|
-
"@types/vscode": "^1.
|
|
108
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
109
|
-
"@typescript-eslint/parser": "^6.
|
|
110
|
-
"eslint": "^8.
|
|
111
|
-
"eslint-plugin-react": "^7.33.
|
|
107
|
+
"@types/vscode": "^1.81.0",
|
|
108
|
+
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
|
109
|
+
"@typescript-eslint/parser": "^6.3.0",
|
|
110
|
+
"eslint": "^8.46.0",
|
|
111
|
+
"eslint-plugin-react": "^7.33.1",
|
|
112
112
|
"eslint-plugin-xlint": "^1.0.6"
|
|
113
113
|
},
|
|
114
114
|
"scripts": {
|
package/process.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ interface StartOptions {
|
|
|
48
48
|
- detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref) whether to break the connection with child (ignore stdio, unref)
|
|
49
49
|
*/
|
|
50
50
|
export declare function start(exe: string, args?: string[], { cwd, encoding, print, stdio, detached, env, window: _window, }?: StartOptions): Promise<ChildProcess>;
|
|
51
|
+
export declare function start_nodejs(js: string, args?: string[], options?: StartOptions): Promise<ChildProcess>;
|
|
51
52
|
export interface CallOptions extends StartOptions {
|
|
52
53
|
throw_code?: boolean;
|
|
53
54
|
input?: string;
|
package/process.js
CHANGED
|
@@ -80,6 +80,9 @@ export async function start(exe, args = [], { cwd, encoding = 'utf-8', print = t
|
|
|
80
80
|
}
|
|
81
81
|
return child;
|
|
82
82
|
}
|
|
83
|
+
export async function start_nodejs(js, args = [], options) {
|
|
84
|
+
return start(exe_nodejs, [js, ...args], options);
|
|
85
|
+
}
|
|
83
86
|
export async function call(exe, args = [], options = {}) {
|
|
84
87
|
const { encoding = 'utf-8', throw_code = true, input, } = options;
|
|
85
88
|
let { stdio = 'pipe', print = true } = options;
|
|
@@ -204,7 +207,7 @@ export async function term_nodejs(fp_js, args = [], { title, inspect, tsnode = f
|
|
|
204
207
|
...resolve ? ['--experimental-specifier-resolution=node'] : [],
|
|
205
208
|
...title ? [`--title=${title}`] : [],
|
|
206
209
|
...inspect ? [
|
|
207
|
-
_break ? `--inspect-brk=
|
|
210
|
+
_break ? `--inspect-brk=localhost:${inspect}` : `--inspect=localhost:${inspect}`
|
|
208
211
|
] : [],
|
|
209
212
|
...tsnode ? [
|
|
210
213
|
'--loader=ts-node/esm',
|
package/repl.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Context } from 'koa';
|
|
|
4
4
|
import './prototype.js';
|
|
5
5
|
declare global {
|
|
6
6
|
var __: any;
|
|
7
|
-
var
|
|
7
|
+
var tstarted: Date;
|
|
8
8
|
var repl_router: (ctx: Context) => Promise<boolean>;
|
|
9
9
|
}
|
|
10
10
|
/** 谨慎使用,webpack 打包后可能会变成 /d:/1/mod/node_modules/xshell/ 这样的编译期路径 */
|
package/repl.js
CHANGED
|
@@ -54,14 +54,14 @@ function trans_import_2_require(import_decl) {
|
|
|
54
54
|
// (import_clause.namedBindings as NamedImports).elements
|
|
55
55
|
return [
|
|
56
56
|
// import mod from 'mod'
|
|
57
|
-
//
|
|
57
|
+
// globalThis.mod = __importDefault(require("mod")).default
|
|
58
58
|
...import_clause.name ? [
|
|
59
|
-
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('
|
|
59
|
+
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('globalThis'), import_clause.name), factory.createPropertyAccessExpression(factory.createCallExpression(factory.createIdentifier('__importDefault'), [], [require_module_stmt]), 'default')))
|
|
60
60
|
] : [],
|
|
61
61
|
// import * as mod from 'mod'
|
|
62
|
-
//
|
|
62
|
+
// globalThis.mod = __importStar(require("mod"))
|
|
63
63
|
...import_clause.namedBindings && isNamespaceImport(import_clause.namedBindings) ? [
|
|
64
|
-
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('
|
|
64
|
+
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('globalThis'), import_clause.namedBindings.name), factory.createCallExpression(factory.createIdentifier('__importStar'), [], [require_module_stmt])))
|
|
65
65
|
] : [],
|
|
66
66
|
// import { element2 as element2_, element3 as element3_, other, default as mod2 } from 'mod2'
|
|
67
67
|
...import_clause.namedBindings && isNamedImports(import_clause.namedBindings) ? [
|
|
@@ -69,10 +69,10 @@ function trans_import_2_require(import_decl) {
|
|
|
69
69
|
factory.createVariableStatement([], [factory.createVariableDeclaration(factory.createObjectBindingPattern(
|
|
70
70
|
// ImportSpecifier[] -> ObjectBindingPattern.BindingElement[]
|
|
71
71
|
import_clause.namedBindings.elements.map(import_specifier => factory.createBindingElement(undefined, import_specifier.propertyName, import_specifier.name, import_specifier.propertyName?.text === 'default' ? require_module_stmt : undefined))), undefined, undefined, require_module_stmt)]),
|
|
72
|
-
// Object.assign(
|
|
72
|
+
// Object.assign(globalThis, { element2_, element3 })
|
|
73
73
|
factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('Object'), 'assign'), [],
|
|
74
74
|
// ImportSpecifier[] -> ObjectBindingPattern.BindingElement[]
|
|
75
|
-
[factory.createIdentifier('
|
|
75
|
+
[factory.createIdentifier('globalThis'), factory.createObjectLiteralExpression(import_clause.namedBindings.elements.map(import_specifier => factory.createShorthandPropertyAssignment(import_specifier.name)), false)])),
|
|
76
76
|
factory.createExpressionStatement(import_clause.namedBindings.elements.length === 1 ?
|
|
77
77
|
// (a)
|
|
78
78
|
factory.createParenthesizedExpression(import_clause.namedBindings.elements[0].name)
|
|
@@ -111,7 +111,7 @@ function trans_class_decl_2_expr(class_decl) {
|
|
|
111
111
|
return class_decl;
|
|
112
112
|
// export class C { } 这样的语句已经在 trans_export_stmt 中被去掉了 export modifier
|
|
113
113
|
return factory.createVariableStatement([], factory.createVariableDeclarationList([
|
|
114
|
-
factory.createVariableDeclaration(class_decl.name, undefined, undefined, factory.createClassExpression(undefined, class_decl.name, undefined,
|
|
114
|
+
factory.createVariableDeclaration(class_decl.name, undefined, undefined, factory.createClassExpression(undefined, class_decl.name, undefined, class_decl.heritageClauses, class_decl.members))
|
|
115
115
|
]));
|
|
116
116
|
}
|
|
117
117
|
function get_expr_of_stmt(statement) {
|
|
@@ -141,7 +141,7 @@ function trans_return_2_expr(stmt) {
|
|
|
141
141
|
return stmt;
|
|
142
142
|
return factory.createExpressionStatement(stmt.expression);
|
|
143
143
|
}
|
|
144
|
-
function assign_var_decl(var_decl, destination = '
|
|
144
|
+
function assign_var_decl(var_decl, destination = 'globalThis') {
|
|
145
145
|
if (!isVariableStatement(var_decl))
|
|
146
146
|
return [var_decl];
|
|
147
147
|
// VariableDeclaration[] -> ObjectLiteral[]
|
|
@@ -243,7 +243,7 @@ export async function eval_ts(code) {
|
|
|
243
243
|
export async function repl_ts(code) {
|
|
244
244
|
console.log(); // 离之前的输出有一个空行
|
|
245
245
|
const timer = new Timer();
|
|
246
|
-
const __ =
|
|
246
|
+
const __ = globalThis.__ = await eval_ts(code);
|
|
247
247
|
timer.stop();
|
|
248
248
|
console.log(inspect(__, { limit: inspection_limit }));
|
|
249
249
|
if (timer.get() >= 500)
|
|
@@ -292,7 +292,7 @@ export async function start_repl() {
|
|
|
292
292
|
})(),
|
|
293
293
|
]);
|
|
294
294
|
console.log(('-'.repeat(30) + '\n' +
|
|
295
|
-
t('xshell 启动成功,用时 ') + delta2str(new Date().getTime() -
|
|
295
|
+
t('xshell 启动成功,用时 ') + delta2str(new Date().getTime() - globalThis.tstarted.getTime()) + '\n' +
|
|
296
296
|
t('正在监听: http://localhost:8421\n') +
|
|
297
297
|
'-'.repeat(30)).green);
|
|
298
298
|
}
|
|
@@ -306,7 +306,7 @@ export async function exit() {
|
|
|
306
306
|
process.exit();
|
|
307
307
|
}
|
|
308
308
|
export async function pollute_global() {
|
|
309
|
-
Object.assign(
|
|
309
|
+
Object.assign(globalThis, {
|
|
310
310
|
__importDefault: (mod) => ((mod?.__esModule) ? mod : { default: mod }),
|
|
311
311
|
__importStar: (mod) => {
|
|
312
312
|
if (mod?.__esModule)
|
|
@@ -320,7 +320,7 @@ export async function pollute_global() {
|
|
|
320
320
|
result.default = mod;
|
|
321
321
|
return result;
|
|
322
322
|
},
|
|
323
|
-
exports:
|
|
323
|
+
exports: globalThis
|
|
324
324
|
});
|
|
325
325
|
await Promise.all([
|
|
326
326
|
pollute_module_default_export('lodash/omit.js', 'omit'),
|
|
@@ -339,9 +339,9 @@ export async function pollute_global() {
|
|
|
339
339
|
console.log(t('所有模块加载成功'));
|
|
340
340
|
}
|
|
341
341
|
export async function pollute_module_exports(fp_mod) {
|
|
342
|
-
Object.assign(
|
|
342
|
+
Object.assign(globalThis, await import(fp_mod));
|
|
343
343
|
}
|
|
344
344
|
export async function pollute_module_default_export(fp_mod, name) {
|
|
345
|
-
|
|
345
|
+
globalThis[name] = (await import(fp_mod)).default;
|
|
346
346
|
}
|
|
347
347
|
//# sourceMappingURL=repl.js.map
|
package/server.js
CHANGED
package/utils.d.ts
CHANGED
|
@@ -89,7 +89,7 @@ export declare function has_chinese(str: string): boolean;
|
|
|
89
89
|
export declare function escape_line_feed(str: string): string;
|
|
90
90
|
/** util.inspect(obj)
|
|
91
91
|
- options
|
|
92
|
-
- limit?: `10000`
|
|
92
|
+
- limit?: `10000` 传 0 时不限制
|
|
93
93
|
- omit?: string[] */
|
|
94
94
|
export declare function inspect(obj: any, options?: util.InspectOptions & {
|
|
95
95
|
limit?: number;
|
package/utils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Stream, Writable, Transform } from 'stream';
|
|
2
2
|
import util from 'util';
|
|
3
|
-
import omit from 'lodash/omit.js';
|
|
4
3
|
import { t } from './i18n/instance.js';
|
|
5
4
|
import './prototype.js';
|
|
6
5
|
export const noop = () => { };
|
|
@@ -253,16 +252,20 @@ export function escape_line_feed(str) {
|
|
|
253
252
|
}
|
|
254
253
|
/** util.inspect(obj)
|
|
255
254
|
- options
|
|
256
|
-
- limit?: `10000`
|
|
255
|
+
- limit?: `10000` 传 0 时不限制
|
|
257
256
|
- omit?: string[] */
|
|
258
257
|
export function inspect(obj, options = {}) {
|
|
259
|
-
if (options.omit)
|
|
260
|
-
obj =
|
|
258
|
+
if (options.omit) {
|
|
259
|
+
obj = { ...obj };
|
|
260
|
+
for (const key of options.omit)
|
|
261
|
+
delete obj[key];
|
|
262
|
+
// 改变了 obj 的引用,去掉 inspect.custom 防止无限递归 inspect
|
|
263
|
+
delete obj[inspect.custom];
|
|
264
|
+
}
|
|
261
265
|
let text = util.inspect(obj, options);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return `${text.slice(0, options.limit)}……'\u001b[39m\n`;
|
|
266
|
+
const limit = options.limit ?? 10000;
|
|
267
|
+
if (limit && text.length > limit)
|
|
268
|
+
return `${text.slice(0, limit)}……'\u001b[39m\n`;
|
|
266
269
|
else
|
|
267
270
|
return text;
|
|
268
271
|
}
|
package/xshell.js
CHANGED