@maiyunnet/kebab 9.2.7 → 9.3.1
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/doc/kebab-rag.md +177 -104
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/s3.d.ts +1 -1
- package/lib/s3.js +1 -1
- package/lib/sql.d.ts +15 -1
- package/lib/sql.js +34 -2
- package/lib/undici.js +2 -2
- package/package.json +1 -1
- package/sys/master.js +88 -54
- package/sys/mod.d.ts +6 -0
- package/sys/mod.js +4 -0
- package/www/example/ctr/test.js +23 -0
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.
|
|
9
|
+
export const VER = '9.3.1';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/s3.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Project: Kebab, User:
|
|
2
|
+
* Project: Kebab, User: JianSuoQiYue
|
|
3
3
|
* Date: 2024-2-18 18:32:45
|
|
4
4
|
* Last: 2024-2-18 18:32:47, 2024-3-16 16:42:27, 2024-5-31 21:36:26, 2024-7-8 00:28:42, 2024-7-19 11:32:43, 2025-6-10 21:45:34, 2025-12-5 23:42:59
|
|
5
5
|
*/
|
package/lib/s3.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Project: Kebab, User:
|
|
2
|
+
* Project: Kebab, User: JianSuoQiYue
|
|
3
3
|
* Date: 2024-2-18 18:32:45
|
|
4
4
|
* Last: 2024-2-18 18:32:47, 2024-3-16 16:42:27, 2024-5-31 21:36:26, 2024-7-8 00:28:42, 2024-7-19 11:32:43, 2025-6-10 21:45:34, 2025-12-5 23:42:59
|
|
5
5
|
*/
|
package/lib/sql.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Project: Kebab, User: JianSuoQiYue
|
|
3
3
|
* Date: 2019-5-27 20:18:50
|
|
4
|
-
* Last: 2020-3-29 19:37:25, 2022-07-24 22:38:11, 2023-5-24 18:49:18, 2023-6-13 22:20:21, 2023-12-11 13:58:54, 2023-12-14 13:14:40, 2023-12-21 00:04:40, 2024-4-11 19:29:29, 2024-9-2 17:15:28, 2025-8-3 21:28:18, 2025-11-9 16:20:23
|
|
4
|
+
* Last: 2020-3-29 19:37:25, 2022-07-24 22:38:11, 2023-5-24 18:49:18, 2023-6-13 22:20:21, 2023-12-11 13:58:54, 2023-12-14 13:14:40, 2023-12-21 00:04:40, 2024-4-11 19:29:29, 2024-9-2 17:15:28, 2025-8-3 21:28:18, 2025-11-9 16:20:23, 2026-4-30 13:49:44
|
|
5
5
|
*/
|
|
6
6
|
import * as kebab from '#kebab/index.js';
|
|
7
7
|
import * as ctr from '#kebab/sys/ctr.js';
|
|
@@ -230,6 +230,11 @@ export declare class Sql {
|
|
|
230
230
|
* @param str
|
|
231
231
|
*/
|
|
232
232
|
private _isField;
|
|
233
|
+
/**
|
|
234
|
+
* --- 判断传入值是否是 value 对象(由 value() 函数创建) ---
|
|
235
|
+
* @param arg
|
|
236
|
+
*/
|
|
237
|
+
private _isValue;
|
|
233
238
|
/** --- 获取占位符 --- */
|
|
234
239
|
private _placeholder;
|
|
235
240
|
/**
|
|
@@ -269,6 +274,15 @@ export declare function column(field: string): {
|
|
|
269
274
|
'token': string;
|
|
270
275
|
'value': string;
|
|
271
276
|
};
|
|
277
|
+
/**
|
|
278
|
+
* --- 创建字面量值对象,用于 where 条件中 v[0] 需要是值而非字段名的场景 ---
|
|
279
|
+
* --- 例:[value('hello'), 'IN', column('tags')] ---
|
|
280
|
+
*/
|
|
281
|
+
export declare function value(val: kebab.DbValue): {
|
|
282
|
+
'type': 'value';
|
|
283
|
+
'token': string;
|
|
284
|
+
'value': kebab.DbValue;
|
|
285
|
+
};
|
|
272
286
|
/**
|
|
273
287
|
* --- 将对象转换为 JSON 字符串并避开类型检查,用于适配 PostgreSQL 的 jsonb 字段 ---
|
|
274
288
|
* @param obj 要转换的 JSON 对象
|
package/lib/sql.js
CHANGED
|
@@ -26,6 +26,8 @@ export var EJSON;
|
|
|
26
26
|
})(EJSON || (EJSON = {}));
|
|
27
27
|
/** --- field 用 token --- */
|
|
28
28
|
let columnToken = '';
|
|
29
|
+
/** --- value 用 token --- */
|
|
30
|
+
let valueToken = '';
|
|
29
31
|
export class Sql {
|
|
30
32
|
/** --- ctr 对象 --- */
|
|
31
33
|
_ctr;
|
|
@@ -549,13 +551,19 @@ export class Sql {
|
|
|
549
551
|
else {
|
|
550
552
|
// --- 2, 6 ---
|
|
551
553
|
const nv = v[2];
|
|
554
|
+
// --- v[0] 也可以是 value() 包裹的字面量值,而不一定是字段名 ---
|
|
555
|
+
const isv0 = this._isValue(v[0]);
|
|
556
|
+
const v0sql = isv0 ? this._placeholder() : this.field(v[0]);
|
|
557
|
+
if (isv0) {
|
|
558
|
+
data.push(v[0].value);
|
|
559
|
+
}
|
|
552
560
|
const isf = this._isField(nv);
|
|
553
561
|
if (isf) {
|
|
554
562
|
// --- 6. field ---
|
|
555
|
-
sql +=
|
|
563
|
+
sql += v0sql + ' ' + v[1] + ' ' + this.field(nv.value) + ' AND ';
|
|
556
564
|
}
|
|
557
565
|
else {
|
|
558
|
-
sql +=
|
|
566
|
+
sql += v0sql + ' ' + v[1] + ' ' + this._placeholder() + ' AND ';
|
|
559
567
|
data.push(nv);
|
|
560
568
|
}
|
|
561
569
|
}
|
|
@@ -962,6 +970,16 @@ export class Sql {
|
|
|
962
970
|
}
|
|
963
971
|
return true;
|
|
964
972
|
}
|
|
973
|
+
/**
|
|
974
|
+
* --- 判断传入值是否是 value 对象(由 value() 函数创建) ---
|
|
975
|
+
* @param arg
|
|
976
|
+
*/
|
|
977
|
+
_isValue(arg) {
|
|
978
|
+
if (arg === null || typeof arg !== 'object' || arg.type !== 'value' || arg.token !== valueToken || arg.value === undefined) {
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
965
983
|
/** --- 获取占位符 --- */
|
|
966
984
|
_placeholder() {
|
|
967
985
|
return this._service === ESERVICE.MYSQL ? '?' : `$${this._placeholderCounter++}`;
|
|
@@ -1172,6 +1190,20 @@ export function column(field) {
|
|
|
1172
1190
|
'value': field
|
|
1173
1191
|
};
|
|
1174
1192
|
}
|
|
1193
|
+
/**
|
|
1194
|
+
* --- 创建字面量值对象,用于 where 条件中 v[0] 需要是值而非字段名的场景 ---
|
|
1195
|
+
* --- 例:[value('hello'), 'IN', column('tags')] ---
|
|
1196
|
+
*/
|
|
1197
|
+
export function value(val) {
|
|
1198
|
+
if (!valueToken) {
|
|
1199
|
+
valueToken = lCore.random(8, lCore.RANDOM_LUNS);
|
|
1200
|
+
}
|
|
1201
|
+
return {
|
|
1202
|
+
'token': valueToken,
|
|
1203
|
+
'type': 'value',
|
|
1204
|
+
'value': val
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1175
1207
|
/**
|
|
1176
1208
|
* --- 将对象转换为 JSON 字符串并避开类型检查,用于适配 PostgreSQL 的 jsonb 字段 ---
|
|
1177
1209
|
* @param obj 要转换的 JSON 对象
|
package/lib/undici.js
CHANGED
|
@@ -460,8 +460,8 @@ export function getFormData() {
|
|
|
460
460
|
}
|
|
461
461
|
/** --- proxy 要剔除的基础头部 --- */
|
|
462
462
|
const proxyContinueHeaders = [
|
|
463
|
-
'host', 'connection', 'http-code', 'http-url',
|
|
464
|
-
'transfer-encoding'
|
|
463
|
+
'host', 'connection', 'keep-alive', 'http-code', 'http-url',
|
|
464
|
+
'transfer-encoding', 'upgrade', 'proxy-connection'
|
|
465
465
|
];
|
|
466
466
|
/**
|
|
467
467
|
* --- 剔除不代理的 header,返回新的 header ---
|
package/package.json
CHANGED
package/sys/master.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Project: Kebab, User: JianSuoQiYue
|
|
3
3
|
* Date: 2019-5-2 21:03:42
|
|
4
|
-
* Last: 2020-3-7 10:33:17, 2022-07-22 13:40:10, 2022-09-06 22:40:58, 2024-2-7 01:44:59, 2024-7-2 15:17:09, 2025-6-13 13:06:43, 2025-12-5 13:15:03, 2026-3-22 00:00:00
|
|
4
|
+
* Last: 2020-3-7 10:33:17, 2022-07-22 13:40:10, 2022-09-06 22:40:58, 2024-2-7 01:44:59, 2024-7-2 15:17:09, 2025-6-13 13:06:43, 2025-12-5 13:15:03, 2026-3-22 00:00:00, 2026-4-30 13:49:44
|
|
5
5
|
*/
|
|
6
6
|
import cluster from 'cluster';
|
|
7
7
|
import * as os from 'os';
|
|
@@ -543,67 +543,101 @@ function startFileWatcher() {
|
|
|
543
543
|
/** --- 是否正在重启中 --- */
|
|
544
544
|
let restarting = false;
|
|
545
545
|
/**
|
|
546
|
-
* ---
|
|
547
|
-
*
|
|
546
|
+
* --- 不需要监听的目录名集合,在 Linux 上 fs.watch recursive 会为每个子目录注册 inotify ---
|
|
547
|
+
* --- 这些目录若被监听会大量消耗系统 file watcher 配额(ENOSPC),必须跳过 ---
|
|
548
|
+
*/
|
|
549
|
+
const SKIP_DIRS = new Set(['.git', '.svn', '.hg', 'node_modules', 'log', 'ftmp']);
|
|
550
|
+
/** --- 触发 worker 重载 --- */
|
|
551
|
+
const triggerReload = (formatName) => {
|
|
552
|
+
if (debounceTimer) {
|
|
553
|
+
clearTimeout(debounceTimer);
|
|
554
|
+
}
|
|
555
|
+
// --- 防抖:500ms 内多次变化只触发一次 ---
|
|
556
|
+
debounceTimer = setTimeout(() => {
|
|
557
|
+
debounceTimer = null;
|
|
558
|
+
if (restarting) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
restarting = true;
|
|
562
|
+
lCore.display(`[HMR] File changed: ${formatName}, reloading workers...`);
|
|
563
|
+
(async () => {
|
|
564
|
+
// --- 为所有子进程发送 stop 信息并重启 ---
|
|
565
|
+
for (const pid in workerList) {
|
|
566
|
+
workerList[pid].worker.send({
|
|
567
|
+
'action': 'stop'
|
|
568
|
+
});
|
|
569
|
+
await createChildProcess(workerList[pid].cpu);
|
|
570
|
+
delete workerList[pid];
|
|
571
|
+
}
|
|
572
|
+
restarting = false;
|
|
573
|
+
lCore.display('[HMR] All workers reloaded.');
|
|
574
|
+
})().catch((e) => {
|
|
575
|
+
restarting = false;
|
|
576
|
+
lCore.display('[HMR] Reload error:', e);
|
|
577
|
+
});
|
|
578
|
+
}, 500);
|
|
579
|
+
};
|
|
580
|
+
/**
|
|
581
|
+
* --- 递归收集指定根目录下所有需要监听的子目录(跳过 SKIP_DIRS 和隐藏目录)---
|
|
582
|
+
* @param dir 当前目录路径
|
|
583
|
+
* @param result 收集结果数组
|
|
584
|
+
*/
|
|
585
|
+
const collectDirs = async (dir, result) => {
|
|
586
|
+
let entries;
|
|
587
|
+
try {
|
|
588
|
+
entries = await fs.promises.readdir(dir, { 'withFileTypes': true });
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
for (const entry of entries) {
|
|
594
|
+
if (!entry.isDirectory()) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
// --- 跳过隐藏目录(以 . 开头)及无需监听的目录 ---
|
|
598
|
+
if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) {
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const subDir = `${dir}/${entry.name}`;
|
|
602
|
+
result.push(subDir);
|
|
603
|
+
await collectDirs(subDir, result);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
/**
|
|
607
|
+
* --- 监听指定目录下所有需要监听的目录(非递归逐个注册,避免 OS 级别监听到 .git 等目录)---
|
|
608
|
+
* @param dir 要监听的根目录路径
|
|
548
609
|
*/
|
|
549
610
|
const watchDir = async (dir) => {
|
|
611
|
+
// --- 去除末尾的路径分隔符,防止拼接时产生双斜杠 ---
|
|
612
|
+
dir = dir.replace(/\/+$/, '');
|
|
550
613
|
if (!await lFs.isDir(dir)) {
|
|
551
614
|
return;
|
|
552
615
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
if (!formatName.endsWith('.js') &&
|
|
561
|
-
!formatName.endsWith('.json')) {
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
// --- 忽略日志目录、临时目录、git 目录和 node_modules ---
|
|
565
|
-
if (formatName.includes('log/') ||
|
|
566
|
-
formatName.includes('ftmp/') ||
|
|
567
|
-
formatName.includes('node_modules/') ||
|
|
568
|
-
formatName.includes('.git/')) {
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
// --- 防抖:500ms 内多次变化只触发一次 ---
|
|
572
|
-
if (debounceTimer) {
|
|
573
|
-
clearTimeout(debounceTimer);
|
|
574
|
-
}
|
|
575
|
-
debounceTimer = setTimeout(() => {
|
|
576
|
-
debounceTimer = null;
|
|
577
|
-
if (restarting) {
|
|
616
|
+
// --- 收集所有需要监听的目录(含根目录本身)---
|
|
617
|
+
const dirs = [dir];
|
|
618
|
+
await collectDirs(dir, dirs);
|
|
619
|
+
for (const d of dirs) {
|
|
620
|
+
try {
|
|
621
|
+
const watcher = fs.watch(d, (eventType, filename) => {
|
|
622
|
+
if (!filename) {
|
|
578
623
|
return;
|
|
579
624
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
(
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
restarting = false;
|
|
595
|
-
lCore.display('[HMR] Reload error:', e);
|
|
596
|
-
});
|
|
597
|
-
}, 500);
|
|
598
|
-
});
|
|
599
|
-
watcher.on('error', (err) => {
|
|
600
|
-
lCore.display(`[HMR] Watcher error on ${dir}:`, err);
|
|
601
|
-
});
|
|
602
|
-
lCore.display(`[HMR] Watching directory: ${dir}`);
|
|
603
|
-
}
|
|
604
|
-
catch (err) {
|
|
605
|
-
lCore.display(`[HMR] Cannot watch directory: ${dir}`, err);
|
|
625
|
+
const formatName = filename.replace(/\\/g, '/');
|
|
626
|
+
// --- 仅关注 .js 文件和 .json 配置文件的变更 ---
|
|
627
|
+
if (!formatName.endsWith('.js') && !formatName.endsWith('.json')) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
triggerReload(formatName);
|
|
631
|
+
});
|
|
632
|
+
watcher.on('error', (err) => {
|
|
633
|
+
lCore.display(`[HMR] Watcher error on ${d}:`, err);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
catch (err) {
|
|
637
|
+
lCore.display(`[HMR] Cannot watch directory: ${d}`, err);
|
|
638
|
+
}
|
|
606
639
|
}
|
|
640
|
+
lCore.display(`[HMR] Watching directory: ${dir} (${dirs.length} dirs)`);
|
|
607
641
|
};
|
|
608
642
|
// --- 监听 www/ 目录(用户项目代码)---
|
|
609
643
|
watchDir(kebab.WWW_CWD).catch(() => { });
|
package/sys/mod.d.ts
CHANGED
|
@@ -89,6 +89,12 @@ export default class Mod {
|
|
|
89
89
|
'token': string;
|
|
90
90
|
'value': string;
|
|
91
91
|
};
|
|
92
|
+
/** --- 创建字面量值对象,用于 where 条件中 v[0] 需要是值而非字段名的场景 --- */
|
|
93
|
+
static value(val: kebab.DbValue): {
|
|
94
|
+
'type': 'value';
|
|
95
|
+
'token': string;
|
|
96
|
+
'value': kebab.DbValue;
|
|
97
|
+
};
|
|
92
98
|
/** --- 创建 JSON 字符串对象,用于 PGSQL 的 jsonb 字段 --- */
|
|
93
99
|
static json(obj: kebab.Json): any;
|
|
94
100
|
/**
|
package/sys/mod.js
CHANGED
|
@@ -119,6 +119,10 @@ export default class Mod {
|
|
|
119
119
|
static column(field) {
|
|
120
120
|
return lSql.column(field);
|
|
121
121
|
}
|
|
122
|
+
/** --- 创建字面量值对象,用于 where 条件中 v[0] 需要是值而非字段名的场景 --- */
|
|
123
|
+
static value(val) {
|
|
124
|
+
return lSql.value(val);
|
|
125
|
+
}
|
|
122
126
|
/** --- 创建 JSON 字符串对象,用于 PGSQL 的 jsonb 字段 --- */
|
|
123
127
|
static json(obj) {
|
|
124
128
|
return lSql.json(obj);
|
package/www/example/ctr/test.js
CHANGED
|
@@ -3438,6 +3438,29 @@ Result:<pre id="result">Nothing.</pre>`);
|
|
|
3438
3438
|
]);</pre>
|
|
3439
3439
|
<b>getSql() :</b> ${s}<br>
|
|
3440
3440
|
<b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
|
|
3441
|
+
<b>format() :</b> ${sql.format(s, sd)}<hr>`);
|
|
3442
|
+
// --- 正则匹配:MySQL 用 REGEXP,PostgreSQL 用 ~ ---
|
|
3443
|
+
// --- value() 对比 column():左侧为字面量值,右侧为字段 ---
|
|
3444
|
+
s = sql.select('*', 'user').where([
|
|
3445
|
+
[lSql.value('info'), this._get['s'] === 'pgsql' ? '~' : 'REGEXP', lSql.column('pattern')]
|
|
3446
|
+
]).getSql();
|
|
3447
|
+
sd = sql.getData();
|
|
3448
|
+
echo.push(`<pre>sql.select('*', 'user').where([
|
|
3449
|
+
[lSql.value('info'), '${this._get['s'] === 'pgsql' ? '~' : 'REGEXP'}', lSql.column('pattern')]
|
|
3450
|
+
]);</pre>
|
|
3451
|
+
<b>getSql() :</b> ${s}<br>
|
|
3452
|
+
<b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
|
|
3453
|
+
<b>format() :</b> ${sql.format(s, sd)}<hr>`);
|
|
3454
|
+
// --- value() 与普通字段混用 ---
|
|
3455
|
+
s = sql.select('*', 'user').where([
|
|
3456
|
+
[lSql.value(10), '<', lSql.column('age')]
|
|
3457
|
+
]).getSql();
|
|
3458
|
+
sd = sql.getData();
|
|
3459
|
+
echo.push(`<pre>sql.select('*', 'user').where([
|
|
3460
|
+
[lSql.value(10), '<', lSql.column('age')]
|
|
3461
|
+
]);</pre>
|
|
3462
|
+
<b>getSql() :</b> ${s}<br>
|
|
3463
|
+
<b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
|
|
3441
3464
|
<b>format() :</b> ${sql.format(s, sd)}`);
|
|
3442
3465
|
break;
|
|
3443
3466
|
}
|