xshell 1.1.5 → 1.1.7

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/builder.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { fileURLToPath } from 'url';
2
+ import { not_empty } from "./prototype.js";
2
3
  import { noprint } from "./process.js";
3
- import { Lock, check, filter_values, not_empty } from "./utils.js";
4
+ import { Lock, check, filter_values } from "./utils.js";
4
5
  import { fcopy, fmkdir, fwrite } from "./file.js";
5
6
  import { path } from "./path.js";
6
7
  const monaco_files = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xshell",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -53,7 +53,7 @@
53
53
  "@babel/parser": "^7.26.3",
54
54
  "@babel/traverse": "^7.26.4",
55
55
  "@koa/cors": "^5.0.0",
56
- "@stylistic/eslint-plugin": "^2.12.0",
56
+ "@stylistic/eslint-plugin": "^2.12.1",
57
57
  "@svgr/webpack": "^8.1.0",
58
58
  "@types/sass-loader": "^8.0.9",
59
59
  "@types/ws": "^8.5.13",
@@ -75,13 +75,13 @@
75
75
  "commander": "^12.1.0",
76
76
  "css-loader": "^7.1.2",
77
77
  "emoji-regex": "^10.4.0",
78
- "eslint": "^9.16.0",
78
+ "eslint": "^9.17.0",
79
79
  "eslint-plugin-import": "^2.31.0",
80
80
  "eslint-plugin-react": "^7.37.2",
81
81
  "gulp-sort": "^2.0.0",
82
82
  "hash-string": "^1.0.0",
83
83
  "https-proxy-agent": "^7.0.6",
84
- "i18next": "^24.0.5",
84
+ "i18next": "^24.1.0",
85
85
  "i18next-scanner": "^4.6.0",
86
86
  "koa": "^2.15.3",
87
87
  "koa-compress": "^5.1.1",
@@ -91,10 +91,10 @@
91
91
  "mime-types": "^2.1.35",
92
92
  "ora": "^8.1.1",
93
93
  "react": "^19.0.0",
94
- "react-i18next": "^15.1.4",
94
+ "react-i18next": "^15.2.0",
95
95
  "react-object-model": "^1.2.20",
96
96
  "resolve-path": "^1.4.0",
97
- "sass": "^1.82.0",
97
+ "sass": "^1.83.0",
98
98
  "sass-loader": "^16.0.4",
99
99
  "source-map-loader": "^5.0.0",
100
100
  "strip-ansi": "^7.1.0",
@@ -126,13 +126,13 @@
126
126
  "@types/koa-compress": "^4.0.6",
127
127
  "@types/lodash": "^4.17.13",
128
128
  "@types/mime-types": "^2.1.4",
129
- "@types/node": "^22.10.1",
129
+ "@types/node": "^22.10.2",
130
130
  "@types/react": "^19.0.1",
131
131
  "@types/through2": "^2.0.41",
132
132
  "@types/tough-cookie": "^4.0.5",
133
133
  "@types/ua-parser-js": "^0.7.39",
134
134
  "@types/vinyl-fs": "^3.0.5",
135
- "@types/vscode": "^1.95.0",
135
+ "@types/vscode": "^1.96.0",
136
136
  "@types/webpack-bundle-analyzer": "^4.7.0"
137
137
  },
