xshell 1.0.104 → 1.0.106

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/Terminal.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import 'xterm/css/xterm.css';
2
- import { Terminal as XTermTerminal } from 'xterm';
3
- import { FitAddon } from 'xterm-addon-fit';
1
+ import '@xterm/xterm/css/xterm.css';
2
+ import { Terminal as XTerminal } from '@xterm/xterm';
3
+ import { FitAddon } from '@xterm/addon-fit';
4
4
  import { Model } from 'react-object-model';
5
5
  import type { Remote } from './net.browser.js';
6
6
  export declare function Terminal({ font }: {
7
7
  font: string;
8
8
  }): import("react/jsx-runtime").JSX.Element;
9
9
  declare class TerminalModel extends Model<TerminalModel> {
10
- term: XTermTerminal;
10
+ term: XTerminal;
11
11
  fit_addon: FitAddon;
12
12
  stdio_id: number;
13
13
  fit(): void;
package/Terminal.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import 'xterm/css/xterm.css';
2
+ import '@xterm/xterm/css/xterm.css';
3
3
  import { useEffect, useRef } from 'react';
4
- import { Terminal as XTermTerminal } from 'xterm';
5
- import { FitAddon } from 'xterm-addon-fit';
6
- import { WebglAddon } from 'xterm-addon-webgl';
7
- import { WebLinksAddon } from 'xterm-addon-web-links';
4
+ import { Terminal as XTerminal } from '@xterm/xterm';
5
+ import { FitAddon } from '@xterm/addon-fit';
6
+ import { WebglAddon } from '@xterm/addon-webgl';
7
+ import { WebLinksAddon } from '@xterm/addon-web-links';
8
8
  // 没有 ui
9
9
  // import { SearchAddon } from 'xterm-addon-search'
10
10
  import { Model } from 'react-object-model';
@@ -14,7 +14,7 @@ export function Terminal({ font }) {
14
14
  useEffect(() => {
15
15
  (async () => {
16
16
  await document.fonts.ready;
17
- let term = new XTermTerminal({
17
+ let term = new XTerminal({
18
18
  fontFamily: font,
19
19
  fontSize: 16,
20
20
  cursorStyle: 'bar',
package/git.d.ts CHANGED
@@ -33,22 +33,4 @@ export declare class Git {
33
33
  checkout(branch?: 'main'): Promise<void>;
34
34
  checkout(branch?: string): Promise<void>;
35
35
  _checkout(branch?: string): Promise<void>;
36
- /** 返回是否进行了修改 */
37
- merge(branch: string, fast_forward?: boolean): Promise<boolean>;
38
- /** 记住当前分支名
39
- checkout 到 <目标分支>
40
- merge 当前分支 */
41
- merge_into(target_branch?: string): Promise<void>;
42
- /** 1. fetch 远程修改
43
- 2. checkout `<branch>` 分支
44
- 3. merge `<remote>/<branch>`
45
-
46
- 执行前需要保证工作目录 clean
47
-
48
- - branch?: `'main'` */
49
- update(branch?: 'main'): Promise<void>;
50
- update(branch: string): Promise<void>;
51
- update(branch: string, remote?: string): Promise<void>;
52
- /** - message?: stash description */
53
- stash(message?: string): Promise<import("./process.js").CallResult<string>>;
54
36
  }
package/git.js CHANGED
@@ -1,4 +1,3 @@
1
- import { assert } from './utils.js';
2
1
  import { call } from './process.js';
3
2
  import { fread, fmkdir } from './file.js';
4
3
  export class Git {
@@ -121,56 +120,5 @@ export class Git {
121
120
  throw error;
122
121
  }
123
122
  }
124
- /** 返回是否进行了修改 */
125
- async merge(branch, fast_forward = true) {
126
- console.log(`合并 ${branch} 分支`);
127
- try {
128
- const { stdout, stderr } = await this.call([
129
- 'merge',
130
- branch,
131
- ...fast_forward ? [] : ['--no-ff']
132
- ], { print: false });
133
- const unchanged = stdout === 'Already up to date.\n';
134
- if (unchanged)
135
- console.log('当前分支无需更新');
136
- else
137
- console.log(stdout.trimEnd());
138
- if (stderr)
139
- console.log(stderr);
140
- return !unchanged;
141
- }
142
- catch (error) {
143
- if (error.result) {
144
- const { stdout, stderr } = error.result;
145
- if (stdout)
146
- console.log(stdout);
147
- if (stderr)
148
- console.log(stderr);
149
- }
150
- throw error;
151
- }
152
- }
153
- /** 记住当前分支名
154
- checkout 到 <目标分支>
155
- merge 当前分支 */
156
- async merge_into(target_branch = 'main') {
157
- const branch = await this.get_branch();
158
- assert(branch !== target_branch);
159
- await this.checkout(target_branch);
160
- await this.merge(branch, true);
161
- }
162
- async update(branch = 'main', remote) {
163
- await this.fetch({ remote });
164
- await this.checkout(branch);
165
- await this.merge(`${remote || 'origin'}/${branch}`);
166
- }
167
- /** - message?: stash description */
168
- async stash(message) {
169
- return this.call([
170
- 'stash',
171
- 'push',
172
- ...message ? ['--message', message] : [],
173
- ]);
174
- }
175
123
  }
176
124
  //# sourceMappingURL=git.js.map
package/i18n/README.md CHANGED
@@ -266,7 +266,6 @@ npm i eslint eslint-plugin-i18n --save-dev
266
266
 
267
267
  ## Runtime Dependencies
268
268
  - i18next
269
- - js-cookie
270
269
  - qs
271
270
  - 使用 Trans 组件时
272
271
  - react
package/i18n/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import Cookies from 'js-cookie';
2
1
  import { default as i18next } from 'i18next';
3
2
  import { Dict } from './dict.js';
4
3
  export const LANGUAGES = ['zh', 'en', 'ja', 'ko'];
@@ -35,8 +34,7 @@ export class I18N {
35
34
  const dict = new Dict(_dict);
36
35
  if (!language && is_browser)
37
36
  language = (new URLSearchParams(location.search).get('language') ||
38
- window.language ||
39
- Cookies.get('language'));
37
+ window.language);
40
38
  if (!language)
41
39
  language = Intl.DateTimeFormat().resolvedOptions().locale.slice(0, 2);
42
40
  if (!I18N.LANGUAGE_REGEXP.test(language)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xshell",
3
- "version": "1.0.104",
3
+ "version": "1.0.106",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "interactive programming"
17
17
  ],
18
18
  "engines": {
19
- "node": ">=21.3.0",
19
+ "node": ">=22.2.0",
20
20
  "vscode": ">=1.81.0"
21
21
  },
22
22
  "scripts": {
@@ -58,9 +58,13 @@
58
58
  "@babel/traverse": "^7.24.5",
59
59
  "@koa/cors": "^5.0.0",
60
60
  "@types/ws": "^8.5.10",
61
- "@typescript-eslint/eslint-plugin": "^7.9.0",
62
- "@typescript-eslint/parser": "^7.9.0",
63
- "@typescript-eslint/utils": "^7.9.0",
61
+ "@typescript-eslint/eslint-plugin": "^7.10.0",
62
+ "@typescript-eslint/parser": "^7.10.0",
63
+ "@typescript-eslint/utils": "^7.10.0",
64
+ "@xterm/addon-fit": "^0.10.0",
65
+ "@xterm/addon-web-links": "^0.11.0",
66
+ "@xterm/addon-webgl": "^0.18.0",
67
+ "@xterm/xterm": "^5.5.0",
64
68
  "ali-oss": "^6.20.0",
65
69
  "archiver": "^7.0.1",
66
70
  "byte-size": "^8.1.1",
@@ -69,17 +73,16 @@
69
73
  "cli-table3": "^0.6.5",
70
74
  "cli-truncate": "^4.0.0",
71
75
  "colors": "^1.4.0",
72
- "commander": "^12.0.0",
76
+ "commander": "^12.1.0",
73
77
  "emoji-regex": "^10.3.0",
74
- "eslint": "^9.2.0",
78
+ "eslint": "^9.3.0",
75
79
  "eslint-plugin-import": "^2.29.1",
76
80
  "eslint-plugin-react": "^7.34.1",
77
81
  "gulp-sort": "^2.0.0",
78
82
  "hash-string": "^1.0.0",
79
83
  "https-proxy-agent": "^7.0.4",
80
- "i18next": "^23.11.4",
84
+ "i18next": "^23.11.5",
81
85
  "i18next-scanner": "^4.4.0",
82
- "js-cookie": "^3.0.5",
83
86
  "koa": "^2.15.3",
84
87
  "koa-compress": "^5.1.1",
85
88
  "lodash": "^4.17.21",
@@ -96,14 +99,10 @@
96
99
  "tslib": "^2.6.2",
97
100
  "typescript": "^5.4.5",
98
101
  "ua-parser-js": "^2.0.0-beta.2",
99
- "undici": "^6.16.1",
102
+ "undici": "^6.18.0",
100
103
  "vinyl": "^3.0.0",
101
104
  "vinyl-fs": "^4.0.0",
102
- "ws": "^8.17.0",
103
- "xterm": "^5.3.0",
104
- "xterm-addon-fit": "^0.8.0",
105
- "xterm-addon-web-links": "^0.9.0",
106
- "xterm-addon-webgl": "^0.16.0"
105
+ "ws": "^8.17.0"
107
106
  },
108
107
  "devDependencies": {
109
108
  "@babel/types": "^7.24.5",
@@ -115,10 +114,9 @@
115
114
  "@types/eslint": "^8.56.10",
116
115
  "@types/estree": "^1.0.5",
117
116
  "@types/gulp-sort": "^2.0.4",
118
- "@types/js-cookie": "^3.0.6",
119
117
  "@types/koa": "^2.15.0",
120
118
  "@types/koa-compress": "^4.0.6",
121
- "@types/lodash": "^4.17.1",
119
+ "@types/lodash": "^4.17.4",
122
120
  "@types/mime-types": "^2.1.4",
123
121
  "@types/node": "^20.12.12",
124
122
  "@types/react": "^18.3.2",
package/process.d.ts CHANGED
@@ -135,6 +135,6 @@ export interface TermNodeOptions extends TermOptions {
135
135
  /** `false` --trace-warnings */
136
136
  trace_warnings?: boolean;
137
137
  }
138
- /** 在 term tab 中创建 node.exe 进程 create the node.exe process in term tab */
139
- export declare function term_nodejs(fp_js: string, args?: string[], { title, inspect, tsnode, break: _break, resolve, trace_warnings, ..._options }?: TermNodeOptions): Promise<ChildProcess>;
138
+ /** 在 term tab 中创建 node.exe 进程 */
139
+ export declare function term_nodejs(fp_js: string, args?: string[], { title, inspect, tsnode, break: _break, resolve, trace_warnings, ...options }?: TermNodeOptions): Promise<ChildProcess>;
140
140
  export {};
package/process.js CHANGED
@@ -178,9 +178,7 @@ export async function call(exe, args = [], options = {}) {
178
178
  - throw_code?: `true` code 不为 0 时是否抛出异常 whether to throw Error when code is not 0 */
179
179
  export async function call_nodejs(js, args = [], { inspect, break: _break, ...options } = {}) {
180
180
  return call(exe_nodejs, [
181
- ...inspect ? [
182
- _break ? `--inspect-brk=localhost:${inspect}` : `--inspect=localhost:${inspect}`
183
- ] : [],
181
+ ...inspect ? [`--inspect${_break ? '-brk' : ''}=localhost:${inspect}`] : [],
184
182
  js,
185
183
  ...args
186
184
  ], options);
@@ -204,15 +202,13 @@ export async function term(exe, args = [], { cwd = process.cwd().fpd, print = tr
204
202
  envs
205
203
  });
206
204
  }
207
- /** 在 term tab 中创建 node.exe 进程 create the node.exe process in term tab */
208
- export async function term_nodejs(fp_js, args = [], { title, inspect, tsnode = false, break: _break, resolve = false, trace_warnings = false, ..._options } = {}) {
205
+ /** 在 term tab 中创建 node.exe 进程 */
206
+ export async function term_nodejs(fp_js, args = [], { title, inspect, tsnode = false, break: _break, resolve = false, trace_warnings = false, ...options } = {}) {
209
207
  return term(exe_nodejs, [
210
208
  ...trace_warnings ? ['--trace-warnings'] : [],
211
209
  ...resolve ? ['--experimental-specifier-resolution=node'] : [],
212
210
  ...title ? [`--title=${title}`] : [],
213
- ...inspect ? [
214
- _break ? `--inspect-brk=localhost:${inspect}` : `--inspect=localhost:${inspect}`
215
- ] : [],
211
+ ...inspect ? [`--inspect${_break ? '-brk' : ''}=localhost:${inspect}`] : [],
216
212
  ...tsnode ? [
217
213
  '--loader=ts-node/esm',
218
214
  '--experimental-specifier-resolution=node'
@@ -221,7 +217,7 @@ export async function term_nodejs(fp_js, args = [], { title, inspect, tsnode = f
221
217
  ...args
222
218
  ], {
223
219
  title,
224
- ..._options,
220
+ ...options,
225
221
  });
226
222
  }
227
223
  const short_exe_names = {
@@ -75,10 +75,14 @@ declare global {
75
75
  text: string;
76
76
  };
77
77
  space(this: string): string;
78
- /** assert 确保字符串以 prefix 开头(失败时抛出错误),并返回去掉该 prefix 的字符串 */
79
- strip_start(this: string, prefix: string): string;
80
- /** assert 确保字符串以 suffix 结尾(失败时抛出错误),并返回去掉该 suffix 的字符串 */
81
- strip_end(this: string, suffix: string): string;
78
+ /** 返回去掉 prefix 开头的字符串,可选 validate = true 确保字符串以 prefix 开头(失败时抛出错误) */
79
+ strip_start(this: string, prefix: string, validate?: boolean): string;
80
+ /** 返回去掉 prefix 开头的字符串,如果没有以 prefix 开头则返回原字符串 */
81
+ strip_if_start(this: string, prefix: string): string;
82
+ /** 返回去掉 suffix 结尾的字符串,可选 validate = true 确保字符串以 suffix 结尾(失败时抛出错误) */
83
+ strip_end(this: string, suffix: string, validate?: boolean): string;
84
+ /** 返回去掉 suffix 结尾的字符串,如果没有以 suffix 结尾则返回原字符串 */
85
+ strip_if_end(this: string, suffix: string): string;
82
86
  /** 等价于 .endsWith('/') */
83
87
  isdir: boolean;
84
88
  /** 以 `/` 分割的路径,可能以 / 结尾 */
@@ -259,17 +259,21 @@ Object.defineProperties(String.prototype, {
259
259
  text: this.slice(i)
260
260
  };
261
261
  },
262
- strip_start(prefix) {
263
- if (this.startsWith(prefix))
264
- return this.slice(prefix.length);
265
- else
262
+ strip_start(prefix, validate) {
263
+ if (validate && !this.startsWith(prefix))
266
264
  throw new Error(`字符串没有以前缀 ${prefix} 开头: ${this}`);
265
+ return this.slice(prefix.length);
267
266
  },
268
- strip_end(suffix) {
269
- if (this.endsWith(suffix))
270
- return this.slice(0, -suffix.length);
271
- else
267
+ strip_if_start(prefix) {
268
+ return this.startsWith(prefix) ? this.slice(prefix.length) : this;
269
+ },
270
+ strip_end(suffix, validate) {
271
+ if (validate && !this.endsWith(suffix))
272
272
  throw new Error(`字符串没有以后缀 ${suffix} 结尾: ${this}`);
273
+ return this.slice(0, -suffix.length);
274
+ },
275
+ strip_if_end(suffix) {
276
+ return this.endsWith(suffix) ? this.slice(0, -suffix.length) : this;
273
277
  },
274
278
  space() {
275
279
  if (!this)
package/prototype.d.ts CHANGED
@@ -98,10 +98,14 @@ declare global {
98
98
  decode_base64(this: string, buffer: true): Buffer;
99
99
  decode_base64(this: string, buffer?: boolean): string | Buffer;
100
100
  space(this: string): string;
101
- /** assert 确保字符串以 prefix 开头(失败时抛出错误),并返回去掉该 prefix 的字符串 */
102
- strip_start(this: string, prefix: string): string;
103
- /** assert 确保字符串以 suffix 结尾(失败时抛出错误),并返回去掉该 suffix 的字符串 */
104
- strip_end(this: string, suffix: string): string;
101
+ /** 返回去掉 prefix 开头的字符串,可选 validate = true 确保字符串以 prefix 开头(失败时抛出错误) */
102
+ strip_start(this: string, prefix: string, validate?: boolean): string;
103
+ /** 返回去掉 prefix 开头的字符串,如果没有以 prefix 开头则返回原字符串 */
104
+ strip_if_start(this: string, prefix: string): string;
105
+ /** 返回去掉 suffix 结尾的字符串,可选 validate = true 确保字符串以 suffix 结尾(失败时抛出错误) */
106
+ strip_end(this: string, suffix: string, validate?: boolean): string;
107
+ /** 返回去掉 suffix 结尾的字符串,如果没有以 suffix 结尾则返回原字符串 */
108
+ strip_if_end(this: string, suffix: string): string;
105
109
  /** 等价于 .endsWith('/') */
106
110
  isdir: boolean;
107
111
  /** 以 `/` 分割的路径,可能以 / 结尾 */
package/prototype.js CHANGED
@@ -278,17 +278,21 @@ if (!globalThis.my_prototype_defined) {
278
278
  strip_ansi() {
279
279
  return strip_ansi(this);
280
280
  },
281
- strip_start(prefix) {
282
- if (this.startsWith(prefix))
283
- return this.slice(prefix.length);
284
- else
281
+ strip_start(prefix, validate) {
282
+ if (validate && !this.startsWith(prefix))
285
283
  throw new Error(`字符串没有以前缀 ${prefix} 开头: ${this}`);
284
+ return this.slice(prefix.length);
286
285
  },
287
- strip_end(suffix) {
288
- if (this.endsWith(suffix))
289
- return this.slice(0, -suffix.length);
290
- else
286
+ strip_if_start(prefix) {
287
+ return this.startsWith(prefix) ? this.slice(prefix.length) : this;
288
+ },
289
+ strip_end(suffix, validate) {
290
+ if (validate && !this.endsWith(suffix))
291
291
  throw new Error(`字符串没有以后缀 ${suffix} 结尾: ${this}`);
292
+ return this.slice(0, -suffix.length);
293
+ },
294
+ strip_if_end(suffix) {
295
+ return this.endsWith(suffix) ? this.slice(0, -suffix.length) : this;
292
296
  },
293
297
  space() {
294
298
  if (!this)
package/server.d.ts CHANGED
@@ -33,6 +33,7 @@ export declare class Server {
33
33
  name: string;
34
34
  UAParser: typeof UAParser;
35
35
  js_exts: Set<string>;
36
+ empty_body_statuses: Set<number>;
36
37
  app: Koa;
37
38
  handler: ReturnType<Koa['callback']>;
38
39
  /** 启用 http server */
@@ -111,7 +112,7 @@ export declare class Server {
111
112
  await this.try_send(
112
113
  ctx,
113
114
  `T:/t/docs${prefix_docs}`,
114
- (path.endsWith('/') ? `${path}index.html` : path).slice(prefix_docs.length),
115
+ (path.endsWith('/') ? `${path}index.html` : path).strip_start(prefix_docs),
115
116
  true
116
117
  )
117
118
  return
package/server.js CHANGED
@@ -8,7 +8,7 @@ import util from 'util';
8
8
  // --- my libs
9
9
  import { t } from './i18n/instance.js';
10
10
  import { request as _request, Remote } from './net.js';
11
- import { inspect, output_width, assert, range_to_numbers, encode, filter_keys, filter_values } from './utils.js';
11
+ import { inspect, output_width, assert, range_to_numbers, encode, filter_keys, filter_values, consume_stream } from './utils.js';
12
12
  import { flist, fread, fstat } from './file.js';
13
13
  // ------------ my server
14
14
  export class Server {
@@ -24,6 +24,7 @@ export class Server {
24
24
  name;
25
25
  UAParser;
26
26
  js_exts = new Set(['.js', '.mjs', '.cjs']);
27
+ empty_body_statuses = new Set([304, 204, 205]);
27
28
  app;
28
29
  handler;
29
30
  /** 启用 http server */
@@ -109,13 +110,13 @@ export class Server {
109
110
  // fpd_certs 文件夹下面的每个 .key 对应一个证书及域名
110
111
  (await flist(fpd_certs, { print: false, filter: /\.key$/ }))
111
112
  .map(async (fname) => {
112
- let domain = fname.slice(0, -'.key'.length);
113
+ let domain = fname.strip_end('.key');
113
114
  const [key, cert] = await Promise.all([
114
115
  fname,
115
116
  `${domain}.crt`,
116
117
  ].map(async (fname) => fread(`${fpd_certs}${fname}`, { print: false })));
117
118
  if (domain.startsWith('star.'))
118
- domain = `*.${domain.slice('star.'.length)}`;
119
+ domain = `*.${domain.strip_start('star.')}`;
119
120
  return [domain, { key, cert }];
120
121
  })));
121
122
  const default_ctx = lazy_secure_ctxs[this.default_hostnames.find(hostname => hostname in lazy_secure_ctxs)];
@@ -124,6 +125,7 @@ export class Server {
124
125
  SNICallback(servername, callback) {
125
126
  let lazy_ctx = lazy_secure_ctxs[servername] ||
126
127
  lazy_secure_ctxs[servername.replace(/^.*?\./, '*.')] ||
128
+ lazy_secure_ctxs[`*.${servername}`] ||
127
129
  default_ctx;
128
130
  callback(null, lazy_ctx.ctx ??= createSecureContext(lazy_ctx));
129
131
  },
@@ -379,9 +381,8 @@ export class Server {
379
381
  // query
380
382
  if (Object.keys(query).length) {
381
383
  let t = inspect(query, { compact: true })
382
- .replace('[Object: null prototype] ', '');
383
- if (t.endsWith('\n'))
384
- t = t.slice(0, -1);
384
+ .replace('[Object: null prototype] ', '')
385
+ .strip_if_end('\n');
385
386
  s += (s + t).width > output_width ? '\n' : ' ';
386
387
  s += t;
387
388
  }
@@ -472,7 +473,17 @@ export class Server {
472
473
  response.status = response_.status;
473
474
  response.set(Server.filter_response_headers(response_.headers));
474
475
  if (response_.body)
475
- response.body = await stream_to_buffer(response_.body);
476
+ // 如果设置了 response.status 304 等状态码,koa/application.js#respond() 方法中会执行
477
+ // ctx.body = null
478
+ // response.res.end()
479
+ // 而 res.end() 会触发 res 流的 finish, 然后由于之前 response.body = response_.body 这条语句
480
+ // 关联了 response_.body 到 res 的 finish 事件,(koa/response.js#set body() 中 onFinish(res, destroy.bind(null, response_.body))
481
+ // 会让 response_.body 这个 UndiciBodyReadable 被提前 finish,而此时
482
+ // 流还没 emit end, 会触发错误 RequestAbortedError [AbortError]: Request aborted
483
+ if (this.empty_body_statuses.has(response_.status))
484
+ consume_stream(response_.body, true);
485
+ else
486
+ response.body = response_.body;
476
487
  }
477
488
  catch (error) {
478
489
  if (error.response?.status !== 404)
@@ -505,7 +516,7 @@ export class Server {
505
516
  await this.try_send(
506
517
  ctx,
507
518
  `T:/t/docs${prefix_docs}`,
508
- (path.endsWith('/') ? `${path}index.html` : path).slice(prefix_docs.length),
519
+ (path.endsWith('/') ? `${path}index.html` : path).strip_start(prefix_docs),
509
520
  true
510
521
  )
511
522
  return
@@ -545,10 +556,7 @@ export class Server {
545
556
  const { request, request: { method } } = ctx;
546
557
  let { response } = ctx;
547
558
  if (!absolute) {
548
- if (fp.startsWith(root))
549
- fp = fp.slice(root.length);
550
- if (fp.startsWith('/'))
551
- fp = fp.slice(1);
559
+ fp = fp.strip_if_start(root).strip_if_start('/');
552
560
  const { default: resolve_safely } = await import('resolve-path');
553
561
  try {
554
562
  fp = resolve_safely(root, fp).fp;
package/utils.d.ts CHANGED
@@ -34,7 +34,7 @@ export declare function map_values<TValue, TNewValue>(obj: {
34
34
  };
35
35
  /** 过滤对象中的 keys, 返回新对象 */
36
36
  export declare function filter_keys<TObj>(obj: TObj, filter: (key: string) => any): TObj;
37
- /** 过滤对象中的 valus, 返回新对象 */
37
+ /** 过滤对象中的 values, 返回新对象 */
38
38
  export declare function filter_values<TObj extends Record<string, any>>(obj: TObj, filter?: (value: TObj[string]) => any): TObj;
39
39
  /** 忽略对象中的 keys, 返回新对象 */
40
40
  export declare function omit<TObj>(obj: TObj, omit_keys: string[]): TObj;
@@ -147,7 +147,7 @@ export declare function map_stream<Out, In = Vinyl>(mapper: (obj: In, cb: Functi
147
147
  failures?: boolean;
148
148
  }): Duplex;
149
149
  export declare function stream_to_lines(stream: Readable): AsyncGenerator<string, void, unknown>;
150
- export declare function pipe_with_error(readable: Readable, transform: Transform): Transform;
150
+ export declare function pipe_with_error<TWritable extends Writable>(readable: Readable, writable: TWritable): TWritable;
151
151
  export declare class WritableMemoryStream extends Writable {
152
152
  chunks: Buffer[];
153
153
  pbuffer: Deferred<Buffer>;
@@ -168,6 +168,8 @@ export declare class DecoderStream extends Transform {
168
168
  _transform(chunk: Uint8Array, encoding: BufferEncoding, callback: TransformCallback): void;
169
169
  _flush(callback: TransformCallback): void;
170
170
  }
171
+ /** 消费一个可读流 */
172
+ export declare function consume_stream(stream: Readable, ignore_error?: boolean): Promise<void>;
171
173
  /** 根据 range 生成整数序列 (iterable)
172
174
  - range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
173
175
  - reverse?: `false` 在 range 内从后往前生成 */
package/utils.js CHANGED
@@ -80,7 +80,7 @@ export function filter_keys(obj, filter) {
80
80
  return Object.fromEntries(Object.entries(obj)
81
81
  .filter(([key]) => filter(key)));
82
82
  }
83
- /** 过滤对象中的 valus, 返回新对象 */
83
+ /** 过滤对象中的 values, 返回新对象 */
84
84
  export function filter_values(obj, filter = not_empty) {
85
85
  return Object.fromEntries(Object.entries(obj)
86
86
  .filter(([, value]) => filter(value)));
@@ -218,18 +218,13 @@ export async function timeout(milliseconds, action) {
218
218
  // eslint-disable-next-line @typescript-eslint/promise-function-async
219
219
  export function defer(initial) {
220
220
  if (initial === undefined) {
221
- let resolve;
222
- let reject;
223
- let promise = new Promise((_resolve, _reject) => {
224
- resolve = _resolve;
225
- reject = _reject;
226
- });
221
+ let { promise, resolve, reject } = Promise.withResolvers();
227
222
  return Object.assign(promise, { resolve, reject });
228
223
  }
229
224
  else
230
225
  return Object.assign(Promise.resolve(initial), {
231
- resolve() { },
232
- reject() { }
226
+ resolve: noop,
227
+ reject: noop
233
228
  });
234
229
  }
235
230
  /** @example
@@ -476,10 +471,10 @@ export async function* stream_to_lines(stream) {
476
471
  buf = chunk.slice(j);
477
472
  }
478
473
  }
479
- export function pipe_with_error(readable, transform) {
474
+ export function pipe_with_error(readable, writable) {
480
475
  // 不知道 transform 作为 AsyncIterable 使用时, emit error 是否会在 for await (...) 循环中触发错误
481
- readable.once('error', error => { transform.emit('error', error); });
482
- return readable.pipe(transform);
476
+ readable.once('error', error => { writable.emit('error', error); });
477
+ return readable.pipe(writable);
483
478
  }
484
479
  export class WritableMemoryStream extends Writable {
485
480
  chunks = [];
@@ -525,6 +520,17 @@ export class DecoderStream extends Transform {
525
520
  callback();
526
521
  }
527
522
  }
523
+ /** 消费一个可读流 */
524
+ export async function consume_stream(stream, ignore_error = false) {
525
+ try {
526
+ for await (const chunk of stream)
527
+ ;
528
+ }
529
+ catch (error) {
530
+ if (!ignore_error)
531
+ throw error;
532
+ }
533
+ }
528
534
  /** 根据 range 生成整数序列 (iterable)
529
535
  - range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
530
536
  - reverse?: `false` 在 range 内从后往前生成 */