@maiyunnet/kebab 5.3.1 → 6.1.0

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/lib/text.js CHANGED
@@ -205,7 +205,7 @@ export function isIPv6(ip) {
205
205
  export const REGEXP_DOMAIN = /^.+?\.((?![0-9]).)+$/i;
206
206
  /**
207
207
  * --- 判断是否是域名 ---
208
- * @param string $domain
208
+ * @param domain 域名
209
209
  * @return bool
210
210
  */
211
211
  export function isDomain(domain) {
@@ -432,7 +432,7 @@ export function getFilename(path) {
432
432
  }
433
433
  /**
434
434
  * --- 将普通的返回 JSON 对象序列化为字符串 ---
435
- * @param o 返回 JSON 对象
435
+ * @param rtn 返回 JSON 对象
436
436
  */
437
437
  export function stringifyResult(rtn) {
438
438
  if (Array.isArray(rtn)) {
package/lib/vector.d.ts CHANGED
@@ -66,6 +66,6 @@ export declare class Vector {
66
66
  }
67
67
  /**
68
68
  * --- 创建一个 Vector 对象 ---
69
- * @param opt 选项
69
+ * @param ctrEtc 控制器或配置信息
70
70
  */
71
71
  export declare function get(ctrEtc: sCtr.Ctr | kebab.IConfigVector): Vector;
package/lib/vector.js CHANGED
@@ -31,8 +31,8 @@ export class Vector {
31
31
  return res;
32
32
  }
33
33
  catch (e) {
34
- lCore.log({}, '[VECTOR][seach][error] ' + e.message, '-error');
35
- lCore.debug('[VECTOR][seach]', e.code, e.message);
34
+ lCore.log({}, '[VECTOR][seach][error] ' + (e.status?.reason ?? ''), '-error');
35
+ lCore.debug('[VECTOR][seach]', e);
36
36
  return false;
37
37
  }
38
38
  }
@@ -50,8 +50,8 @@ export class Vector {
50
50
  return res;
51
51
  }
52
52
  catch (e) {
53
- lCore.log({}, '[VECTOR][insert][error] ' + e.message, '-error');
54
- lCore.debug('[VECTOR][insert]', e.code, e.message);
53
+ lCore.log({}, '[VECTOR][insert][error] ' + (e.status?.reason ?? ''), '-error');
54
+ lCore.debug('[VECTOR][insert]', e);
55
55
  return false;
56
56
  }
57
57
  }
@@ -69,8 +69,8 @@ export class Vector {
69
69
  return res;
70
70
  }
71
71
  catch (e) {
72
- lCore.log({}, '[VECTOR][delete][error] ' + e.message, '-error');
73
- lCore.debug('[VECTOR][delete]', e.code, e.message);
72
+ lCore.log({}, '[VECTOR][delete][error] ' + (e.status?.reason ?? ''), '-error');
73
+ lCore.debug('[VECTOR][delete]', e);
74
74
  return false;
75
75
  }
76
76
  }
@@ -107,7 +107,7 @@ export class Vector {
107
107
  }
108
108
  /**
109
109
  * --- 创建一个 Vector 对象 ---
110
- * @param opt 选项
110
+ * @param ctrEtc 控制器或配置信息
111
111
  */
112
112
  export function get(ctrEtc) {
113
113
  if (ctrEtc instanceof sCtr.Ctr) {
package/lib/ws.d.ts CHANGED
@@ -166,6 +166,5 @@ export declare function rproxy(ctr: sCtr.Ctr, url: string, opt?: IRproxyOptions)
166
166
  * @param ctr 当前控制器
167
167
  * @param host 反代真实请求地址
168
168
  * @param port 反代真实请求端口
169
- * @param opt 参数
170
169
  */
171
170
  export declare function rsocket(ctr: sCtr.Ctr, host: string, port: number): Promise<boolean>;
package/lib/ws.js CHANGED
@@ -418,7 +418,6 @@ export async function rproxy(ctr, url, opt = {}) {
418
418
  * @param ctr 当前控制器
419
419
  * @param host 反代真实请求地址
420
420
  * @param port 反代真实请求端口
421
- * @param opt 参数
422
421
  */
423
422
  export async function rsocket(ctr, host, port) {
424
423
  return new Promise(resolve => {
package/lib/zip.d.ts CHANGED
@@ -29,7 +29,6 @@ export declare class Zip {
29
29
  /**
30
30
  * --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
31
31
  * @param path 对象路径
32
- * @param options 选项
33
32
  */
34
33
  stats(path: string): IZipStats | null;
35
34
  /**
package/lib/zip.js CHANGED
@@ -58,7 +58,6 @@ export class Zip {
58
58
  /**
59
59
  * --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
60
60
  * @param path 对象路径
61
- * @param options 选项
62
61
  */
63
62
  stats(path) {
64
63
  path = lText.urlResolve(this._path, path);
package/lib/zlib.d.ts CHANGED
@@ -49,8 +49,7 @@ export declare function createBrotliDecompress(): zlib.BrotliDecompress;
49
49
  export declare function createCompress(types: string, options?: zlib.ZlibOptions): ICompress | null;
50
50
  /**
51
51
  * --- 根据字符串创建解压类型 ---
52
- * @param types 用 , 间隔的字符串,如 gzip,deflate
53
- * @param options 选项
52
+ * @param types 用 , 间隔的字符串,如 gzip, deflate
54
53
  */
55
54
  export declare function createDecompress(types: string): ICompress | null;
56
55
  /**
package/lib/zlib.js CHANGED
@@ -81,8 +81,7 @@ export function createCompress(types, options = {}) {
81
81
  }
82
82
  /**
83
83
  * --- 根据字符串创建解压类型 ---
84
- * @param types 用 , 间隔的字符串,如 gzip,deflate
85
- * @param options 选项
84
+ * @param types 用 , 间隔的字符串,如 gzip, deflate
86
85
  */
87
86
  export function createDecompress(types) {
88
87
  const type = getTypeByTypes(types);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "5.3.1",
3
+ "version": "6.1.0",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -12,6 +12,9 @@
12
12
  "author": "hanguoshuai",
13
13
  "license": "Apache-2.0",
14
14
  "types": "./index.d.ts",
15
+ "scripts": {
16
+ "build-md-doc": "bash ./utils/generate-api-doc-md.sh"
17
+ },
15
18
  "bin": {
16
19
  "kebab": "./bin/kebab.js"
17
20
  },
@@ -19,29 +22,31 @@
19
22
  "#kebab/*": "./*"
20
23
  },
21
24
  "dependencies": {
22
- "@aws-sdk/client-s3": "^3.927.0",
23
- "@aws-sdk/lib-storage": "^3.927.0",
25
+ "@aws-sdk/client-s3": "^3.939.0",
26
+ "@aws-sdk/lib-storage": "^3.939.0",
24
27
  "@litert/http-client": "^1.1.2",
25
28
  "@litert/mime": "^0.1.3",
26
- "@litert/redis": "^3.0.5",
29
+ "@litert/redis": "^3.0.6",
27
30
  "@litert/websocket": "^0.2.8",
28
31
  "@types/ssh2": "^1.15.5",
29
- "@zilliz/milvus2-sdk-node": "^2.6.4",
32
+ "@zilliz/milvus2-sdk-node": "^2.6.5",
30
33
  "ejs": "^3.1.10",
31
34
  "jszip": "^3.10.1",
32
35
  "mysql2": "^3.15.3",
33
36
  "node-cron": "^4.2.1",
34
- "openai": "^6.8.1",
37
+ "openai": "^6.9.1",
35
38
  "pg": "^8.16.3",
36
39
  "ssh2": "^1.17.0",
37
40
  "svg-captcha": "^1.4.0",
38
- "tencentcloud-sdk-nodejs": "^4.1.140"
41
+ "tencentcloud-sdk-nodejs": "^4.1.149"
39
42
  },
40
43
  "devDependencies": {
41
44
  "@litert/eslint-plugin-rules": "^0.3.1",
42
45
  "@types/ejs": "^3.1.5",
43
- "@types/node": "^24.10.0",
46
+ "@types/node": "^24.10.1",
44
47
  "@types/pg": "^8.15.6",
48
+ "typedoc": "^0.28.14",
49
+ "typedoc-plugin-markdown": "^4.9.0",
45
50
  "typescript": "^5.9.3"
46
51
  }
47
52
  }
package/sys/cmd.js CHANGED
@@ -115,13 +115,6 @@ async function run() {
115
115
  config.db['PGSQL'].read.name ??= 'maiyun';
116
116
  config.db['PGSQL'].read.user ??= 'root';
117
117
  config.db['PGSQL'].read.pwd ??= 'DashAdmin';
118
- // --- config - jwt ---
119
- config.jwt ??= {};
120
- config.jwt.name ??= 'KE_JWT';
121
- config.jwt.ttl ??= 172800;
122
- config.jwt.ssl ??= false;
123
- config.jwt.secret ??= 'MUSTCHANGE';
124
- config.jwt.auth ??= false;
125
118
  // --- config - dns ---
126
119
  config.dns ??= {};
127
120
  config.dns['DNSPOD'] ??= {};
package/sys/ctr.d.ts CHANGED
@@ -33,8 +33,6 @@ export declare class Ctr {
33
33
  protected _files: Record<string, kebab.IPostFile | kebab.IPostFile[]>;
34
34
  /** --- Cookie 数组 --- */
35
35
  protected _cookie: Record<string, string>;
36
- /** --- Jwt 数组 --- */
37
- protected _jwt: Record<string, any>;
38
36
  /** --- Session 数组 --- */
39
37
  protected _session: Record<string, any>;
40
38
  /** --- Session --- 对象 */
package/sys/ctr.js CHANGED
@@ -37,8 +37,6 @@ export class Ctr {
37
37
  this._files = {};
38
38
  /** --- Cookie 数组 --- */
39
39
  this._cookie = {};
40
- /** --- Jwt 数组 --- */
41
- this._jwt = {};
42
40
  /** --- Session 数组 --- */
43
41
  this._session = {};
44
42
  /** --- Session --- 对象 */
package/sys/mod.d.ts CHANGED
@@ -8,11 +8,11 @@ import * as lDb from '#kebab/lib/db.js';
8
8
  import * as sCtr from '#kebab/sys/ctr.js';
9
9
  import * as kebab from '#kebab/index.js';
10
10
  /** --- 只获取变量 --- */
11
- type TOnlyProperties<T> = {
11
+ export type TOnlyProperties<T> = {
12
12
  [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];
13
13
  };
14
14
  /** --- 条数列表 --- */
15
- declare class Rows<T extends Mod> implements IRows<T> {
15
+ export declare class Rows<T extends Mod> implements IRows<T> {
16
16
  private readonly _items;
17
17
  constructor(initialItems?: T[]);
18
18
  /** --- 总行数 --- */
@@ -63,7 +63,6 @@ export default class Mod {
63
63
  protected _ctr?: sCtr.Ctr;
64
64
  /**
65
65
  * --- 构造函数 ---
66
- * @param ctr Ctr 对象
67
66
  * @param opt 选项
68
67
  */
69
68
  constructor(opt: {
@@ -170,6 +169,18 @@ export default class Mod {
170
169
  'by'?: [string | string[], 'DESC' | 'ASC'];
171
170
  'limit'?: [number, number?];
172
171
  }): lSql.Sql;
172
+ /**
173
+ * --- 批量更新数据 ---
174
+ * @param db 数据库对象
175
+ * @param data 数据列表
176
+ * @param key 用于定位的主键或唯一键字段名
177
+ * @param opt 选项
178
+ */
179
+ static updateList(db: lDb.Pool | lDb.Transaction, data: Array<Record<string, any>>, key: string, opt?: {
180
+ 'ctr'?: sCtr.Ctr;
181
+ 'pre'?: string;
182
+ 'index'?: string;
183
+ }): Promise<boolean | null>;
173
184
  /**
174
185
  * --- select 自定字段 ---
175
186
  * @param db 数据库对象
@@ -217,7 +228,6 @@ export default class Mod {
217
228
  * --- 根据主键(或 key 字段)获取对象 ---
218
229
  * @param db 数据库对象
219
230
  * @param val 主键值
220
- * @param lock 是否加锁
221
231
  * @param opt 选项
222
232
  */
223
233
  static find<T extends Mod>(db: lDb.Pool | lDb.Transaction, val: string | number | null, opt?: {
@@ -286,8 +296,14 @@ export default class Mod {
286
296
  get(n: string): any;
287
297
  /**
288
298
  * --- 创建数据 ---
299
+ * @return true-成功,false-报错,null-唯一键非 _$key 键冲突
300
+ */
301
+ create(): Promise<boolean | null>;
302
+ /**
303
+ * --- 插入数据,如果存在则更新(UPSERT) ---
304
+ * @param conflict 冲突字段,不能为 _$key 或 _$primary,应该是你要判断的唯一索引字段
289
305
  */
290
- create(): Promise<boolean>;
306
+ upsert(conflict: string | string[]): Promise<boolean>;
291
307
  /**
292
308
  * --- 刷新当前模型获取最新数据 ---
293
309
  * @param lock 是否加锁
@@ -295,8 +311,9 @@ export default class Mod {
295
311
  refresh(lock?: boolean): Promise<boolean | null>;
296
312
  /**
297
313
  * --- 更新 set 的数据到数据库,有未保存数据时才保存 ---
314
+ * @param where 自定义筛选条件,默认根据主键筛选
298
315
  */
299
- save(): Promise<boolean>;
316
+ save(where?: any): Promise<boolean>;
300
317
  /**
301
318
  * --- 移除本条目 ---
302
319
  */
@@ -394,13 +411,11 @@ export default class Mod {
394
411
  /**
395
412
  * --- 筛选器 ---
396
413
  * @param s 筛选条件数组或字符串
397
- * @param raw 是否包含已被软删除的数据
398
414
  */
399
415
  filter(s: kebab.Json): this;
400
416
  /**
401
417
  * --- 是 filter 的别名 ---
402
418
  * @param s 筛选条件数组或字符串
403
- * @param raw 是否包含已被软删除的数据
404
419
  */
405
420
  where(s: kebab.Json): this;
406
421
  /**
@@ -487,4 +502,3 @@ export interface IModUnionItem {
487
502
  'field': string;
488
503
  'where'?: any;
489
504
  }
490
- export {};
package/sys/mod.js CHANGED
@@ -8,7 +8,7 @@ import * as lDb from '#kebab/lib/db.js';
8
8
  import * as lCore from '#kebab/lib/core.js';
9
9
  import * as lText from '#kebab/lib/text.js';
10
10
  /** --- 条数列表 --- */
11
- class Rows {
11
+ export class Rows {
12
12
  constructor(initialItems = []) {
13
13
  this._items = initialItems;
14
14
  }
@@ -57,7 +57,6 @@ export default class Mod {
57
57
  static { this._$pre = null; }
58
58
  /**
59
59
  * --- 构造函数 ---
60
- * @param ctr Ctr 对象
61
60
  * @param opt 选项
62
61
  */
63
62
  constructor(opt) {
@@ -274,6 +273,78 @@ export default class Mod {
274
273
  }
275
274
  return sq;
276
275
  }
276
+ /**
277
+ * --- 批量更新数据 ---
278
+ * @param db 数据库对象
279
+ * @param data 数据列表
280
+ * @param key 用于定位的主键或唯一键字段名
281
+ * @param opt 选项
282
+ */
283
+ static async updateList(db, data, key, opt = {}) {
284
+ if (!data.length) {
285
+ return true;
286
+ }
287
+ // --- 获取所有涉及的字段(除了 key) ---
288
+ const columns = new Set();
289
+ for (const item of data) {
290
+ for (const k in item) {
291
+ if (k !== key) {
292
+ columns.add(k);
293
+ }
294
+ }
295
+ }
296
+ if (columns.size === 0) {
297
+ return true;
298
+ }
299
+ const cols = Array.from(columns);
300
+ // --- 计算分批大小 ---
301
+ // --- 每个字段需要 2 个占位符 (WHEN ? THEN ?),加上 WHERE IN (?) 的 1 个 ---
302
+ // --- Total params per row = cols.length * 2 + 1 ---
303
+ const paramCountPerRow = cols.length * 2 + 1;
304
+ const batchSize = Math.floor(60000 / paramCountPerRow);
305
+ const batches = [];
306
+ for (let i = 0; i < data.length; i += batchSize) {
307
+ batches.push(data.slice(i, i + batchSize));
308
+ }
309
+ for (const batch of batches) {
310
+ const sq = lSql.get(opt.pre ?? this._$pre ?? opt.ctr, {
311
+ 'service': db.getService() ?? lDb.ESERVICE.PGSQL,
312
+ });
313
+ const updates = {};
314
+ const keys = [];
315
+ for (const col of cols) {
316
+ let caseSql = `(CASE ${sq.field(key)}`;
317
+ const params = [];
318
+ let hasUpdate = false;
319
+ for (const item of batch) {
320
+ if (item[col] !== undefined) {
321
+ caseSql += ` WHEN ? THEN ?`;
322
+ params.push(item[key], item[col]);
323
+ hasUpdate = true;
324
+ }
325
+ }
326
+ if (hasUpdate) {
327
+ caseSql += ` ELSE ${sq.field(col)} END)`;
328
+ updates[col] = [caseSql, params];
329
+ }
330
+ }
331
+ // --- 收集 keys ---
332
+ for (const item of batch) {
333
+ keys.push(item[key]);
334
+ }
335
+ if (Object.keys(updates).length === 0) {
336
+ continue;
337
+ }
338
+ sq.update(this._$table + (opt.index ? ('_' + opt.index) : ''), updates)
339
+ .where({ [key]: keys });
340
+ const r = await db.execute(sq.getSql(), sq.getData());
341
+ if (r.packet === null) {
342
+ lCore.log(opt.ctr ?? {}, '[MOD][updateList] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
343
+ return false;
344
+ }
345
+ }
346
+ return true;
347
+ }
277
348
  /**
278
349
  * --- select 自定字段 ---
279
350
  * @param db 数据库对象
@@ -326,7 +397,6 @@ export default class Mod {
326
397
  * --- 根据主键(或 key 字段)获取对象 ---
327
398
  * @param db 数据库对象
328
399
  * @param val 主键值
329
- * @param lock 是否加锁
330
400
  * @param opt 选项
331
401
  */
332
402
  static async find(db, val, opt = {}) {
@@ -436,7 +506,7 @@ export default class Mod {
436
506
  return rtn;
437
507
  }
438
508
  /**
439
- * --- 设置一个/多个属性,值为 underfind 则不会被更新 ---
509
+ * --- 设置一个/多个属性,值为 undefined 则不会被更新 ---
440
510
  * @param n 字符串或键/值
441
511
  * @param v 可能是数字
442
512
  */
@@ -476,6 +546,7 @@ export default class Mod {
476
546
  }
477
547
  /**
478
548
  * --- 创建数据 ---
549
+ * @return true-成功,false-报错,null-唯一键非 _$key 键冲突
479
550
  */
480
551
  async create() {
481
552
  const cstr = this.constructor;
@@ -510,6 +581,7 @@ export default class Mod {
510
581
  // --- MYSQL ---
511
582
  const match = /for key '([\w.]+)'/.exec(r.error.message);
512
583
  if (match?.[1].includes(cstr._$index)) {
584
+ // --- key 索引重复,继续生成 key ---
513
585
  continue;
514
586
  }
515
587
  }
@@ -517,11 +589,12 @@ export default class Mod {
517
589
  // --- PGSQL ---
518
590
  const match = /constraint "([\w.]+)"/.exec(r.error.message);
519
591
  if (match?.[1].includes(cstr._$index)) {
592
+ // --- key 索引重复,继续生成 key ---
520
593
  continue;
521
594
  }
522
595
  }
523
596
  // --- 1062 非 index 冲突,那需要用户自行处理(可能不允许重复的邮箱) ---
524
- return false;
597
+ return null;
525
598
  }
526
599
  // --- 未处理的错误 ---
527
600
  const service = this._db.getService();
@@ -538,8 +611,9 @@ export default class Mod {
538
611
  if (r.error.errno !== 1062) {
539
612
  lCore.debug('[MOD][create1][' + cstr._$table + ']', r);
540
613
  lCore.log(this._ctr ?? {}, '[MOD][create1][' + cstr._$table + '] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
614
+ return false;
541
615
  }
542
- return false;
616
+ return null;
543
617
  }
544
618
  }
545
619
  if (r.packet?.affected) {
@@ -552,6 +626,35 @@ export default class Mod {
552
626
  return false;
553
627
  }
554
628
  }
629
+ /**
630
+ * --- 插入数据,如果存在则更新(UPSERT) ---
631
+ * @param conflict 冲突字段,不能为 _$key 或 _$primary,应该是你要判断的唯一索引字段
632
+ */
633
+ async upsert(conflict) {
634
+ // --- 这里没用 mysql 的 INSERT ... ON DUPLICATE KEY UPDATE 或 pgsql 的 ON CONFLICT DO UPDATE ---
635
+ // --- 因为还要处理 _$key 的自动生成 ---
636
+ const res = await this.create();
637
+ if (res) {
638
+ // --- 创建成功 ---
639
+ return true;
640
+ }
641
+ if (res === false) {
642
+ // --- 系统错误 ---
643
+ return false;
644
+ }
645
+ // --- res === null,唯一键冲突,执行更新 ---
646
+ /** --- 冲突字段列表 --- */
647
+ const conflicts = typeof conflict === 'string' ? [conflict] : conflict;
648
+ const where = {};
649
+ for (const field of conflicts) {
650
+ if (!this._updates[field]) {
651
+ return false;
652
+ }
653
+ where[field] = this._data[field];
654
+ delete this._updates[field];
655
+ }
656
+ return this.save(where);
657
+ }
555
658
  /**
556
659
  * --- 刷新当前模型获取最新数据 ---
557
660
  * @param lock 是否加锁
@@ -581,8 +684,9 @@ export default class Mod {
581
684
  }
582
685
  /**
583
686
  * --- 更新 set 的数据到数据库,有未保存数据时才保存 ---
687
+ * @param where 自定义筛选条件,默认根据主键筛选
584
688
  */
585
- async save() {
689
+ async save(where) {
586
690
  if (Object.keys(this._updates).length === 0) {
587
691
  return true;
588
692
  }
@@ -591,9 +695,9 @@ export default class Mod {
591
695
  for (const k in this._updates) {
592
696
  updates[k] = this._data[k];
593
697
  }
594
- this._sql.update(cstr._$table + (this._index ? ('_' + this._index[0]) : ''), [updates]).where([{
595
- [cstr._$primary]: this._data[cstr._$primary]
596
- }]);
698
+ this._sql.update(cstr._$table + (this._index ? ('_' + this._index[0]) : ''), [updates]).where(where ?? {
699
+ [cstr._$primary]: this._data[cstr._$primary]
700
+ });
597
701
  const r = await this._db.execute(this._sql.getSql(), this._sql.getData());
598
702
  if (r.packet === null) {
599
703
  lCore.log(this._ctr ?? {}, '[MOD][save] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
@@ -1158,7 +1262,6 @@ export default class Mod {
1158
1262
  /**
1159
1263
  * --- 筛选器 ---
1160
1264
  * @param s 筛选条件数组或字符串
1161
- * @param raw 是否包含已被软删除的数据
1162
1265
  */
1163
1266
  filter(s) {
1164
1267
  this._sql.where(s);
@@ -1167,7 +1270,6 @@ export default class Mod {
1167
1270
  /**
1168
1271
  * --- 是 filter 的别名 ---
1169
1272
  * @param s 筛选条件数组或字符串
1170
- * @param raw 是否包含已被软删除的数据
1171
1273
  */
1172
1274
  where(s) {
1173
1275
  return this.filter(s);
package/sys/route.js CHANGED
@@ -470,7 +470,6 @@ export async function run(data) {
470
470
  cctr.setPrototype('_files', middle.getPrototype('_files'));
471
471
  cctr.setPrototype('_post', middle.getPrototype('_post'));
472
472
  cctr.setPrototype('_cookie', cookies);
473
- cctr.setPrototype('_jwt', middle.getPrototype('_jwt'));
474
473
  if (!cctr.getPrototype('_sess') && middle.getPrototype('_sess')) {
475
474
  cctr.setPrototype('_session', middle.getPrototype('_session'));
476
475
  cctr.setPrototype('_sess', middle.getPrototype('_sess'));
@@ -32,6 +32,8 @@ export default class extends sCtr.Ctr {
32
32
  modSplit1(): Promise<void>;
33
33
  modSplit2(): Promise<kebab.Json[]>;
34
34
  modInsert(): Promise<string>;
35
+ modUpsert(): Promise<string>;
36
+ modUpdateList(): Promise<string>;
35
37
  captchaFastbuild(): string;
36
38
  captchaBase64(): string;
37
39
  coreRandom(): string;
@@ -83,8 +85,6 @@ export default class extends sCtr.Ctr {
83
85
  scan3(): Promise<kebab.Json>;
84
86
  private _scanLink;
85
87
  session(): Promise<string | kebab.Json[]>;
86
- jwt(): Promise<string | kebab.Json[]>;
87
- jwt1(): Promise<[number, kebab.Json]>;
88
88
  sql(): string;
89
89
  consistentHash(): string;
90
90
  consistentDistributed(): string;