138
138
  "pnpm": {
package/path.js CHANGED
@@ -4,10 +4,9 @@ export function to_fp(str) {
4
4
  return str;
5
5
  const fp = str.replaceAll('\\', '/');
6
6
  // 转换小写盘符开头的路径
7
- return fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z' ?
8
- fp[0].toUpperCase() + fp.slice(1)
9
- :
10
- fp;
7
+ return fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z'
8
+ ? fp[0].toUpperCase() + fp.slice(1)
9
+ : fp;
11
10
  }
12
11
  /** Normalize a string path, reducing '..' and '.' parts.
13
12
  When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved.
package/process.d.ts CHANGED
@@ -67,7 +67,6 @@ export interface StartOptions extends BaseOptions {
67
67
  - envs?: `process.env` 覆盖/添加到 process.env 的环境变量,传 null 时可以取消设置该变量
68
68
  - encoding?: `'utf-8'` 子进程输出编码
69
69
  - print?: `true` 是否打印启动命令行
70
- - stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理
71
70
  - input?: string, 启动子进程之后写入到子进程 stdin 中的内容,写完后关闭子进程 stdin (pty 不关闭)
72
71
  - fp_stdin?: 使用文件作为标准输入,设置 stdio 中对应的值
73
72
  - fp_stdout?: 使用文件作为标准输出,设置 stdio 中对应的值
@@ -151,10 +150,7 @@ export interface StartNodeJsOptions extends StartOptions {
151
150
  - options
152
151
  - cwd?: `'T:/'`
153
152
  - envs?: `process.env` 覆盖/添加到 process.env 的环境变量
154
- - encoding?: `'utf-8'` 子进程输出编码
155
153
  - print?: `true` print 选项,支持设置细项
156
- - stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理
157
- - detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref)
158
154
  - inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
159
155
  - break?: break at first line */
160
156
  export declare function start_nodejs(js: string, args?: string[], { inspect, break: _break, ...options }?: StartNodeJsOptions): Promise<ChildProcess>;
package/process.js CHANGED
@@ -110,7 +110,6 @@ stdio }) {
110
110
  - envs?: `process.env` 覆盖/添加到 process.env 的环境变量,传 null 时可以取消设置该变量
111
111
  - encoding?: `'utf-8'` 子进程输出编码
112
112
  - print?: `true` 是否打印启动命令行
113
- - stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理
114
113
  - input?: string, 启动子进程之后写入到子进程 stdin 中的内容,写完后关闭子进程 stdin (pty 不关闭)
115
114
  - fp_stdin?: 使用文件作为标准输入,设置 stdio 中对应的值
116
115
  - fp_stdout?: 使用文件作为标准输出,设置 stdio 中对应的值
@@ -248,10 +247,7 @@ export async function call_nodejs(js, args = [], { inspect, break: _break, ...op
248
247
  - options
249
248
  - cwd?: `'T:/'`
250
249
  - envs?: `process.env` 覆盖/添加到 process.env 的环境变量
251
- - encoding?: `'utf-8'` 子进程输出编码
252
250
  - print?: `true` print 选项,支持设置细项
253
- - stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理
254
- - detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref)
255
251
  - inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
256
252
  - break?: break at first line */
257
253
  export async function start_nodejs(js, args = [], { inspect, break: _break, ...options } = {}) {
@@ -146,6 +146,15 @@ declare global {
146
146
  /** 等价于 .at(-1) */
147
147
  last: T;
148
148
  indent(this: string[], width?: number, c?: string): string[];
149
+ /** 对数组中所有元素求和 (+), 返回结果 */
150
+ sum(this: T[]): T;
151
+ /** 查找数组中最大的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
152
+ max(this: T[], selector?: keyof T | KeySelector<T>): T;
153
+ /** 查找数组中最小的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
154
+ min(this: T[], selector?: keyof T | KeySelector<T>): T;
155
+ /** 去除重复元素(可按 selector 去重),重复值保留最后出现的那个
156
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
157
+ unique(this: T[], selector?: keyof T | KeySelector<T>): T[];
149
158
  /**
150
159
  - trim_line?: `true`
151
160
  - rm_empty_lines?: `true`
@@ -174,6 +183,15 @@ interface SliceOptions {
174
183
  last?: boolean;
175
184
  }
176
185
  export declare const emoji_regex: RegExp;
186
+ export declare const noop: () => void;
187
+ export declare const ident: <T>(x: T) => T;
188
+ export declare const build_selector: <TObj>(key: keyof TObj) => (obj: TObj) => TObj[keyof TObj];
189
+ export type KeySelector<TObj = any, TKey extends keyof TObj = keyof TObj> = (key: TObj) => TObj[TKey];
190
+ /** value 不为 null 或 undefined */
191
+ export declare const not_empty: (value: any) => boolean;
192
+ export declare const empty: (value: any) => boolean;
193
+ export declare const is_key_type: IsKeyType;
194
+ type IsKeyType = (key: any) => key is string | number | symbol;
177
195
  export declare function to_method_property_descriptors(methods: {
178
196
  [name: string]: Function;
179
197
  }): PropertyDescriptorMap;
@@ -3,6 +3,14 @@ import byte_size from 'byte-size';
3
3
  import EmojiRegex from 'emoji-regex';
4
4
  import { t } from "./i18n/instance.js";
5
5
  export const emoji_regex = EmojiRegex();
6
+ export const noop = () => { };
7
+ export const ident = (x) => x;
8
+ export const build_selector = (key) => (obj) => obj[key];
9
+ /** value 不为 null 或 undefined */
10
+ export const not_empty = (value) => value !== null && value !== undefined;
11
+ export const empty = (value) => value === undefined || value === null;
12
+ const key_types = ['string', 'number', 'symbol'];
13
+ export const is_key_type = ((key) => key_types.includes(typeof key));
6
14
  export function to_method_property_descriptors(methods) {
7
15
  return Object.fromEntries(Object.entries(methods)
8
16
  .map(([name, value]) => ([name, {
@@ -444,10 +452,10 @@ Object.defineProperties(Array.prototype, {
444
452
  if (trim_line)
445
453
  lines = lines.map(line => line.trim());
446
454
  if (rm_empty_lines)
447
- return lines.filter(line => line);
455
+ return lines.filter(Boolean);
448
456
  if (rm_last_empty_lines) {
449
457
  lines.reverse();
450
- const i_not_empty = lines.findIndex(line => line);
458
+ const i_not_empty = lines.findIndex(Boolean);
451
459
  if (i_not_empty !== -1)
452
460
  lines = lines.slice(i_not_empty);
453
461
  lines.reverse();
@@ -459,6 +467,53 @@ Object.defineProperties(Array.prototype, {
459
467
  const indent = character.repeat(width);
460
468
  return this.map(line => indent + line);
461
469
  },
470
+ sum() {
471
+ return this.length
472
+ ? this.reduce((acc, x) => acc + x)
473
+ : undefined;
474
+ },
475
+ max(selector = ident) {
476
+ if (!this.length)
477
+ return undefined;
478
+ if (typeof selector === 'string')
479
+ selector = build_selector(selector);
480
+ let max = selector(this[0]);
481
+ let imax = 0;
482
+ for (let i = 0; i < this.length; i++) {
483
+ const value = selector(this[i]);
484
+ if (value > max) {
485
+ max = value;
486
+ imax = i;
487
+ }
488
+ }
489
+ return this[imax];
490
+ },
491
+ min(selector = ident) {
492
+ if (!this.length)
493
+ return undefined;
494
+ if (typeof selector === 'string')
495
+ selector = build_selector(selector);
496
+ let min = selector(this[0]);
497
+ let imin = 0;
498
+ for (let i = 0; i < this.length; i++) {
499
+ const value = selector(this[i]);
500
+ if (value < min) {
501
+ min = value;
502
+ imin = i;
503
+ }
504
+ }
505
+ return this[imin];
506
+ },
507
+ unique(selector) {
508
+ if (!selector)
509
+ return [...new Set(this)];
510
+ if (is_key_type(selector))
511
+ selector = build_selector(selector);
512
+ let map = new Map();
513
+ for (const x of this)
514
+ map.set(selector(x), x);
515
+ return [...map.values()];
516
+ },
462
517
  join_lines(append = true) {
463
518
  return `${this.join('\n')}${append ? '\n' : ''}`;
464
519
  }
package/prototype.d.ts CHANGED
@@ -171,6 +171,15 @@ declare global {
171
171
  log(this: string[], limit?: number): void;
172
172
  indent(this: string[], width?: number, c?: string): string[];
173
173
  indent2to4(this: string[]): string[];
174
+ /** 对数组中所有元素求和 (+), 返回结果 */
175
+ sum(this: T[]): T;
176
+ /** 查找数组中最大的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
177
+ max(this: T[], selector?: keyof T | KeySelector<T>): T;
178
+ /** 查找数组中最小的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
179
+ min(this: T[], selector?: keyof T | KeySelector<T>): T;
180
+ /** 去除重复元素(可按 selector 去重),重复值保留最后出现的那个
181
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
182
+ unique(this: T[], selector?: keyof T | KeySelector<T>): T[];
174
183
  /**
175
184
  - trim_line?: `true`
176
185
  - rm_empty_lines?: `true`
@@ -206,6 +215,15 @@ interface SliceOptions {
206
215
  import chalk from 'chalk';
207
216
  export declare const emoji_regex: RegExp;
208
217
  export { chalk };
218
+ export declare const noop: () => void;
219
+ export declare const ident: <T>(x: T) => T;
220
+ export declare const build_selector: <TObj>(key: keyof TObj) => (obj: TObj) => TObj[keyof TObj];
221
+ export type KeySelector<TObj = any, TKey extends keyof TObj = keyof TObj> = (key: TObj) => TObj[TKey];
222
+ /** value 不为 null 或 undefined */
223
+ export declare const not_empty: (value: any) => boolean;
224
+ export declare const empty: (value: any) => boolean;
225
+ export declare const is_key_type: IsKeyType;
226
+ type IsKeyType = (key: any) => key is string | number | symbol;
209
227
  export declare function to_method_property_descriptors(methods: {
210
228
  [name: string]: Function;
211
229
  }): PropertyDescriptorMap;
package/prototype.js CHANGED
@@ -7,6 +7,14 @@ import { t } from "./i18n/instance.js";
7
7
  chalk.level = 2;
8
8
  export const emoji_regex = EmojiRegex();
9
9
  export { chalk };
10
+ export const noop = () => { };
11
+ export const ident = (x) => x;
12
+ export const build_selector = (key) => (obj) => obj[key];
13
+ /** value 不为 null 或 undefined */
14
+ export const not_empty = (value) => value !== null && value !== undefined;
15
+ export const empty = (value) => value === undefined || value === null;
16
+ const key_types = ['string', 'number', 'symbol'];
17
+ export const is_key_type = ((key) => key_types.includes(typeof key));
10
18
  export function to_method_property_descriptors(methods) {
11
19
  return Object.fromEntries(Object.entries(methods)
12
20
  .map(([name, value]) => ([name, {
@@ -482,10 +490,10 @@ if (!globalThis.my_prototype_defined) {
482
490
  if (trim_line)
483
491
  lines = lines.map(line => line.trim());
484
492
  if (rm_empty_lines)
485
- return lines.filter(line => line);
493
+ return lines.filter(Boolean);
486
494
  if (rm_last_empty_lines) {
487
495
  lines.reverse();
488
- const i_not_empty = lines.findIndex(line => line);
496
+ const i_not_empty = lines.findIndex(Boolean);
489
497
  if (i_not_empty !== -1)
490
498
  lines = lines.slice(i_not_empty);
491
499
  lines.reverse();
@@ -512,6 +520,53 @@ if (!globalThis.my_prototype_defined) {
512
520
  return this.split_indents()
513
521
  .map(line => ' '.repeat(line.indent * 2) + line.text);
514
522
  },
523
+ sum() {
524
+ return this.length
525
+ ? this.reduce((acc, x) => acc + x)
526
+ : undefined;
527
+ },
528
+ max(selector = ident) {
529
+ if (!this.length)
530
+ return undefined;
531
+ if (is_key_type(selector))
532
+ selector = build_selector(selector);
533
+ let max = selector(this[0]);
534
+ let imax = 0;
535
+ for (let i = 0; i < this.length; i++) {
536
+ const value = selector(this[i]);
537
+ if (value > max) {
538
+ max = value;
539
+ imax = i;
540
+ }
541
+ }
542
+ return this[imax];
543
+ },
544
+ min(selector = ident) {
545
+ if (!this.length)
546
+ return undefined;
547
+ if (is_key_type(selector))
548
+ selector = build_selector(selector);
549
+ let min = selector(this[0]);
550
+ let imin = 0;
551
+ for (let i = 0; i < this.length; i++) {
552
+ const value = selector(this[i]);
553
+ if (value < min) {
554
+ min = value;
555
+ imin = i;
556
+ }
557
+ }
558
+ return this[imin];
559
+ },
560
+ unique(selector) {
561
+ if (!selector)
562
+ return [...new Set(this)];
563
+ if (is_key_type(selector))
564
+ selector = build_selector(selector);
565
+ let map = new Map();
566
+ for (const x of this)
567
+ map.set(selector(x), x);
568
+ return [...map.values()];
569
+ },
515
570
  join_lines(append = true) {
516
571
  return `${this.join('\n')}${append ? '\n' : ''}`;
517
572
  }
@@ -1,6 +1,4 @@
1
- export declare const noop: () => void;
2
- /** value 不为 null 或 undefined */
3
- export declare const not_empty: (value: any) => boolean;
1
+ import { type KeySelector } from './prototype.browser.ts';
4
2
  export declare function assert(assertion: any, message?: string): never | void;
5
3
  /** 做参数校验,逻辑检查 */
6
4
  export declare function check(condition: any, message?: string): never | void;
@@ -11,8 +9,8 @@ export declare function check(condition: any, message?: string): never | void;
11
9
  export declare function log<TObj>(obj: TObj): TObj;
12
10
  export declare function log<TObj>(label: string, obj: TObj): TObj;
13
11
  /** 数组或 iterable 去重(可按 selector 去重),重复值保留最后出现的那个
14
- - selector?: 可以是 key (string) 或 (obj: any) => any */
15
- export declare function unique<TObj>(iterable: TObj[] | Iterable<TObj>, selector?: string | ((obj: TObj) => any)): TObj[];
12
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
13
+ export declare function unique<TObj>(iterable: TObj[] | Iterable<TObj>, selector?: keyof TObj | KeySelector<TObj>): TObj[];
16
14
  /** 生成 0, 1, ..., n - 1 (不包括 n) 的数组,支持传入 generator 函数,通过 index 生成各个元素
17
15
  @example seq(10, i => `item-${i}`) */
18
16
  export declare function seq<T = number>(n: number, generator?: (index: number) => T): T[];
package/utils.browser.js CHANGED
@@ -1,7 +1,5 @@
1
+ import { is_key_type, build_selector, not_empty } from "./prototype.browser.js";
1
2
  import { t } from "./i18n/instance.js";
2
- export const noop = () => { };
3
- /** value 不为 null 或 undefined */
4
- export const not_empty = (value) => value !== null && value !== undefined;
5
3
  export function assert(assertion, message) {
6
4
  if (!assertion) {
7
5
  debugger;
@@ -28,14 +26,15 @@ export function log(...args) {
28
26
  }
29
27
  }
30
28
  /** 数组或 iterable 去重(可按 selector 去重),重复值保留最后出现的那个
31
- - selector?: 可以是 key (string) 或 (obj: any) => any */
29
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
32
30
  export function unique(iterable, selector) {
33
31
  if (!selector)
34
32
  return [...new Set(iterable)];
33
+ if (is_key_type(selector))
34
+ selector = build_selector(selector);
35
35
  let map = new Map();
36
- const is_str_selector = typeof selector === 'string';
37
36
  for (const x of iterable)
38
- map.set(is_str_selector ? x[selector] : selector(x), x);
37
+ map.set(selector(x), x);
39
38
  return [...map.values()];
40
39
  }
41
40
  /** 生成 0, 1, ..., n - 1 (不包括 n) 的数组,支持传入 generator 函数,通过 index 生成各个元素
package/utils.d.ts CHANGED
@@ -2,10 +2,7 @@ import { Writable, Transform, type Readable, type Duplex, type TransformCallback
2
2
  import util from 'util';
3
3
  import type { TimerOptions } from 'timers';
4
4
  import type Vinyl from 'vinyl';
5
- import './prototype.ts';
6
- export declare const noop: () => void;
7
- /** value 不为 null 或 undefined */
8
- export declare const not_empty: (value: any) => boolean;
5
+ import { type KeySelector } from './prototype.ts';
9
6
  /** `230` term 字符宽度 (实际上有 240) */
10
7
  export declare const output_width = 230;
11
8
  export declare function set_inspect_options(colors?: boolean): void;
@@ -18,13 +15,12 @@ export declare function check(condition: any, message?: string): never | void;
18
15
  log(obj) */
19
16
  export declare function log<TObj>(obj: TObj): TObj;
20
17
  export declare function log<TObj>(label: string, obj: TObj): TObj;
18
+ /** 数组或 iterable 去重(可按 selector 去重),重复值保留最后出现的那个
19
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
20
+ export declare function unique<TObj>(iterable: TObj[] | Iterable<TObj>, selector?: keyof TObj | KeySelector<TObj>): TObj[];
21
21
  /** 生成 0, 1, ..., n - 1 (不包括 n) 的数组,支持传入 generator 函数,通过 index 生成各个元素
22
22
  @example seq(10, i => `item-${i}`) */
23
23
  export declare function seq<T = number>(n: number, generator?: (index: number) => T): T[];
24
- /** 数组或 iterable 去重(可按 selector 去重)
25
- - selector?: 可以是 key (string) 或 (obj: any) => any
26
- */
27
- export declare function unique<T>(iterable: T[] | Iterable<T>, selector?: string | ((obj: T) => any)): any[];
28
24
  /** 排序对象中 keys 的顺序,返回新的对象 */
29
25
  export declare function sort_keys<TObj>(obj: TObj): TObj;
30
26
  /** 将 keys, values 数组按对应的顺序组合成一个对象 */
package/utils.js CHANGED
@@ -2,10 +2,7 @@ import { Stream, Writable, Transform } from 'stream';
2
2
  import util from 'util';
3
3
  import timers from 'timers/promises';
4
4
  import { t } from "./i18n/instance.js";
5
- import "./prototype.js";
6
- export const noop = () => { };
7
- /** value 不为 null 或 undefined */
8
- export const not_empty = (value) => value !== null && value !== undefined;
5
+ import { build_selector, not_empty, is_key_type, noop } from "./prototype.js";
9
6
  /** `230` term 字符宽度 (实际上有 240) */
10
7
  export const output_width = 230;
11
8
  export function set_inspect_options(colors = true) {
@@ -49,6 +46,18 @@ export function log(...args) {
49
46
  return obj;
50
47
  }
51
48
  }
49
+ /** 数组或 iterable 去重(可按 selector 去重),重复值保留最后出现的那个
50
+ - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
51
+ export function unique(iterable, selector) {
52
+ if (!selector)
53
+ return [...new Set(iterable)];
54
+ if (is_key_type(selector))
55
+ selector = build_selector(selector);
56
+ let map = new Map();
57
+ for (const x of iterable)
58
+ map.set(selector(x), x);
59
+ return [...map.values()];
60
+ }
52
61
  /** 生成 0, 1, ..., n - 1 (不包括 n) 的数组,支持传入 generator 函数,通过 index 生成各个元素
53
62
  @example seq(10, i => `item-${i}`) */
54
63
  export function seq(n, generator) {
@@ -57,18 +66,6 @@ export function seq(n, generator) {
57
66
  a[i] = generator ? generator(i) : i;
58
67
  return a;
59
68
  }
60
- /** 数组或 iterable 去重(可按 selector 去重)
61
- - selector?: 可以是 key (string) 或 (obj: any) => any
62
- */
63
- export function unique(iterable, selector) {
64
- if (!selector)
65
- return [...new Set(iterable)];
66
- let map = new Map();
67
- const is_str_selector = typeof selector === 'string';
68
- for (const x of iterable)
69
- map.set(is_str_selector ? x[selector] : selector(x), x);
70
- return [...map.values()];
71
- }
72
69
  /** 排序对象中 keys 的顺序,返回新的对象 */
73
70
  export function sort_keys(obj) {
74
71
  return Object.fromEntries(Object.entries(obj)