xshell 1.0.72 → 1.0.74

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.d.ts CHANGED
@@ -105,8 +105,9 @@ export declare function ffstat(handle: fsp.FileHandle): Promise<fs.BigIntStats>;
105
105
 
106
106
  返回是否实际进行了删除操作
107
107
  Returns whether the delete operation actually took place */
108
- export declare function fdelete(fp: string, { print }?: {
108
+ export declare function fdelete(fp: string, { print, red }?: {
109
109
  print?: boolean;
110
+ red?: boolean;
110
111
  }): Promise<boolean>;
111
112
  /** 复制文件或文件夹
112
113
  会在因不存在父文件夹导致复制失败时,自动创建父文件夹,并再次尝试复制
package/file.js CHANGED
@@ -194,25 +194,22 @@ export async function ffstat(handle) {
194
194
 
195
195
  返回是否实际进行了删除操作
196
196
  Returns whether the delete operation actually took place */
197
- export async function fdelete(fp, { print = true } = {}) {
197
+ export async function fdelete(fp, { print = true, red = true } = {}) {
198
198
  assert(fp.length >= 6, `fp: ${fp} ${t('不能太短,防止误删文件')}`);
199
199
  assert(path.isAbsolute(fp), t('fp 必须是绝对路径'));
200
+ const { isdir } = fp;
200
201
  try {
201
202
  await fsp.rm(fp, { recursive: true });
202
- if (print)
203
- if (fp.isdir)
204
- console.log((t('删除了文件夹: ') + fp).red);
205
- else
206
- console.log((t('删除了文件: ') + fp).red);
203
+ if (print) {
204
+ const message = `${isdir ? t('删除了文件夹') : t('删除了文件')} ${fp}`;
205
+ console.log(red ? message.red : message);
206
+ }
207
207
  return true;
208
208
  }
209
209
  catch (error) {
210
210
  if (error.code === 'ENOENT') {
211
211
  if (print)
212
- if (fp.isdir)
213
- console.log(t('文件夹已不存在: ') + fp);
214
- else
215
- console.log(t('文件已不存在: ') + fp);
212
+ console.log(`${isdir ? t(`已不存在文件`) : t('已不存在文件夹')} ${fp}`);
216
213
  return false;
217
214
  }
218
215
  throw error;
package/i18n/dict.json CHANGED
@@ -361,5 +361,17 @@
361
361
  },
362
362
  "{{name}} 启动成功,正在监听 {{ports}} 端口": {
363
363
  "en": "{{name}} started successfully and is listening on {{ports}} port"
364
+ },
365
+ "删除了文件夹": {
366
+ "en": "deleted folder"
367
+ },
368
+ "删除了文件": {
369
+ "en": "deleted file"
370
+ },
371
+ "已不存在文件": {
372
+ "en": "file no longer exists:"
373
+ },
374
+ "已不存在文件夹": {
375
+ "en": "folder no longer exists:"
364
376
  }
365
377
  }
package/i18n/i18n-scan.js CHANGED
File without changes
package/net.browser.d.ts CHANGED
@@ -98,7 +98,7 @@ export declare function connect_websocket(url: string | URL, { protocols, on_mes
98
98
  - 数组: 会自动被封装为 { id: 相同, data: 返回值, done: true } 这样的消息并调用 websocket.send 将其发送
99
99
  - void: 什么都不做
100
100
  - 以上的 promise */
101
- export type MessageHandler = (message: Message, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
101
+ export type MessageHandler<TData extends any[] = any[]> = (message: Message<TData>, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
102
102
  /** 二进制消息格式
103
103
  - json.length (小端序): 4 字节
104
104
  - json 数据
@@ -162,7 +162,7 @@ export declare class Remote {
162
162
  funcs: Record<string, MessageHandler>;
163
163
  /** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
164
164
  一元 rpc 接收方不需要设置 handlers, 发送方需要 */
165
- handlers: Map<number, MessageHandler>;
165
+ handlers: Map<number, MessageHandler<any[]>>;
166
166
  /** `false` 打印所有交互的 rpc messages */
167
167
  print: boolean;
168
168
  reconnecting: boolean;
package/net.browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t } from './i18n/instance.js';
2
2
  import './prototype.browser.js'; // to_time_str()
3
- import { assert, concat, genid, delay, Lock } from './utils.browser.js';
3
+ import { assert, concat, genid, delay, Lock, encode, decode } from './utils.browser.js';
4
4
  // ------------------------------------ fetch, request
5
5
  const drop_request_headers = new Set([
6
6
  // : 开头的 key
@@ -143,8 +143,6 @@ export async function request_json(url, options) {
143
143
  throw error;
144
144
  }
145
145
  }
146
- let decoder = new TextDecoder();
147
- let encoder = new TextEncoder();
148
146
  export class WebSocketConnectionError extends Error {
149
147
  name = 'WebSocketConnectionError';
150
148
  // 这里不保留 websocket 引用,防止循环引用导致 JSON 序列化失败
@@ -280,7 +278,7 @@ export class Remote {
280
278
  const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
281
279
  const len_json = dv.getUint32(0, true);
282
280
  let offset = 4 + len_json;
283
- let message = JSON.parse(decoder.decode(buffer.subarray(4, offset)));
281
+ let message = JSON.parse(decode(buffer.subarray(4, offset)));
284
282
  if (message.bins) {
285
283
  const { bins } = message;
286
284
  let { data } = message;
@@ -318,7 +316,7 @@ export class Remote {
318
316
  ...bins.length ? { bins } : {},
319
317
  };
320
318
  // 有可能 data_json 含有循环引用导致 JSON.stringify 报错
321
- const str_json = encoder.encode(JSON.stringify(data_json));
319
+ const str_json = encode(JSON.stringify(data_json));
322
320
  let dv = new DataView(new ArrayBuffer(4));
323
321
  dv.setUint32(0, str_json.length, true);
324
322
  return concat([dv, str_json, ...bufs]);
package/net.d.ts CHANGED
@@ -150,7 +150,7 @@ on_message, on_error, on_close }: {
150
150
  - 数组: 会自动被封装为 { id: 相同, data: 返回值, done: true } 这样的消息并调用 websocket.send 将其发送
151
151
  - void: 什么都不做
152
152
  - 以上的 promise */
153
- export type MessageHandler = (message: Message, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
153
+ export type MessageHandler<TData extends any[] = any[]> = (message: Message<TData>, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
154
154
  /** 二进制消息格式
155
155
  - json.length (小端序): 4 字节
156
156
  - json 数据
@@ -214,7 +214,7 @@ export declare class Remote {
214
214
  funcs: Record<string, MessageHandler>;
215
215
  /** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
216
216
  一元 rpc 接收方不需要设置 handlers, 发送方需要 */
217
- handlers: Map<number, MessageHandler>;
217
+ handlers: Map<number, MessageHandler<any[]>>;
218
218
  /** `false` 打印所有交互的 rpc messages */
219
219
  print: boolean;
220
220
  reconnecting: boolean;
package/net.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t } from './i18n/instance.js';
2
2
  import './prototype.js';
3
- import { inspect, concat, assert, genid, delay, Lock } from './utils.js';
3
+ import { inspect, concat, assert, genid, delay, Lock, encode, decode } from './utils.js';
4
4
  export const WebSocketConnecting = 0;
5
5
  export const WebSocketOpen = 1;
6
6
  export const WebSocketClosing = 2;
@@ -286,8 +286,6 @@ export async function rpc(func, args, { url = 'http://localhost:8421/api/rpc', a
286
286
  }
287
287
  });
288
288
  }
289
- let decoder = new TextDecoder();
290
- let encoder = new TextEncoder();
291
289
  export class WebSocketConnectionError extends Error {
292
290
  name = 'WebSocketConnectionError';
293
291
  // 这里不保留 websocket 引用,防止循环引用导致 JSON 序列化失败
@@ -443,7 +441,7 @@ export class Remote {
443
441
  const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
444
442
  const len_json = dv.getUint32(0, true);
445
443
  let offset = 4 + len_json;
446
- let message = JSON.parse(decoder.decode(buffer.subarray(4, offset)));
444
+ let message = JSON.parse(decode(buffer.subarray(4, offset)));
447
445
  if (message.bins) {
448
446
  const { bins } = message;
449
447
  let { data } = message;
@@ -481,7 +479,7 @@ export class Remote {
481
479
  ...bins.length ? { bins } : {},
482
480
  };
483
481
  // 有可能 data_json 含有循环引用导致 JSON.stringify 报错
484
- const str_json = encoder.encode(JSON.stringify(data_json));
482
+ const str_json = encode(JSON.stringify(data_json));
485
483
  let dv = new DataView(new ArrayBuffer(4));
486
484
  dv.setUint32(0, str_json.length, true);
487
485
  return concat([dv, str_json, ...bufs]);
package/package.json CHANGED
@@ -1,128 +1,134 @@
1
1
  {
2
- "name": "xshell",
3
- "version": "1.0.72",
4
- "type": "module",
5
- "main": "./index.js",
6
- "bin": {
7
- "xshell": "./xshell.js",
8
- "i18n-scan": "./i18n/i18n-scan.js"
9
- },
10
- "description": "xshell is a shell designed to provide a brand new human-computer interaction experience.",
11
- "keywords": [
12
- "shell",
13
- "node",
14
- "repl",
15
- "i18n",
16
- "interactive programming"
17
- ],
18
- "engines": {
19
- "node": ">=21.3.0",
20
- "vscode": ">=1.81.0"
21
- },
22
- "author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
23
- "publisher": "ShenHongFei",
24
- "homepage": "https://www.npmjs.com/package/xshell",
25
- "icon": "xshell.png",
26
- "extensionKind": [
27
- "workspace"
28
- ],
29
- "activationEvents": [
30
- "onStartupFinished"
31
- ],
32
- "contributes": {
33
- "commands": [
34
- {
35
- "command": "xshell_repl",
36
- "title": "xshell_repl"
37
- }
2
+ "name": "xshell",
3
+ "version": "1.0.74",
4
+ "type": "module",
5
+ "main": "./index.js",
6
+ "bin": {
7
+ "xshell": "./xshell.js",
8
+ "i18n-scan": "./i18n/i18n-scan.js"
9
+ },
10
+ "description": "xshell is a shell designed to provide a brand new human-computer interaction experience.",
11
+ "keywords": [
12
+ "shell",
13
+ "node",
14
+ "repl",
15
+ "i18n",
16
+ "interactive programming"
38
17
  ],
39
- "keybindings": [
40
- {
41
- "command": "xshell_repl",
42
- "key": "ctrl+enter",
43
- "when": "editorTextFocus && (editorLangId == 'typescript' || editorLangId == 'javascript' || editorLangId == 'typescriptreact' || editorLangId == 'javascriptreact')"
44
- }
45
- ]
46
- },
47
- "dependencies": {
48
- "@babel/core": "^7.23.6",
49
- "@babel/parser": "^7.23.6",
50
- "@babel/traverse": "^7.23.6",
51
- "@koa/cors": "^5.0.0",
52
- "@types/ws": "^8.5.10",
53
- "ali-oss": "^6.18.1",
54
- "archiver": "^6.0.1",
55
- "byte-size": "^8.1.1",
56
- "chalk": "^5.3.0",
57
- "chardet": "^2.0.0",
58
- "cli-table3": "^0.6.3",
59
- "cli-truncate": "^4.0.0",
60
- "colors": "^1.4.0",
61
- "commander": "^11.1.0",
62
- "emoji-regex": "^10.3.0",
63
- "fetch-cookie": "^2.1.0",
64
- "gulp-sort": "^2.0.0",
65
- "hash-string": "^1.0.0",
66
- "i18next": "^23.7.9",
67
- "i18next-scanner": "^4.4.0",
68
- "js-cookie": "^3.0.5",
69
- "koa": "^2.14.2",
70
- "koa-compress": "^5.1.1",
71
- "lodash": "^4.17.21",
72
- "map-stream": "0.0.7",
73
- "mime-types": "^2.1.35",
74
- "ora": "^7.0.1",
75
- "react": "^18.2.0",
76
- "react-i18next": "^13.5.0",
77
- "react-object-model": "^1.2.1",
78
- "resolve-path": "^1.4.0",
79
- "strip-ansi": "^7.1.0",
80
- "through2": "^4.0.2",
81
- "tough-cookie": "^4.1.3",
82
- "tslib": "^2.6.2",
83
- "typescript": "^5.3.3",
84
- "ua-parser-js": "2.0.0-alpha.2",
85
- "undici": "^6.0.1",
86
- "vinyl": "^3.0.0",
87
- "vinyl-fs": "^4.0.0",
88
- "ws": "^8.15.1",
89
- "xterm": "^5.3.0",
90
- "xterm-addon-fit": "^0.8.0",
91
- "xterm-addon-web-links": "^0.9.0",
92
- "xterm-addon-webgl": "^0.16.0"
93
- },
94
- "devDependencies": {
95
- "@babel/types": "^7.23.6",
96
- "@types/ali-oss": "^6.16.11",
97
- "@types/archiver": "^6.0.2",
98
- "@types/babel__traverse": "^7.20.4",
99
- "@types/byte-size": "^8.1.2",
100
- "@types/chardet": "^0.8.3",
101
- "@types/gulp-sort": "2.0.4",
102
- "@types/js-cookie": "^3.0.6",
103
- "@types/koa": "^2.13.12",
104
- "@types/koa-compress": "^4.0.6",
105
- "@types/lodash": "^4.14.202",
106
- "@types/mime-types": "^2.1.4",
107
- "@types/node": "^20.10.4",
108
- "@types/react": "^18.2.45",
109
- "@types/through2": "^2.0.41",
110
- "@types/tough-cookie": "^4.0.5",
111
- "@types/ua-parser-js": "^0.7.39",
112
- "@types/vinyl-fs": "^3.0.5",
113
- "@types/vscode": "^1.85.0",
114
- "@typescript-eslint/eslint-plugin": "^6.14.0",
115
- "@typescript-eslint/parser": "^6.14.0",
116
- "eslint": "^8.55.0",
117
- "eslint-plugin-react": "^7.33.2",
118
- "eslint-plugin-xlint": "^1.0.11"
119
- },
120
- "scripts": {
121
- "start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
122
- "typecheck": "tsc --noEmit",
123
- "build": "tsc",
124
- "package": "vsce package",
125
- "publish:npm": "npm publish --access=public",
126
- "publish:extension": "vsce publish"
127
- }
128
- }
18
+ "engines": {
19
+ "node": ">=21.3.0",
20
+ "vscode": ">=1.81.0"
21
+ },
22
+ "scripts": {
23
+ "start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
24
+ "typecheck": "tsc --noEmit",
25
+ "build": "tsc",
26
+ "package": "vsce package",
27
+ "publish:npm": "npm publish --access=public",
28
+ "publish:extension": "vsce publish"
29
+ },
30
+ "author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
31
+ "publisher": "ShenHongFei",
32
+ "homepage": "https://www.npmjs.com/package/xshell",
33
+ "icon": "xshell.png",
34
+ "extensionKind": [
35
+ "workspace"
36
+ ],
37
+ "activationEvents": [
38
+ "onStartupFinished"
39
+ ],
40
+ "contributes": {
41
+ "commands": [
42
+ {
43
+ "command": "xshell_repl",
44
+ "title": "xshell_repl"
45
+ }
46
+ ],
47
+ "keybindings": [
48
+ {
49
+ "command": "xshell_repl",
50
+ "key": "ctrl+enter",
51
+ "when": "editorTextFocus && (editorLangId == 'typescript' || editorLangId == 'javascript' || editorLangId == 'typescriptreact' || editorLangId == 'javascriptreact')"
52
+ }
53
+ ]
54
+ },
55
+ "dependencies": {
56
+ "@babel/core": "^7.23.6",
57
+ "@babel/parser": "^7.23.6",
58
+ "@babel/traverse": "^7.23.6",
59
+ "@koa/cors": "^5.0.0",
60
+ "@types/ws": "^8.5.10",
61
+ "ali-oss": "^6.18.1",
62
+ "archiver": "^6.0.1",
63
+ "byte-size": "^8.1.1",
64
+ "chalk": "^5.3.0",
65
+ "chardet": "^2.0.0",
66
+ "cli-table3": "^0.6.3",
67
+ "cli-truncate": "^4.0.0",
68
+ "colors": "^1.4.0",
69
+ "commander": "^11.1.0",
70
+ "emoji-regex": "^10.3.0",
71
+ "fetch-cookie": "^2.1.0",
72
+ "gulp-sort": "^2.0.0",
73
+ "hash-string": "^1.0.0",
74
+ "i18next": "^23.7.9",
75
+ "i18next-scanner": "^4.4.0",
76
+ "js-cookie": "^3.0.5",
77
+ "koa": "^2.14.2",
78
+ "koa-compress": "^5.1.1",
79
+ "lodash": "^4.17.21",
80
+ "map-stream": "0.0.7",
81
+ "mime-types": "^2.1.35",
82
+ "ora": "^7.0.1",
83
+ "react": "^18.2.0",
84
+ "react-i18next": "^13.5.0",
85
+ "react-object-model": "^1.2.1",
86
+ "resolve-path": "^1.4.0",
87
+ "strip-ansi": "^7.1.0",
88
+ "through2": "^4.0.2",
89
+ "tough-cookie": "^4.1.3",
90
+ "tslib": "^2.6.2",
91
+ "typescript": "^5.3.3",
92
+ "ua-parser-js": "2.0.0-alpha.2",
93
+ "undici": "^6.0.1",
94
+ "vinyl": "^3.0.0",
95
+ "vinyl-fs": "^4.0.0",
96
+ "ws": "^8.15.1",
97
+ "xterm": "^5.3.0",
98
+ "xterm-addon-fit": "^0.8.0",
99
+ "xterm-addon-web-links": "^0.9.0",
100
+ "xterm-addon-webgl": "^0.16.0"
101
+ },
102
+ "devDependencies": {
103
+ "@babel/types": "^7.23.6",
104
+ "@types/ali-oss": "^6.16.11",
105
+ "@types/archiver": "^6.0.2",
106
+ "@types/babel__traverse": "^7.20.4",
107
+ "@types/byte-size": "^8.1.2",
108
+ "@types/chardet": "^0.8.3",
109
+ "@types/gulp-sort": "2.0.4",
110
+ "@types/js-cookie": "^3.0.6",
111
+ "@types/koa": "^2.13.12",
112
+ "@types/koa-compress": "^4.0.6",
113
+ "@types/lodash": "^4.14.202",
114
+ "@types/mime-types": "^2.1.4",
115
+ "@types/node": "^20.10.4",
116
+ "@types/react": "^18.2.45",
117
+ "@types/through2": "^2.0.41",
118
+ "@types/tough-cookie": "^4.0.5",
119
+ "@types/ua-parser-js": "^0.7.39",
120
+ "@types/vinyl-fs": "^3.0.5",
121
+ "@types/vscode": "^1.85.0",
122
+ "@typescript-eslint/eslint-plugin": "^6.14.0",
123
+ "@typescript-eslint/parser": "^6.14.0",
124
+ "eslint": "^8.55.0",
125
+ "eslint-plugin-react": "^7.33.2",
126
+ "eslint-plugin-xlint": "^1.0.11"
127
+ },
128
+ "pnpm": {
129
+ "patchedDependencies": {
130
+ "@types/byte-size@8.1.2": "patches/@types__byte-size@8.1.2.patch",
131
+ "koa@2.14.2": "patches/koa@2.14.2.patch"
132
+ }
133
+ }
134
+ }
package/server.d.ts CHANGED
@@ -58,7 +58,6 @@ export declare class Server {
58
58
  /** 原始 process.stdout.write 函数 bind 后的备份 */
59
59
  stdout_write: Function;
60
60
  stderr_write: Function;
61
- encoder: TextEncoder;
62
61
  constructor({ name, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, }: {
63
62
  name: string;
64
63
  http?: boolean;
@@ -121,4 +120,7 @@ export declare class Server {
121
120
  absolute?: boolean;
122
121
  download?: boolean;
123
122
  }): Promise<string>;
123
+ /** - range: 取值为逗号分割的多个可用端口或端口区间 (不能含有空格,包含区间右值),比如:`8321,8322,8300-8310,11000-11999
124
+ - reverse?: `false` 在 range 内从后往前尝试 */
125
+ get_available_port(range: string, reverse?: boolean): Promise<number>;
124
126
  }
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 { stream_to_buffer, inspect, output_width, assert } from './utils.js';
11
+ import { stream_to_buffer, inspect, output_width, assert, range_to_numbers, encode } from './utils.js';
12
12
  import { flist, fread, fstat } from './file.js';
13
13
  // ------------ my server
14
14
  export class Server {
@@ -47,7 +47,6 @@ export class Server {
47
47
  /** 原始 process.stdout.write 函数 bind 后的备份 */
48
48
  stdout_write;
49
49
  stderr_write;
50
- encoder = new TextEncoder();
51
50
  constructor({ name, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, }) {
52
51
  this.name = name;
53
52
  if (http)
@@ -155,7 +154,7 @@ export class Server {
155
154
  // send 时有可能 websocket 连接断开,抛异常,为防止循环调用 (console.error -> stdio subscriber -> throw error -> console.error)
156
155
  // 这里只能忽略错误
157
156
  try {
158
- await this.remote.send({ id, data: [typeof chunk === 'string' ? this.encoder.encode(chunk) : chunk] }, websocket);
157
+ await this.remote.send({ id, data: [typeof chunk === 'string' ? encode(chunk) : chunk] }, websocket);
159
158
  }
160
159
  catch { }
161
160
  };
@@ -615,5 +614,30 @@ export class Server {
615
614
  }
616
615
  return fp;
617
616
  }
617
+ /** - range: 取值为逗号分割的多个可用端口或端口区间 (不能含有空格,包含区间右值),比如:`8321,8322,8300-8310,11000-11999
618
+ - reverse?: `false` 在 range 内从后往前尝试 */
619
+ async get_available_port(range, reverse = false) {
620
+ for (const port of range_to_numbers(range, reverse)) {
621
+ let server = http_create_server();
622
+ try {
623
+ await new Promise((resolve, reject) => {
624
+ server.once('error', reject);
625
+ server.listen(port, resolve);
626
+ });
627
+ return port;
628
+ }
629
+ catch (error) {
630
+ if (error.code === 'EADDRINUSE')
631
+ console.log(`端口 ${port} 监听失败:${error.message}`);
632
+ else
633
+ throw error;
634
+ }
635
+ finally {
636
+ server.closeAllConnections();
637
+ server.close();
638
+ }
639
+ }
640
+ throw new Error(`端口范围 {${range}} 内没有可用的端口`);
641
+ }
618
642
  }
619
643
  //# sourceMappingURL=server.js.map
@@ -50,8 +50,13 @@ export declare class Lock<TResource = void> {
50
50
  request<TResult>(action: LockedAction<TResource, TResult>, signal?: AbortSignal): Promise<TResult>;
51
51
  }
52
52
  export declare function pause(milliseconds?: number): Promise<void>;
53
+ /** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
54
+ export declare function encode(str: string): Uint8Array;
55
+ /** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
56
+ 在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
57
+ export declare function decode(buffer: Uint8Array): string;
53
58
  /** 字符串字典序比较 */
54
- export declare function strcmp(l: string, r: string): 1 | 0 | -1;
59
+ export declare function strcmp(l: string, r: string): 0 | 1 | -1;
55
60
  /** 比较 1.10.02 这种版本号 */
56
61
  export declare function vercmp(l: string, r: string): number;
57
62
  export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
package/utils.browser.js CHANGED
@@ -122,6 +122,18 @@ export async function pause(milliseconds = 3000) {
122
122
  debugger;
123
123
  }
124
124
  globalThis.pause = pause;
125
+ // ------------ text
126
+ let encoder = new TextEncoder();
127
+ /** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
128
+ export function encode(str) {
129
+ return encoder.encode(str);
130
+ }
131
+ let decoder = new TextDecoder();
132
+ /** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
133
+ 在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
134
+ export function decode(buffer) {
135
+ return decoder.decode(buffer);
136
+ }
125
137
  /** 字符串字典序比较 */
126
138
  export function strcmp(l, r) {
127
139
  if (l === r)
package/utils.d.ts CHANGED
@@ -22,7 +22,7 @@ export declare function unique<T>(iterable: T[] | Iterable<T>, selector?: string
22
22
  /** 排序对象中 key 的顺序,返回新的对象 */
23
23
  export declare function sort_keys<T>(obj: T): T;
24
24
  /** 字符串字典序比较 */
25
- export declare function strcmp(l: string, r: string): 1 | 0 | -1;
25
+ export declare function strcmp(l: string, r: string): 0 | 1 | -1;
26
26
  /** 比较 1.10.02 这种版本号 */
27
27
  export declare function vercmp(l: string, r: string): number;
28
28
  export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
@@ -94,6 +94,11 @@ export declare class Lock<TResource = void> {
94
94
  在 signal aborted 时抛出错误结束等待,且由内部实现自动释放资源 */
95
95
  request<TResult>(action: LockedAction<TResource, TResult>, signal?: AbortSignal): Promise<TResult>;
96
96
  }
97
+ /** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
98
+ export declare function encode(str: string): Uint8Array;
99
+ /** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
100
+ 在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
101
+ export declare function decode(buffer: Uint8Array): string;
97
102
  export declare function has_chinese(str: string): boolean;
98
103
  export declare function escape_line_feed(str: string): string;
99
104
  export declare function lowercase_first_letter(str: string): string;
@@ -143,3 +148,7 @@ export declare class DecoderStream extends Transform {
143
148
  _transform(chunk: Uint8Array, encoding: BufferEncoding, callback: TransformCallback): void;
144
149
  _flush(callback: TransformCallback): void;
145
150
  }
151
+ /** 根据 range 生成整数序列 (iterable)
152
+ - range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
153
+ - reverse?: `false` 在 range 内从后往前生成 */
154
+ export declare function range_to_numbers(range: string, reverse?: boolean): Generator<number, void, unknown>;
package/utils.js CHANGED
@@ -286,6 +286,17 @@ export class Lock {
286
286
  }
287
287
  }
288
288
  // ------------ text
289
+ let encoder = new TextEncoder();
290
+ /** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
291
+ export function encode(str) {
292
+ return encoder.encode(str);
293
+ }
294
+ let decoder = new TextDecoder();
295
+ /** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
296
+ 在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
297
+ export function decode(buffer) {
298
+ return decoder.decode(buffer);
299
+ }
289
300
  export function has_chinese(str) {
290
301
  return /[\u4E00-\u9FA5]/.test(str);
291
302
  }
@@ -499,4 +510,23 @@ export class DecoderStream extends Transform {
499
510
  callback();
500
511
  }
501
512
  }
513
+ /** 根据 range 生成整数序列 (iterable)
514
+ - range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
515
+ - reverse?: `false` 在 range 内从后往前生成 */
516
+ export function* range_to_numbers(range, reverse = false) {
517
+ let parts = range.split(',');
518
+ if (reverse)
519
+ parts.reverse();
520
+ for (const part of parts) {
521
+ const [left, right] = part.split('-').map(n => Number(n));
522
+ if (!right)
523
+ yield left;
524
+ if (reverse)
525
+ for (let i = right; i >= left; i--)
526
+ yield i;
527
+ else
528
+ for (let i = left; i <= right; i++)
529
+ yield i;
530
+ }
531
+ }
502
532
  //# sourceMappingURL=utils.js.map
package/xshell.png ADDED
Binary file