@maiyunnet/kebab 2.0.7 → 2.0.8

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.
Files changed (83) hide show
  1. package/index.d.ts +11 -1
  2. package/index.js +13 -1
  3. package/lib/buffer.d.ts +25 -0
  4. package/lib/buffer.js +30 -5
  5. package/lib/captcha.d.ts +15 -0
  6. package/lib/captcha.js +20 -0
  7. package/lib/consistent.d.ts +51 -0
  8. package/lib/consistent.js +59 -0
  9. package/lib/core.d.ts +134 -0
  10. package/lib/core.js +176 -0
  11. package/lib/crypto.d.ts +75 -6
  12. package/lib/crypto.js +206 -38
  13. package/lib/db.d.ts +104 -0
  14. package/lib/db.js +126 -0
  15. package/lib/dns.d.ts +51 -0
  16. package/lib/dns.js +54 -2
  17. package/lib/fs.d.ts +100 -0
  18. package/lib/fs.js +118 -0
  19. package/lib/jwt.d.ts +43 -0
  20. package/lib/jwt.js +45 -0
  21. package/lib/kv.d.ts +362 -0
  22. package/lib/kv.js +377 -0
  23. package/lib/lan.d.ts +6 -0
  24. package/lib/lan.js +7 -0
  25. package/lib/net/formdata.d.ts +38 -0
  26. package/lib/net/formdata.js +43 -0
  27. package/lib/net/request.d.ts +62 -0
  28. package/lib/net/request.js +57 -0
  29. package/lib/net/response.d.ts +21 -0
  30. package/lib/net/response.js +16 -0
  31. package/lib/net.d.ts +86 -0
  32. package/lib/net.js +140 -0
  33. package/lib/s3.d.ts +52 -0
  34. package/lib/s3.js +51 -0
  35. package/lib/scan.d.ts +52 -0
  36. package/lib/scan.js +84 -0
  37. package/lib/session.d.ts +31 -0
  38. package/lib/session.js +52 -1
  39. package/lib/sql.d.ts +176 -0
  40. package/lib/sql.js +287 -2
  41. package/lib/ssh/sftp.d.ts +106 -0
  42. package/lib/ssh/sftp.js +106 -0
  43. package/lib/ssh/shell.d.ts +37 -0
  44. package/lib/ssh/shell.js +31 -0
  45. package/lib/ssh.d.ts +32 -0
  46. package/lib/ssh.js +32 -0
  47. package/lib/text.d.ts +131 -0
  48. package/lib/text.js +188 -0
  49. package/lib/time.d.ts +53 -0
  50. package/lib/time.js +55 -0
  51. package/lib/ws.d.ts +68 -0
  52. package/lib/ws.js +74 -0
  53. package/lib/zip.d.ts +53 -0
  54. package/lib/zip.js +73 -0
  55. package/lib/zlib.d.ts +76 -0
  56. package/lib/zlib.js +78 -0
  57. package/main.d.ts +6 -1
  58. package/main.js +11 -1
  59. package/package.json +1 -1
  60. package/sys/child.js +104 -0
  61. package/sys/cmd.js +28 -0
  62. package/sys/ctr.d.ts +166 -0
  63. package/sys/ctr.js +177 -0
  64. package/sys/master.js +63 -0
  65. package/sys/mod.d.ts +266 -0
  66. package/sys/mod.js +335 -0
  67. package/sys/route.d.ts +34 -0
  68. package/sys/route.js +164 -0
  69. package/www/example/ctr/test.d.ts +3 -0
  70. package/www/example/ctr/test.js +63 -1
  71. package/www/example/mod/test.js +14 -0
  72. package/www/example/mod/testdata.js +9 -0
  73. package/www/example/ws/test.js +1 -0
  74. package/.VSCodeCounter/2025-02-14_14-46-44/details.md +0 -82
  75. package/.VSCodeCounter/2025-02-14_14-46-44/diff-details.md +0 -15
  76. package/.VSCodeCounter/2025-02-14_14-46-44/diff.csv +0 -2
  77. package/.VSCodeCounter/2025-02-14_14-46-44/diff.md +0 -19
  78. package/.VSCodeCounter/2025-02-14_14-46-44/diff.txt +0 -22
  79. package/.VSCodeCounter/2025-02-14_14-46-44/results.csv +0 -69
  80. package/.VSCodeCounter/2025-02-14_14-46-44/results.json +0 -1
  81. package/.VSCodeCounter/2025-02-14_14-46-44/results.md +0 -48
  82. package/.VSCodeCounter/2025-02-14_14-46-44/results.txt +0 -118
  83. package/.vscode/tasks.json +0 -15
