xshell 1.3.0 → 1.3.2
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/apps.js +1 -1
- package/builder.js +7 -3
- package/development.js +1 -1
- package/file.d.ts +9 -5
- package/file.js +7 -3
- package/net.browser.js +34 -36
- package/net.common.d.ts +5 -2
- package/net.common.js +8 -3
- package/net.d.ts +4 -2
- package/net.js +65 -65
- package/package.json +4 -4
- package/path.d.ts +2 -2
- package/platform.common.d.ts +1 -1
- package/process.d.ts +2 -2
- package/process.js +2 -2
- package/prototype.js +1 -1
- package/repl.js +2 -2
- package/server.d.ts +3 -3
- package/server.js +6 -6
- package/utils.common.d.ts +18 -1
- package/utils.common.js +52 -0
- package/utils.d.ts +2 -2
- package/utils.js +3 -3
package/apps.js
CHANGED
package/builder.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url';
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
2
|
import { not_empty } from "./prototype.js";
|
|
3
3
|
import { noprint } from "./process.js";
|
|
4
4
|
import { Lock, filter_values } from "./utils.js";
|
|
5
|
-
import { fcopy, fmkdir, fwrite, fread, print_info } from "./file.js";
|
|
5
|
+
import { fcopy, fmkdir, fwrite, fread, print_info, ramdisk } from "./file.js";
|
|
6
6
|
import { path } from "./path.js";
|
|
7
7
|
function get_react_js(production, src, map) {
|
|
8
8
|
return `${src ? import.meta.dirname.fpd : 'vendors/react/'}react.${production ? 'production' : 'development'}.js${map ? '.map' : ''}`;
|
|
@@ -472,8 +472,12 @@ export class Bundler {
|
|
|
472
472
|
const { BundleAnalyzerPlugin } = await import('webpack-bundle-analyzer');
|
|
473
473
|
return [
|
|
474
474
|
new BundleAnalyzerPlugin({
|
|
475
|
-
|
|
475
|
+
analyzerMode: 'static',
|
|
476
476
|
openAnalyzer: false,
|
|
477
|
+
reportFilename: ramdisk ?
|
|
478
|
+
`T:/t/webpack-analysis/${this.name}.html`
|
|
479
|
+
:
|
|
480
|
+
'report.html'
|
|
477
481
|
})
|
|
478
482
|
];
|
|
479
483
|
})() : [],
|
package/development.js
CHANGED
package/file.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { promises as fsp, default as fs } from 'fs';
|
|
1
|
+
import { promises as fsp, default as fs } from 'node:fs';
|
|
2
2
|
import type { UnzipFileFilter } from './fflate.ts';
|
|
3
3
|
export { fsp };
|
|
4
4
|
export type Encoding = 'utf-8' | 'gb18030' | 'shift-jis' | 'utf-16le';
|
|
@@ -208,11 +208,15 @@ export interface FMkdirOptions {
|
|
|
208
208
|
export declare function fmkdir(fpd: string, { print }?: FMkdirOptions): Promise<string>;
|
|
209
209
|
/** 创建软链接
|
|
210
210
|
- fp_real: 现在真实文件/文件夹的路径
|
|
211
|
-
- fp_link: 目标链接文件/文件夹的路径
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
- fp_link: 目标链接文件/文件夹的路径
|
|
212
|
+
- options?:
|
|
213
|
+
- skip_existing?: `false` 忽略已存在的同名的文件,文件夹
|
|
214
|
+
- print?: `true`
|
|
215
|
+
- junction? */
|
|
216
|
+
export declare function flink(fp_real: string, fp_link: string, { skip_existing, print, junction }?: {
|
|
215
217
|
skip_existing?: boolean;
|
|
218
|
+
print?: boolean;
|
|
219
|
+
junction?: boolean;
|
|
216
220
|
}): Promise<void>;
|
|
217
221
|
export interface ZipOptions {
|
|
218
222
|
dirname?: string;
|
package/file.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { promises as fsp, default as fs } from 'fs';
|
|
1
|
+
import { promises as fsp, default as fs } from 'node:fs';
|
|
2
2
|
import { isArrayBuffer, isUint8Array } from 'util/types';
|
|
3
3
|
import { t } from "./i18n/instance.js";
|
|
4
4
|
import { noop, to_json } from "./prototype.js";
|
|
@@ -456,8 +456,12 @@ export async function fmkdir(fpd, { print = true } = {}) {
|
|
|
456
456
|
}
|
|
457
457
|
/** 创建软链接
|
|
458
458
|
- fp_real: 现在真实文件/文件夹的路径
|
|
459
|
-
- fp_link: 目标链接文件/文件夹的路径
|
|
460
|
-
|
|
459
|
+
- fp_link: 目标链接文件/文件夹的路径
|
|
460
|
+
- options?:
|
|
461
|
+
- skip_existing?: `false` 忽略已存在的同名的文件,文件夹
|
|
462
|
+
- print?: `true`
|
|
463
|
+
- junction? */
|
|
464
|
+
export async function flink(fp_real, fp_link, { skip_existing = false, print = true, junction = false } = {}) {
|
|
461
465
|
check(path.isAbsolute(fp_real) && path.isAbsolute(fp_link), 'fp 必须是绝对路径');
|
|
462
466
|
const is_fpd_real = fp_real.isdir;
|
|
463
467
|
const is_fpd_link = fp_link.isdir;
|
package/net.browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t } from "./i18n/instance.js";
|
|
2
|
-
import { assert, delay } from "./utils.browser.js";
|
|
2
|
+
import { assert, check, delay } from "./utils.browser.js";
|
|
3
3
|
import { drop_request_headers } from "./net.common.js";
|
|
4
4
|
export * from "./net.common.js";
|
|
5
5
|
async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
|
|
@@ -63,33 +63,7 @@ export async function request(url, options = {}) {
|
|
|
63
63
|
...cors ? { mode: 'cors' } : {},
|
|
64
64
|
headers,
|
|
65
65
|
// --- body
|
|
66
|
-
body: (
|
|
67
|
-
if (body === undefined)
|
|
68
|
-
return;
|
|
69
|
-
switch (type) {
|
|
70
|
-
case 'application/json':
|
|
71
|
-
return typeof body === 'string' ||
|
|
72
|
-
ArrayBuffer.isView(body) ||
|
|
73
|
-
body instanceof ArrayBuffer ||
|
|
74
|
-
body instanceof Blob ?
|
|
75
|
-
body
|
|
76
|
-
:
|
|
77
|
-
JSON.stringify(body);
|
|
78
|
-
case 'application/x-www-form-urlencoded':
|
|
79
|
-
return body instanceof URLSearchParams ? body : new URLSearchParams(body);
|
|
80
|
-
case 'multipart/form-data':
|
|
81
|
-
if (body instanceof FormData)
|
|
82
|
-
return body;
|
|
83
|
-
else {
|
|
84
|
-
let form = new FormData();
|
|
85
|
-
for (const key in body) {
|
|
86
|
-
let value = body[key];
|
|
87
|
-
form.set(key, value);
|
|
88
|
-
}
|
|
89
|
-
return form;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
})(),
|
|
66
|
+
body: get_request_body(body, type),
|
|
93
67
|
};
|
|
94
68
|
let response;
|
|
95
69
|
try {
|
|
@@ -111,14 +85,7 @@ export async function request(url, options = {}) {
|
|
|
111
85
|
}
|
|
112
86
|
if (raw)
|
|
113
87
|
return response;
|
|
114
|
-
const body_ = await (
|
|
115
|
-
if (!response.body)
|
|
116
|
-
return encoding === 'binary' ? new ArrayBuffer(0) : '';
|
|
117
|
-
if (encoding === 'binary')
|
|
118
|
-
return response.arrayBuffer();
|
|
119
|
-
else
|
|
120
|
-
return response.text();
|
|
121
|
-
})();
|
|
88
|
+
const body_ = await get_response_body(response, encoding);
|
|
122
89
|
return full ? {
|
|
123
90
|
status: response.status,
|
|
124
91
|
headers: response.headers,
|
|
@@ -127,6 +94,37 @@ export async function request(url, options = {}) {
|
|
|
127
94
|
:
|
|
128
95
|
body_;
|
|
129
96
|
}
|
|
97
|
+
function get_request_body(body, type) {
|
|
98
|
+
if (body === undefined)
|
|
99
|
+
return;
|
|
100
|
+
if (type === 'application/json') {
|
|
101
|
+
return typeof body === 'string' ||
|
|
102
|
+
ArrayBuffer.isView(body) ||
|
|
103
|
+
body instanceof ArrayBuffer ||
|
|
104
|
+
body instanceof Blob ?
|
|
105
|
+
body
|
|
106
|
+
:
|
|
107
|
+
JSON.stringify(body);
|
|
108
|
+
}
|
|
109
|
+
if (type === 'application/x-www-form-urlencoded')
|
|
110
|
+
return body instanceof URLSearchParams ? body : new URLSearchParams(body);
|
|
111
|
+
check(type === 'multipart/form-data');
|
|
112
|
+
if (body instanceof FormData)
|
|
113
|
+
return body;
|
|
114
|
+
let form = new FormData();
|
|
115
|
+
for (const key in body) {
|
|
116
|
+
let value = body[key];
|
|
117
|
+
form.set(key, value);
|
|
118
|
+
}
|
|
119
|
+
return form;
|
|
120
|
+
}
|
|
121
|
+
async function get_response_body(response, encoding) {
|
|
122
|
+
if (!response.body)
|
|
123
|
+
return encoding === 'binary' ? new ArrayBuffer(0) : '';
|
|
124
|
+
if (encoding === 'binary')
|
|
125
|
+
return response.arrayBuffer();
|
|
126
|
+
return response.text();
|
|
127
|
+
}
|
|
130
128
|
/** 发起 http 请求并将响应体作为 json 解析 */
|
|
131
129
|
export async function request_json(url, options) {
|
|
132
130
|
const body = await request(url, options);
|
package/net.common.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface RemoteOptions {
|
|
|
100
100
|
/** 使用者自定义的在 websocket 连接出错时,或者 handlers 出错时的处理
|
|
101
101
|
用户设置后会覆盖默认的 print 错误功能 */
|
|
102
102
|
on_error?(error: WebSocketConnectionError | Error, remote: Remote): void;
|
|
103
|
+
/** 使用者自定义的,在重连之前调用的函数,比如可以修改 url,仅连接发起方有效 */
|
|
104
|
+
on_reconnect?(this: Remote): Promise<void>;
|
|
103
105
|
}
|
|
104
106
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
105
107
|
使用 remote.call() 进行一元 rpc
|
|
@@ -146,10 +148,11 @@ export declare class Remote {
|
|
|
146
148
|
error: WebSocketConnectionError | Error;
|
|
147
149
|
/** 作为 websocket 连接发起方,传入 url
|
|
148
150
|
作为 websocket 连接接收方,传入 websocket + name */
|
|
149
|
-
constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, }?: RemoteOptions);
|
|
151
|
+
constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect }?: RemoteOptions);
|
|
150
152
|
/** 统一处理首次连接和连接后的 websocket 错误 */
|
|
151
153
|
_on_error: (error: WebSocketConnectionError | Error) => void;
|
|
152
|
-
|
|
154
|
+
on_reconnect?: (this: Remote) => Promise<void>;
|
|
155
|
+
reconnect: () => Promise<void>;
|
|
153
156
|
_on_message: (data: ArrayBuffer) => void;
|
|
154
157
|
/** 使用者自定义的在 websocket 连接出错时,或者 handlers 出错时的处理
|
|
155
158
|
用户设置后会覆盖默认的 print 错误功能 */
|
package/net.common.js
CHANGED
|
@@ -238,12 +238,14 @@ export class Remote {
|
|
|
238
238
|
error;
|
|
239
239
|
/** 作为 websocket 连接发起方,传入 url
|
|
240
240
|
作为 websocket 连接接收方,传入 websocket + name */
|
|
241
|
-
constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, } = {}) {
|
|
241
|
+
constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect } = {}) {
|
|
242
242
|
if (name)
|
|
243
243
|
this.name = name;
|
|
244
244
|
if (url) {
|
|
245
245
|
check(!websocket, '构建 Remote 时 url 和 websocket 只能传其中一个');
|
|
246
246
|
this.url = url;
|
|
247
|
+
if (on_reconnect)
|
|
248
|
+
this.on_reconnect = on_reconnect;
|
|
247
249
|
}
|
|
248
250
|
else { // 连接接收方,一定是 nodejs 环境
|
|
249
251
|
check(websocket, '构建 Remote 时需传入 url 或者 websocket');
|
|
@@ -308,13 +310,16 @@ export class Remote {
|
|
|
308
310
|
!this.reconnecting &&
|
|
309
311
|
this.url &&
|
|
310
312
|
(this.funcs || this.probe)) {
|
|
311
|
-
this.reconnecting = setTimeout(this.reconnect, this.error ?
|
|
313
|
+
this.reconnecting = setTimeout(this.reconnect, this.error ? 20_000 : 2_000);
|
|
312
314
|
this.error = error;
|
|
313
315
|
}
|
|
314
316
|
this.on_error(error, this);
|
|
315
317
|
};
|
|
316
|
-
|
|
318
|
+
on_reconnect;
|
|
319
|
+
reconnect = async () => {
|
|
317
320
|
this.reconnecting = null;
|
|
321
|
+
if (this.on_reconnect)
|
|
322
|
+
await this.on_reconnect();
|
|
318
323
|
this.try_connect();
|
|
319
324
|
};
|
|
320
325
|
_on_message = (data) => {
|
package/net.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Readable } from 'stream';
|
|
1
|
+
import { type Readable } from 'node:stream';
|
|
2
2
|
import type { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
|
|
3
3
|
import type { Encoding } from './file.ts';
|
|
4
4
|
import { inspect } from './utils.ts';
|
|
@@ -48,6 +48,7 @@ export interface RequestOptions {
|
|
|
48
48
|
retry: boolean;
|
|
49
49
|
timeout: boolean;
|
|
50
50
|
};
|
|
51
|
+
long_connection?: true;
|
|
51
52
|
}
|
|
52
53
|
export interface RequestFullOptions extends RequestOptions {
|
|
53
54
|
full: true;
|
|
@@ -96,7 +97,8 @@ export declare class StatusCodeError extends Error {
|
|
|
96
97
|
- decode?: `true` 根据 content-encoding: br 等解码压缩后的 response.body
|
|
97
98
|
- print?:
|
|
98
99
|
- retry: `true` 是否打印 "等待 1 秒后重试 request" 提示信息
|
|
99
|
-
- timeout: `true` 是否打印最后一次超时重试失败时的错误
|
|
100
|
+
- timeout: `true` 是否打印最后一次超时重试失败时的错误
|
|
101
|
+
- long_connection?: 用于保持长 tcp 连接,保留 nat 端口映射记录 */
|
|
100
102
|
export declare function request(url: string | URL): Promise<string>;
|
|
101
103
|
export declare function request(url: string | URL, options: RequestRawOptions): Promise<RawResponse>;
|
|
102
104
|
export declare function request(url: string | URL, options: RequestFullOptions & {
|
package/net.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import zlib from 'zlib';
|
|
2
|
-
import { buffer as stream_to_buffer, text as stream_to_text } from 'stream/consumers';
|
|
3
|
-
import { isReadable } from 'stream';
|
|
4
|
-
import { inspect, assert, delay, pipe_with_error, map_values, unique, timeout, check, colored } from "./utils.js";
|
|
1
|
+
import zlib from 'node:zlib';
|
|
2
|
+
import { buffer as stream_to_buffer, text as stream_to_text } from 'node:stream/consumers';
|
|
3
|
+
import { isReadable } from 'node:stream';
|
|
4
|
+
import { inspect, assert, delay, pipe_with_error, map_values, unique, timeout, check, colored, encode } from "./utils.js";
|
|
5
5
|
import { drop_request_headers } from "./net.common.js";
|
|
6
6
|
export * from "./net.common.js";
|
|
7
7
|
export var MyProxy;
|
|
@@ -24,9 +24,9 @@ export const cookies = {
|
|
|
24
24
|
this.jar = new CookieJar(this.store = new MemoryCookieStore());
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
|
-
let
|
|
27
|
+
let undici;
|
|
28
|
+
let dispatchers = {};
|
|
28
29
|
async function request_retry(url, options, _timeout, retries = 0, count = 0, print) {
|
|
29
|
-
const { default: undici } = await import('undici');
|
|
30
30
|
try {
|
|
31
31
|
if (_timeout > 0) {
|
|
32
32
|
// 设置给 undici 设置 timeout, signal 不一定管用,还是得自己兜底
|
|
@@ -65,15 +65,13 @@ export class StatusCodeError extends Error {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
export async function request(url, options = {}) {
|
|
68
|
-
|
|
69
|
-
UndiciProxyAgent ??= undici.ProxyAgent;
|
|
70
|
-
UndiciFormData ??= undici.FormData;
|
|
68
|
+
undici ??= (await import('undici')).default;
|
|
71
69
|
const { Cookie } = await import('tough-cookie');
|
|
72
70
|
await cookies.init();
|
|
73
71
|
const { queries, headers: _headers, body, type = 'application/json', timeout = 5 * 1000, auth, cookies: _cookies, raw = false, full = false, redirect = 'follow', decode = true, print = {
|
|
74
72
|
timeout: true,
|
|
75
73
|
retry: true
|
|
76
|
-
} } = options;
|
|
74
|
+
}, long_connection } = options;
|
|
77
75
|
let { method, retries, encoding, proxy, } = options;
|
|
78
76
|
url = new URL(url);
|
|
79
77
|
if (queries)
|
|
@@ -126,23 +124,11 @@ export async function request(url, options = {}) {
|
|
|
126
124
|
headers[key] = _headers[key];
|
|
127
125
|
}
|
|
128
126
|
}
|
|
127
|
+
if (proxy === true)
|
|
128
|
+
proxy = MyProxy.socks5;
|
|
129
129
|
let undici_options = {
|
|
130
130
|
...method ? { method } : {},
|
|
131
|
-
dispatcher:
|
|
132
|
-
const { default: {
|
|
133
|
-
// @ts-ignore
|
|
134
|
-
ProxyAgent, Agent, interceptors } } = await import('undici');
|
|
135
|
-
if (proxy === true)
|
|
136
|
-
proxy = MyProxy.socks5;
|
|
137
|
-
return agents[`${proxy || 'direct'}.${redirect}`] ??= (() => {
|
|
138
|
-
let dispatcher = proxy ? new ProxyAgent({ uri: proxy }) : new Agent();
|
|
139
|
-
if (redirect === 'follow')
|
|
140
|
-
dispatcher = dispatcher.compose(
|
|
141
|
-
// todo: 强制手动处理重定向,来正确处理 cookie ?
|
|
142
|
-
interceptors.redirect({ maxRedirections: 5 }));
|
|
143
|
-
return dispatcher;
|
|
144
|
-
})();
|
|
145
|
-
})(),
|
|
131
|
+
dispatcher: get_dispatcher(proxy, redirect, long_connection),
|
|
146
132
|
// 下面这些 timeout 都不是总的时间
|
|
147
133
|
headersTimeout: timeout,
|
|
148
134
|
// 从收完 headers 开始算
|
|
@@ -150,30 +136,7 @@ export async function request(url, options = {}) {
|
|
|
150
136
|
// @ts-ignore 没有类型声明,实际可用
|
|
151
137
|
connectTimeout: timeout,
|
|
152
138
|
headers,
|
|
153
|
-
|
|
154
|
-
body: (() => {
|
|
155
|
-
if (body === undefined)
|
|
156
|
-
return;
|
|
157
|
-
if (typeof body?.read === 'function' && isReadable(body))
|
|
158
|
-
return body;
|
|
159
|
-
switch (type) {
|
|
160
|
-
case 'application/json': // 可能的类型 string | Record<string, any> | Uint8Array
|
|
161
|
-
if (typeof body === 'string')
|
|
162
|
-
return body;
|
|
163
|
-
if (body instanceof Uint8Array)
|
|
164
|
-
return body;
|
|
165
|
-
assert(!(body instanceof ArrayBuffer || ArrayBuffer.isView(body)));
|
|
166
|
-
return JSON.stringify(body);
|
|
167
|
-
case 'application/x-www-form-urlencoded':
|
|
168
|
-
return (body instanceof URLSearchParams ? body : new URLSearchParams(body)).toString();
|
|
169
|
-
case 'multipart/form-data': {
|
|
170
|
-
let form = new UndiciFormData();
|
|
171
|
-
for (const key in body)
|
|
172
|
-
form.set(key, body[key]);
|
|
173
|
-
return form;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
})()
|
|
139
|
+
body: get_request_body(body, type)
|
|
177
140
|
};
|
|
178
141
|
let response;
|
|
179
142
|
try {
|
|
@@ -265,25 +228,62 @@ export async function request(url, options = {}) {
|
|
|
265
228
|
}
|
|
266
229
|
if (raw)
|
|
267
230
|
return response;
|
|
268
|
-
const body_ = await (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (encoding === 'binary')
|
|
272
|
-
return stream_to_buffer(response.body);
|
|
273
|
-
encoding ||= /charset=(.*)/.exec(response.headers['content-type'])?.[1] || 'utf-8';
|
|
274
|
-
if (/utf-?8/i.test(encoding))
|
|
275
|
-
return stream_to_text(response.body);
|
|
276
|
-
return new TextDecoder(encoding)
|
|
277
|
-
.decode(await stream_to_buffer(response.body));
|
|
278
|
-
})();
|
|
279
|
-
return full ? {
|
|
280
|
-
status: response.status,
|
|
281
|
-
headers: response.headers,
|
|
282
|
-
body: body_
|
|
283
|
-
}
|
|
231
|
+
const body_ = await get_response_body(response.body, encoding, response.headers);
|
|
232
|
+
return full ?
|
|
233
|
+
{ status: response.status, headers: response.headers, body: body_ }
|
|
284
234
|
:
|
|
285
235
|
body_;
|
|
286
236
|
}
|
|
237
|
+
function get_dispatcher(proxy, redirect, long_connection) {
|
|
238
|
+
const key = `${proxy || (long_connection ? 'direct.long' : 'direct')}.${redirect}`;
|
|
239
|
+
let dispatcher = dispatchers[key];
|
|
240
|
+
if (dispatcher)
|
|
241
|
+
return dispatcher;
|
|
242
|
+
dispatcher = proxy ?
|
|
243
|
+
// @ts-ignore
|
|
244
|
+
new undici.ProxyAgent({ uri: proxy })
|
|
245
|
+
:
|
|
246
|
+
new undici.Agent({ keepAliveTimeout: long_connection ? 30_000 : undefined });
|
|
247
|
+
if (redirect === 'follow')
|
|
248
|
+
dispatcher = dispatcher.compose(
|
|
249
|
+
// todo: 强制手动处理重定向,来正确处理 cookie ?
|
|
250
|
+
undici.interceptors.redirect({ maxRedirections: 5 }));
|
|
251
|
+
return dispatchers[key] = dispatcher;
|
|
252
|
+
}
|
|
253
|
+
function get_request_body(body, type) {
|
|
254
|
+
if (body === undefined)
|
|
255
|
+
return undefined;
|
|
256
|
+
if (typeof body?.read === 'function' && isReadable(body))
|
|
257
|
+
return body;
|
|
258
|
+
if (type === 'application/json') {
|
|
259
|
+
// 可能的类型 string | Record<string, any> | Uint8Array
|
|
260
|
+
if (typeof body === 'string')
|
|
261
|
+
return encode(body);
|
|
262
|
+
if (body instanceof Uint8Array)
|
|
263
|
+
return body;
|
|
264
|
+
check(!(body instanceof ArrayBuffer || ArrayBuffer.isView(body)));
|
|
265
|
+
return JSON.stringify(body);
|
|
266
|
+
}
|
|
267
|
+
if (type === 'application/x-www-form-urlencoded')
|
|
268
|
+
return encode((body instanceof URLSearchParams ? body : new URLSearchParams(body)).toString());
|
|
269
|
+
check(type === 'multipart/form-data');
|
|
270
|
+
// undici.FormData 会和 globalThis.FormData 不一致,禁止传入 FormData 类型
|
|
271
|
+
let form = new undici.FormData();
|
|
272
|
+
for (const key in body)
|
|
273
|
+
form.set(key, body[key]);
|
|
274
|
+
return form;
|
|
275
|
+
}
|
|
276
|
+
async function get_response_body(body, encoding, headers) {
|
|
277
|
+
if (!body)
|
|
278
|
+
return encoding === 'binary' ? Buffer.from([]) : '';
|
|
279
|
+
if (encoding === 'binary')
|
|
280
|
+
return stream_to_buffer(body);
|
|
281
|
+
encoding ||= /charset=(.*)/.exec(headers['content-type'])?.[1] || 'utf-8';
|
|
282
|
+
if (/utf-?8/i.test(encoding))
|
|
283
|
+
return stream_to_text(body);
|
|
284
|
+
return new TextDecoder(encoding)
|
|
285
|
+
.decode(await stream_to_buffer(body));
|
|
286
|
+
}
|
|
287
287
|
/** 发起 http 请求并将响应体作为 json 解析 */
|
|
288
288
|
export async function request_json(url, options) {
|
|
289
289
|
const body = await request(url, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"commander": "^14.0.2",
|
|
65
65
|
"css-loader": "^7.1.2",
|
|
66
66
|
"emoji-regex": "^10.6.0",
|
|
67
|
-
"eslint": "^9.
|
|
67
|
+
"eslint": "^9.39.0",
|
|
68
68
|
"eslint-plugin-import": "^2.32.0",
|
|
69
69
|
"eslint-plugin-react": "^7.37.5",
|
|
70
70
|
"https-proxy-agent": "^7.0.6",
|
|
@@ -76,9 +76,9 @@
|
|
|
76
76
|
"mime-types": "^3.0.1",
|
|
77
77
|
"p-map": "^7.0.3",
|
|
78
78
|
"react": "^19.2.0",
|
|
79
|
-
"react-i18next": "^16.2.
|
|
79
|
+
"react-i18next": "^16.2.3",
|
|
80
80
|
"resolve-path": "^1.4.0",
|
|
81
|
-
"sass": "^1.93.
|
|
81
|
+
"sass": "^1.93.3",
|
|
82
82
|
"sass-loader": "^16.0.6",
|
|
83
83
|
"source-map-loader": "^5.0.0",
|
|
84
84
|
"strip-ansi": "^7.1.2",
|
package/path.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export declare function extname(path: string): string;
|
|
|
54
54
|
/** `/` */
|
|
55
55
|
export declare const sep = "/";
|
|
56
56
|
/** The platform-specific file delimiter. ';' or ':'. */
|
|
57
|
-
export declare const delimiter: "
|
|
57
|
+
export declare const delimiter: ":" | ";";
|
|
58
58
|
/** Returns an object from a path string - the opposite of format().
|
|
59
59
|
@param path path to evaluate.
|
|
60
60
|
@throws {TypeError} if `path` is not a string. */
|
|
@@ -81,7 +81,7 @@ export declare let path: {
|
|
|
81
81
|
basename: typeof basename;
|
|
82
82
|
extname: typeof extname;
|
|
83
83
|
sep: string;
|
|
84
|
-
delimiter: "
|
|
84
|
+
delimiter: ":" | ";";
|
|
85
85
|
parse: typeof parse;
|
|
86
86
|
format: typeof format;
|
|
87
87
|
toNamespacedPath: typeof toNamespacedPath;
|
package/platform.common.d.ts
CHANGED
package/process.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { InspectOptions } from 'util';
|
|
2
|
-
import { type ChildProcess } from 'child_process';
|
|
1
|
+
import type { InspectOptions } from 'node:util';
|
|
2
|
+
import { type ChildProcess } from 'node:child_process';
|
|
3
3
|
import './prototype.ts';
|
|
4
4
|
import { type Encoding } from './file.ts';
|
|
5
5
|
import type { MyProxy } from './net.ts';
|
package/process.js
CHANGED
package/prototype.js
CHANGED
package/repl.js
CHANGED
|
@@ -6,8 +6,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import repl from 'repl';
|
|
10
|
-
import process from 'process';
|
|
9
|
+
import repl from 'node:repl';
|
|
10
|
+
import process from 'node:process';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { path } from "./path.js";
|
|
13
13
|
import { t } from "./i18n/instance.js";
|
package/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Server as HttpServer, type IncomingHttpHeaders, type IncomingMessage } from 'http';
|
|
2
|
-
import { type Http2SecureServer, type IncomingHttpHeaders as IncomingHttp2Headers } from 'http2';
|
|
3
|
-
import type { Duplex } from 'stream';
|
|
1
|
+
import { type Server as HttpServer, type IncomingHttpHeaders, type IncomingMessage } from 'node:http';
|
|
2
|
+
import { type Http2SecureServer, type IncomingHttpHeaders as IncomingHttp2Headers } from 'node:http2';
|
|
3
|
+
import type { Duplex } from 'node:stream';
|
|
4
4
|
import type { WebSocketServer } from 'ws';
|
|
5
5
|
import { default as Koa, type Context, type Next } from 'koa';
|
|
6
6
|
import { Remote, type RequestOptions, type RawResponse, type MessageHandler } from './net.ts';
|
package/server.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createServer as http_create_server, } from 'http';
|
|
2
|
-
import { createSecureServer as http2_create_server } from 'http2';
|
|
3
|
-
import { createSecureContext } from 'tls';
|
|
4
|
-
import zlib from 'zlib';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import { buffer as stream_to_buffer } from 'stream/consumers';
|
|
1
|
+
import { createServer as http_create_server, } from 'node:http';
|
|
2
|
+
import { createSecureServer as http2_create_server } from 'node:http2';
|
|
3
|
+
import { createSecureContext } from 'node:tls';
|
|
4
|
+
import zlib from 'node:zlib';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import { buffer as stream_to_buffer } from 'node:stream/consumers';
|
|
7
7
|
import node_sea from 'node:sea';
|
|
8
8
|
import { default as Koa } from 'koa';
|
|
9
9
|
import KoaCors from '@koa/cors';
|
package/utils.common.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TimerOptions } from 'timers';
|
|
1
|
+
import type { TimerOptions } from 'node:timers';
|
|
2
2
|
import { type Mapper } from './prototype.common.ts';
|
|
3
3
|
export declare function delay(milliseconds: number, options?: TimerOptions): Promise<void>;
|
|
4
4
|
export declare function assert<T>(assertion: T, message?: string): T;
|
|
@@ -124,6 +124,13 @@ export interface Deferred<TValue> extends Promise<TValue> {
|
|
|
124
124
|
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
125
125
|
注: 下面的方法不能标记为 aysnc function, 否则会对返回值再做一层 Promise.resolve() 导致 reject, resolve 属性丢失 */
|
|
126
126
|
export declare function defer<TValue>(initial?: TValue): Deferred<TValue>;
|
|
127
|
+
export interface Deferred2<TValue> extends Promise<TValue> {
|
|
128
|
+
resolve(value: TValue | PromiseLike<TValue>): void;
|
|
129
|
+
reject(reason?: Error): void;
|
|
130
|
+
get settled(): boolean;
|
|
131
|
+
}
|
|
132
|
+
/** 有 settled 状态的 defer */
|
|
133
|
+
export declare function defer2<TValue>(initial?: TValue): Deferred2<TValue>;
|
|
127
134
|
export interface LockedAction<TResource, TResult> {
|
|
128
135
|
(resource: TResource): TResult | Promise<TResult>;
|
|
129
136
|
}
|
|
@@ -166,3 +173,13 @@ export declare function tomorrow(date?: Date | string | number): Date;
|
|
|
166
173
|
export declare function to_csv_field(str: string): string;
|
|
167
174
|
/** 设置 error.message 同时更新 error.stack */
|
|
168
175
|
export declare function set_error_message(error: Error, message: string): Error;
|
|
176
|
+
/** 比较两个数组中的元素完全相同,数组元素用引用比较 */
|
|
177
|
+
export declare function array_equals(a: any[], b: any[]): boolean;
|
|
178
|
+
export declare function nowstr(with_date?: boolean): string;
|
|
179
|
+
interface MyTimeout {
|
|
180
|
+
clear(): void;
|
|
181
|
+
ref: NodeJS.Timeout | number;
|
|
182
|
+
}
|
|
183
|
+
/** 面向对象的 timeout, callback 在后的 timeout */
|
|
184
|
+
export declare function set_timeout(delay: number, callback: VoidFunction): MyTimeout;
|
|
185
|
+
export {};
|
package/utils.common.js
CHANGED
|
@@ -396,6 +396,33 @@ export function defer(initial) {
|
|
|
396
396
|
reject: noop
|
|
397
397
|
});
|
|
398
398
|
}
|
|
399
|
+
/** 有 settled 状态的 defer */
|
|
400
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
401
|
+
export function defer2(initial) {
|
|
402
|
+
if (initial === undefined) {
|
|
403
|
+
let settled = false;
|
|
404
|
+
let { promise, resolve, reject } = Promise.withResolvers();
|
|
405
|
+
return Object.assign(promise, {
|
|
406
|
+
resolve(value) {
|
|
407
|
+
settled = true;
|
|
408
|
+
resolve(value);
|
|
409
|
+
},
|
|
410
|
+
reject(error) {
|
|
411
|
+
settled = true;
|
|
412
|
+
reject(error);
|
|
413
|
+
},
|
|
414
|
+
get settled() {
|
|
415
|
+
return settled;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
else
|
|
420
|
+
return Object.assign(Promise.resolve(initial), {
|
|
421
|
+
resolve: noop,
|
|
422
|
+
reject: noop,
|
|
423
|
+
settled: true
|
|
424
|
+
});
|
|
425
|
+
}
|
|
399
426
|
/** @example
|
|
400
427
|
let lock = new Lock(redis)
|
|
401
428
|
|
|
@@ -537,4 +564,29 @@ export function set_error_message(error, message) {
|
|
|
537
564
|
error.stack.slice_from('\n', { optional: true });
|
|
538
565
|
return error;
|
|
539
566
|
}
|
|
567
|
+
/** 比较两个数组中的元素完全相同,数组元素用引用比较 */
|
|
568
|
+
export function array_equals(a, b) {
|
|
569
|
+
if (!a && !b)
|
|
570
|
+
return true;
|
|
571
|
+
if (a && !b || !a && b)
|
|
572
|
+
return false;
|
|
573
|
+
return a.every((x, i) => x === b[i]);
|
|
574
|
+
}
|
|
575
|
+
export function nowstr(with_date = false) {
|
|
576
|
+
const date = new Date();
|
|
577
|
+
return with_date ? date.to_str() : date.to_time_str();
|
|
578
|
+
}
|
|
579
|
+
/** 面向对象的 timeout, callback 在后的 timeout */
|
|
580
|
+
export function set_timeout(delay, callback) {
|
|
581
|
+
let ret = {
|
|
582
|
+
ref: setTimeout(callback, delay),
|
|
583
|
+
clear() {
|
|
584
|
+
if (!ret.ref)
|
|
585
|
+
return;
|
|
586
|
+
clearTimeout(ret.ref);
|
|
587
|
+
ret.ref = null;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
return ret;
|
|
591
|
+
}
|
|
540
592
|
//# sourceMappingURL=utils.common.js.map
|
package/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Writable, Transform, type Readable, type Duplex, type TransformCallback } from 'stream';
|
|
2
|
-
import util from 'util';
|
|
1
|
+
import { Writable, Transform, type Readable, type Duplex, type TransformCallback } from 'node:stream';
|
|
2
|
+
import util from 'node:util';
|
|
3
3
|
import './prototype.ts';
|
|
4
4
|
import './platform.ts';
|
|
5
5
|
export * from './utils.common.ts';
|
package/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Stream, Writable, Transform } from 'stream';
|
|
2
|
-
import util from 'util';
|
|
3
|
-
import ncrypto from 'crypto';
|
|
1
|
+
import { Stream, Writable, Transform } from 'node:stream';
|
|
2
|
+
import util from 'node:util';
|
|
3
|
+
import ncrypto from 'node:crypto';
|
|
4
4
|
import "./prototype.js";
|
|
5
5
|
import "./platform.js";
|
|
6
6
|
import { assert, decode, defer, delay } from "./utils.common.js";
|