package/sys/ctr.js CHANGED
@@ -42,9 +42,15 @@ const crypto = __importStar(require("../lib/crypto"));
42
42
  const session = __importStar(require("../lib/session"));
43
43
  const text = __importStar(require("../lib/text"));
44
44
  const sRoute = __importStar(require("../sys/route"));
45
+ /** --- 已加载的 DATA 数据缓存(不是语言包)-- */
45
46
  let loadedData = {};
47
+ /** --- 已加载的语言文件列表 --- */
46
48
  let localeFiles = [];
49
+ /** --- 已经加载的语言包(全局) --- */
47
50
  let localeData = {};
51
+ /**
52
+ * --- 清除已经加载的 data 与语言包文件缓存 ---
53
+ */
48
54
  function clearLocaleData() {
49
55
  loadedData = {};
50
56
  localeFiles = [];
@@ -52,26 +58,45 @@ function clearLocaleData() {
52
58
  }
53
59
  class Ctr {
54
60
  constructor(config, req, res) {
61
+ /** --- 路由参数序列数组 --- */
55
62
  this._param = [];
63
+ /** --- 当前的 action 名 --- */
56
64
  this._action = '';
65
+ /** --- 请求的 header 列表,key 均为小写 --- */
57
66
  this._headers = {};
67
+ /** --- 原始 POST 数据 --- */
58
68
  this._rawPost = {};
69
+ /** --- POST 数据 --- */
59
70
  this._post = {};
71
+ /** --- 原始 input 字符串 */
60
72
  this._input = '';
73
+ /** --- 上传的文件列表 --- */
61
74
  this._files = {};
75
+ /** --- Cookie 数组 --- */
62
76
  this._cookie = {};
77
+ /** --- Jwt 数组 --- */
63
78
  this._jwt = {};
79
+ /** --- Session 数组 --- */
64
80
  this._session = {};
81
+ /** --- Session --- 对象 */
65
82
  this._sess = null;
83
+ /** --- XSRF TOKEN 值 --- */
66
84
  this._xsrf = '';
85
+ /** --- 自定义 http code --- */
67
86
  this._httpCode = 0;
87
+ // --- Kebab: true,Mutton: false,全局常量等对象 ---
88
+ /** --- 当前语言名 --- */
68
89
  this._locale = 'en';
90
+ /** --- 本 ctr 已加载的语言文件列表 --- */
69
91
  this._localeFiles = [];
92
+ /** --- 本 ctr 的 locale data --- */
70
93
  this._localeData = {};
94
+ /** --- 一些需要等待的事项的记录(异步任务、事务) --- */
71
95
  this._waitInfo = {
72
96
  'asyncTask': {
73
97
  'count': 0,
74
98
  'resolve': () => {
99
+ // --- NOTHING ---
75
100
  },
76
101
  'callback': function () {
77
102
  return new Promise((resolve) => {
@@ -81,6 +106,7 @@ class Ctr {
81
106
  },
82
107
  'transaction': 0
83
108
  };
109
+ /** --- auth 对象,user, pwd --- */
84
110
  this._authorization = null;
85
111
  this._config = config;
86
112
  this._req = req;
@@ -89,12 +115,17 @@ class Ctr {
89
115
  }
90
116
  this._cacheTTL = config.set.cacheTtl;
91
117
  }
118
+ /** --- 当前用户连接是否还在连接中 --- */
92
119
  get isAvail() {
93
120
  return this._req.socket.writable;
94
121
  }
122
+ /** --- 获取当前过期时间 --- */
95
123
  get timeout() {
96
124
  return this._timer?.timeout ?? 30_000;
97
125
  }
126
+ /**
127
+ * --- 设置当前过期时间 ---
128
+ */
98
129
  set timeout(num) {
99
130
  if (!this._timer) {
100
131
  return;
@@ -103,6 +134,10 @@ class Ctr {
103
134
  clearTimeout(this._timer.timer);
104
135
  this._timer.timer = setTimeout(this._timer.callback, num);
105
136
  }
137
+ /**
138
+ * --- 执行一段跳出堆栈的异步代码,代码执行完成前,热更新不会杀死当面进程 且 ftmp 临时文件不会被清除 ---
139
+ * @param func 异步代码
140
+ */
106
141
  _asyncTask(func) {
107
142
  ++this._waitInfo.asyncTask.count;
108
143
  setTimeout(() => {
@@ -125,15 +160,26 @@ class Ctr {
125
160
  getPrototype(name) {
126
161
  return this[name];
127
162
  }
163
+ /** --- 设置类内部的 prototype --- */
128
164
  setPrototype(name, val) {
129
165
  this[name] = val;
130
166
  }
167
+ /**
168
+ * --- 实例化后会执行的方法,可重写此方法 ---
169
+ */
131
170
  onLoad() {
132
171
  return true;
133
172
  }
173
+ /**
174
+ * --- 整个结束前会执行本方法,可重写此方法对输出结果再处理一次(Websocket 模式无效) ---
175
+ * @param rtn 之前用户的输出结果
176
+ */
134
177
  onUnload(rtn) {
135
178
  return rtn;
136
179
  }
180
+ /**
181
+ * --- WebSocket 下在建立 Server 连接之前可对 WebSocket 的信息进行配置 ---
182
+ */
137
183
  onUpgrade() {
138
184
  return {};
139
185
  }
@@ -143,19 +189,37 @@ class Ctr {
143
189
  onMessage() {
144
190
  return;
145
191
  }
192
+ /**
193
+ * --- WebSocket 下连接恢复可写入状态后会调用此事件,可重写此方法 ---
194
+ */
146
195
  onDrain() {
147
196
  return;
148
197
  }
198
+ /**
199
+ * --- WebSocket 下连接被终止后会自动被调用的事件,可重写此方法 ---
200
+ */
149
201
  onClose() {
150
202
  return;
151
203
  }
204
+ /**
205
+ * --- 获取截止当前时间的总运行时间 ---
206
+ * @param ms 为 true 为毫秒,否则为秒
207
+ */
152
208
  _getRunTime(ms = false) {
153
209
  const t = process.hrtime.bigint() - this._config.const.startTime;
154
210
  return ms ? Number(t) / 1000000 : Number(t) / 1000000000;
155
211
  }
212
+ /**
213
+ * --- 获取截止当前内存的使用情况 ---
214
+ */
156
215
  _getMemoryUsage() {
157
216
  return process.memoryUsage().rss - this._config.const.startMemory;
158
217
  }
218
+ /**
219
+ * --- 加载视图 ---
220
+ * @param path
221
+ * @param data
222
+ */
159
223
  async _loadView(path, data = {}) {
160
224
  const content = await fs.getContent(this._config.const.viewPath + path + '.ejs', 'utf8');
161
225
  if (!content) {
@@ -167,21 +231,34 @@ class Ctr {
167
231
  data._staticVer = this._config.set.staticVer;
168
232
  data._staticPath = this._config.set.staticPath;
169
233
  data._staticPathFull = this._config.set.staticPathFull;
234
+ // --- 语言包 ---
170
235
  data.l = (key, data) => {
171
236
  return this._l(key, data);
172
237
  };
173
238
  return lCore.purify(ejs.render(content, data));
174
239
  }
240
+ /**
241
+ * --- 检测提交的数据类型 ---
242
+ * @param input 要校验的输入项
243
+ * @param rule 规则
244
+ * @param rtn 返回值
245
+ */
175
246
  _checkInput(input, rule, rtn) {
247
+ // --- 遍历规则 ---
248
+ // --- input, {'xx': ['require', '> 6', [0, 'xx 必须大于 6']], 'yy': [], '_xsrf': []], rtn ---
176
249
  for (const key in rule) {
177
250
  const val = rule[key];
251
+ // --- key 就是上面的 xx ---
178
252
  if (input[key] === undefined) {
253
+ // --- 原值不存在则设定为 null ---
179
254
  input[key] = null;
180
255
  }
256
+ // --- 判断是否需要遍历 val ---
181
257
  const c = val.length;
182
258
  if (c === 0) {
183
259
  continue;
184
260
  }
261
+ // --- ['require', '> 6', [0, 'xx 必须大于 6']] ---
185
262
  const lastK = c - 1;
186
263
  if ((val[lastK][0] === undefined) || (val[lastK][1] === undefined) || !Number.isInteger(val[lastK][0]) || (typeof val[lastK][1] !== 'string')) {
187
264
  rtn[0] = 0;
@@ -199,10 +276,12 @@ class Ctr {
199
276
  }
200
277
  return false;
201
278
  }
279
+ // --- 判断提交的数据是否在此 array 之内,若没有提交数据,则自动设置为第一个项 ---
202
280
  if (input[key] === null) {
203
281
  input[key] = v[0];
204
282
  }
205
283
  else if (!v.includes(input[key])) {
284
+ // --- 不在 ---
206
285
  rtn[0] = val[lastK][0];
207
286
  rtn[1] = val[lastK][1];
208
287
  if (val[lastK][2]) {
@@ -212,6 +291,7 @@ class Ctr {
212
291
  }
213
292
  }
214
293
  else if (v instanceof RegExp) {
294
+ // --- 正则 ---
215
295
  if (input[key] !== null && !v.test(input[key])) {
216
296
  rtn[0] = val[lastK][0];
217
297
  rtn[1] = val[lastK][1];
@@ -222,6 +302,7 @@ class Ctr {
222
302
  }
223
303
  }
224
304
  else if (typeof v === 'object' && v.type !== undefined) {
305
+ // --- core.checkType ---
225
306
  if (input[key] !== null) {
226
307
  const r = lCore.checkType(input[key], v.type);
227
308
  if (r) {
@@ -297,6 +378,7 @@ class Ctr {
297
378
  case 'bool':
298
379
  case 'boolean': {
299
380
  if (input[key] !== null && (typeof input[key] !== 'boolean')) {
381
+ // --- 如果不是 bool 直接失败,字符串的 true, false 也会失败 ---
300
382
  rtn[0] = val[lastK][0];
301
383
  rtn[1] = val[lastK][1];
302
384
  if (val[lastK][2]) {
@@ -308,6 +390,7 @@ class Ctr {
308
390
  }
309
391
  case 'string': {
310
392
  if (input[key] !== null && (typeof input[key] !== 'string')) {
393
+ // --- 如果不是 string 直接失败 ---
311
394
  rtn[0] = val[lastK][0];
312
395
  rtn[1] = val[lastK][1];
313
396
  if (val[lastK][2]) {
@@ -321,6 +404,7 @@ class Ctr {
321
404
  let match;
322
405
  if (input[key] !== null) {
323
406
  if (v[0] === '/') {
407
+ // --- 正则 ---
324
408
  if (!(new RegExp(v.slice(1, -1))).test(input[key])) {
325
409
  rtn[0] = val[lastK][0];
326
410
  rtn[1] = val[lastK][1];
@@ -331,6 +415,7 @@ class Ctr {
331
415
  }
332
416
  }
333
417
  else if ((match = /^([><=]+) *([0-9]+)$/.exec(v))) {
418
+ // --- 判断表达式 ---
334
419
  let needReturn = false;
335
420
  const inputNum = Number(input[key]);
336
421
  const num = Number(match[2]);
@@ -402,11 +487,22 @@ class Ctr {
402
487
  }
403
488
  return true;
404
489
  }
490
+ /**
491
+ * --- 检测提交的数据类型(会检测 XSRF) ---
492
+ * @param input 要校验的输入项
493
+ * @param rule 规则
494
+ * @param rtn 返回值
495
+ */
405
496
  _checkXInput(input, rule, rtn) {
406
497
  rule['_xsrf'] ??= ['require', this._cookie['XSRF-TOKEN'], [0, 'Bad request, no permission.']];
407
498
  return this._checkInput(input, rule, rtn);
408
499
  }
500
+ /**
501
+ * --- 当前页面开启 XSRF 支持(主要检测 cookie 是否存在) ---
502
+ * --- 如果当前页面有 CDN,请不要使用 ---
503
+ */
409
504
  _enabledXsrf() {
505
+ // --- 设置 XSRF 值 ---
410
506
  if (this._cookie['XSRF-TOKEN'] === undefined) {
411
507
  const xsrf = lCore.random(16, lCore.RANDOM_LUN);
412
508
  this._xsrf = xsrf;
@@ -420,9 +516,17 @@ class Ctr {
420
516
  this._xsrf = this._cookie['XSRF-TOKEN'];
421
517
  }
422
518
  }
519
+ /**
520
+ * --- 获取 Auth 字符串,用于客户端提交 ---
521
+ * @param user 用户名
522
+ * @param pwd 密码
523
+ */
423
524
  _getBasicAuth(user, pwd) {
424
525
  return 'Basic ' + crypto.base64Encode(user + ':' + pwd);
425
526
  }
527
+ /**
528
+ * --- 根据用户 ua 获取当前用户的设备类型 ---
529
+ */
426
530
  _device() {
427
531
  const ua = this._req.headers['user-agent']?.toLowerCase();
428
532
  if (!ua) {
@@ -437,6 +541,9 @@ class Ctr {
437
541
  }
438
542
  return 'unknown';
439
543
  }
544
+ /**
545
+ * --- 通过 header 或 _auth 获取鉴权信息或 JWT 信息(不解析) ---
546
+ */
440
547
  getAuthorization() {
441
548
  if (this._authorization !== null) {
442
549
  return this._authorization;
@@ -459,6 +566,7 @@ class Ctr {
459
566
  return false;
460
567
  }
461
568
  if (authArr[1].includes('.')) {
569
+ // --- 不解析,解析使用 JWT 类解析 ---
462
570
  return authArr[1];
463
571
  }
464
572
  if (!(auth = crypto.base64Decode(authArr[1]))) {
@@ -468,6 +576,10 @@ class Ctr {
468
576
  this._authorization = { 'user': authArr[0], 'pwd': authArr[1] ?? '' };
469
577
  return this._authorization;
470
578
  }
579
+ /**
580
+ * --- 获取 data 数据 ---
581
+ * @param path 文件路径(不含扩展名)
582
+ */
471
583
  async _loadData(path) {
472
584
  const realPath = this._config.const.dataPath + path + '.json';
473
585
  if (loadedData[realPath]) {
@@ -481,22 +593,41 @@ class Ctr {
481
593
  loadedData[realPath] = json;
482
594
  return json;
483
595
  }
596
+ /**
597
+ * --- 跳转(302临时跳转),支持相对本项目根路径的路径或绝对路径 ---
598
+ * @param location 相对或绝对网址
599
+ */
484
600
  _location(location) {
485
601
  if (this._res) {
486
602
  this._res.setHeader('location', text.urlResolve(this._config.const.urlBase, location));
603
+ // this._res.writeHead(302); Kebab 中要在最后设置,否则会报错:ERR_HTTP_HEADERS_SENT
487
604
  }
488
605
  return false;
489
606
  }
607
+ /**
608
+ * --- 开启 Session ---
609
+ * @param link Kv 或 Db 实例
610
+ * @param auth 设为 true 则从头 Authorization 或 post _auth 值读取 token
611
+ * @param opt name, ttl, ssl, sqlPre
612
+ */
490
613
  async _startSession(link, auth = false, opt = {}) {
491
614
  this._sess = new session.Session();
492
615
  await this._sess.init(this, link, auth, opt);
493
616
  }
617
+ // --- 本地化 ---
618
+ /**
619
+ * --- 设定语言并加载语言包 ---
620
+ * @param loc 要加载的目标语言
621
+ * @param pkg 包名,为空自动填充为 default
622
+ */
494
623
  async _loadLocale(loc, pkg = 'default') {
495
624
  const lName = loc + '.' + pkg;
496
625
  this._locale = loc;
497
626
  if (!this._localeFiles.includes(lName)) {
627
+ // --- 检测全局缓存是否加载 ---
498
628
  const lPath = this._config.const.dataPath + 'locale/' + lName;
499
629
  if (!localeFiles.includes(lPath)) {
630
+ // --- 全局缓存没有,先加载全局缓存 ---
500
631
  const locData = await this._loadData('locale/' + lName);
501
632
  if (locData === null) {
502
633
  return false;
@@ -509,6 +640,7 @@ class Ctr {
509
640
  else {
510
641
  this._locale = loc;
511
642
  }
643
+ // --- 缓存中一定有文件 ---
512
644
  this._localeData[loc] ??= {};
513
645
  for (const key in localeData[lPath]) {
514
646
  this._localeData[loc][key] = localeData[lPath][key];
@@ -531,6 +663,10 @@ class Ctr {
531
663
  }
532
664
  }
533
665
  }
666
+ /**
667
+ * --- 根据当前后台语言包设置情况获取 JSON 字符串传输到前台 ---
668
+ * @return string
669
+ */
534
670
  _getLocaleJsonString() {
535
671
  if (this._localeData[this._locale] !== undefined) {
536
672
  return text.stringifyJson(this._localeData[this._locale]);
@@ -539,9 +675,16 @@ class Ctr {
539
675
  return '{}';
540
676
  }
541
677
  }
678
+ /**
679
+ * --- 获取当前语言名 ---
680
+ */
542
681
  _getLocale() {
543
682
  return this._locale;
544
683
  }
684
+ /**
685
+ * --- 开启跨域请求 ---
686
+ * 返回 true 接续执行,返回 false 需要中断用户本次访问(options请求)
687
+ */
545
688
  _cross() {
546
689
  this._res.setHeader('access-control-allow-origin', '*');
547
690
  this._res.setHeader('access-control-allow-headers', '*');
@@ -552,6 +695,13 @@ class Ctr {
552
695
  }
553
696
  return true;
554
697
  }
698
+ // --- 以下:Mutton: false, Kebab: true ---
699
+ /**
700
+ * --- 获取语言包值 ---
701
+ * @param key
702
+ * @param data 要替换的数据
703
+ */
704
+ // eslint-disable-next-line @typescript-eslint/naming-convention
555
705
  _l(key, data) {
556
706
  if (!this._localeData[this._locale]) {
557
707
  return '[LocaleError]' + key;
@@ -573,24 +723,51 @@ class Ctr {
573
723
  return this._localeData[this._locale][key];
574
724
  }
575
725
  }
726
+ /**
727
+ * --- 发送 socket 文本 ---
728
+ * @param data 要发送的信息
729
+ */
576
730
  _writeText(data) {
577
731
  return this._socket.writeText(data);
578
732
  }
733
+ /**
734
+ * --- 发送结果对象文本 ---
735
+ * @param data 要发送的结果对象,如 [0, 'Failed.']
736
+ */
579
737
  _writeResult(data) {
580
738
  return this._socket.writeResult(data);
581
739
  }
740
+ /**
741
+ * --- 发送 socket 二进制 ---
742
+ * @param data 要发送的信息
743
+ */
582
744
  _writeBinary(data) {
583
745
  return this._socket.writeBinary(data);
584
746
  }
747
+ /**
748
+ * --- 发送 socket ping ---
749
+ * @param data 要发送的信息
750
+ */
585
751
  _ping() {
586
752
  return this._socket.ping();
587
753
  }
754
+ /**
755
+ * --- 发送 socket pong ---
756
+ * @param data 要发送的信息
757
+ */
588
758
  _pong() {
589
759
  return this._socket.pong();
590
760
  }
761
+ /**
762
+ * --- 主动关闭当前 socket 连接 ---
763
+ */
591
764
  _end() {
592
765
  this._socket.end();
593
766
  }
767
+ /**
768
+ * --- 获取 formdata 的信息 ---
769
+ * @param events 文件处理情况
770
+ */
594
771
  async _handleFormData(events = {}) {
595
772
  const rtn = await sRoute.getFormData(this._req, events);
596
773
  if (!rtn) {