befly 3.16.11 → 3.17.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/befly.js CHANGED
@@ -10505,6 +10505,13 @@ var parse5 = function(p) {
10505
10505
  };
10506
10506
  };
10507
10507
  // configs/constConfig.js
10508
+ var STRING_KIND_TYPES = ["char", "varchar", "enum", "tinytext", "text", "mediumtext", "longtext"];
10509
+ var TEXT_KIND_TYPES = ["tinytext", "text", "mediumtext", "longtext"];
10510
+ var INT_KIND_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint"];
10511
+ var DECIMAL_KIND_TYPES = ["decimal"];
10512
+ var FLOAT_KIND_TYPES = ["float", "double"];
10513
+ var JSON_KIND_TYPES = ["json"];
10514
+ var ENUM_KIND_TYPES = ["enum"];
10508
10515
  var RUN_MODE_VALUES = ["development", "production"];
10509
10516
  var DB_ID_MODE_VALUES = ["timeId", "autoId"];
10510
10517
 
@@ -10542,6 +10549,18 @@ function isNullable(value, extraValues = []) {
10542
10549
  }
10543
10550
  return false;
10544
10551
  }
10552
+ function isMinLengthString(value, minLength) {
10553
+ if (typeof value !== "string") {
10554
+ return false;
10555
+ }
10556
+ if (!Number.isFinite(minLength)) {
10557
+ return false;
10558
+ }
10559
+ if (minLength <= 0) {
10560
+ return true;
10561
+ }
10562
+ return value.trim().length >= minLength;
10563
+ }
10545
10564
  function isNonEmptyString(value) {
10546
10565
  return typeof value === "string" && value.trim().length > 0;
10547
10566
  }
@@ -10782,6 +10801,22 @@ function sanitizeLogObject(obj, options) {
10782
10801
  }
10783
10802
 
10784
10803
  // utils/util.js
10804
+ function canConvertToNumber(value) {
10805
+ const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
10806
+ const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
10807
+ if (value > maxSafe || value < minSafe) {
10808
+ return null;
10809
+ }
10810
+ const asNumber = Number(value);
10811
+ if (!Number.isSafeInteger(asNumber)) {
10812
+ return null;
10813
+ }
10814
+ const text = String(asNumber);
10815
+ if (text.includes("e") || text.includes("E")) {
10816
+ return null;
10817
+ }
10818
+ return asNumber;
10819
+ }
10785
10820
  function getTypeTag(value) {
10786
10821
  if (value === null)
10787
10822
  return "null";
@@ -10814,6 +10849,67 @@ function normalizePositiveInt(value, fallback, min, max) {
10814
10849
  return max;
10815
10850
  return v;
10816
10851
  }
10852
+ function toWordParts(input) {
10853
+ const normalized = String(input).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim();
10854
+ if (normalized.length === 0) {
10855
+ return [];
10856
+ }
10857
+ return normalized.split(/\s+/).filter((p) => p.length > 0);
10858
+ }
10859
+ function upperFirst(s) {
10860
+ if (s.length === 0) {
10861
+ return s;
10862
+ }
10863
+ return s.charAt(0).toUpperCase() + s.slice(1);
10864
+ }
10865
+ function camelCase(input) {
10866
+ const parts = toWordParts(input);
10867
+ if (parts.length === 0) {
10868
+ return "";
10869
+ }
10870
+ const firstPart = parts[0];
10871
+ if (!firstPart) {
10872
+ return "";
10873
+ }
10874
+ const first = firstPart.toLowerCase();
10875
+ const rest = parts.slice(1).map((p) => upperFirst(p.toLowerCase()));
10876
+ return [first].concat(rest).join("");
10877
+ }
10878
+ function normalizeToWords(input) {
10879
+ return String(input).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim();
10880
+ }
10881
+ function snakeCase(input) {
10882
+ const normalized = normalizeToWords(input);
10883
+ if (normalized.length === 0) {
10884
+ return "";
10885
+ }
10886
+ return normalized.split(/\s+/).filter((p) => p.length > 0).map((p) => p.toLowerCase()).join("_");
10887
+ }
10888
+ var keysToCamel = (obj) => {
10889
+ if (!obj || !isPlainObject2(obj))
10890
+ return obj;
10891
+ const result = {};
10892
+ for (const [key, value] of Object.entries(obj)) {
10893
+ const camelKey = camelCase(key);
10894
+ result[camelKey] = value;
10895
+ }
10896
+ return result;
10897
+ };
10898
+ var keysToSnake = (obj) => {
10899
+ if (!obj || !isPlainObject2(obj))
10900
+ return obj;
10901
+ const result = {};
10902
+ for (const [key, value] of Object.entries(obj)) {
10903
+ const snakeKey = snakeCase(key);
10904
+ result[snakeKey] = value;
10905
+ }
10906
+ return result;
10907
+ };
10908
+ var arrayKeysToCamel = (arr) => {
10909
+ if (!arr || !Array.isArray(arr))
10910
+ return arr;
10911
+ return arr.map((item) => keysToCamel(item));
10912
+ };
10817
10913
  function genShortId() {
10818
10914
  const timePart = Date.now().toString(36);
10819
10915
  let rand = "";
@@ -10825,6 +10921,7 @@ function genShortId() {
10825
10921
 
10826
10922
  // lib/logger.js
10827
10923
  var INITIAL_CWD = process.cwd();
10924
+ var RUNTIME_LOG_DIR = nodePathResolve(INITIAL_CWD, "logs");
10828
10925
  var BUILTIN_SENSITIVE_KEYS = ["*password*", "pass", "pwd", "*token*", "access_token", "refresh_token", "accessToken", "refreshToken", "authorization", "cookie", "set-cookie", "*secret*", "apiKey", "api_key", "privateKey", "private_key"];
10829
10926
  var sanitizeOptions = {
10830
10927
  maxStringLen: 200,
@@ -10851,7 +10948,7 @@ var appFileSink = null;
10851
10948
  var errorFileSink = null;
10852
10949
  var config2 = {
10853
10950
  debug: 0,
10854
- dir: "./logs",
10951
+ dir: RUNTIME_LOG_DIR,
10855
10952
  maxSize: 20,
10856
10953
  runtimeEnv: "production"
10857
10954
  };
@@ -11010,7 +11107,7 @@ class LogFileSink {
11010
11107
  getFilePath(date3, index) {
11011
11108
  const suffix = index > 0 ? `.${index}` : "";
11012
11109
  const filename = this.prefix === "app" ? `${date3}${suffix}.log` : `${this.prefix}.${date3}${suffix}.log`;
11013
- return nodePathJoin(resolveLogDir(), filename);
11110
+ return nodePathJoin(RUNTIME_LOG_DIR, filename);
11014
11111
  }
11015
11112
  async ensureStreamReady(nextChunkBytes) {
11016
11113
  const date3 = formatYmdHms(Reflect.construct(Date, []), "date");
@@ -11084,21 +11181,13 @@ async function shutdown() {
11084
11181
  errorFileSink = null;
11085
11182
  }
11086
11183
  }
11087
- function resolveLogDir() {
11088
- const rawDir = config2.dir || "./logs";
11089
- if (nodePathIsAbsolute(rawDir)) {
11090
- return rawDir;
11091
- }
11092
- return nodePathResolve(INITIAL_CWD, rawDir);
11093
- }
11094
11184
  function ensureLogDirExists() {
11095
- const dir = resolveLogDir();
11096
11185
  try {
11097
- if (!existsSync(dir)) {
11098
- mkdirSync(dir, { recursive: true });
11186
+ if (!existsSync(RUNTIME_LOG_DIR)) {
11187
+ mkdirSync(RUNTIME_LOG_DIR, { recursive: true });
11099
11188
  }
11100
11189
  } catch (error48) {
11101
- throw new Error(`\u521B\u5EFA logs \u76EE\u5F55\u5931\u8D25: ${dir}. ${error48?.message || error48}`, {
11190
+ throw new Error(`\u521B\u5EFA logs \u76EE\u5F55\u5931\u8D25: ${RUNTIME_LOG_DIR}. ${error48?.message || error48}`, {
11102
11191
  cause: error48,
11103
11192
  code: "runtime"
11104
11193
  });
@@ -11108,10 +11197,11 @@ function configure(cfg) {
11108
11197
  shutdown();
11109
11198
  config2 = Object.assign({
11110
11199
  debug: 0,
11111
- dir: "./logs",
11200
+ dir: RUNTIME_LOG_DIR,
11112
11201
  maxSize: 20,
11113
11202
  runtimeEnv: "production"
11114
11203
  }, cfg);
11204
+ config2.dir = RUNTIME_LOG_DIR;
11115
11205
  if (cfg && !isString(cfg.runtimeEnv) && isString(cfg.runMode)) {
11116
11206
  config2.runtimeEnv = cfg.runMode;
11117
11207
  }
@@ -12322,217 +12412,3735 @@ async function syncMenu(ctx, menus) {
12322
12412
  }
12323
12413
  }
12324
12414
 
12325
- // utils/calcPerfTime.js
12326
- var calcPerfTime = (startTime, endTime = Bun.nanoseconds()) => {
12327
- const elapsedMs = (endTime - startTime) / 1e6;
12328
- if (elapsedMs < 1000) {
12329
- return `${elapsedMs.toFixed(2)} \u6BEB\u79D2`;
12330
- } else {
12331
- const elapsedSeconds = elapsedMs / 1000;
12332
- return `${elapsedSeconds.toFixed(2)} \u79D2`;
12333
- }
12334
- };
12415
+ // scripts/syncDb/index.js
12416
+ import { mkdir as mkdir3 } from "fs/promises";
12417
+ import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
12335
12418
 
12336
- // utils/scanFiles.js
12337
- import { existsSync as existsSync2 } from "fs";
12419
+ // scripts/syncDb/diff.js
12420
+ import { join as join2 } from "path";
12338
12421
 
12339
- // utils/importDefault.js
12340
- import { isAbsolute as isAbsolute2 } from "path";
12341
- import { pathToFileURL } from "url";
12342
- function isWindowsAbsPath(file) {
12343
- return /^[a-zA-Z]:[\\/]/.test(file);
12344
- }
12345
- function toFileImportUrl(file) {
12346
- if (isAbsolute2(file) || isWindowsAbsPath(file)) {
12347
- return pathToFileURL(file).href;
12422
+ // scripts/syncDb/transform.js
12423
+ function resolveSyncDbEnumInput(columnType) {
12424
+ const matched = /^enum\((.*)\)$/.exec(columnType);
12425
+ if (!matched || !matched[1]) {
12426
+ return "";
12348
12427
  }
12349
- return file;
12350
- }
12351
- async function importDefault(file, defaultValue) {
12352
- try {
12353
- const isJson = file.endsWith(".json");
12354
- const mod = isJson ? await import(toFileImportUrl(file), { with: { type: "json" } }) : await import(file);
12355
- const value = mod?.default;
12356
- if (isNullable(value)) {
12357
- return defaultValue;
12358
- }
12359
- const expectedType = getTypeTag(defaultValue);
12360
- const actualType = getTypeTag(value);
12361
- if (expectedType !== actualType) {
12362
- Logger.warn("importDefault \u5BFC\u5165\u7C7B\u578B\u4E0E\u9ED8\u8BA4\u503C\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u56DE\u9000\u5230\u9ED8\u8BA4\u503C", { file, expectedType, actualType });
12363
- return defaultValue;
12364
- }
12365
- return value;
12366
- } catch (err) {
12367
- Logger.warn("importDefault \u5BFC\u5165\u5931\u8D25\uFF0C\u5DF2\u56DE\u9000\u5230\u9ED8\u8BA4\u503C", { err, file });
12368
- return defaultValue;
12428
+ const values = [];
12429
+ const enumValueRegexp = /'((?:[^'\\]|\\.)*)'/g;
12430
+ let execResult = enumValueRegexp.exec(matched[1]);
12431
+ while (execResult) {
12432
+ const decodedValue = String(execResult[1]).replaceAll("\\'", "'");
12433
+ values.push(decodedValue);
12434
+ execResult = enumValueRegexp.exec(matched[1]);
12435
+ }
12436
+ if (values.length === 0) {
12437
+ return "";
12369
12438
  }
12439
+ return values.join("|");
12370
12440
  }
12371
-
12372
- // utils/scanFiles.js
12373
- async function scanFiles(dir, source, type, pattern) {
12374
- try {
12375
- const glob = new Bun.Glob(pattern);
12376
- if (!existsSync2(dir))
12377
- return [];
12378
- const results = [];
12379
- const files = await glob.scan({
12380
- cwd: dir,
12381
- onlyFiles: true,
12382
- absolute: true,
12383
- followSymlinks: true
12384
- });
12385
- for await (const file of files) {
12386
- const filePath = normalize(file);
12387
- const parsedFile = parse5(filePath);
12388
- const fileName = parsedFile.name;
12389
- const parsedRelativePath = parse5(relative(dir, filePath));
12390
- const relativePath = parsedRelativePath.dir ? join(parsedRelativePath.dir, parsedRelativePath.name) : parsedRelativePath.name;
12391
- if (relativePath.split("/").some((part) => part.startsWith("_")))
12392
- continue;
12393
- const content = await importDefault(filePath, {});
12394
- const contentObj = isPlainObject2(content) ? content : {};
12395
- const base = {
12396
- source,
12397
- type,
12398
- filePath,
12399
- relativePath,
12400
- fileName,
12401
- apiPath: `/api/${source}/${relativePath}`
12402
- };
12403
- if (type === "table") {
12404
- base["fieldsDef"] = contentObj;
12405
- }
12406
- if (type === "api") {
12407
- base["name"] = "";
12408
- }
12409
- if (type !== "table") {
12410
- for (const [key, value] of Object.entries(contentObj)) {
12411
- if (base[key])
12412
- continue;
12413
- base[key] = value;
12414
- }
12415
- }
12416
- results.push(base);
12441
+ function resolveSyncDbInputByColumn(columnMeta) {
12442
+ const dataType = String(columnMeta.dataType || "").toLowerCase();
12443
+ const columnType = String(columnMeta.columnType || "").toLowerCase();
12444
+ if (ENUM_KIND_TYPES.includes(dataType)) {
12445
+ const enumInput = resolveSyncDbEnumInput(columnType);
12446
+ if (isNonEmptyString(enumInput)) {
12447
+ return enumInput;
12417
12448
  }
12418
- return results;
12419
- } catch (error48) {
12420
- throw new Error(`\u626B\u63CF\u5931\u8D25: source=${source} type=${type} dir=${dir} pattern=${pattern}`, {
12421
- cause: error48,
12422
- code: "runtime"
12423
- });
12449
+ return "string";
12424
12450
  }
12425
- }
12426
-
12427
- // utils/scanSources.js
12428
- var scanSources = async () => {
12429
- const apis = [];
12430
- const plugins = [];
12431
- const hooks = [];
12432
- const tables = [];
12433
- const allCoreTables = await scanFiles(coreTableDir, "core", "table", "*.json");
12434
- for (const item of allCoreTables) {
12435
- tables.push(item);
12451
+ if (JSON_KIND_TYPES.includes(dataType)) {
12452
+ return "json";
12436
12453
  }
12437
- const allAppTables = await scanFiles(appTableDir, "app", "table", "*.json");
12438
- for (const item of allAppTables) {
12439
- tables.push(item);
12454
+ if (INT_KIND_TYPES.includes(dataType)) {
12455
+ return "integer";
12440
12456
  }
12441
- const allCorePlugins = await scanFiles(corePluginDir, "core", "plugin", "*.js");
12442
- for (const item of allCorePlugins) {
12443
- plugins.push(item);
12457
+ if (DECIMAL_KIND_TYPES.includes(dataType) || FLOAT_KIND_TYPES.includes(dataType)) {
12458
+ return "number";
12444
12459
  }
12445
- const allAppPlugins = await scanFiles(appPluginDir, "app", "plugin", "*.js");
12446
- for (const item of allAppPlugins) {
12447
- plugins.push(item);
12460
+ if (STRING_KIND_TYPES.includes(dataType) || TEXT_KIND_TYPES.includes(dataType)) {
12461
+ if (dataType === "char") {
12462
+ return "char";
12463
+ }
12464
+ return "string";
12448
12465
  }
12449
- const allCoreHooks = await scanFiles(coreHookDir, "core", "hook", "*.js");
12450
- for (const item of allCoreHooks) {
12451
- hooks.push(item);
12466
+ if (dataType.includes("date") || dataType.includes("time") || dataType === "year") {
12467
+ return "string";
12452
12468
  }
12453
- const allAppHooks = await scanFiles(appHookDir, "app", "hook", "*.js");
12454
- for (const item of allAppHooks) {
12455
- hooks.push(item);
12469
+ if (dataType === "bit" || dataType === "boolean") {
12470
+ return "integer";
12456
12471
  }
12457
- const allCoreApis = await scanFiles(coreApiDir, "core", "api", "**/*.js");
12458
- for (const item of allCoreApis) {
12459
- apis.push(item);
12472
+ return "string";
12473
+ }
12474
+ function resolveSyncDbMaxByColumn(columnMeta) {
12475
+ const dataType = String(columnMeta.dataType || "").toLowerCase();
12476
+ if (!(STRING_KIND_TYPES.includes(dataType) || TEXT_KIND_TYPES.includes(dataType))) {
12477
+ return null;
12460
12478
  }
12461
- const allAppApis = await scanFiles(appApiDir, "app", "api", "**/*.js");
12462
- for (const item of allAppApis) {
12463
- apis.push(item);
12479
+ if (!isNumber(columnMeta.charLength)) {
12480
+ return null;
12481
+ }
12482
+ if (!Number.isFinite(columnMeta.charLength)) {
12483
+ return null;
12484
+ }
12485
+ if (columnMeta.charLength <= 0 || columnMeta.charLength > Number.MAX_SAFE_INTEGER) {
12486
+ return null;
12487
+ }
12488
+ return Math.floor(columnMeta.charLength);
12489
+ }
12490
+ function toSyncDbFieldDef(columnMeta) {
12491
+ const fieldName = camelCase(String(columnMeta.columnName || ""));
12492
+ const fieldDisplayName = isNonEmptyString(columnMeta.columnComment) ? String(columnMeta.columnComment).trim() : fieldName;
12493
+ const fieldInput = resolveSyncDbInputByColumn(columnMeta);
12494
+ const fieldMax = resolveSyncDbMaxByColumn(columnMeta);
12495
+ const fieldDef = {
12496
+ name: fieldDisplayName,
12497
+ input: fieldInput
12498
+ };
12499
+ if (isNumber(fieldMax)) {
12500
+ fieldDef.max = fieldMax;
12464
12501
  }
12465
12502
  return {
12466
- hooks,
12467
- plugins,
12468
- apis,
12469
- tables
12503
+ fieldName,
12504
+ fieldDef
12470
12505
  };
12471
- };
12472
-
12473
- // utils/deepMerge.js
12474
- function clone2(value) {
12475
- return structuredClone(value);
12476
12506
  }
12477
- function concat(targetArr, sourceArr, cloneTargetItems) {
12478
- const nextArr = [];
12479
- if (cloneTargetItems) {
12480
- for (const item of targetArr) {
12481
- nextArr.push(clone2(item));
12507
+
12508
+ // scripts/syncDb/diff.js
12509
+ function groupSyncDbColumns(rows) {
12510
+ const grouped = new Map;
12511
+ for (const row of rows) {
12512
+ const tableName = String(row.tableName || "");
12513
+ if (!isNonEmptyString(tableName)) {
12514
+ continue;
12482
12515
  }
12483
- } else {
12484
- for (const item of targetArr) {
12485
- nextArr.push(item);
12516
+ if (tableName.toLowerCase().startsWith("befly_")) {
12517
+ continue;
12486
12518
  }
12519
+ if (!grouped.has(tableName)) {
12520
+ grouped.set(tableName, []);
12521
+ }
12522
+ const tableColumns = grouped.get(tableName);
12523
+ tableColumns.push({
12524
+ tableName,
12525
+ tableComment: row.tableComment,
12526
+ columnName: row.columnName,
12527
+ dataType: row.dataType,
12528
+ columnType: row.columnType,
12529
+ charLength: row.charLength,
12530
+ columnComment: row.columnComment,
12531
+ ordinalPosition: row.ordinalPosition
12532
+ });
12487
12533
  }
12488
- for (const item of sourceArr) {
12489
- nextArr.push(clone2(item));
12490
- }
12491
- return nextArr;
12492
- }
12493
- function mergeValue(baseValue, incomingValue) {
12494
- if (Array.isArray(baseValue) && Array.isArray(incomingValue)) {
12495
- return concat(baseValue, incomingValue, false);
12496
- }
12497
- if (isPlainObject2(baseValue) && isPlainObject2(incomingValue)) {
12498
- mergeInto(baseValue, incomingValue);
12499
- return baseValue;
12500
- }
12501
- return clone2(incomingValue);
12534
+ return grouped;
12502
12535
  }
12503
- function mergeInto(base, incoming) {
12504
- for (const key of Object.keys(incoming)) {
12505
- const incomingValue = incoming[key];
12506
- if (incomingValue === undefined) {
12536
+ function buildSyncDbDiff(groupedDbColumns, existingTableMap) {
12537
+ const missingTables = [];
12538
+ const missingFieldsByTable = {};
12539
+ for (const [tableName, columns] of groupedDbColumns.entries()) {
12540
+ const tableFileName = camelCase(tableName);
12541
+ const existing = existingTableMap.get(String(tableFileName).toLowerCase());
12542
+ if (!existing) {
12543
+ missingTables.push({
12544
+ tableName,
12545
+ tableFileName,
12546
+ columns
12547
+ });
12507
12548
  continue;
12508
12549
  }
12509
- base[key] = mergeValue(base[key], incomingValue);
12550
+ const existingFields = isPlainObject2(existing.tableDef) ? existing.tableDef : {};
12551
+ const missingFields = [];
12552
+ for (const columnMeta of columns) {
12553
+ const fieldInfo = toSyncDbFieldDef(columnMeta);
12554
+ if (Object.prototype.hasOwnProperty.call(existingFields, fieldInfo.fieldName)) {
12555
+ continue;
12556
+ }
12557
+ missingFields.push({
12558
+ tableName,
12559
+ tableFileName,
12560
+ dbColumnName: columnMeta.columnName,
12561
+ dbDataType: columnMeta.dataType,
12562
+ dbColumnType: columnMeta.columnType,
12563
+ dbCharLength: columnMeta.charLength,
12564
+ fieldName: fieldInfo.fieldName,
12565
+ fieldDef: fieldInfo.fieldDef
12566
+ });
12567
+ }
12568
+ if (missingFields.length > 0) {
12569
+ missingFieldsByTable[tableFileName] = {
12570
+ tableName,
12571
+ tableFileName,
12572
+ filePath: existing.filePath,
12573
+ fields: missingFields
12574
+ };
12575
+ }
12510
12576
  }
12511
- return base;
12577
+ return {
12578
+ missingTables,
12579
+ missingFieldsByTable
12580
+ };
12512
12581
  }
12513
- function deepMerge(source, target) {
12514
- if (Array.isArray(source) && Array.isArray(target)) {
12515
- const base = clone2(source);
12516
- const incoming = clone2(target);
12517
- return concat(base, incoming, false);
12582
+ async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
12583
+ let createdTableFileCount = 0;
12584
+ let appendedFieldCount = 0;
12585
+ for (const missingTable of diff.missingTables) {
12586
+ const tableDef = {};
12587
+ for (const columnMeta of missingTable.columns) {
12588
+ const fieldInfo = toSyncDbFieldDef(columnMeta);
12589
+ tableDef[fieldInfo.fieldName] = fieldInfo.fieldDef;
12590
+ }
12591
+ const filePath = join2(tablesDir, `${missingTable.tableFileName}.json`);
12592
+ await Bun.write(filePath, `${JSON.stringify(tableDef, null, 4)}
12593
+ `);
12594
+ createdTableFileCount = createdTableFileCount + 1;
12518
12595
  }
12519
- if (isPlainObject2(source) && isPlainObject2(target)) {
12520
- const base = clone2(source);
12521
- const incoming = clone2(target);
12522
- return mergeInto(base, incoming);
12596
+ for (const [tableFileName, tableInfo] of Object.entries(diff.missingFieldsByTable)) {
12597
+ const existing = existingTableMap.get(String(tableFileName).toLowerCase());
12598
+ const targetFilePath = existing?.filePath || join2(tablesDir, `${tableFileName}.json`);
12599
+ const currentTableDef = isPlainObject2(existing?.tableDef) ? existing.tableDef : {};
12600
+ for (const fieldItem of tableInfo.fields) {
12601
+ currentTableDef[fieldItem.fieldName] = fieldItem.fieldDef;
12602
+ appendedFieldCount = appendedFieldCount + 1;
12603
+ }
12604
+ await Bun.write(targetFilePath, `${JSON.stringify(currentTableDef, null, 4)}
12605
+ `);
12523
12606
  }
12524
- throw new Error("deepMerge: source/target \u5FC5\u987B\u540C\u4E3A\u6570\u7EC4\u6216\u540C\u4E3A\u666E\u901A\u5BF9\u8C61", {
12525
- cause: null,
12526
- code: "validation",
12527
- subsystem: "utils",
12528
- operation: "deepMerge"
12529
- });
12607
+ return {
12608
+ createdTableFileCount,
12609
+ appendedFieldCount
12610
+ };
12611
+ }
12612
+ function countSyncDbMissingFields(diff) {
12613
+ let missingFieldCount = 0;
12614
+ for (const item of Object.values(diff.missingFieldsByTable)) {
12615
+ missingFieldCount = missingFieldCount + item.fields.length;
12616
+ }
12617
+ return missingFieldCount;
12530
12618
  }
12531
12619
 
12532
- // utils/sortModules.js
12533
- function sortModules(items) {
12534
- const result = [];
12535
- const visited = new Set;
12620
+ // scripts/syncDb/context.js
12621
+ import { mkdir } from "fs/promises";
12622
+ import { basename as basename2, resolve as resolve2, join as join3 } from "path";
12623
+
12624
+ // utils/fieldClear.js
12625
+ function isObject2(val) {
12626
+ return val !== null && typeof val === "object" && !Array.isArray(val);
12627
+ }
12628
+ function isArray(val) {
12629
+ return Array.isArray(val);
12630
+ }
12631
+ function fieldClear(data, options = {}) {
12632
+ const { pickKeys, omitKeys, keepValues, excludeValues, keepMap } = options;
12633
+ const filterObj = (obj) => {
12634
+ let result = {};
12635
+ let keys = Object.keys(obj);
12636
+ if (pickKeys && pickKeys.length) {
12637
+ keys = keys.filter((k) => pickKeys.includes(k));
12638
+ }
12639
+ if (omitKeys && omitKeys.length) {
12640
+ keys = keys.filter((k) => !omitKeys.includes(k));
12641
+ }
12642
+ for (const key of keys) {
12643
+ const value = obj[key];
12644
+ if (keepMap && key in keepMap) {
12645
+ if (Object.is(keepMap[key], value)) {
12646
+ result[key] = value;
12647
+ continue;
12648
+ }
12649
+ }
12650
+ if (keepValues && keepValues.length && !keepValues.includes(value)) {
12651
+ continue;
12652
+ }
12653
+ if (excludeValues && excludeValues.length && excludeValues.includes(value)) {
12654
+ continue;
12655
+ }
12656
+ result[key] = value;
12657
+ }
12658
+ return result;
12659
+ };
12660
+ if (isArray(data)) {
12661
+ return data.map((item) => isObject2(item) ? filterObj(item) : item).filter((item) => {
12662
+ if (isObject2(item)) {
12663
+ return Object.keys(item).length > 0;
12664
+ }
12665
+ return true;
12666
+ });
12667
+ }
12668
+ if (isObject2(data)) {
12669
+ return filterObj(data);
12670
+ }
12671
+ return data;
12672
+ }
12673
+
12674
+ // lib/sqlBuilder/check.js
12675
+ class SqlCheck {
12676
+ static assertNoUndefinedParam(value, label) {
12677
+ if (value === undefined) {
12678
+ throw new Error(`${label} \u4E0D\u80FD\u4E3A undefined`, {
12679
+ cause: null,
12680
+ code: "validation"
12681
+ });
12682
+ }
12683
+ }
12684
+ static startsWithQuote(value) {
12685
+ const trimmed = value.trim();
12686
+ return trimmed.startsWith("`") || trimmed.startsWith('"');
12687
+ }
12688
+ static isQuotedIdentPaired(value) {
12689
+ const trimmed = value.trim();
12690
+ if (!isMinLengthString(trimmed, 2)) {
12691
+ return false;
12692
+ }
12693
+ const first = trimmed[0];
12694
+ const last = trimmed[trimmed.length - 1];
12695
+ if (first === "`" && last === "`") {
12696
+ return true;
12697
+ }
12698
+ if (first === '"' && last === '"') {
12699
+ return true;
12700
+ }
12701
+ return false;
12702
+ }
12703
+ static assertPairedQuotedIdentIfStartsWithQuote(value, label) {
12704
+ if (SqlCheck.startsWithQuote(value) && !SqlCheck.isQuotedIdentPaired(value)) {
12705
+ throw new Error(`${label} \u5F15\u7528\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u4F7F\u7528\u6210\u5BF9\u7684 \`...\` \u6216 "..." (value: ${value})`, {
12706
+ cause: null,
12707
+ code: "validation"
12708
+ });
12709
+ }
12710
+ }
12711
+ static assertSafeIdentifierPart(part, kind) {
12712
+ if (!SqlCheck.SAFE_IDENTIFIER_RE.test(part)) {
12713
+ throw new Error(`\u65E0\u6548\u7684 ${kind} \u6807\u8BC6\u7B26: ${part}`, {
12714
+ cause: null,
12715
+ code: "validation"
12716
+ });
12717
+ }
12718
+ }
12719
+ static assertSafeAlias(aliasPart) {
12720
+ if (SqlCheck.isQuotedIdentPaired(aliasPart)) {
12721
+ return;
12722
+ }
12723
+ if (!SqlCheck.SAFE_IDENTIFIER_RE.test(aliasPart)) {
12724
+ throw new Error(`\u65E0\u6548\u7684\u5B57\u6BB5\u522B\u540D: ${aliasPart}`, {
12725
+ cause: null,
12726
+ code: "validation"
12727
+ });
12728
+ }
12729
+ }
12730
+ static assertNoExprField(field) {
12731
+ if (!isNonEmptyString(field)) {
12732
+ return;
12733
+ }
12734
+ const trimmed = field.trim();
12735
+ if (trimmed.includes("(") || trimmed.includes(")")) {
12736
+ throw new Error(`\u5B57\u6BB5\u5305\u542B\u51FD\u6570/\u8868\u8FBE\u5F0F\uFF0C\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${trimmed})`, {
12737
+ cause: null,
12738
+ code: "validation"
12739
+ });
12740
+ }
12741
+ }
12742
+ static assertBatchInsertRowsConsistent(rows, options) {
12743
+ if (!Array.isArray(rows)) {
12744
+ throw new Error("\u6279\u91CF\u63D2\u5165 rows \u5FC5\u987B\u662F\u6570\u7EC4", {
12745
+ cause: null,
12746
+ code: "validation"
12747
+ });
12748
+ }
12749
+ if (rows.length === 0) {
12750
+ throw new Error(`\u63D2\u5165\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A (table: ${options.table})`, {
12751
+ cause: null,
12752
+ code: "validation"
12753
+ });
12754
+ }
12755
+ const first = rows[0];
12756
+ if (!first || typeof first !== "object" || Array.isArray(first)) {
12757
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7684\u6BCF\u4E00\u884C\u5FC5\u987B\u662F\u5BF9\u8C61 (table: ${options.table}, rowIndex: 0)`, {
12758
+ cause: null,
12759
+ code: "validation"
12760
+ });
12761
+ }
12762
+ const fields = Object.keys(first);
12763
+ if (fields.length === 0) {
12764
+ throw new Error(`\u63D2\u5165\u6570\u636E\u5FC5\u987B\u81F3\u5C11\u6709\u4E00\u4E2A\u5B57\u6BB5 (table: ${options.table})`, {
12765
+ cause: null,
12766
+ code: "validation"
12767
+ });
12768
+ }
12769
+ const fieldSet = new Set(fields);
12770
+ for (let i = 0;i < rows.length; i++) {
12771
+ const row = rows[i];
12772
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
12773
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7684\u6BCF\u4E00\u884C\u5FC5\u987B\u662F\u5BF9\u8C61 (table: ${options.table}, rowIndex: ${i})`, {
12774
+ cause: null,
12775
+ code: "validation"
12776
+ });
12777
+ }
12778
+ const rowKeys = Object.keys(row);
12779
+ if (rowKeys.length !== fields.length) {
12780
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u6BCF\u884C\u5B57\u6BB5\u5FC5\u987B\u4E00\u81F4 (table: ${options.table}, rowIndex: ${i})`, {
12781
+ cause: null,
12782
+ code: "validation"
12783
+ });
12784
+ }
12785
+ for (const key of rowKeys) {
12786
+ if (!fieldSet.has(key)) {
12787
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u6BCF\u884C\u5B57\u6BB5\u5FC5\u987B\u4E00\u81F4 (table: ${options.table}, rowIndex: ${i}, extraField: ${key})`, {
12788
+ cause: null,
12789
+ code: "validation"
12790
+ });
12791
+ }
12792
+ }
12793
+ for (const field of fields) {
12794
+ if (!(field in row)) {
12795
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7F3A\u5C11\u5B57\u6BB5 (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
12796
+ cause: null,
12797
+ code: "validation"
12798
+ });
12799
+ }
12800
+ SqlCheck.assertNoUndefinedParam(row[field], `\u6279\u91CF\u63D2\u5165\u5B57\u6BB5\u503C (table: ${options.table}, rowIndex: ${i}, field: ${field})`);
12801
+ }
12802
+ }
12803
+ return fields;
12804
+ }
12805
+ }
12806
+ SqlCheck.SAFE_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
12807
+
12808
+ // lib/sqlBuilder/errors.js
12809
+ var SqlErrors = {
12810
+ QUOTE_IDENT_NEED_STRING: (identifier) => `quoteIdent \u9700\u8981\u5B57\u7B26\u4E32\u7C7B\u578B\u6807\u8BC6\u7B26 (identifier: ${String(identifier)})`,
12811
+ IDENT_EMPTY: "SQL \u6807\u8BC6\u7B26\u4E0D\u80FD\u4E3A\u7A7A",
12812
+ FIELD_EXPR_NOT_ALLOWED: (field) => `\u5B57\u6BB5\u5305\u542B\u51FD\u6570/\u8868\u8FBE\u5F0F\uFF0C\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
12813
+ FIELD_INVALID: (field) => `\u5B57\u6BB5\u683C\u5F0F\u975E\u6CD5\uFF0C\u8BF7\u4F7F\u7528\u7B80\u5355\u5B57\u6BB5\u540D\u6216\u5B89\u5168\u5F15\u7528\uFF0C\u590D\u6742\u8868\u8FBE\u5F0F\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${field})`,
12814
+ FROM_EMPTY: "FROM \u8868\u540D\u4E0D\u80FD\u4E3A\u7A7A",
12815
+ FROM_NEED_NON_EMPTY: (table) => `FROM \u8868\u540D\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32 (table: ${String(table)})`,
12816
+ FROM_REQUIRED: "FROM \u8868\u540D\u662F\u5FC5\u9700\u7684",
12817
+ COUNT_NEED_FROM: "COUNT \u9700\u8981 FROM \u8868\u540D",
12818
+ TABLE_REF_TOO_MANY_PARTS: (table) => `\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08\u5305\u542B\u8FC7\u591A\u7247\u6BB5\uFF09\u3002\u8BF7\u4F7F\u7528 fromRaw \u663E\u5F0F\u4F20\u5165\u590D\u6742\u8868\u8FBE\u5F0F (table: ${table})`,
12819
+ TABLE_REF_SCHEMA_TOO_DEEP: (table) => `\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08schema \u5C42\u7EA7\u8FC7\u6DF1\uFF09\u3002\u8BF7\u4F7F\u7528 fromRaw (table: ${table})`,
12820
+ SCHEMA_QUOTE_NOT_PAIRED: (schema) => `schema \u6807\u8BC6\u7B26\u5F15\u7528\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u4F7F\u7528\u6210\u5BF9\u7684 \`...\` \u6216 "..." (schema: ${schema})`,
12821
+ TABLE_QUOTE_NOT_PAIRED: (tableName) => `table \u6807\u8BC6\u7B26\u5F15\u7528\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u4F7F\u7528\u6210\u5BF9\u7684 \`...\` \u6216 "..." (table: ${tableName})`,
12822
+ SELECT_FIELDS_INVALID: "SELECT \u5B57\u6BB5\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u6570\u7EC4",
12823
+ SELECT_RAW_NEED_NON_EMPTY: (expr) => `selectRaw \u9700\u8981\u975E\u7A7A\u5B57\u7B26\u4E32 (expr: ${String(expr)})`,
12824
+ FROM_RAW_NEED_NON_EMPTY: (tableExpr) => `fromRaw \u9700\u8981\u975E\u7A7A\u5B57\u7B26\u4E32 (tableExpr: ${String(tableExpr)})`,
12825
+ WHERE_RAW_NEED_NON_EMPTY: (sql) => `whereRaw \u9700\u8981\u975E\u7A7A\u5B57\u7B26\u4E32 (sql: ${String(sql)})`,
12826
+ WHERE_VALUE_REQUIRED: "where(field, value) \u4E0D\u5141\u8BB8\u7701\u7565 value\u3002\u82E5\u9700\u4F20\u5165\u539F\u59CB WHERE\uFF0C\u8BF7\u4F7F\u7528 whereRaw",
12827
+ JOIN_NEED_STRING: (table, on) => `JOIN \u8868\u540D\u548C\u6761\u4EF6\u5FC5\u987B\u662F\u5B57\u7B26\u4E32 (table: ${String(table)}, on: ${String(on)})`,
12828
+ ORDER_BY_NEED_ARRAY: 'orderBy \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u683C\u5F0F\u4E3A "\u5B57\u6BB5#\u65B9\u5411"',
12829
+ ORDER_BY_ITEM_NEED_HASH: (item) => `orderBy \u5B57\u6BB5\u5FC5\u987B\u662F "\u5B57\u6BB5#\u65B9\u5411" \u683C\u5F0F\u7684\u5B57\u7B26\u4E32\uFF08\u4F8B\u5982\uFF1A"name#ASC", "id#DESC"\uFF09 (item: ${String(item)})`,
12830
+ ORDER_BY_FIELD_EMPTY: (item) => `orderBy \u4E2D\u5B57\u6BB5\u540D\u4E0D\u80FD\u4E3A\u7A7A (item: ${item})`,
12831
+ ORDER_BY_DIR_INVALID: (direction) => `ORDER BY \u65B9\u5411\u5FC5\u987B\u662F ASC \u6216 DESC (direction: ${direction})`,
12832
+ LIMIT_MUST_NON_NEGATIVE: (count) => `LIMIT \u6570\u91CF\u5FC5\u987B\u662F\u975E\u8D1F\u6570 (count: ${String(count)})`,
12833
+ OFFSET_MUST_NON_NEGATIVE: (offset) => `OFFSET \u5FC5\u987B\u662F\u975E\u8D1F\u6570 (offset: ${String(offset)})`,
12834
+ OFFSET_COUNT_MUST_NON_NEGATIVE: (count) => `OFFSET \u5FC5\u987B\u662F\u975E\u8D1F\u6570 (count: ${String(count)})`,
12835
+ INSERT_NEED_TABLE: (table) => `INSERT \u9700\u8981\u8868\u540D (table: ${String(table)})`,
12836
+ INSERT_NEED_DATA: (table, data) => `INSERT \u9700\u8981\u6570\u636E (table: ${String(table)}, data: ${JSON.stringify(data)})`,
12837
+ INSERT_NEED_AT_LEAST_ONE_FIELD: (table) => `\u63D2\u5165\u6570\u636E\u5FC5\u987B\u81F3\u5C11\u6709\u4E00\u4E2A\u5B57\u6BB5 (table: ${table})`,
12838
+ UPDATE_NEED_TABLE: "UPDATE \u9700\u8981\u8868\u540D",
12839
+ UPDATE_NEED_OBJECT: "UPDATE \u9700\u8981\u6570\u636E\u5BF9\u8C61",
12840
+ UPDATE_NEED_AT_LEAST_ONE_FIELD: "\u66F4\u65B0\u6570\u636E\u5FC5\u987B\u81F3\u5C11\u6709\u4E00\u4E2A\u5B57\u6BB5",
12841
+ UPDATE_NEED_WHERE: "\u4E3A\u5B89\u5168\u8D77\u89C1\uFF0CUPDATE \u9700\u8981 WHERE \u6761\u4EF6",
12842
+ DELETE_NEED_TABLE: "DELETE \u9700\u8981\u8868\u540D",
12843
+ DELETE_NEED_WHERE: "\u4E3A\u5B89\u5168\u8D77\u89C1\uFF0CDELETE \u9700\u8981 WHERE \u6761\u4EF6",
12844
+ TO_DELETE_IN_NEED_TABLE: (table) => `toDeleteInSql \u9700\u8981\u975E\u7A7A\u8868\u540D (table: ${String(table)})`,
12845
+ TO_DELETE_IN_NEED_ID_FIELD: (idField) => `toDeleteInSql \u9700\u8981\u975E\u7A7A idField (idField: ${String(idField)})`,
12846
+ TO_DELETE_IN_NEED_IDS: "toDeleteInSql \u9700\u8981 ids \u6570\u7EC4",
12847
+ TO_UPDATE_CASE_NEED_TABLE: (table) => `toUpdateCaseByIdSql \u9700\u8981\u975E\u7A7A\u8868\u540D (table: ${String(table)})`,
12848
+ TO_UPDATE_CASE_NEED_ID_FIELD: (idField) => `toUpdateCaseByIdSql \u9700\u8981\u975E\u7A7A idField (idField: ${String(idField)})`,
12849
+ TO_UPDATE_CASE_NEED_ROWS: "toUpdateCaseByIdSql \u9700\u8981 rows \u6570\u7EC4",
12850
+ TO_UPDATE_CASE_NEED_FIELDS: "toUpdateCaseByIdSql \u9700\u8981 fields \u6570\u7EC4",
12851
+ IN_NEED_ARRAY: (operator) => `$in \u64CD\u4F5C\u7B26\u7684\u503C\u5FC5\u987B\u662F\u6570\u7EC4 (operator: ${operator})`,
12852
+ IN_NEED_NON_EMPTY: "$in \u64CD\u4F5C\u7B26\u7684\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A\u3002\u63D0\u793A\uFF1A\u7A7A\u6570\u7EC4\u4F1A\u5BFC\u81F4\u67E5\u8BE2\u6C38\u8FDC\u4E0D\u5339\u914D\u4EFB\u4F55\u8BB0\u5F55\uFF0C\u8FD9\u901A\u5E38\u4E0D\u662F\u9884\u671F\u884C\u4E3A\u3002\u8BF7\u68C0\u67E5\u67E5\u8BE2\u6761\u4EF6\u6216\u79FB\u9664\u8BE5\u5B57\u6BB5\u3002",
12853
+ NIN_NEED_ARRAY: (operator) => `$nin/$notIn \u64CD\u4F5C\u7B26\u7684\u503C\u5FC5\u987B\u662F\u6570\u7EC4 (operator: ${operator})`,
12854
+ NIN_NEED_NON_EMPTY: "$nin/$notIn \u64CD\u4F5C\u7B26\u7684\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A\u3002\u63D0\u793A\uFF1A\u7A7A\u6570\u7EC4\u4F1A\u5BFC\u81F4\u67E5\u8BE2\u5339\u914D\u6240\u6709\u8BB0\u5F55\uFF0C\u8FD9\u901A\u5E38\u4E0D\u662F\u9884\u671F\u884C\u4E3A\u3002\u8BF7\u68C0\u67E5\u67E5\u8BE2\u6761\u4EF6\u6216\u79FB\u9664\u8BE5\u5B57\u6BB5\u3002"
12855
+ };
12856
+
12857
+ // lib/sqlBuilder/util.js
12858
+ function resolveQuoteIdent(options) {
12859
+ if (options && options.quoteIdent) {
12860
+ return options.quoteIdent;
12861
+ }
12862
+ return (identifier) => {
12863
+ if (!isString(identifier)) {
12864
+ throw new Error(SqlErrors.QUOTE_IDENT_NEED_STRING(identifier), {
12865
+ cause: null,
12866
+ code: "validation"
12867
+ });
12868
+ }
12869
+ const trimmed = identifier.trim();
12870
+ if (!trimmed) {
12871
+ throw new Error(SqlErrors.IDENT_EMPTY, {
12872
+ cause: null,
12873
+ code: "validation"
12874
+ });
12875
+ }
12876
+ const escaped = trimmed.replace(/`/g, "``");
12877
+ return `\`${escaped}\``;
12878
+ };
12879
+ }
12880
+ function isQuotedIdent(value) {
12881
+ return SqlCheck.isQuotedIdentPaired(value);
12882
+ }
12883
+ function startsWithQuote(value) {
12884
+ return SqlCheck.startsWithQuote(value);
12885
+ }
12886
+ function escapeField(field, quoteIdent) {
12887
+ if (!isString(field)) {
12888
+ return field;
12889
+ }
12890
+ const trimmed = field.trim();
12891
+ SqlCheck.assertPairedQuotedIdentIfStartsWithQuote(trimmed, "\u5B57\u6BB5\u6807\u8BC6\u7B26");
12892
+ if (trimmed === "*" || isQuotedIdent(trimmed)) {
12893
+ return trimmed;
12894
+ }
12895
+ try {
12896
+ SqlCheck.assertNoExprField(trimmed);
12897
+ } catch {
12898
+ throw new Error(SqlErrors.FIELD_EXPR_NOT_ALLOWED(trimmed), {
12899
+ cause: null,
12900
+ code: "validation"
12901
+ });
12902
+ }
12903
+ if (trimmed.toUpperCase().includes(" AS ")) {
12904
+ const parts = trimmed.split(/\s+AS\s+/i);
12905
+ if (parts.length !== 2) {
12906
+ throw new Error(SqlErrors.FIELD_INVALID(trimmed), {
12907
+ cause: null,
12908
+ code: "validation"
12909
+ });
12910
+ }
12911
+ const fieldPart = parts[0];
12912
+ const aliasPart = parts[1];
12913
+ const cleanFieldPart = fieldPart.trim();
12914
+ const cleanAliasPart = aliasPart.trim();
12915
+ SqlCheck.assertSafeAlias(cleanAliasPart);
12916
+ return `${escapeField(cleanFieldPart, quoteIdent)} AS ${cleanAliasPart}`;
12917
+ }
12918
+ if (trimmed.includes(".")) {
12919
+ const parts = trimmed.split(".");
12920
+ return parts.map((part) => {
12921
+ const cleanPart = part.trim();
12922
+ if (cleanPart === "*" || isQuotedIdent(cleanPart)) {
12923
+ return cleanPart;
12924
+ }
12925
+ SqlCheck.assertPairedQuotedIdentIfStartsWithQuote(cleanPart, "\u5B57\u6BB5\u6807\u8BC6\u7B26");
12926
+ return quoteIdent(cleanPart);
12927
+ }).join(".");
12928
+ }
12929
+ return quoteIdent(trimmed);
12930
+ }
12931
+ function escapeTable(table, quoteIdent) {
12932
+ if (!isString(table)) {
12933
+ return table;
12934
+ }
12935
+ const trimmed = table.trim();
12936
+ if (isQuotedIdent(trimmed)) {
12937
+ return trimmed;
12938
+ }
12939
+ const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
12940
+ if (parts.length === 0) {
12941
+ throw new Error(SqlErrors.FROM_EMPTY, {
12942
+ cause: null,
12943
+ code: "validation"
12944
+ });
12945
+ }
12946
+ if (parts.length > 2) {
12947
+ throw new Error(SqlErrors.TABLE_REF_TOO_MANY_PARTS(trimmed), {
12948
+ cause: null,
12949
+ code: "validation"
12950
+ });
12951
+ }
12952
+ const namePart = parts[0].trim();
12953
+ const aliasPart = parts.length === 2 ? parts[1].trim() : null;
12954
+ const nameSegments = namePart.split(".");
12955
+ if (nameSegments.length > 2) {
12956
+ throw new Error(SqlErrors.TABLE_REF_SCHEMA_TOO_DEEP(trimmed), {
12957
+ cause: null,
12958
+ code: "validation"
12959
+ });
12960
+ }
12961
+ let escapedName = "";
12962
+ if (nameSegments.length === 2) {
12963
+ const schemaRaw = nameSegments[0];
12964
+ const tableNameRaw = nameSegments[1];
12965
+ const schema = schemaRaw.trim();
12966
+ const tableName = tableNameRaw.trim();
12967
+ const escapedSchema = isQuotedIdent(schema) ? schema : (() => {
12968
+ if (startsWithQuote(schema) && !isQuotedIdent(schema)) {
12969
+ throw new Error(SqlErrors.SCHEMA_QUOTE_NOT_PAIRED(schema), {
12970
+ cause: null,
12971
+ code: "validation"
12972
+ });
12973
+ }
12974
+ SqlCheck.assertSafeIdentifierPart(schema, "schema");
12975
+ return quoteIdent(schema);
12976
+ })();
12977
+ const escapedTableName = isQuotedIdent(tableName) ? tableName : (() => {
12978
+ if (startsWithQuote(tableName) && !isQuotedIdent(tableName)) {
12979
+ throw new Error(SqlErrors.TABLE_QUOTE_NOT_PAIRED(tableName), {
12980
+ cause: null,
12981
+ code: "validation"
12982
+ });
12983
+ }
12984
+ SqlCheck.assertSafeIdentifierPart(tableName, "table");
12985
+ return quoteIdent(tableName);
12986
+ })();
12987
+ escapedName = `${escapedSchema}.${escapedTableName}`;
12988
+ } else {
12989
+ const tableNameRaw = nameSegments[0];
12990
+ const tableName = tableNameRaw.trim();
12991
+ if (isQuotedIdent(tableName)) {
12992
+ escapedName = tableName;
12993
+ } else {
12994
+ if (startsWithQuote(tableName) && !isQuotedIdent(tableName)) {
12995
+ throw new Error(SqlErrors.TABLE_QUOTE_NOT_PAIRED(tableName), {
12996
+ cause: null,
12997
+ code: "validation"
12998
+ });
12999
+ }
13000
+ SqlCheck.assertSafeIdentifierPart(tableName, "table");
13001
+ escapedName = quoteIdent(tableName);
13002
+ }
13003
+ }
13004
+ if (aliasPart) {
13005
+ SqlCheck.assertSafeIdentifierPart(aliasPart, "alias");
13006
+ return `${escapedName} ${aliasPart}`;
13007
+ }
13008
+ return escapedName;
13009
+ }
13010
+ function validateParam(value) {
13011
+ SqlCheck.assertNoUndefinedParam(value, "SQL \u53C2\u6570\u503C");
13012
+ }
13013
+ function normalizeLimitValue(count, offset) {
13014
+ if (typeof count !== "number" || count < 0) {
13015
+ throw new Error(SqlErrors.LIMIT_MUST_NON_NEGATIVE(count), {
13016
+ cause: null,
13017
+ code: "validation"
13018
+ });
13019
+ }
13020
+ const limitValue = Math.floor(count);
13021
+ let offsetValue = null;
13022
+ if (!isNullable(offset)) {
13023
+ if (typeof offset !== "number" || offset < 0) {
13024
+ throw new Error(SqlErrors.OFFSET_MUST_NON_NEGATIVE(offset), {
13025
+ cause: null,
13026
+ code: "validation"
13027
+ });
13028
+ }
13029
+ offsetValue = Math.floor(offset);
13030
+ }
13031
+ return { limitValue, offsetValue };
13032
+ }
13033
+ function normalizeOffsetValue(count) {
13034
+ if (typeof count !== "number" || count < 0) {
13035
+ throw new Error(SqlErrors.OFFSET_COUNT_MUST_NON_NEGATIVE(count), {
13036
+ cause: null,
13037
+ code: "validation"
13038
+ });
13039
+ }
13040
+ return Math.floor(count);
13041
+ }
13042
+ function toSqlParams(params) {
13043
+ if (isNullable(params)) {
13044
+ return [];
13045
+ }
13046
+ if (!Array.isArray(params)) {
13047
+ throw new Error(`SQL \u53C2\u6570\u5FC5\u987B\u662F\u6570\u7EC4\uFF0C\u5F53\u524D\u7C7B\u578B: ${typeof params}`, {
13048
+ cause: null,
13049
+ code: "validation"
13050
+ });
13051
+ }
13052
+ const out = [];
13053
+ for (const value of params) {
13054
+ if (value === undefined) {
13055
+ throw new Error("SQL \u53C2\u6570\u4E0D\u80FD\u4E3A undefined", {
13056
+ cause: null,
13057
+ code: "validation"
13058
+ });
13059
+ }
13060
+ if (typeof value === "bigint") {
13061
+ out.push(value.toString());
13062
+ continue;
13063
+ }
13064
+ out.push(value);
13065
+ }
13066
+ return out;
13067
+ }
13068
+
13069
+ // lib/sqlBuilder/parser.js
13070
+ function createWhereRoot() {
13071
+ return { type: "group", join: "AND", items: [] };
13072
+ }
13073
+ function appendSelectItems(list, fields) {
13074
+ if (Array.isArray(fields)) {
13075
+ for (const field of fields) {
13076
+ list.push({ type: "field", value: field });
13077
+ }
13078
+ return;
13079
+ }
13080
+ if (isString(fields)) {
13081
+ list.push({ type: "field", value: fields });
13082
+ return;
13083
+ }
13084
+ throw new Error(SqlErrors.SELECT_FIELDS_INVALID, {
13085
+ cause: null,
13086
+ code: "validation"
13087
+ });
13088
+ }
13089
+ function appendSelectRaw(list, expr) {
13090
+ if (!isNonEmptyString(expr)) {
13091
+ throw new Error(SqlErrors.SELECT_RAW_NEED_NON_EMPTY(expr), {
13092
+ cause: null,
13093
+ code: "validation"
13094
+ });
13095
+ }
13096
+ list.push({ type: "raw", value: expr });
13097
+ }
13098
+ function setFromValue(state, table, isRaw) {
13099
+ if (!isNonEmptyString(table)) {
13100
+ const err = isRaw ? SqlErrors.FROM_RAW_NEED_NON_EMPTY(table) : SqlErrors.FROM_NEED_NON_EMPTY(table);
13101
+ throw new Error(err, {
13102
+ cause: null,
13103
+ code: "validation"
13104
+ });
13105
+ }
13106
+ state.from = { type: isRaw ? "raw" : "table", value: table.trim() };
13107
+ }
13108
+ function appendJoinItem(list, joinType, table, on) {
13109
+ if (!isString(table) || !isString(on)) {
13110
+ throw new Error(SqlErrors.JOIN_NEED_STRING(table, on), {
13111
+ cause: null,
13112
+ code: "validation"
13113
+ });
13114
+ }
13115
+ list.push({ type: joinType, table, on });
13116
+ }
13117
+ function appendOrderByItems(list, fields) {
13118
+ if (!Array.isArray(fields)) {
13119
+ throw new Error(SqlErrors.ORDER_BY_NEED_ARRAY, {
13120
+ cause: null,
13121
+ code: "validation"
13122
+ });
13123
+ }
13124
+ for (const item of fields) {
13125
+ if (!isString(item) || !item.includes("#")) {
13126
+ throw new Error(SqlErrors.ORDER_BY_ITEM_NEED_HASH(item), {
13127
+ cause: null,
13128
+ code: "validation"
13129
+ });
13130
+ }
13131
+ const parts = item.split("#");
13132
+ if (parts.length !== 2) {
13133
+ throw new Error(SqlErrors.ORDER_BY_ITEM_NEED_HASH(item), {
13134
+ cause: null,
13135
+ code: "validation"
13136
+ });
13137
+ }
13138
+ const fieldName = parts[0];
13139
+ const direction = parts[1];
13140
+ const cleanField = fieldName.trim();
13141
+ const cleanDir = direction.trim().toUpperCase();
13142
+ if (!cleanField) {
13143
+ throw new Error(SqlErrors.ORDER_BY_FIELD_EMPTY(item), {
13144
+ cause: null,
13145
+ code: "validation"
13146
+ });
13147
+ }
13148
+ if (!["ASC", "DESC"].includes(cleanDir)) {
13149
+ throw new Error(SqlErrors.ORDER_BY_DIR_INVALID(cleanDir), {
13150
+ cause: null,
13151
+ code: "validation"
13152
+ });
13153
+ }
13154
+ list.push({ field: cleanField, dir: cleanDir });
13155
+ }
13156
+ }
13157
+ function appendGroupByItems(list, field) {
13158
+ if (Array.isArray(field)) {
13159
+ for (const item of field) {
13160
+ if (isString(item)) {
13161
+ list.push(item);
13162
+ }
13163
+ }
13164
+ return;
13165
+ }
13166
+ if (isString(field)) {
13167
+ list.push(field);
13168
+ }
13169
+ }
13170
+ function appendHavingItems(list, condition) {
13171
+ if (isString(condition)) {
13172
+ list.push(condition);
13173
+ }
13174
+ }
13175
+ function appendWhereInput(root, conditionOrField, value) {
13176
+ if (conditionOrField && typeof conditionOrField === "object" && !Array.isArray(conditionOrField)) {
13177
+ const node = parseWhereObject(conditionOrField);
13178
+ if (node && node.items.length > 0) {
13179
+ root.items.push(node);
13180
+ }
13181
+ return;
13182
+ }
13183
+ if (isString(conditionOrField)) {
13184
+ if (value === undefined) {
13185
+ throw new Error(SqlErrors.WHERE_VALUE_REQUIRED, {
13186
+ cause: null,
13187
+ code: "validation"
13188
+ });
13189
+ }
13190
+ const node = buildOperatorNode(conditionOrField, "=", value);
13191
+ if (node) {
13192
+ root.items.push(node);
13193
+ }
13194
+ }
13195
+ }
13196
+ function appendWhereRaw(root, sql, params) {
13197
+ if (!isNonEmptyString(sql)) {
13198
+ throw new Error(SqlErrors.WHERE_RAW_NEED_NON_EMPTY(sql), {
13199
+ cause: null,
13200
+ code: "validation"
13201
+ });
13202
+ }
13203
+ const paramList = Array.isArray(params) ? params : [];
13204
+ for (const param of paramList) {
13205
+ validateParam(param);
13206
+ }
13207
+ root.items.push({ type: "raw", sql, params: paramList });
13208
+ }
13209
+ function parseWhereObject(whereObj) {
13210
+ if (!whereObj || typeof whereObj !== "object") {
13211
+ return { type: "group", join: "AND", items: [] };
13212
+ }
13213
+ const group = { type: "group", join: "AND", items: [] };
13214
+ for (const [key, value] of Object.entries(whereObj)) {
13215
+ if (value === undefined) {
13216
+ continue;
13217
+ }
13218
+ if (key === "$and") {
13219
+ if (Array.isArray(value)) {
13220
+ for (const condition of value) {
13221
+ if (condition && typeof condition === "object" && !Array.isArray(condition)) {
13222
+ const sub = parseWhereObject(condition);
13223
+ if (sub.items.length > 0) {
13224
+ if (sub.join === "AND") {
13225
+ for (const item of sub.items) {
13226
+ group.items.push(item);
13227
+ }
13228
+ } else {
13229
+ group.items.push(sub);
13230
+ }
13231
+ }
13232
+ }
13233
+ }
13234
+ }
13235
+ continue;
13236
+ }
13237
+ if (key === "$or") {
13238
+ if (Array.isArray(value)) {
13239
+ const orGroup = { type: "group", join: "OR", items: [] };
13240
+ for (const condition of value) {
13241
+ if (!condition || typeof condition !== "object" || Array.isArray(condition)) {
13242
+ continue;
13243
+ }
13244
+ const sub = parseWhereObject(condition);
13245
+ if (sub.items.length > 0) {
13246
+ orGroup.items.push(sub);
13247
+ }
13248
+ }
13249
+ if (orGroup.items.length > 0) {
13250
+ group.items.push(orGroup);
13251
+ }
13252
+ }
13253
+ continue;
13254
+ }
13255
+ if (key.includes("$")) {
13256
+ const lastDollarIndex = key.lastIndexOf("$");
13257
+ const fieldName = key.substring(0, lastDollarIndex);
13258
+ const operator = "$" + key.substring(lastDollarIndex + 1);
13259
+ const node2 = buildOperatorNode(fieldName, operator, value);
13260
+ if (node2) {
13261
+ group.items.push(node2);
13262
+ }
13263
+ continue;
13264
+ }
13265
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
13266
+ for (const [op, val] of Object.entries(value)) {
13267
+ const node2 = buildOperatorNode(key, op, val);
13268
+ if (node2) {
13269
+ group.items.push(node2);
13270
+ }
13271
+ }
13272
+ continue;
13273
+ }
13274
+ const node = buildOperatorNode(key, "=", value);
13275
+ if (node) {
13276
+ group.items.push(node);
13277
+ }
13278
+ }
13279
+ return group;
13280
+ }
13281
+ function buildOperatorNode(fieldName, operator, value) {
13282
+ switch (operator) {
13283
+ case "$in":
13284
+ if (!Array.isArray(value)) {
13285
+ throw new Error(SqlErrors.IN_NEED_ARRAY(operator), {
13286
+ cause: null,
13287
+ code: "validation"
13288
+ });
13289
+ }
13290
+ if (value.length === 0) {
13291
+ throw new Error(SqlErrors.IN_NEED_NON_EMPTY, {
13292
+ cause: null,
13293
+ code: "validation"
13294
+ });
13295
+ }
13296
+ return { type: "op", field: fieldName, operator, value };
13297
+ case "$nin":
13298
+ case "$notIn":
13299
+ if (!Array.isArray(value)) {
13300
+ throw new Error(SqlErrors.NIN_NEED_ARRAY(operator), {
13301
+ cause: null,
13302
+ code: "validation"
13303
+ });
13304
+ }
13305
+ if (value.length === 0) {
13306
+ throw new Error(SqlErrors.NIN_NEED_NON_EMPTY, {
13307
+ cause: null,
13308
+ code: "validation"
13309
+ });
13310
+ }
13311
+ return { type: "op", field: fieldName, operator, value };
13312
+ case "$between":
13313
+ case "$notBetween":
13314
+ if (Array.isArray(value) && value.length === 2) {
13315
+ return { type: "op", field: fieldName, operator, value };
13316
+ }
13317
+ return null;
13318
+ case "$null":
13319
+ case "$notNull":
13320
+ if (value === true) {
13321
+ return { type: "op", field: fieldName, operator, value };
13322
+ }
13323
+ return null;
13324
+ default:
13325
+ validateParam(value);
13326
+ return { type: "op", field: fieldName, operator, value };
13327
+ }
13328
+ }
13329
+
13330
+ // lib/sqlBuilder/compiler.js
13331
+ function compileSelect(model, quoteIdent) {
13332
+ let sql = "SELECT ";
13333
+ const params = [];
13334
+ if (model.select.length > 0) {
13335
+ const fields = model.select.map((item) => {
13336
+ if (item.type === "raw") {
13337
+ return item.value;
13338
+ }
13339
+ return escapeField(item.value, quoteIdent);
13340
+ });
13341
+ sql += fields.join(", ");
13342
+ } else {
13343
+ sql += "*";
13344
+ }
13345
+ if (!model.from) {
13346
+ throw new Error(SqlErrors.FROM_REQUIRED, {
13347
+ cause: null,
13348
+ code: "validation"
13349
+ });
13350
+ }
13351
+ const fromSql = model.from.type === "raw" ? model.from.value : escapeTable(model.from.value, quoteIdent);
13352
+ sql += ` FROM ${fromSql}`;
13353
+ if (model.joins.length > 0) {
13354
+ const joinSql = model.joins.map((join3) => {
13355
+ const joinTable = escapeTable(join3.table, quoteIdent);
13356
+ const joinType = join3.type.toUpperCase();
13357
+ return `${joinType} JOIN ${joinTable} ON ${join3.on}`;
13358
+ }).join(" ");
13359
+ sql += ` ${joinSql}`;
13360
+ }
13361
+ const whereResult = compileWhere(model.where, quoteIdent);
13362
+ if (whereResult.sql) {
13363
+ sql += ` WHERE ${whereResult.sql}`;
13364
+ for (const param of whereResult.params) {
13365
+ params.push(param);
13366
+ }
13367
+ }
13368
+ if (model.groupBy.length > 0) {
13369
+ const groupSql = model.groupBy.map((field) => escapeField(field, quoteIdent)).join(", ");
13370
+ sql += ` GROUP BY ${groupSql}`;
13371
+ }
13372
+ if (model.having.length > 0) {
13373
+ sql += ` HAVING ${model.having.join(" AND ")}`;
13374
+ }
13375
+ if (model.orderBy.length > 0) {
13376
+ const orderSql = model.orderBy.map((item) => {
13377
+ const escapedField = escapeField(item.field, quoteIdent);
13378
+ return `${escapedField} ${item.dir}`;
13379
+ }).join(", ");
13380
+ sql += ` ORDER BY ${orderSql}`;
13381
+ }
13382
+ if (model.limit !== null) {
13383
+ sql += ` LIMIT ${model.limit}`;
13384
+ if (model.offset !== null) {
13385
+ sql += ` OFFSET ${model.offset}`;
13386
+ }
13387
+ }
13388
+ return { sql, params };
13389
+ }
13390
+ function compileCount(model, quoteIdent) {
13391
+ let sql = "SELECT COUNT(*) as total";
13392
+ const params = [];
13393
+ if (!model.from) {
13394
+ throw new Error(SqlErrors.COUNT_NEED_FROM, {
13395
+ cause: null,
13396
+ code: "validation"
13397
+ });
13398
+ }
13399
+ const fromSql = model.from.type === "raw" ? model.from.value : escapeTable(model.from.value, quoteIdent);
13400
+ sql += ` FROM ${fromSql}`;
13401
+ if (model.joins.length > 0) {
13402
+ const joinSql = model.joins.map((join3) => {
13403
+ const joinTable = escapeTable(join3.table, quoteIdent);
13404
+ const joinType = join3.type.toUpperCase();
13405
+ return `${joinType} JOIN ${joinTable} ON ${join3.on}`;
13406
+ }).join(" ");
13407
+ sql += ` ${joinSql}`;
13408
+ }
13409
+ const whereResult = compileWhere(model.where, quoteIdent);
13410
+ if (whereResult.sql) {
13411
+ sql += ` WHERE ${whereResult.sql}`;
13412
+ for (const param of whereResult.params) {
13413
+ params.push(param);
13414
+ }
13415
+ }
13416
+ return { sql, params };
13417
+ }
13418
+ function compileInsert(table, data, quoteIdent) {
13419
+ if (!table || !isString(table)) {
13420
+ throw new Error(SqlErrors.INSERT_NEED_TABLE(table), {
13421
+ cause: null,
13422
+ code: "validation"
13423
+ });
13424
+ }
13425
+ if (!data || typeof data !== "object") {
13426
+ throw new Error(SqlErrors.INSERT_NEED_DATA(table, data), {
13427
+ cause: null,
13428
+ code: "validation"
13429
+ });
13430
+ }
13431
+ const escapedTable = escapeTable(table, quoteIdent);
13432
+ if (Array.isArray(data)) {
13433
+ const fields2 = SqlCheck.assertBatchInsertRowsConsistent(data, { table });
13434
+ const escapedFields2 = fields2.map((field) => escapeField(field, quoteIdent));
13435
+ const placeholders2 = fields2.map(() => "?").join(", ");
13436
+ const values = data.map(() => `(${placeholders2})`).join(", ");
13437
+ const sql2 = `INSERT INTO ${escapedTable} (${escapedFields2.join(", ")}) VALUES ${values}`;
13438
+ const params2 = [];
13439
+ for (let i = 0;i < data.length; i++) {
13440
+ const row = data[i];
13441
+ for (const field of fields2) {
13442
+ const value = row[field];
13443
+ validateParam(value);
13444
+ params2.push(value);
13445
+ }
13446
+ }
13447
+ return { sql: sql2, params: params2 };
13448
+ }
13449
+ const fields = Object.keys(data);
13450
+ if (fields.length === 0) {
13451
+ throw new Error(SqlErrors.INSERT_NEED_AT_LEAST_ONE_FIELD(table), {
13452
+ cause: null,
13453
+ code: "validation"
13454
+ });
13455
+ }
13456
+ for (const field of fields) {
13457
+ validateParam(data[field]);
13458
+ }
13459
+ const escapedFields = fields.map((field) => escapeField(field, quoteIdent));
13460
+ const placeholders = fields.map(() => "?").join(", ");
13461
+ const sql = `INSERT INTO ${escapedTable} (${escapedFields.join(", ")}) VALUES (${placeholders})`;
13462
+ const params = [];
13463
+ for (const field of fields) {
13464
+ params.push(data[field]);
13465
+ }
13466
+ return { sql, params };
13467
+ }
13468
+ function compileUpdate(model, table, data, quoteIdent) {
13469
+ if (!table || !isString(table)) {
13470
+ throw new Error(SqlErrors.UPDATE_NEED_TABLE, {
13471
+ cause: null,
13472
+ code: "validation"
13473
+ });
13474
+ }
13475
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
13476
+ throw new Error(SqlErrors.UPDATE_NEED_OBJECT, {
13477
+ cause: null,
13478
+ code: "validation"
13479
+ });
13480
+ }
13481
+ const fields = Object.keys(data);
13482
+ if (fields.length === 0) {
13483
+ throw new Error(SqlErrors.UPDATE_NEED_AT_LEAST_ONE_FIELD, {
13484
+ cause: null,
13485
+ code: "validation"
13486
+ });
13487
+ }
13488
+ const escapedTable = escapeTable(table, quoteIdent);
13489
+ const setFields = fields.map((field) => `${escapeField(field, quoteIdent)} = ?`);
13490
+ const params = [];
13491
+ for (const value of Object.values(data)) {
13492
+ params.push(value);
13493
+ }
13494
+ let sql = `UPDATE ${escapedTable} SET ${setFields.join(", ")}`;
13495
+ const whereResult = compileWhere(model.where, quoteIdent);
13496
+ if (whereResult.sql) {
13497
+ sql += ` WHERE ${whereResult.sql}`;
13498
+ for (const param of whereResult.params) {
13499
+ params.push(param);
13500
+ }
13501
+ } else {
13502
+ throw new Error(SqlErrors.UPDATE_NEED_WHERE, {
13503
+ cause: null,
13504
+ code: "validation"
13505
+ });
13506
+ }
13507
+ return { sql, params };
13508
+ }
13509
+ function compileDelete(model, table, quoteIdent) {
13510
+ if (!table || !isString(table)) {
13511
+ throw new Error(SqlErrors.DELETE_NEED_TABLE, {
13512
+ cause: null,
13513
+ code: "validation"
13514
+ });
13515
+ }
13516
+ const escapedTable = escapeTable(table, quoteIdent);
13517
+ let sql = `DELETE FROM ${escapedTable}`;
13518
+ const whereResult = compileWhere(model.where, quoteIdent);
13519
+ if (whereResult.sql) {
13520
+ sql += ` WHERE ${whereResult.sql}`;
13521
+ } else {
13522
+ throw new Error(SqlErrors.DELETE_NEED_WHERE, {
13523
+ cause: null,
13524
+ code: "validation"
13525
+ });
13526
+ }
13527
+ return { sql, params: whereResult.params };
13528
+ }
13529
+ function compileWhere(node, quoteIdent) {
13530
+ if (!node) {
13531
+ return { sql: "", params: [] };
13532
+ }
13533
+ if (node.type === "raw") {
13534
+ const params = Array.isArray(node.params) ? node.params.slice() : [];
13535
+ return { sql: node.sql, params };
13536
+ }
13537
+ if (node.type === "op") {
13538
+ return compileOperatorNode(node, quoteIdent);
13539
+ }
13540
+ if (node.type === "group") {
13541
+ if (!node.items || node.items.length === 0) {
13542
+ return { sql: "", params: [] };
13543
+ }
13544
+ const parts = [];
13545
+ const params = [];
13546
+ for (const item of node.items) {
13547
+ const built = compileWhere(item, quoteIdent);
13548
+ if (!built.sql) {
13549
+ continue;
13550
+ }
13551
+ let clause = built.sql;
13552
+ if (node.join === "OR") {
13553
+ clause = `(${clause})`;
13554
+ } else if (item.type === "group" && item.join === "OR") {
13555
+ clause = `(${clause})`;
13556
+ }
13557
+ parts.push(clause);
13558
+ for (const param of built.params) {
13559
+ params.push(param);
13560
+ }
13561
+ }
13562
+ return { sql: parts.join(` ${node.join} `), params };
13563
+ }
13564
+ return { sql: "", params: [] };
13565
+ }
13566
+ function compileOperatorNode(node, quoteIdent) {
13567
+ const escapedField = escapeField(node.field, quoteIdent);
13568
+ const operator = node.operator;
13569
+ switch (operator) {
13570
+ case "$ne":
13571
+ case "$not":
13572
+ validateParam(node.value);
13573
+ return { sql: `${escapedField} != ?`, params: [node.value] };
13574
+ case "$in": {
13575
+ const placeholders = node.value.map(() => "?").join(",");
13576
+ const params = [];
13577
+ for (const item of node.value) {
13578
+ params.push(item);
13579
+ }
13580
+ return { sql: `${escapedField} IN (${placeholders})`, params };
13581
+ }
13582
+ case "$nin":
13583
+ case "$notIn": {
13584
+ const placeholders = node.value.map(() => "?").join(",");
13585
+ const params = [];
13586
+ for (const item of node.value) {
13587
+ params.push(item);
13588
+ }
13589
+ return { sql: `${escapedField} NOT IN (${placeholders})`, params };
13590
+ }
13591
+ case "$like":
13592
+ validateParam(node.value);
13593
+ return { sql: `${escapedField} LIKE ?`, params: [node.value] };
13594
+ case "$notLike":
13595
+ validateParam(node.value);
13596
+ return { sql: `${escapedField} NOT LIKE ?`, params: [node.value] };
13597
+ case "$gt":
13598
+ validateParam(node.value);
13599
+ return { sql: `${escapedField} > ?`, params: [node.value] };
13600
+ case "$gte":
13601
+ validateParam(node.value);
13602
+ return { sql: `${escapedField} >= ?`, params: [node.value] };
13603
+ case "$lt":
13604
+ validateParam(node.value);
13605
+ return { sql: `${escapedField} < ?`, params: [node.value] };
13606
+ case "$lte":
13607
+ validateParam(node.value);
13608
+ return { sql: `${escapedField} <= ?`, params: [node.value] };
13609
+ case "$between":
13610
+ return { sql: `${escapedField} BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
13611
+ case "$notBetween":
13612
+ return { sql: `${escapedField} NOT BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
13613
+ case "$null":
13614
+ return { sql: `${escapedField} IS NULL`, params: [] };
13615
+ case "$notNull":
13616
+ return { sql: `${escapedField} IS NOT NULL`, params: [] };
13617
+ default:
13618
+ validateParam(node.value);
13619
+ return { sql: `${escapedField} = ?`, params: [node.value] };
13620
+ }
13621
+ }
13622
+
13623
+ // lib/sqlBuilder/batch.js
13624
+ function toDeleteInSql(options) {
13625
+ if (!isNonEmptyString(options.table)) {
13626
+ throw new Error(SqlErrors.TO_DELETE_IN_NEED_TABLE(options.table), {
13627
+ cause: null,
13628
+ code: "validation"
13629
+ });
13630
+ }
13631
+ if (!isNonEmptyString(options.idField)) {
13632
+ throw new Error(SqlErrors.TO_DELETE_IN_NEED_ID_FIELD(options.idField), {
13633
+ cause: null,
13634
+ code: "validation"
13635
+ });
13636
+ }
13637
+ if (!Array.isArray(options.ids)) {
13638
+ throw new Error(SqlErrors.TO_DELETE_IN_NEED_IDS, {
13639
+ cause: null,
13640
+ code: "validation"
13641
+ });
13642
+ }
13643
+ if (options.ids.length === 0) {
13644
+ return { sql: "", params: [] };
13645
+ }
13646
+ const placeholders = options.ids.map(() => "?").join(",");
13647
+ const sql = `DELETE FROM ${options.quoteIdent(options.table)} WHERE ${options.quoteIdent(options.idField)} IN (${placeholders})`;
13648
+ const params = [];
13649
+ for (const id of options.ids) {
13650
+ params.push(id);
13651
+ }
13652
+ return { sql, params };
13653
+ }
13654
+ function toUpdateCaseByIdSql(options) {
13655
+ if (!isNonEmptyString(options.table)) {
13656
+ throw new Error(SqlErrors.TO_UPDATE_CASE_NEED_TABLE(options.table), {
13657
+ cause: null,
13658
+ code: "validation"
13659
+ });
13660
+ }
13661
+ if (!isNonEmptyString(options.idField)) {
13662
+ throw new Error(SqlErrors.TO_UPDATE_CASE_NEED_ID_FIELD(options.idField), {
13663
+ cause: null,
13664
+ code: "validation"
13665
+ });
13666
+ }
13667
+ if (!Array.isArray(options.rows)) {
13668
+ throw new Error(SqlErrors.TO_UPDATE_CASE_NEED_ROWS, {
13669
+ cause: null,
13670
+ code: "validation"
13671
+ });
13672
+ }
13673
+ if (options.rows.length === 0) {
13674
+ return { sql: "", params: [] };
13675
+ }
13676
+ if (!Array.isArray(options.fields)) {
13677
+ throw new Error(SqlErrors.TO_UPDATE_CASE_NEED_FIELDS, {
13678
+ cause: null,
13679
+ code: "validation"
13680
+ });
13681
+ }
13682
+ if (options.fields.length === 0) {
13683
+ return { sql: "", params: [] };
13684
+ }
13685
+ const ids = options.rows.map((row) => row.id);
13686
+ const placeholders = ids.map(() => "?").join(",");
13687
+ const setSqlList = [];
13688
+ const args = [];
13689
+ const quotedId = options.quoteIdent(options.idField);
13690
+ for (const field of options.fields) {
13691
+ const whenList = [];
13692
+ for (const row of options.rows) {
13693
+ if (!(field in row.data)) {
13694
+ continue;
13695
+ }
13696
+ whenList.push("WHEN ? THEN ?");
13697
+ args.push(row.id);
13698
+ const value = row.data[field];
13699
+ SqlCheck.assertNoUndefinedParam(value, "SQL \u53C2\u6570\u503C");
13700
+ args.push(value);
13701
+ }
13702
+ if (whenList.length === 0) {
13703
+ continue;
13704
+ }
13705
+ const quotedField = options.quoteIdent(field);
13706
+ setSqlList.push(`${quotedField} = CASE ${quotedId} ${whenList.join(" ")} ELSE ${quotedField} END`);
13707
+ }
13708
+ setSqlList.push(`${options.quoteIdent(options.updatedAtField)} = ?`);
13709
+ args.push(options.updatedAtValue);
13710
+ for (const id of ids) {
13711
+ args.push(id);
13712
+ }
13713
+ let sql = `UPDATE ${options.quoteIdent(options.table)} SET ${setSqlList.join(", ")} WHERE ${quotedId} IN (${placeholders})`;
13714
+ if (options.stateGtZero && options.stateField) {
13715
+ sql += ` AND ${options.quoteIdent(options.stateField)} > 0`;
13716
+ }
13717
+ return { sql, params: args };
13718
+ }
13719
+
13720
+ // lib/sqlBuilder/index.js
13721
+ function createModel() {
13722
+ return {
13723
+ select: [],
13724
+ from: null,
13725
+ where: createWhereRoot(),
13726
+ joins: [],
13727
+ orderBy: [],
13728
+ groupBy: [],
13729
+ having: [],
13730
+ limit: null,
13731
+ offset: null
13732
+ };
13733
+ }
13734
+
13735
+ class SqlBuilder {
13736
+ _model;
13737
+ _quoteIdent;
13738
+ constructor(options) {
13739
+ this._quoteIdent = resolveQuoteIdent(options);
13740
+ this._model = createModel();
13741
+ }
13742
+ reset() {
13743
+ this._model = createModel();
13744
+ return this;
13745
+ }
13746
+ getWhereConditions() {
13747
+ const result = compileWhere(this._model.where, this._quoteIdent);
13748
+ return { sql: result.sql, params: result.params };
13749
+ }
13750
+ select(fields = "*") {
13751
+ appendSelectItems(this._model.select, fields);
13752
+ return this;
13753
+ }
13754
+ selectRaw(expr) {
13755
+ appendSelectRaw(this._model.select, expr);
13756
+ return this;
13757
+ }
13758
+ from(table) {
13759
+ setFromValue(this._model, table, false);
13760
+ return this;
13761
+ }
13762
+ fromRaw(tableExpr) {
13763
+ setFromValue(this._model, tableExpr, true);
13764
+ return this;
13765
+ }
13766
+ where(conditionOrField, value) {
13767
+ appendWhereInput(this._model.where, conditionOrField, value);
13768
+ return this;
13769
+ }
13770
+ whereRaw(sql, params) {
13771
+ appendWhereRaw(this._model.where, sql, params);
13772
+ return this;
13773
+ }
13774
+ leftJoin(table, on) {
13775
+ appendJoinItem(this._model.joins, "left", table, on);
13776
+ return this;
13777
+ }
13778
+ rightJoin(table, on) {
13779
+ appendJoinItem(this._model.joins, "right", table, on);
13780
+ return this;
13781
+ }
13782
+ innerJoin(table, on) {
13783
+ appendJoinItem(this._model.joins, "inner", table, on);
13784
+ return this;
13785
+ }
13786
+ orderBy(fields) {
13787
+ appendOrderByItems(this._model.orderBy, fields);
13788
+ return this;
13789
+ }
13790
+ groupBy(field) {
13791
+ appendGroupByItems(this._model.groupBy, field);
13792
+ return this;
13793
+ }
13794
+ having(condition) {
13795
+ appendHavingItems(this._model.having, condition);
13796
+ return this;
13797
+ }
13798
+ limit(count, offset) {
13799
+ const result = normalizeLimitValue(count, offset);
13800
+ this._model.limit = result.limitValue;
13801
+ this._model.offset = result.offsetValue;
13802
+ return this;
13803
+ }
13804
+ offset(count) {
13805
+ const offsetValue = normalizeOffsetValue(count);
13806
+ this._model.offset = offsetValue;
13807
+ return this;
13808
+ }
13809
+ toSelectSql() {
13810
+ return compileSelect(this._model, this._quoteIdent);
13811
+ }
13812
+ toInsertSql(table, data) {
13813
+ return compileInsert(table, data, this._quoteIdent);
13814
+ }
13815
+ toUpdateSql(table, data) {
13816
+ return compileUpdate(this._model, table, data, this._quoteIdent);
13817
+ }
13818
+ toDeleteSql(table) {
13819
+ return compileDelete(this._model, table, this._quoteIdent);
13820
+ }
13821
+ toCountSql() {
13822
+ return compileCount(this._model, this._quoteIdent);
13823
+ }
13824
+ static toDeleteInSql(options) {
13825
+ return toDeleteInSql(options);
13826
+ }
13827
+ static toUpdateCaseByIdSql(options) {
13828
+ return toUpdateCaseByIdSql(options);
13829
+ }
13830
+ }
13831
+
13832
+ // lib/dbHelper/context.js
13833
+ function quoteIdentMySql(identifier) {
13834
+ if (!isString(identifier)) {
13835
+ throw new Error(`quoteIdentifier \u9700\u8981\u5B57\u7B26\u4E32\u7C7B\u578B\u6807\u8BC6\u7B26 (identifier: ${String(identifier)})`, {
13836
+ cause: null,
13837
+ code: "validation"
13838
+ });
13839
+ }
13840
+ const trimmed = identifier.trim();
13841
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
13842
+ throw new Error(`\u65E0\u6548\u7684 SQL \u6807\u8BC6\u7B26: ${trimmed}`, {
13843
+ cause: null,
13844
+ code: "validation"
13845
+ });
13846
+ }
13847
+ return `\`${trimmed}\``;
13848
+ }
13849
+ function hasBegin(sql) {
13850
+ return typeof sql.begin === "function";
13851
+ }
13852
+
13853
+ class DbSqlError extends Error {
13854
+ constructor(message, options) {
13855
+ super(message);
13856
+ this.originalError = options.originalError;
13857
+ this.params = options.params;
13858
+ this.duration = options.duration;
13859
+ this.sqlInfo = options.sqlInfo;
13860
+ }
13861
+ }
13862
+
13863
+ class TransAbortError extends Error {
13864
+ constructor(payload) {
13865
+ super("TRANSACTION_ABORT");
13866
+ this.payload = payload;
13867
+ }
13868
+ }
13869
+ function isBeflyResponse(value) {
13870
+ if (!isPlainObject2(value)) {
13871
+ return false;
13872
+ }
13873
+ const record2 = value;
13874
+ return isNumber(record2["code"]) && isString(record2["msg"]);
13875
+ }
13876
+ function convertBigIntFields(arr, fields) {
13877
+ if (isNullable(arr)) {
13878
+ return arr;
13879
+ }
13880
+ const defaultFields = ["id", "pid", "sort"];
13881
+ const buildFields = (userFields) => {
13882
+ if (!userFields || userFields.length === 0) {
13883
+ return defaultFields;
13884
+ }
13885
+ const merged = ["id", "pid", "sort"];
13886
+ for (const f of userFields) {
13887
+ if (!isString(f)) {
13888
+ continue;
13889
+ }
13890
+ if (!isNonEmptyString(f)) {
13891
+ continue;
13892
+ }
13893
+ const trimmed = f.trim();
13894
+ if (!merged.includes(trimmed)) {
13895
+ merged.push(trimmed);
13896
+ }
13897
+ }
13898
+ return merged;
13899
+ };
13900
+ const effectiveFields = buildFields(fields);
13901
+ const fieldSet = new Set(effectiveFields);
13902
+ const convertRecord = (source) => {
13903
+ const converted = {};
13904
+ for (const [key, value] of Object.entries(source)) {
13905
+ let nextValue = value;
13906
+ if (!isNullable(value)) {
13907
+ const shouldConvert = fieldSet.has(key) || key.endsWith("Id") || key.endsWith("_id") || key.endsWith("At") || key.endsWith("_at");
13908
+ if (shouldConvert) {
13909
+ let bigintValue = null;
13910
+ if (typeof value === "bigint") {
13911
+ bigintValue = value;
13912
+ } else if (isString(value)) {
13913
+ if (/^-?\d+$/.test(value)) {
13914
+ try {
13915
+ bigintValue = BigInt(value);
13916
+ } catch {
13917
+ bigintValue = null;
13918
+ }
13919
+ }
13920
+ }
13921
+ if (bigintValue !== null) {
13922
+ const convertedNumber = canConvertToNumber(bigintValue);
13923
+ if (convertedNumber !== null) {
13924
+ nextValue = convertedNumber;
13925
+ }
13926
+ }
13927
+ }
13928
+ }
13929
+ converted[key] = nextValue;
13930
+ }
13931
+ return converted;
13932
+ };
13933
+ if (Array.isArray(arr)) {
13934
+ return arr.map((item) => convertRecord(item));
13935
+ }
13936
+ if (typeof arr === "object") {
13937
+ return convertRecord(arr);
13938
+ }
13939
+ return arr;
13940
+ }
13941
+
13942
+ // lib/dbHelper/validate.js
13943
+ function assertNonEmptyString(value, label) {
13944
+ if (!isNonEmptyString(value)) {
13945
+ throw new Error(`${label} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`, {
13946
+ cause: null,
13947
+ code: "validation"
13948
+ });
13949
+ }
13950
+ }
13951
+ function assertPlainObjectValue(value, label) {
13952
+ if (!isPlainObject2(value)) {
13953
+ throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`, {
13954
+ cause: null,
13955
+ code: "validation"
13956
+ });
13957
+ }
13958
+ }
13959
+ function validateWhereObject(where, label, required2 = false) {
13960
+ if (isNullable(where)) {
13961
+ if (required2) {
13962
+ throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`, {
13963
+ cause: null,
13964
+ code: "validation"
13965
+ });
13966
+ }
13967
+ return;
13968
+ }
13969
+ if (!isPlainObject2(where)) {
13970
+ throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`, {
13971
+ cause: null,
13972
+ code: "validation"
13973
+ });
13974
+ }
13975
+ }
13976
+ function validateOrderByItems(orderBy, label) {
13977
+ if (!Array.isArray(orderBy)) {
13978
+ throw new Error(`${label} \u5FC5\u987B\u662F\u6570\u7EC4`, {
13979
+ cause: null,
13980
+ code: "validation"
13981
+ });
13982
+ }
13983
+ for (const item of orderBy) {
13984
+ if (!isString(item) || !item.includes("#")) {
13985
+ throw new Error(`${label} \u5B57\u6BB5\u5FC5\u987B\u662F "\u5B57\u6BB5#\u65B9\u5411" \u683C\u5F0F\u7684\u5B57\u7B26\u4E32`, {
13986
+ cause: null,
13987
+ code: "validation"
13988
+ });
13989
+ }
13990
+ const parts = item.split("#");
13991
+ if (parts.length !== 2) {
13992
+ throw new Error(`${label} \u5B57\u6BB5\u5FC5\u987B\u662F "\u5B57\u6BB5#\u65B9\u5411" \u683C\u5F0F\u7684\u5B57\u7B26\u4E32`, {
13993
+ cause: null,
13994
+ code: "validation"
13995
+ });
13996
+ }
13997
+ const field = isString(parts[0]) ? parts[0].trim() : "";
13998
+ const direction = isString(parts[1]) ? parts[1].trim().toUpperCase() : "";
13999
+ if (!field) {
14000
+ throw new Error(`${label} \u4E2D\u5B57\u6BB5\u540D\u4E0D\u80FD\u4E3A\u7A7A`, {
14001
+ cause: null,
14002
+ code: "validation"
14003
+ });
14004
+ }
14005
+ if (direction !== "ASC" && direction !== "DESC") {
14006
+ throw new Error(`${label} \u65B9\u5411\u5FC5\u987B\u662F ASC \u6216 DESC`, {
14007
+ cause: null,
14008
+ code: "validation"
14009
+ });
14010
+ }
14011
+ }
14012
+ }
14013
+ function validateJoins(joins, label) {
14014
+ if (!Array.isArray(joins)) {
14015
+ throw new Error(`${label} \u5FC5\u987B\u662F\u6570\u7EC4`, {
14016
+ cause: null,
14017
+ code: "validation"
14018
+ });
14019
+ }
14020
+ for (let i = 0;i < joins.length; i++) {
14021
+ const join3 = joins[i];
14022
+ if (!isPlainObject2(join3)) {
14023
+ throw new Error(`${label}[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`, {
14024
+ cause: null,
14025
+ code: "validation"
14026
+ });
14027
+ }
14028
+ assertNonEmptyString(join3.table, `${label}[${i}].table`);
14029
+ assertNonEmptyString(join3.on, `${label}[${i}].on`);
14030
+ if (!isNullable(join3.type)) {
14031
+ const type = isString(join3.type) ? join3.type.trim().toLowerCase() : "";
14032
+ if (type !== "left" && type !== "right" && type !== "inner") {
14033
+ throw new Error(`${label}[${i}].type \u53EA\u5141\u8BB8 left/right/inner`, {
14034
+ cause: null,
14035
+ code: "validation"
14036
+ });
14037
+ }
14038
+ }
14039
+ }
14040
+ }
14041
+ function validateQueryOptions(options, label) {
14042
+ if (!isPlainObject2(options)) {
14043
+ throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`, {
14044
+ cause: null,
14045
+ code: "validation"
14046
+ });
14047
+ }
14048
+ assertNonEmptyString(options.table, `${label}.table`);
14049
+ if (!isNullable(options.where)) {
14050
+ validateWhereObject(options.where, `${label}.where`);
14051
+ }
14052
+ if (!isNullable(options.fields) && !Array.isArray(options.fields)) {
14053
+ throw new Error(`${label}.fields \u5FC5\u987B\u662F\u6570\u7EC4`, {
14054
+ cause: null,
14055
+ code: "validation"
14056
+ });
14057
+ }
14058
+ if (!isNullable(options.orderBy)) {
14059
+ validateOrderByItems(options.orderBy, `${label}.orderBy`);
14060
+ }
14061
+ if (!isNullable(options.joins)) {
14062
+ validateJoins(options.joins, `${label}.joins`);
14063
+ }
14064
+ if (!isNullable(options.page) && (!isFiniteNumber(options.page) || Math.floor(options.page) !== options.page)) {
14065
+ throw new Error(`${label}.page \u5FC5\u987B\u662F\u6574\u6570`, {
14066
+ cause: null,
14067
+ code: "validation"
14068
+ });
14069
+ }
14070
+ if (!isNullable(options.limit) && (!isFiniteNumber(options.limit) || Math.floor(options.limit) !== options.limit)) {
14071
+ throw new Error(`${label}.limit \u5FC5\u987B\u662F\u6574\u6570`, {
14072
+ cause: null,
14073
+ code: "validation"
14074
+ });
14075
+ }
14076
+ }
14077
+ function validateWriteData(data, label) {
14078
+ assertPlainObjectValue(data, label);
14079
+ }
14080
+ function validateBatchDataList(dataList, label) {
14081
+ if (!Array.isArray(dataList)) {
14082
+ throw new Error(`${label} \u5FC5\u987B\u662F\u6570\u7EC4`, {
14083
+ cause: null,
14084
+ code: "validation"
14085
+ });
14086
+ }
14087
+ for (let i = 0;i < dataList.length; i++) {
14088
+ if (!isPlainObject2(dataList[i])) {
14089
+ throw new Error(`${label}[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`, {
14090
+ cause: null,
14091
+ code: "validation"
14092
+ });
14093
+ }
14094
+ }
14095
+ }
14096
+ function validateTableName(table, label) {
14097
+ assertNonEmptyString(table, label);
14098
+ }
14099
+ function validateTableWhereOptions(options, label, required2 = false) {
14100
+ validateTableName(options.table, `${label}.table`);
14101
+ validateWhereObject(options.where, `${label}.where`, required2);
14102
+ }
14103
+ function validateTableDataOptions(options, label) {
14104
+ validateTableName(options.table, `${label}.table`);
14105
+ validateWriteData(options.data, `${label}.data`);
14106
+ }
14107
+ function validateTableBatchDataOptions(table, dataList, label) {
14108
+ validateTableName(table, `${label}.table`);
14109
+ validateBatchDataList(dataList, `${label}.dataList`);
14110
+ }
14111
+ function validateNoJoinReadOptions(options, label, joinErrorMessage) {
14112
+ validateTableWhereOptions(options, label, false);
14113
+ validateNoJoins(options.joins, joinErrorMessage);
14114
+ validateSimpleTableName(options.table, label);
14115
+ }
14116
+ function validateIncrementOptions(table, field, where, value, label) {
14117
+ validateTableWhereOptions({ table, where }, label, true);
14118
+ validateSafeFieldName(snakeCase(table));
14119
+ validateSafeFieldName(snakeCase(field));
14120
+ validateIncrementValue(table, field, value);
14121
+ }
14122
+ function validateSimpleTableName(rawTable, label) {
14123
+ const table = isString(rawTable) ? rawTable.trim() : "";
14124
+ if (!table) {
14125
+ throw new Error(`${label}.table \u4E0D\u80FD\u4E3A\u7A7A`, {
14126
+ cause: null,
14127
+ code: "validation"
14128
+ });
14129
+ }
14130
+ if (table.includes(" ")) {
14131
+ throw new Error(`${label} \u4E0D\u652F\u6301\u522B\u540D\u8868\u5199\u6CD5\uFF08table: ${table}\uFF09`, {
14132
+ cause: null,
14133
+ code: "validation"
14134
+ });
14135
+ }
14136
+ if (table.includes(".")) {
14137
+ throw new Error(`${label} \u4E0D\u652F\u6301 schema.table \u5199\u6CD5\uFF08table: ${table}\uFF09`, {
14138
+ cause: null,
14139
+ code: "validation"
14140
+ });
14141
+ }
14142
+ }
14143
+ function validateSafeFieldName(field) {
14144
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(field)) {
14145
+ throw new Error(`\u65E0\u6548\u7684\u5B57\u6BB5\u540D: ${field}\uFF0C\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u4E0B\u5212\u7EBF`, {
14146
+ cause: null,
14147
+ code: "validation"
14148
+ });
14149
+ }
14150
+ }
14151
+ function validatePageLimitRange(prepared, rawTable) {
14152
+ if (prepared.page < 1 || prepared.page > 1e4) {
14153
+ throw new Error(`\u9875\u7801\u5FC5\u987B\u5728 1 \u5230 10000 \u4E4B\u95F4 (table: ${rawTable}, page: ${prepared.page}, limit: ${prepared.limit})`, {
14154
+ cause: null,
14155
+ code: "validation"
14156
+ });
14157
+ }
14158
+ if (prepared.limit < 1 || prepared.limit > 1000) {
14159
+ throw new Error(`\u6BCF\u9875\u6570\u91CF\u5FC5\u987B\u5728 1 \u5230 1000 \u4E4B\u95F4 (table: ${rawTable}, page: ${prepared.page}, limit: ${prepared.limit})`, {
14160
+ cause: null,
14161
+ code: "validation"
14162
+ });
14163
+ }
14164
+ }
14165
+ function validateNoJoins(joins, message) {
14166
+ if (Array.isArray(joins) && joins.length > 0) {
14167
+ throw new Error(message, {
14168
+ cause: null,
14169
+ code: "validation"
14170
+ });
14171
+ }
14172
+ }
14173
+ function validateIncrementValue(table, field, value) {
14174
+ if (typeof value !== "number" || isNaN(value)) {
14175
+ throw new Error(`\u81EA\u589E\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684\u6570\u5B57 (table: ${table}, field: ${field}, value: ${value})`, {
14176
+ cause: null,
14177
+ code: "validation"
14178
+ });
14179
+ }
14180
+ }
14181
+ function validateExecuteSql(sql) {
14182
+ if (!isString(sql)) {
14183
+ throw new Error(`execute \u53EA\u63A5\u53D7\u5B57\u7B26\u4E32\u7C7B\u578B\u7684 SQL\uFF0C\u6536\u5230\u7C7B\u578B: ${typeof sql}\uFF0C\u503C: ${JSON.stringify(sql)}`, {
14184
+ cause: null,
14185
+ code: "validation"
14186
+ });
14187
+ }
14188
+ }
14189
+ function validateExcludeFieldsResult(resultFields, table, excludeSnakeFields) {
14190
+ if (resultFields.length === 0) {
14191
+ throw new Error(`\u6392\u9664\u5B57\u6BB5\u540E\u6CA1\u6709\u5269\u4F59\u5B57\u6BB5\u53EF\u67E5\u8BE2\u3002\u8868: ${table}, \u6392\u9664: ${excludeSnakeFields.join(", ")}`, {
14192
+ cause: null,
14193
+ code: "validation"
14194
+ });
14195
+ }
14196
+ }
14197
+ function validateTimeIdValue(id) {
14198
+ if (!Number.isFinite(id) || id <= 0) {
14199
+ throw new Error(`buildInsertRow(timeId) \u5931\u8D25\uFF1Aid \u5FC5\u987B\u4E3A > 0 \u7684\u6709\u9650 number (id: ${String(id)})`, {
14200
+ cause: null,
14201
+ code: "validation"
14202
+ });
14203
+ }
14204
+ }
14205
+ function validateInsertBatchSize(count, maxBatchSize) {
14206
+ if (count > maxBatchSize) {
14207
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u6570\u91CF ${count} \u8D85\u8FC7\u6700\u5927\u9650\u5236 ${maxBatchSize}`, {
14208
+ cause: null,
14209
+ code: "validation"
14210
+ });
14211
+ }
14212
+ }
14213
+ function assertNoExprField(field) {
14214
+ if (!isString(field)) {
14215
+ return;
14216
+ }
14217
+ if (!isNonEmptyString(field)) {
14218
+ return;
14219
+ }
14220
+ const trimmed = field.trim();
14221
+ if (trimmed.includes("(") || trimmed.includes(")")) {
14222
+ throw new Error(`\u5B57\u6BB5\u5305\u542B\u51FD\u6570/\u8868\u8FBE\u5F0F\uFF0C\u8BF7\u4F7F\u7528 selectRaw/whereRaw (field: ${trimmed})`, {
14223
+ cause: null,
14224
+ code: "validation"
14225
+ });
14226
+ }
14227
+ }
14228
+ function assertNoUndefinedInRecord(row, label) {
14229
+ for (const [key, value] of Object.entries(row)) {
14230
+ if (value === undefined) {
14231
+ throw new Error(`${label} \u5B58\u5728 undefined \u5B57\u6BB5\u503C (field: ${key})`, {
14232
+ cause: null,
14233
+ code: "validation"
14234
+ });
14235
+ }
14236
+ }
14237
+ }
14238
+ function assertBatchInsertRowsConsistent(rows, options) {
14239
+ if (!Array.isArray(rows)) {
14240
+ throw new Error("\u6279\u91CF\u63D2\u5165 rows \u5FC5\u987B\u662F\u6570\u7EC4", {
14241
+ cause: null,
14242
+ code: "validation"
14243
+ });
14244
+ }
14245
+ if (rows.length === 0) {
14246
+ throw new Error(`\u63D2\u5165\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A (table: ${options.table})`, {
14247
+ cause: null,
14248
+ code: "validation"
14249
+ });
14250
+ }
14251
+ const first = rows[0];
14252
+ if (!first || typeof first !== "object" || Array.isArray(first)) {
14253
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7684\u6BCF\u4E00\u884C\u5FC5\u987B\u662F\u5BF9\u8C61 (table: ${options.table}, rowIndex: 0)`, {
14254
+ cause: null,
14255
+ code: "validation"
14256
+ });
14257
+ }
14258
+ const fields = Object.keys(first);
14259
+ if (fields.length === 0) {
14260
+ throw new Error(`\u63D2\u5165\u6570\u636E\u5FC5\u987B\u81F3\u5C11\u6709\u4E00\u4E2A\u5B57\u6BB5 (table: ${options.table})`, {
14261
+ cause: null,
14262
+ code: "validation"
14263
+ });
14264
+ }
14265
+ const fieldSet = new Set(fields);
14266
+ for (let i = 0;i < rows.length; i++) {
14267
+ const row = rows[i];
14268
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
14269
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7684\u6BCF\u4E00\u884C\u5FC5\u987B\u662F\u5BF9\u8C61 (table: ${options.table}, rowIndex: ${i})`, {
14270
+ cause: null,
14271
+ code: "validation"
14272
+ });
14273
+ }
14274
+ const rowKeys = Object.keys(row);
14275
+ if (rowKeys.length !== fields.length) {
14276
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u6BCF\u884C\u5B57\u6BB5\u5FC5\u987B\u4E00\u81F4 (table: ${options.table}, rowIndex: ${i})`, {
14277
+ cause: null,
14278
+ code: "validation"
14279
+ });
14280
+ }
14281
+ for (const key of rowKeys) {
14282
+ if (!fieldSet.has(key)) {
14283
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u6BCF\u884C\u5B57\u6BB5\u5FC5\u987B\u4E00\u81F4 (table: ${options.table}, rowIndex: ${i}, extraField: ${key})`, {
14284
+ cause: null,
14285
+ code: "validation"
14286
+ });
14287
+ }
14288
+ }
14289
+ for (const field of fields) {
14290
+ if (!(field in row)) {
14291
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u7F3A\u5C11\u5B57\u6BB5 (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
14292
+ cause: null,
14293
+ code: "validation"
14294
+ });
14295
+ }
14296
+ if (row[field] === undefined) {
14297
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u5B57\u6BB5\u503C\u4E0D\u80FD\u4E3A undefined (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
14298
+ cause: null,
14299
+ code: "validation"
14300
+ });
14301
+ }
14302
+ }
14303
+ }
14304
+ return fields;
14305
+ }
14306
+ function parseTableRef(tableRef) {
14307
+ if (!isString(tableRef)) {
14308
+ throw new Error(`tableRef \u5FC5\u987B\u662F\u5B57\u7B26\u4E32 (tableRef: ${String(tableRef)})`, {
14309
+ cause: null,
14310
+ code: "validation"
14311
+ });
14312
+ }
14313
+ const trimmed = tableRef.trim();
14314
+ if (!isNonEmptyString(tableRef)) {
14315
+ throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A", {
14316
+ cause: null,
14317
+ code: "validation"
14318
+ });
14319
+ }
14320
+ const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
14321
+ if (parts.length === 0) {
14322
+ throw new Error("tableRef \u4E0D\u80FD\u4E3A\u7A7A", {
14323
+ cause: null,
14324
+ code: "validation"
14325
+ });
14326
+ }
14327
+ if (parts.length > 2) {
14328
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08\u5305\u542B\u8FC7\u591A\u7247\u6BB5\uFF09\u3002\u8BF7\u4F7F\u7528\u6700\u7B80\u5F62\u5F0F\uFF1Atable \u6216 table alias \u6216 schema.table \u6216 schema.table alias (tableRef: ${trimmed})`, {
14329
+ cause: null,
14330
+ code: "validation"
14331
+ });
14332
+ }
14333
+ const namePart = parts[0];
14334
+ if (!isNonEmptyString(namePart)) {
14335
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11\u8868\u540D (tableRef: ${trimmed})`, {
14336
+ cause: null,
14337
+ code: "validation"
14338
+ });
14339
+ }
14340
+ let aliasPart = null;
14341
+ if (parts.length === 2) {
14342
+ const alias = parts[1];
14343
+ if (!isNonEmptyString(alias)) {
14344
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1A\u7F3A\u5C11 alias (tableRef: ${trimmed})`, {
14345
+ cause: null,
14346
+ code: "validation"
14347
+ });
14348
+ }
14349
+ aliasPart = alias;
14350
+ }
14351
+ const nameSegments = namePart.split(".");
14352
+ if (nameSegments.length > 2) {
14353
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u8868\u5F15\u7528\u683C\u5F0F\uFF08schema \u5C42\u7EA7\u8FC7\u6DF1\uFF09 (tableRef: ${trimmed})`, {
14354
+ cause: null,
14355
+ code: "validation"
14356
+ });
14357
+ }
14358
+ if (nameSegments.length === 2) {
14359
+ const schema = nameSegments[0];
14360
+ const table2 = nameSegments[1];
14361
+ if (!isNonEmptyString(schema)) {
14362
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Aschema \u4E3A\u7A7A (tableRef: ${trimmed})`, {
14363
+ cause: null,
14364
+ code: "validation"
14365
+ });
14366
+ }
14367
+ if (!isNonEmptyString(table2)) {
14368
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`, {
14369
+ cause: null,
14370
+ code: "validation"
14371
+ });
14372
+ }
14373
+ return { schema, table: table2, alias: aliasPart };
14374
+ }
14375
+ const table = nameSegments[0];
14376
+ if (!isNonEmptyString(table)) {
14377
+ throw new Error(`tableRef \u89E3\u6790\u5931\u8D25\uFF1Atable \u4E3A\u7A7A (tableRef: ${trimmed})`, {
14378
+ cause: null,
14379
+ code: "validation"
14380
+ });
14381
+ }
14382
+ return { schema: null, table, alias: aliasPart };
14383
+ }
14384
+ function validateGeneratedBatchId(id, table, index) {
14385
+ if (typeof id !== "number") {
14386
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u751F\u6210 ID \u5931\u8D25\uFF1Aids[${index}] \u4E0D\u662F number (table: ${table})`, {
14387
+ cause: null,
14388
+ code: "runtime"
14389
+ });
14390
+ }
14391
+ }
14392
+ function validateAndClassifyFields(fields) {
14393
+ if (!fields || fields.length === 0) {
14394
+ return { type: "all", fields: [] };
14395
+ }
14396
+ if (fields.some((f) => f === "*")) {
14397
+ throw new Error("fields \u4E0D\u652F\u6301 * \u661F\u53F7\uFF0C\u8BF7\u4F7F\u7528\u7A7A\u6570\u7EC4 [] \u6216\u4E0D\u4F20\u53C2\u6570\u8868\u793A\u67E5\u8BE2\u6240\u6709\u5B57\u6BB5", {
14398
+ cause: null,
14399
+ code: "validation"
14400
+ });
14401
+ }
14402
+ if (fields.some((f) => !isNonEmptyString(f))) {
14403
+ throw new Error("fields \u4E0D\u80FD\u5305\u542B\u7A7A\u5B57\u7B26\u4E32\u6216\u65E0\u6548\u503C", {
14404
+ cause: null,
14405
+ code: "validation"
14406
+ });
14407
+ }
14408
+ for (const rawField of fields) {
14409
+ const checkField = rawField.startsWith("!") ? rawField.substring(1) : rawField;
14410
+ assertNoExprField(checkField);
14411
+ }
14412
+ const includeFields = fields.filter((f) => !f.startsWith("!"));
14413
+ const excludeFields = fields.filter((f) => f.startsWith("!"));
14414
+ if (includeFields.length > 0 && excludeFields.length === 0) {
14415
+ return { type: "include", fields: includeFields };
14416
+ }
14417
+ if (excludeFields.length > 0 && includeFields.length === 0) {
14418
+ return { type: "exclude", fields: excludeFields.map((f) => f.substring(1)) };
14419
+ }
14420
+ throw new Error(`fields \u4E0D\u80FD\u540C\u65F6\u5305\u542B\u666E\u901A\u5B57\u6BB5\u548C\u6392\u9664\u5B57\u6BB5\uFF08! \u5F00\u5934\uFF09\u3002\u53EA\u80FD\u4F7F\u7528\u4EE5\u4E0B3\u79CD\u65B9\u5F0F\u4E4B\u4E00\uFF1A
14421
+ 1. \u7A7A\u6570\u7EC4 [] \u6216\u4E0D\u4F20\uFF08\u67E5\u8BE2\u6240\u6709\uFF09
14422
+ 2. \u5168\u90E8\u6307\u5B9A\u5B57\u6BB5 ["id", "name"]
14423
+ 3. \u5168\u90E8\u6392\u9664\u5B57\u6BB5 ["!password", "!token"]`, {
14424
+ cause: null,
14425
+ code: "validation"
14426
+ });
14427
+ }
14428
+
14429
+ // lib/dbHelper/util.js
14430
+ function normalizeTableRef(tableRef) {
14431
+ const parsed = parseTableRef(tableRef);
14432
+ const schemaPart = parsed.schema ? snakeCase(parsed.schema) : null;
14433
+ const tablePart = snakeCase(parsed.table);
14434
+ const aliasPart = parsed.alias ? snakeCase(parsed.alias) : null;
14435
+ let result = schemaPart ? `${schemaPart}.${tablePart}` : tablePart;
14436
+ if (aliasPart) {
14437
+ result = `${result} ${aliasPart}`;
14438
+ }
14439
+ return result;
14440
+ }
14441
+ function getJoinMainQualifier(tableRef) {
14442
+ const parsed = parseTableRef(tableRef);
14443
+ if (parsed.alias) {
14444
+ return snakeCase(parsed.alias);
14445
+ }
14446
+ return snakeCase(parsed.table);
14447
+ }
14448
+ function toNumberFromSql(value) {
14449
+ if (isNullable(value)) {
14450
+ return 0;
14451
+ }
14452
+ if (typeof value === "number") {
14453
+ if (!Number.isFinite(value)) {
14454
+ return 0;
14455
+ }
14456
+ return value;
14457
+ }
14458
+ if (typeof value === "bigint") {
14459
+ const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
14460
+ const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
14461
+ if (value <= maxSafe && value >= minSafe) {
14462
+ return Number(value);
14463
+ }
14464
+ return 0;
14465
+ }
14466
+ if (typeof value === "string") {
14467
+ if (!value.trim()) {
14468
+ return 0;
14469
+ }
14470
+ const parsed = Number(value);
14471
+ if (!Number.isFinite(parsed)) {
14472
+ return 0;
14473
+ }
14474
+ return parsed;
14475
+ }
14476
+ return 0;
14477
+ }
14478
+
14479
+ // lib/dbHelper/builders.js
14480
+ function clearDeep(value, options) {
14481
+ const arrayObjectKeys = Array.isArray(options?.arrayObjectKeys) ? options.arrayObjectKeys : ["$or", "$and"];
14482
+ const arrayObjectKeySet = new Set;
14483
+ for (const key of arrayObjectKeys) {
14484
+ arrayObjectKeySet.add(key);
14485
+ }
14486
+ const depthRaw = isFiniteNumber(options?.depth) ? Math.floor(options.depth) : 0;
14487
+ const depth = depthRaw < 0 ? 0 : depthRaw;
14488
+ const clearInternal = (input, remainingDepth) => {
14489
+ if (!input || typeof input !== "object") {
14490
+ return input;
14491
+ }
14492
+ if (Array.isArray(input)) {
14493
+ return input;
14494
+ }
14495
+ const canRecurse = remainingDepth === 0 || remainingDepth > 1;
14496
+ const childDepth = remainingDepth === 0 ? 0 : remainingDepth - 1;
14497
+ const result = {};
14498
+ for (const [key, item] of Object.entries(input)) {
14499
+ if (isNullable(item)) {
14500
+ continue;
14501
+ }
14502
+ if (arrayObjectKeySet.has(key)) {
14503
+ if (!Array.isArray(item)) {
14504
+ continue;
14505
+ }
14506
+ const outList = [];
14507
+ for (const child of item) {
14508
+ if (!child || typeof child !== "object" || Array.isArray(child)) {
14509
+ continue;
14510
+ }
14511
+ const cleaned = clearInternal(child, remainingDepth);
14512
+ if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) {
14513
+ continue;
14514
+ }
14515
+ if (Object.keys(cleaned).length === 0) {
14516
+ continue;
14517
+ }
14518
+ outList.push(cleaned);
14519
+ }
14520
+ if (outList.length > 0) {
14521
+ result[key] = outList;
14522
+ }
14523
+ continue;
14524
+ }
14525
+ if (typeof item === "object" && !Array.isArray(item)) {
14526
+ if (!canRecurse) {
14527
+ result[key] = item;
14528
+ continue;
14529
+ }
14530
+ const cleanedObj = clearInternal(item, childDepth);
14531
+ if (!cleanedObj || typeof cleanedObj !== "object" || Array.isArray(cleanedObj)) {
14532
+ continue;
14533
+ }
14534
+ if (Object.keys(cleanedObj).length === 0) {
14535
+ continue;
14536
+ }
14537
+ result[key] = cleanedObj;
14538
+ continue;
14539
+ }
14540
+ result[key] = item;
14541
+ }
14542
+ return result;
14543
+ };
14544
+ return clearInternal(value, depth);
14545
+ }
14546
+ async function fieldsToSnake(table, fields, getTableColumns) {
14547
+ if (!fields || !Array.isArray(fields)) {
14548
+ return ["*"];
14549
+ }
14550
+ const classified = validateAndClassifyFields(fields);
14551
+ if (classified.type === "all") {
14552
+ return ["*"];
14553
+ }
14554
+ if (classified.type === "include") {
14555
+ return classified.fields.map((field) => processJoinField(field));
14556
+ }
14557
+ if (classified.type === "exclude") {
14558
+ const allColumns = await getTableColumns(table);
14559
+ const excludeSnakeFields = classified.fields.map((f) => snakeCase(f));
14560
+ const resultFields = allColumns.filter((col) => !excludeSnakeFields.includes(col));
14561
+ validateExcludeFieldsResult(resultFields, table, excludeSnakeFields);
14562
+ return resultFields;
14563
+ }
14564
+ return ["*"];
14565
+ }
14566
+ function orderByToSnake(orderBy) {
14567
+ if (!orderBy || !Array.isArray(orderBy)) {
14568
+ return orderBy;
14569
+ }
14570
+ return orderBy.map((item) => {
14571
+ if (!isString(item) || !item.includes("#")) {
14572
+ return item;
14573
+ }
14574
+ const parts = item.split("#");
14575
+ if (parts.length !== 2) {
14576
+ return item;
14577
+ }
14578
+ const field = parts[0];
14579
+ const direction = parts[1];
14580
+ if (!isString(field) || !isString(direction)) {
14581
+ return item;
14582
+ }
14583
+ return `${snakeCase(field.trim())}#${direction.trim()}`;
14584
+ });
14585
+ }
14586
+ function processJoinField(field) {
14587
+ assertNoExprField(field);
14588
+ if (field === "*" || field.startsWith("`")) {
14589
+ return field;
14590
+ }
14591
+ if (field.toUpperCase().includes(" AS ")) {
14592
+ const parts = field.split(/\s+AS\s+/i);
14593
+ const fieldPart = parts[0];
14594
+ const aliasPart = parts[1];
14595
+ if (!isString(fieldPart) || !isString(aliasPart)) {
14596
+ return field;
14597
+ }
14598
+ return `${processJoinField(fieldPart.trim())} AS ${aliasPart.trim()}`;
14599
+ }
14600
+ if (field.includes(".")) {
14601
+ const parts = field.split(".");
14602
+ if (parts.length < 2) {
14603
+ return snakeCase(field);
14604
+ }
14605
+ if (parts.some((p) => !isNonEmptyString(p))) {
14606
+ return field;
14607
+ }
14608
+ const fieldName = parts[parts.length - 1];
14609
+ const tableName = parts.slice(0, parts.length - 1).join(".");
14610
+ if (!isString(fieldName) || !isString(tableName)) {
14611
+ return snakeCase(field);
14612
+ }
14613
+ const normalizedTableName = tableName.split(".").map((item) => {
14614
+ const trimmed = item.trim();
14615
+ if (!trimmed) {
14616
+ return trimmed;
14617
+ }
14618
+ return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
14619
+ }).join(".");
14620
+ return `${normalizedTableName}.${snakeCase(fieldName)}`;
14621
+ }
14622
+ return snakeCase(field);
14623
+ }
14624
+ function processJoinWhereKey(key) {
14625
+ if (key === "$or" || key === "$and") {
14626
+ return key;
14627
+ }
14628
+ assertNoExprField(key);
14629
+ if (key.includes("$")) {
14630
+ const lastDollarIndex = key.lastIndexOf("$");
14631
+ const fieldPart = key.substring(0, lastDollarIndex);
14632
+ const operator = key.substring(lastDollarIndex);
14633
+ if (fieldPart.includes(".")) {
14634
+ const parts = fieldPart.split(".");
14635
+ if (parts.length < 2) {
14636
+ return `${snakeCase(fieldPart)}${operator}`;
14637
+ }
14638
+ if (parts.some((p) => !isNonEmptyString(p))) {
14639
+ return `${snakeCase(fieldPart)}${operator}`;
14640
+ }
14641
+ const fieldName = parts[parts.length - 1];
14642
+ const tableName = parts.slice(0, parts.length - 1).join(".");
14643
+ if (!isString(fieldName) || !isString(tableName)) {
14644
+ return `${snakeCase(fieldPart)}${operator}`;
14645
+ }
14646
+ const normalizedTableName = tableName.split(".").map((item) => {
14647
+ const trimmed = item.trim();
14648
+ if (!trimmed) {
14649
+ return trimmed;
14650
+ }
14651
+ return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
14652
+ }).join(".");
14653
+ return `${normalizedTableName}.${snakeCase(fieldName)}${operator}`;
14654
+ }
14655
+ return `${snakeCase(fieldPart)}${operator}`;
14656
+ }
14657
+ if (key.includes(".")) {
14658
+ const parts = key.split(".");
14659
+ if (parts.length < 2) {
14660
+ return snakeCase(key);
14661
+ }
14662
+ if (parts.some((p) => !isNonEmptyString(p))) {
14663
+ return snakeCase(key);
14664
+ }
14665
+ const fieldName = parts[parts.length - 1];
14666
+ const tableName = parts.slice(0, parts.length - 1).join(".");
14667
+ if (!isString(fieldName) || !isString(tableName)) {
14668
+ return snakeCase(key);
14669
+ }
14670
+ const normalizedTableName = tableName.split(".").map((item) => {
14671
+ const trimmed = item.trim();
14672
+ if (!trimmed) {
14673
+ return trimmed;
14674
+ }
14675
+ return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
14676
+ }).join(".");
14677
+ return `${normalizedTableName}.${snakeCase(fieldName)}`;
14678
+ }
14679
+ return snakeCase(key);
14680
+ }
14681
+ function processJoinWhere(where) {
14682
+ if (!where || typeof where !== "object") {
14683
+ return where;
14684
+ }
14685
+ if (Array.isArray(where)) {
14686
+ return where.map((item) => processJoinWhere(item));
14687
+ }
14688
+ const result = {};
14689
+ for (const [key, value] of Object.entries(where)) {
14690
+ const newKey = processJoinWhereKey(key);
14691
+ if (key === "$or" || key === "$and") {
14692
+ result[newKey] = Array.isArray(value) ? value.map((item) => processJoinWhere(item)) : [];
14693
+ } else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
14694
+ result[newKey] = processJoinWhere(value);
14695
+ } else {
14696
+ result[newKey] = value;
14697
+ }
14698
+ }
14699
+ return result;
14700
+ }
14701
+ function processJoinOrderBy(orderBy) {
14702
+ if (!orderBy || !Array.isArray(orderBy)) {
14703
+ return orderBy;
14704
+ }
14705
+ return orderBy.map((item) => {
14706
+ if (!isString(item) || !item.includes("#")) {
14707
+ return item;
14708
+ }
14709
+ const parts = item.split("#");
14710
+ if (parts.length !== 2) {
14711
+ return item;
14712
+ }
14713
+ const field = parts[0];
14714
+ const direction = parts[1];
14715
+ if (!isString(field) || !isString(direction)) {
14716
+ return item;
14717
+ }
14718
+ return `${processJoinField(field.trim())}#${direction.trim()}`;
14719
+ });
14720
+ }
14721
+ function processJoinOn(on) {
14722
+ if (!isString(on)) {
14723
+ return String(on);
14724
+ }
14725
+ const raw = String(on);
14726
+ if (!isNonEmptyString(raw)) {
14727
+ return raw;
14728
+ }
14729
+ const replaceSegment = (segment) => {
14730
+ return segment.replace(/([a-zA-Z_][a-zA-Z0-9_]*)(\s*\.\s*)([a-zA-Z_][a-zA-Z0-9_]*)/g, (_match, left, dot, right) => {
14731
+ return `${snakeCase(left)}${dot}${snakeCase(right)}`;
14732
+ });
14733
+ };
14734
+ let result = "";
14735
+ let buffer = "";
14736
+ let quote = null;
14737
+ let prev = "";
14738
+ for (const ch of raw) {
14739
+ if (quote) {
14740
+ result += ch;
14741
+ if (ch === quote && prev !== "\\") {
14742
+ quote = null;
14743
+ }
14744
+ prev = ch;
14745
+ continue;
14746
+ }
14747
+ if (ch === "'" || ch === '"' || ch === "`") {
14748
+ if (buffer.length > 0) {
14749
+ result += replaceSegment(buffer);
14750
+ buffer = "";
14751
+ }
14752
+ quote = ch;
14753
+ result += ch;
14754
+ prev = ch;
14755
+ continue;
14756
+ }
14757
+ buffer += ch;
14758
+ prev = ch;
14759
+ }
14760
+ if (buffer.length > 0) {
14761
+ result += replaceSegment(buffer);
14762
+ }
14763
+ return result;
14764
+ }
14765
+ function addDefaultStateFilter(where = {}, table, hasJoins = false) {
14766
+ const hasStateCondition = Object.keys(where).some((key) => key.startsWith("state") || key.includes(".state"));
14767
+ if (hasStateCondition) {
14768
+ return where;
14769
+ }
14770
+ if (hasJoins && table) {
14771
+ const result2 = {};
14772
+ for (const [key, value] of Object.entries(where)) {
14773
+ result2[key] = value;
14774
+ }
14775
+ result2[`${table}.state$gt`] = 0;
14776
+ return result2;
14777
+ }
14778
+ const result = {};
14779
+ for (const [key, value] of Object.entries(where)) {
14780
+ result[key] = value;
14781
+ }
14782
+ result.state$gt = 0;
14783
+ return result;
14784
+ }
14785
+ function whereKeysToSnake(where) {
14786
+ if (!where || typeof where !== "object") {
14787
+ return where;
14788
+ }
14789
+ if (Array.isArray(where)) {
14790
+ return where.map((item) => whereKeysToSnake(item));
14791
+ }
14792
+ const result = {};
14793
+ for (const [key, value] of Object.entries(where)) {
14794
+ if (key === "$or" || key === "$and") {
14795
+ result[key] = Array.isArray(value) ? value.map((item) => whereKeysToSnake(item)) : [];
14796
+ continue;
14797
+ }
14798
+ assertNoExprField(key);
14799
+ if (key.includes("$")) {
14800
+ const lastDollarIndex = key.lastIndexOf("$");
14801
+ const fieldName = key.substring(0, lastDollarIndex);
14802
+ const operator = key.substring(lastDollarIndex);
14803
+ result[snakeCase(fieldName) + operator] = value;
14804
+ continue;
14805
+ }
14806
+ const snakeKey = snakeCase(key);
14807
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
14808
+ result[snakeKey] = whereKeysToSnake(value);
14809
+ } else {
14810
+ result[snakeKey] = value;
14811
+ }
14812
+ }
14813
+ return result;
14814
+ }
14815
+ function serializeArrayFields(data) {
14816
+ const serialized = {};
14817
+ for (const [key, value] of Object.entries(data)) {
14818
+ if (isNullable(value)) {
14819
+ serialized[key] = value;
14820
+ continue;
14821
+ }
14822
+ if (Array.isArray(value)) {
14823
+ serialized[key] = JSON.stringify(value);
14824
+ continue;
14825
+ }
14826
+ serialized[key] = value;
14827
+ }
14828
+ return serialized;
14829
+ }
14830
+ function deserializeArrayFields(data) {
14831
+ if (!data) {
14832
+ return null;
14833
+ }
14834
+ const deserialized = {};
14835
+ for (const [key, value] of Object.entries(data)) {
14836
+ deserialized[key] = value;
14837
+ }
14838
+ for (const [key, value] of Object.entries(deserialized)) {
14839
+ if (!isString(value)) {
14840
+ continue;
14841
+ }
14842
+ if (value.startsWith("[") && value.endsWith("]")) {
14843
+ try {
14844
+ const parsed = JSON.parse(value);
14845
+ if (Array.isArray(parsed)) {
14846
+ deserialized[key] = parsed;
14847
+ }
14848
+ } catch {
14849
+ continue;
14850
+ }
14851
+ }
14852
+ }
14853
+ return deserialized;
14854
+ }
14855
+ function cleanAndSnakeAndSerializeWriteData(data, excludeValues = [null, undefined]) {
14856
+ const cleanData = fieldClear(data, { excludeValues });
14857
+ const snakeData = keysToSnake(cleanData);
14858
+ return serializeArrayFields(snakeData);
14859
+ }
14860
+ function stripSystemFieldsForWrite(data, options) {
14861
+ const result = {};
14862
+ for (const [key, value] of Object.entries(data)) {
14863
+ if (key === "id")
14864
+ continue;
14865
+ if (key === "created_at")
14866
+ continue;
14867
+ if (key === "updated_at")
14868
+ continue;
14869
+ if (key === "deleted_at")
14870
+ continue;
14871
+ if (!options.allowState && key === "state")
14872
+ continue;
14873
+ result[key] = value;
14874
+ }
14875
+ return result;
14876
+ }
14877
+ function buildInsertRow(options) {
14878
+ const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
14879
+ const userData = stripSystemFieldsForWrite(serializedData, { allowState: false });
14880
+ const result = {};
14881
+ for (const [key, value] of Object.entries(userData)) {
14882
+ result[key] = value;
14883
+ }
14884
+ if (options.idMode === "timeId") {
14885
+ validateTimeIdValue(options.id);
14886
+ result["id"] = options.id;
14887
+ }
14888
+ result["created_at"] = options.now;
14889
+ result["updated_at"] = options.now;
14890
+ result["state"] = 1;
14891
+ return result;
14892
+ }
14893
+ function buildUpdateRow(options) {
14894
+ const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
14895
+ const userData = stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
14896
+ const result = {};
14897
+ for (const [key, value] of Object.entries(userData)) {
14898
+ result[key] = value;
14899
+ }
14900
+ result["updated_at"] = options.now;
14901
+ return result;
14902
+ }
14903
+ function buildPartialUpdateData(options) {
14904
+ const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
14905
+ return stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
14906
+ }
14907
+ var builderMethods = {
14908
+ createSqlBuilder() {
14909
+ return new SqlBuilder({ quoteIdent: quoteIdentMySql });
14910
+ },
14911
+ createListBuilder(prepared, whereFiltered, limit, offset) {
14912
+ const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered).limit(limit);
14913
+ this.applyJoins(builder, prepared.joins);
14914
+ if (!isNullable(offset)) {
14915
+ builder.offset(offset);
14916
+ }
14917
+ if (prepared.orderBy && prepared.orderBy.length > 0) {
14918
+ builder.orderBy(prepared.orderBy);
14919
+ }
14920
+ return builder;
14921
+ },
14922
+ async fetchCount(prepared, whereFiltered, alias) {
14923
+ const builder = this.createSqlBuilder().selectRaw(alias).from(prepared.table).where(whereFiltered);
14924
+ this.applyJoins(builder, prepared.joins);
14925
+ const result = builder.toSelectSql();
14926
+ const executeRes = await this.execute(result.sql, result.params);
14927
+ const total = executeRes.data?.[0]?.total || executeRes.data?.[0]?.count || 0;
14928
+ return {
14929
+ total,
14930
+ sql: executeRes.sql
14931
+ };
14932
+ },
14933
+ normalizeRowData(row, bigintFields) {
14934
+ if (!row) {
14935
+ return {};
14936
+ }
14937
+ const camelRow = keysToCamel(row);
14938
+ const deserialized = deserializeArrayFields(camelRow);
14939
+ if (!deserialized) {
14940
+ return {};
14941
+ }
14942
+ const convertedList = convertBigIntFields([deserialized], bigintFields);
14943
+ if (convertedList && convertedList.length > 0) {
14944
+ return convertedList[0];
14945
+ }
14946
+ return deserialized;
14947
+ },
14948
+ normalizeListData(list, bigintFields) {
14949
+ const camelList = arrayKeysToCamel(list);
14950
+ const deserializedList = camelList.map((item) => deserializeArrayFields(item)).filter((item) => item !== null);
14951
+ return convertBigIntFields(deserializedList, bigintFields);
14952
+ },
14953
+ normalizeJoinOptions(options, cleanWhere) {
14954
+ const processedFields = (options.fields || []).map((field) => processJoinField(field));
14955
+ const normalizedTableRef = normalizeTableRef(options.table);
14956
+ const mainQualifier = getJoinMainQualifier(options.table);
14957
+ return {
14958
+ table: normalizedTableRef,
14959
+ tableQualifier: mainQualifier,
14960
+ fields: processedFields.length > 0 ? processedFields : ["*"],
14961
+ where: processJoinWhere(cleanWhere),
14962
+ joins: options.joins,
14963
+ orderBy: processJoinOrderBy(options.orderBy || []),
14964
+ page: options.page || 1,
14965
+ limit: options.limit || 10
14966
+ };
14967
+ },
14968
+ async normalizeSingleTableOptions(options, cleanWhere) {
14969
+ const snakeTable = snakeCase(options.table);
14970
+ const processedFields = await fieldsToSnake(snakeTable, options.fields || [], this.getTableColumns.bind(this));
14971
+ return {
14972
+ table: snakeTable,
14973
+ tableQualifier: snakeTable,
14974
+ fields: processedFields,
14975
+ where: whereKeysToSnake(cleanWhere),
14976
+ joins: undefined,
14977
+ orderBy: orderByToSnake(options.orderBy || []),
14978
+ page: options.page || 1,
14979
+ limit: options.limit || 10
14980
+ };
14981
+ },
14982
+ buildQueryOptions(options, defaults) {
14983
+ const output = {
14984
+ table: options.table,
14985
+ page: defaults.page,
14986
+ limit: defaults.limit
14987
+ };
14988
+ if (options.fields !== undefined)
14989
+ output.fields = options.fields;
14990
+ if (options.bigint !== undefined)
14991
+ output.bigint = options.bigint;
14992
+ if (options.where !== undefined)
14993
+ output.where = options.where;
14994
+ if (options.joins !== undefined)
14995
+ output.joins = options.joins;
14996
+ if (options.orderBy !== undefined)
14997
+ output.orderBy = options.orderBy;
14998
+ return output;
14999
+ },
15000
+ resolveFieldValue(record2, field) {
15001
+ if (!isPlainObject2(record2)) {
15002
+ return null;
15003
+ }
15004
+ if (Object.hasOwn(record2, field)) {
15005
+ return record2[field];
15006
+ }
15007
+ const camelField = field.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
15008
+ if (camelField !== field && Object.hasOwn(record2, camelField)) {
15009
+ return record2[camelField];
15010
+ }
15011
+ const snakeField = field.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
15012
+ if (snakeField !== field && Object.hasOwn(record2, snakeField)) {
15013
+ return record2[snakeField];
15014
+ }
15015
+ return null;
15016
+ },
15017
+ async getTableColumns(table) {
15018
+ const quotedTable = quoteIdentMySql(table);
15019
+ const executeRes = await this.execute(`SHOW COLUMNS FROM ${quotedTable}`, []);
15020
+ const result = executeRes.data;
15021
+ if (!result || result.length === 0) {
15022
+ throw new Error(`\u8868 ${table} \u4E0D\u5B58\u5728\u6216\u6CA1\u6709\u5B57\u6BB5`, {
15023
+ cause: null,
15024
+ code: "runtime"
15025
+ });
15026
+ }
15027
+ const columnNames = [];
15028
+ for (const row of result) {
15029
+ const record2 = row;
15030
+ const name = record2["Field"];
15031
+ if (isNonEmptyString(name)) {
15032
+ columnNames.push(name);
15033
+ }
15034
+ }
15035
+ return columnNames;
15036
+ },
15037
+ async prepareQueryOptions(options, label = "queryOptions") {
15038
+ validateQueryOptions(options, label);
15039
+ const cleanWhere = clearDeep(options.where || {});
15040
+ const hasJoins = options.joins && options.joins.length > 0;
15041
+ if (hasJoins) {
15042
+ return this.normalizeJoinOptions(options, cleanWhere);
15043
+ }
15044
+ return await this.normalizeSingleTableOptions(options, cleanWhere);
15045
+ },
15046
+ applyJoins(builder, joins) {
15047
+ if (!joins || joins.length === 0)
15048
+ return;
15049
+ for (const join3 of joins) {
15050
+ const processedTable = normalizeTableRef(join3.table);
15051
+ const processedOn = processJoinOn(join3.on);
15052
+ const type = join3.type || "left";
15053
+ switch (type) {
15054
+ case "inner":
15055
+ builder.innerJoin(processedTable, processedOn);
15056
+ break;
15057
+ case "right":
15058
+ builder.rightJoin(processedTable, processedOn);
15059
+ break;
15060
+ case "left":
15061
+ default:
15062
+ builder.leftJoin(processedTable, processedOn);
15063
+ break;
15064
+ }
15065
+ }
15066
+ }
15067
+ };
15068
+
15069
+ // lib/dbHelper/dataOps.js
15070
+ var dataOpsMethods = {
15071
+ async getCount(options) {
15072
+ const { table, where, joins, tableQualifier } = await this.prepareQueryOptions(options, "getCount.options");
15073
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
15074
+ const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasJoins);
15075
+ const result = await this.fetchCount({ table, joins }, whereFiltered, "COUNT(*) as count");
15076
+ return {
15077
+ data: result.total,
15078
+ sql: result.sql
15079
+ };
15080
+ },
15081
+ async getOne(options) {
15082
+ const { table, fields, where, joins, tableQualifier } = await this.prepareQueryOptions(options, "getOne.options");
15083
+ const hasJoins = Array.isArray(joins) && joins.length > 0;
15084
+ const whereFiltered = addDefaultStateFilter(where, tableQualifier, hasJoins);
15085
+ const builder = this.createSqlBuilder().select(fields).from(table).where(whereFiltered);
15086
+ this.applyJoins(builder, joins);
15087
+ const { sql, params } = builder.toSelectSql();
15088
+ const executeRes = await this.execute(sql, params);
15089
+ const result = executeRes.data;
15090
+ const data = this.normalizeRowData(result?.[0] || null, options.bigint);
15091
+ return {
15092
+ data,
15093
+ sql: executeRes.sql
15094
+ };
15095
+ },
15096
+ async getDetail(options) {
15097
+ return await this.getOne(options);
15098
+ },
15099
+ async getList(options) {
15100
+ const prepared = await this.prepareQueryOptions(options, "getList.options");
15101
+ validatePageLimitRange(prepared, options.table);
15102
+ const hasJoins = Array.isArray(prepared.joins) && prepared.joins.length > 0;
15103
+ const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasJoins);
15104
+ const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
15105
+ const total = countResult.total;
15106
+ const offset = (prepared.page - 1) * prepared.limit;
15107
+ const dataBuilder = this.createListBuilder(prepared, whereFiltered, prepared.limit, offset);
15108
+ const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
15109
+ if (total === 0) {
15110
+ return {
15111
+ data: {
15112
+ lists: [],
15113
+ total: 0,
15114
+ page: prepared.page,
15115
+ limit: prepared.limit,
15116
+ pages: 0
15117
+ },
15118
+ sql: {
15119
+ count: countResult.sql,
15120
+ data: {
15121
+ sql: dataSql,
15122
+ params: dataParams,
15123
+ duration: 0
15124
+ }
15125
+ }
15126
+ };
15127
+ }
15128
+ const listExecuteRes = await this.execute(dataSql, dataParams);
15129
+ const list = listExecuteRes.data || [];
15130
+ return {
15131
+ data: {
15132
+ lists: this.normalizeListData(list, options.bigint),
15133
+ total,
15134
+ page: prepared.page,
15135
+ limit: prepared.limit,
15136
+ pages: Math.ceil(total / prepared.limit)
15137
+ },
15138
+ sql: {
15139
+ count: countResult.sql,
15140
+ data: listExecuteRes.sql
15141
+ }
15142
+ };
15143
+ },
15144
+ async getAll(options) {
15145
+ const MAX_LIMIT = 1e4;
15146
+ const WARNING_LIMIT = 1000;
15147
+ const prepareOptions = this.buildQueryOptions(options, { page: 1, limit: 10 });
15148
+ const prepared = await this.prepareQueryOptions(prepareOptions, "getAll.options");
15149
+ const hasJoins = Array.isArray(prepared.joins) && prepared.joins.length > 0;
15150
+ const whereFiltered = addDefaultStateFilter(prepared.where, prepared.tableQualifier, hasJoins);
15151
+ const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
15152
+ const total = countResult.total;
15153
+ if (total === 0) {
15154
+ return {
15155
+ data: {
15156
+ lists: [],
15157
+ total: 0
15158
+ },
15159
+ sql: {
15160
+ count: countResult.sql
15161
+ }
15162
+ };
15163
+ }
15164
+ const dataBuilder = this.createListBuilder(prepared, whereFiltered, MAX_LIMIT, null);
15165
+ const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
15166
+ const listExecuteRes = await this.execute(dataSql, dataParams);
15167
+ const result = listExecuteRes.data || [];
15168
+ if (result.length >= WARNING_LIMIT) {
15169
+ Logger.warn("getAll \u8FD4\u56DE\u6570\u636E\u8FC7\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528 getList \u5206\u9875\u67E5\u8BE2", { table: options.table, count: result.length, total });
15170
+ }
15171
+ if (result.length >= MAX_LIMIT) {
15172
+ Logger.warn(`getAll \u8FBE\u5230\u6700\u5927\u9650\u5236 ${MAX_LIMIT}\uFF0C\u5B9E\u9645\u603B\u6570 ${total}\uFF0C\u53EA\u8FD4\u56DE\u524D ${MAX_LIMIT} \u6761`, { table: options.table, limit: MAX_LIMIT, total });
15173
+ }
15174
+ const lists = this.normalizeListData(result, options.bigint);
15175
+ return {
15176
+ data: {
15177
+ lists,
15178
+ total
15179
+ },
15180
+ sql: {
15181
+ count: countResult.sql,
15182
+ data: listExecuteRes.sql
15183
+ }
15184
+ };
15185
+ },
15186
+ async exists(options) {
15187
+ validateNoJoinReadOptions(options, "exists", "exists \u4E0D\u652F\u6301 joins\uFF08\u8BF7\u4F7F\u7528\u663E\u5F0F query \u6216\u62C6\u5206\u67E5\u8BE2\uFF09");
15188
+ const snakeTable = snakeCase(options.table);
15189
+ const snakeWhere = whereKeysToSnake(clearDeep(options.where || {}));
15190
+ const whereFiltered = addDefaultStateFilter(snakeWhere, snakeTable, false);
15191
+ const builder = this.createSqlBuilder().selectRaw("COUNT(1) as cnt").from(snakeTable).where(whereFiltered).limit(1);
15192
+ const { sql, params } = builder.toSelectSql();
15193
+ const executeRes = await this.execute(sql, params);
15194
+ const exists = (executeRes.data?.[0]?.cnt || 0) > 0;
15195
+ return { data: exists, sql: executeRes.sql };
15196
+ },
15197
+ async getFieldValue(options) {
15198
+ validateNoJoinReadOptions(options, "getFieldValue", "getFieldValue \u4E0D\u652F\u6301 joins\uFF08\u8BF7\u4F7F\u7528 getOne/getList \u5E76\u81EA\u884C\u53D6\u5B57\u6BB5\uFF09");
15199
+ const field = options.field;
15200
+ validateSafeFieldName(field);
15201
+ const oneOptions = {
15202
+ table: options.table
15203
+ };
15204
+ if (options.where !== undefined)
15205
+ oneOptions.where = options.where;
15206
+ if (options.joins !== undefined)
15207
+ oneOptions.joins = options.joins;
15208
+ if (options.orderBy !== undefined)
15209
+ oneOptions.orderBy = options.orderBy;
15210
+ if (options.page !== undefined)
15211
+ oneOptions.page = options.page;
15212
+ if (options.limit !== undefined)
15213
+ oneOptions.limit = options.limit;
15214
+ oneOptions.fields = [field];
15215
+ const oneRes = await this.getOne(oneOptions);
15216
+ const result = oneRes.data;
15217
+ const value = this.resolveFieldValue(result, field);
15218
+ return {
15219
+ data: value,
15220
+ sql: oneRes.sql
15221
+ };
15222
+ },
15223
+ async insData(options) {
15224
+ validateTableDataOptions(options, "insData");
15225
+ const { table, data } = options;
15226
+ const snakeTable = snakeCase(table);
15227
+ const now = Date.now();
15228
+ let processed;
15229
+ if (this.idMode === "autoId") {
15230
+ processed = buildInsertRow({ idMode: "autoId", data, now });
15231
+ } else {
15232
+ let id;
15233
+ try {
15234
+ id = await this.redis.genTimeID();
15235
+ } catch (error48) {
15236
+ throw new Error(`\u751F\u6210 ID \u5931\u8D25\uFF0CRedis \u53EF\u80FD\u4E0D\u53EF\u7528 (table: ${table})`, {
15237
+ cause: error48,
15238
+ code: "runtime",
15239
+ subsystem: "db",
15240
+ operation: "genTimeId",
15241
+ table
15242
+ });
15243
+ }
15244
+ processed = buildInsertRow({ idMode: "timeId", data, id, now });
15245
+ }
15246
+ assertNoUndefinedInRecord(processed, `insData \u63D2\u5165\u6570\u636E (table: ${snakeTable})`);
15247
+ const builder = this.createSqlBuilder();
15248
+ const { sql, params } = builder.toInsertSql(snakeTable, processed);
15249
+ const executeRes = await this.execute(sql, params);
15250
+ const processedId = processed["id"];
15251
+ const processedIdNum = isNumber(processedId) ? processedId : 0;
15252
+ const lastInsertRowidNum = toNumberFromSql(executeRes.data?.lastInsertRowid);
15253
+ const insertedId = this.idMode === "autoId" ? lastInsertRowidNum || 0 : processedIdNum || lastInsertRowidNum || 0;
15254
+ if (this.idMode === "autoId" && insertedId <= 0) {
15255
+ throw new Error(`\u63D2\u5165\u5931\u8D25\uFF1AautoId \u6A21\u5F0F\u4E0B\u65E0\u6CD5\u83B7\u53D6 lastInsertRowid (table: ${table})`, {
15256
+ cause: null,
15257
+ code: "runtime"
15258
+ });
15259
+ }
15260
+ return {
15261
+ data: insertedId,
15262
+ sql: executeRes.sql
15263
+ };
15264
+ },
15265
+ async insBatch(table, dataList) {
15266
+ validateTableBatchDataOptions(table, dataList, "insBatch");
15267
+ if (dataList.length === 0) {
15268
+ return {
15269
+ data: [],
15270
+ sql: { sql: "", params: [], duration: 0 }
15271
+ };
15272
+ }
15273
+ const MAX_BATCH_SIZE = 1000;
15274
+ validateInsertBatchSize(dataList.length, MAX_BATCH_SIZE);
15275
+ const snakeTable = snakeCase(table);
15276
+ const now = Date.now();
15277
+ let ids = [];
15278
+ let processedList;
15279
+ if (this.idMode === "autoId") {
15280
+ processedList = dataList.map((data) => {
15281
+ return buildInsertRow({ idMode: "autoId", data, now });
15282
+ });
15283
+ } else {
15284
+ const nextIds = [];
15285
+ for (let i = 0;i < dataList.length; i++) {
15286
+ nextIds.push(await this.redis.genTimeID());
15287
+ }
15288
+ ids = nextIds;
15289
+ processedList = dataList.map((data, index) => {
15290
+ const id = nextIds[index];
15291
+ validateGeneratedBatchId(id, snakeTable, index);
15292
+ return buildInsertRow({ idMode: "timeId", data, id, now });
15293
+ });
15294
+ }
15295
+ const insertFields = assertBatchInsertRowsConsistent(processedList, { table: snakeTable });
15296
+ const builder = this.createSqlBuilder();
15297
+ const { sql, params } = builder.toInsertSql(snakeTable, processedList);
15298
+ try {
15299
+ const executeRes = await this.execute(sql, params);
15300
+ if (this.idMode === "autoId") {
15301
+ const firstId = toNumberFromSql(executeRes.data?.lastInsertRowid);
15302
+ if (firstId <= 0) {
15303
+ throw new Error(`\u6279\u91CF\u63D2\u5165\u5931\u8D25\uFF1AautoId \u6A21\u5F0F\u4E0B\u65E0\u6CD5\u83B7\u53D6 lastInsertRowid (table: ${table})`, {
15304
+ cause: null,
15305
+ code: "runtime"
15306
+ });
15307
+ }
15308
+ const outIds = [];
15309
+ for (let i = 0;i < dataList.length; i++) {
15310
+ outIds.push(firstId + i);
15311
+ }
15312
+ return {
15313
+ data: outIds,
15314
+ sql: executeRes.sql
15315
+ };
15316
+ }
15317
+ return {
15318
+ data: ids,
15319
+ sql: executeRes.sql
15320
+ };
15321
+ } catch (error48) {
15322
+ Logger.error("\u6279\u91CF\u63D2\u5165\u5931\u8D25", error48, {
15323
+ table,
15324
+ snakeTable,
15325
+ count: dataList.length,
15326
+ fields: insertFields
15327
+ });
15328
+ throw new Error("\u6279\u91CF\u63D2\u5165\u5931\u8D25", {
15329
+ cause: error48,
15330
+ code: "runtime",
15331
+ subsystem: "sql",
15332
+ operation: "insBatch",
15333
+ table,
15334
+ snakeTable,
15335
+ count: dataList.length,
15336
+ fields: insertFields
15337
+ });
15338
+ }
15339
+ },
15340
+ async delForceBatch(table, ids) {
15341
+ validateTableName(table, "delForceBatch.table");
15342
+ if (ids.length === 0) {
15343
+ return {
15344
+ data: 0,
15345
+ sql: { sql: "", params: [], duration: 0 }
15346
+ };
15347
+ }
15348
+ const snakeTable = snakeCase(table);
15349
+ const query = SqlBuilder.toDeleteInSql({
15350
+ table: snakeTable,
15351
+ idField: "id",
15352
+ ids,
15353
+ quoteIdent: quoteIdentMySql
15354
+ });
15355
+ const executeRes = await this.execute(query.sql, query.params);
15356
+ const changes = toNumberFromSql(executeRes.data?.changes);
15357
+ return {
15358
+ data: changes,
15359
+ sql: executeRes.sql
15360
+ };
15361
+ },
15362
+ async updBatch(table, dataList) {
15363
+ validateTableBatchDataOptions(table, dataList, "updBatch");
15364
+ if (dataList.length === 0) {
15365
+ return {
15366
+ data: 0,
15367
+ sql: { sql: "", params: [], duration: 0 }
15368
+ };
15369
+ }
15370
+ const snakeTable = snakeCase(table);
15371
+ const now = Date.now();
15372
+ const processedList = [];
15373
+ const fieldSet = new Set;
15374
+ for (const item of dataList) {
15375
+ const userData = buildPartialUpdateData({ data: item.data, allowState: true });
15376
+ for (const key of Object.keys(userData)) {
15377
+ fieldSet.add(key);
15378
+ }
15379
+ processedList.push({ id: item.id, data: userData });
15380
+ }
15381
+ const fields = Array.from(fieldSet).sort();
15382
+ if (fields.length === 0) {
15383
+ return {
15384
+ data: 0,
15385
+ sql: { sql: "", params: [], duration: 0 }
15386
+ };
15387
+ }
15388
+ const query = SqlBuilder.toUpdateCaseByIdSql({
15389
+ table: snakeTable,
15390
+ idField: "id",
15391
+ rows: processedList,
15392
+ fields,
15393
+ quoteIdent: quoteIdentMySql,
15394
+ updatedAtField: "updated_at",
15395
+ updatedAtValue: now,
15396
+ stateField: "state",
15397
+ stateGtZero: true
15398
+ });
15399
+ const executeRes = await this.execute(query.sql, query.params);
15400
+ const changes = toNumberFromSql(executeRes.data?.changes);
15401
+ return {
15402
+ data: changes,
15403
+ sql: executeRes.sql
15404
+ };
15405
+ },
15406
+ async updData(options) {
15407
+ validateTableDataOptions(options, "updData");
15408
+ validateTableWhereOptions(options, "updData", true);
15409
+ const { table, data, where } = options;
15410
+ const snakeTable = snakeCase(table);
15411
+ const snakeWhere = whereKeysToSnake(clearDeep(where));
15412
+ const processed = buildUpdateRow({ data, now: Date.now(), allowState: true });
15413
+ const whereFiltered = addDefaultStateFilter(snakeWhere, snakeTable, false);
15414
+ const builder = this.createSqlBuilder().where(whereFiltered);
15415
+ const { sql, params } = builder.toUpdateSql(snakeTable, processed);
15416
+ const executeRes = await this.execute(sql, params);
15417
+ const changes = toNumberFromSql(executeRes.data?.changes);
15418
+ return {
15419
+ data: changes,
15420
+ sql: executeRes.sql
15421
+ };
15422
+ },
15423
+ async delData(options) {
15424
+ validateTableWhereOptions(options, "delData", true);
15425
+ const { table, where } = options;
15426
+ return await this.updData({
15427
+ table,
15428
+ data: { state: 0, deleted_at: Date.now() },
15429
+ where
15430
+ });
15431
+ },
15432
+ async delForce(options) {
15433
+ validateTableWhereOptions(options, "delForce", true);
15434
+ const { table, where } = options;
15435
+ const snakeTable = snakeCase(table);
15436
+ const snakeWhere = whereKeysToSnake(clearDeep(where));
15437
+ const builder = this.createSqlBuilder().where(snakeWhere);
15438
+ const { sql, params } = builder.toDeleteSql(snakeTable);
15439
+ const executeRes = await this.execute(sql, params);
15440
+ const changes = toNumberFromSql(executeRes.data?.changes);
15441
+ return {
15442
+ data: changes,
15443
+ sql: executeRes.sql
15444
+ };
15445
+ },
15446
+ async disableData(options) {
15447
+ validateTableWhereOptions(options, "disableData", true);
15448
+ const { table, where } = options;
15449
+ return await this.updData({
15450
+ table,
15451
+ data: {
15452
+ state: 2
15453
+ },
15454
+ where
15455
+ });
15456
+ },
15457
+ async enableData(options) {
15458
+ validateTableWhereOptions(options, "enableData", true);
15459
+ const { table, where } = options;
15460
+ return await this.updData({
15461
+ table,
15462
+ data: {
15463
+ state: 1
15464
+ },
15465
+ where
15466
+ });
15467
+ },
15468
+ async increment(table, field, where, value = 1) {
15469
+ validateIncrementOptions(table, field, where, value, "increment");
15470
+ const snakeTable = snakeCase(table);
15471
+ const snakeField = snakeCase(field);
15472
+ const snakeWhere = whereKeysToSnake(clearDeep(where));
15473
+ const whereFiltered = addDefaultStateFilter(snakeWhere, snakeTable, false);
15474
+ const builder = this.createSqlBuilder().where(whereFiltered);
15475
+ const { sql: whereClause, params: whereParams } = builder.getWhereConditions();
15476
+ const quotedTable = quoteIdentMySql(snakeTable);
15477
+ const quotedField = quoteIdentMySql(snakeField);
15478
+ const sql = whereClause ? `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ? WHERE ${whereClause}` : `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ?`;
15479
+ const params = [value];
15480
+ for (const param of whereParams) {
15481
+ params.push(param);
15482
+ }
15483
+ const executeRes = await this.execute(sql, params);
15484
+ const changes = toNumberFromSql(executeRes.data?.changes);
15485
+ return {
15486
+ data: changes,
15487
+ sql: executeRes.sql
15488
+ };
15489
+ },
15490
+ async decrement(table, field, where, value = 1) {
15491
+ return await this.increment(table, field, where, -value);
15492
+ }
15493
+ };
15494
+
15495
+ // lib/dbHelper/execute.js
15496
+ var executeMethods = {
15497
+ async execute(sql, params) {
15498
+ if (!this.sql) {
15499
+ throw new Error("\u6570\u636E\u5E93\u8FDE\u63A5\u672A\u521D\u59CB\u5316", {
15500
+ cause: null,
15501
+ code: "runtime"
15502
+ });
15503
+ }
15504
+ validateExecuteSql(sql);
15505
+ const startTime = Date.now();
15506
+ const safeParams = toSqlParams(params);
15507
+ try {
15508
+ let queryResult;
15509
+ if (safeParams.length > 0) {
15510
+ queryResult = await this.sql.unsafe(sql, safeParams);
15511
+ } else {
15512
+ queryResult = await this.sql.unsafe(sql);
15513
+ }
15514
+ const duration3 = Date.now() - startTime;
15515
+ return {
15516
+ data: queryResult,
15517
+ sql: {
15518
+ sql,
15519
+ params: safeParams,
15520
+ duration: duration3
15521
+ }
15522
+ };
15523
+ } catch (error48) {
15524
+ const duration3 = Date.now() - startTime;
15525
+ const msg = error48 instanceof Error ? error48.message : String(error48);
15526
+ throw new DbSqlError(`SQL\u6267\u884C\u5931\u8D25: ${msg}`, {
15527
+ originalError: error48,
15528
+ params: safeParams,
15529
+ duration: duration3,
15530
+ sqlInfo: {
15531
+ sql,
15532
+ params: safeParams,
15533
+ duration: duration3
15534
+ }
15535
+ });
15536
+ }
15537
+ },
15538
+ async tableExists(tableName) {
15539
+ const snakeTableName = snakeCase(tableName);
15540
+ const executeRes = await this.execute("SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", [snakeTableName]);
15541
+ const exists = (executeRes.data?.[0]?.count || 0) > 0;
15542
+ return {
15543
+ data: exists,
15544
+ sql: executeRes.sql
15545
+ };
15546
+ }
15547
+ };
15548
+
15549
+ // lib/dbHelper/transaction.js
15550
+ var transactionMethods = {
15551
+ async trans(callback) {
15552
+ if (this.isTransaction) {
15553
+ const innerResult = await callback(this);
15554
+ if (isBeflyResponse(innerResult) && innerResult.code !== 0) {
15555
+ throw new TransAbortError(innerResult);
15556
+ }
15557
+ return innerResult;
15558
+ }
15559
+ const sql = this.sql;
15560
+ if (!sql) {
15561
+ throw new Error("\u6570\u636E\u5E93\u8FDE\u63A5\u672A\u521D\u59CB\u5316", {
15562
+ cause: null,
15563
+ code: "runtime"
15564
+ });
15565
+ }
15566
+ if (!hasBegin(sql)) {
15567
+ throw new Error("\u5F53\u524D SQL \u5BA2\u6237\u7AEF\u4E0D\u652F\u6301\u4E8B\u52A1 begin() \u65B9\u6CD5", {
15568
+ cause: null,
15569
+ code: "runtime"
15570
+ });
15571
+ }
15572
+ try {
15573
+ return await sql.begin(async (tx) => {
15574
+ const trans = new this.constructor({ redis: this.redis, dbName: this.dbName, sql: tx, idMode: this.idMode });
15575
+ const result = await callback(trans);
15576
+ if (isBeflyResponse(result) && result.code !== 0) {
15577
+ throw new TransAbortError(result);
15578
+ }
15579
+ return result;
15580
+ });
15581
+ } catch (error48) {
15582
+ if (error48 instanceof TransAbortError) {
15583
+ return error48.payload;
15584
+ }
15585
+ throw error48;
15586
+ }
15587
+ }
15588
+ };
15589
+
15590
+ // lib/dbHelper/index.js
15591
+ function DbHelper(options) {
15592
+ this.redis = options.redis;
15593
+ if (!isNonEmptyString(options.dbName)) {
15594
+ throw new Error("DbHelper \u521D\u59CB\u5316\u5931\u8D25\uFF1AdbName \u5FC5\u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32", {
15595
+ cause: null,
15596
+ code: "validation"
15597
+ });
15598
+ }
15599
+ this.dbName = options.dbName;
15600
+ this.sql = options.sql || null;
15601
+ this.isTransaction = Boolean(options.sql);
15602
+ this.idMode = options.idMode === "autoId" ? "autoId" : "timeId";
15603
+ }
15604
+ Object.assign(DbHelper.prototype, builderMethods, executeMethods, dataOpsMethods, transactionMethods);
15605
+ DbHelper.convertBigIntFields = convertBigIntFields;
15606
+
15607
+ // utils/importDefault.js
15608
+ import { isAbsolute as isAbsolute2 } from "path";
15609
+ import { pathToFileURL } from "url";
15610
+ function isWindowsAbsPath(file) {
15611
+ return /^[a-zA-Z]:[\\/]/.test(file);
15612
+ }
15613
+ function toFileImportUrl(file) {
15614
+ if (isAbsolute2(file) || isWindowsAbsPath(file)) {
15615
+ return pathToFileURL(file).href;
15616
+ }
15617
+ return file;
15618
+ }
15619
+ async function importDefault(file, defaultValue) {
15620
+ try {
15621
+ const isJson = file.endsWith(".json");
15622
+ const mod = isJson ? await import(toFileImportUrl(file), { with: { type: "json" } }) : await import(file);
15623
+ const value = mod?.default;
15624
+ if (isNullable(value)) {
15625
+ return defaultValue;
15626
+ }
15627
+ const expectedType = getTypeTag(defaultValue);
15628
+ const actualType = getTypeTag(value);
15629
+ if (expectedType !== actualType) {
15630
+ Logger.warn("importDefault \u5BFC\u5165\u7C7B\u578B\u4E0E\u9ED8\u8BA4\u503C\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u56DE\u9000\u5230\u9ED8\u8BA4\u503C", { file, expectedType, actualType });
15631
+ return defaultValue;
15632
+ }
15633
+ return value;
15634
+ } catch (err) {
15635
+ Logger.warn("importDefault \u5BFC\u5165\u5931\u8D25\uFF0C\u5DF2\u56DE\u9000\u5230\u9ED8\u8BA4\u503C", { err, file });
15636
+ return defaultValue;
15637
+ }
15638
+ }
15639
+
15640
+ // scripts/syncDb/query.js
15641
+ var SYNC_DB_COLUMNS_SQL = `
15642
+ SELECT
15643
+ c.TABLE_NAME AS tableName,
15644
+ t.TABLE_COMMENT AS tableComment,
15645
+ c.COLUMN_NAME AS columnName,
15646
+ c.DATA_TYPE AS dataType,
15647
+ c.COLUMN_TYPE AS columnType,
15648
+ c.CHARACTER_MAXIMUM_LENGTH AS charLength,
15649
+ c.COLUMN_COMMENT AS columnComment,
15650
+ c.ORDINAL_POSITION AS ordinalPosition
15651
+ FROM information_schema.COLUMNS AS c
15652
+ LEFT JOIN information_schema.TABLES AS t
15653
+ ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
15654
+ AND t.TABLE_NAME = c.TABLE_NAME
15655
+ WHERE c.TABLE_SCHEMA = DATABASE()
15656
+ ORDER BY c.TABLE_NAME ASC, c.ORDINAL_POSITION ASC
15657
+ `;
15658
+ async function querySyncDbColumns(mysql) {
15659
+ const queryRes = await mysql.execute(SYNC_DB_COLUMNS_SQL);
15660
+ if (!Array.isArray(queryRes.data)) {
15661
+ return [];
15662
+ }
15663
+ return queryRes.data;
15664
+ }
15665
+
15666
+ // scripts/syncDb/context.js
15667
+ async function loadSyncDbTableDefs(tablesDir) {
15668
+ const tableMap = new Map;
15669
+ const glob = new Bun.Glob("*.json");
15670
+ const files = await glob.scan({
15671
+ cwd: tablesDir,
15672
+ onlyFiles: true,
15673
+ absolute: true,
15674
+ followSymlinks: true
15675
+ });
15676
+ for await (const filePath of files) {
15677
+ const fileName = basename2(filePath, ".json");
15678
+ const tableKey = String(camelCase(fileName)).toLowerCase();
15679
+ if (fileName.startsWith("_")) {
15680
+ continue;
15681
+ }
15682
+ const loaded = await importDefault(filePath, {});
15683
+ if (!isPlainObject2(loaded)) {
15684
+ Logger.warn("tables \u5B9A\u4E49\u6587\u4EF6\u4E0D\u662F\u5BF9\u8C61\uFF0C\u5DF2\u6309\u7A7A\u5BF9\u8C61\u5904\u7406", {
15685
+ filePath
15686
+ });
15687
+ tableMap.set(tableKey, {
15688
+ filePath,
15689
+ tableDef: {}
15690
+ });
15691
+ continue;
15692
+ }
15693
+ tableMap.set(tableKey, {
15694
+ filePath,
15695
+ tableDef: loaded
15696
+ });
15697
+ }
15698
+ return tableMap;
15699
+ }
15700
+ async function prepareSyncDbBaseContext(mysqlConfig) {
15701
+ const tablesDir = resolve2(join3(process.cwd(), "tables"));
15702
+ await mkdir(tablesDir, { recursive: true });
15703
+ if (!isPlainObject2(mysqlConfig)) {
15704
+ throw new Error("syncDb \u7F3A\u5C11 mysql \u914D\u7F6E", {
15705
+ cause: null,
15706
+ code: "validation",
15707
+ subsystem: "scripts",
15708
+ operation: "syncDb"
15709
+ });
15710
+ }
15711
+ await Connect.connectMysql(mysqlConfig);
15712
+ const mysql = new DbHelper({
15713
+ redis: null,
15714
+ dbName: mysqlConfig.database,
15715
+ sql: Connect.getMysql(),
15716
+ idMode: mysqlConfig.idMode
15717
+ });
15718
+ const dbColumns = await querySyncDbColumns(mysql);
15719
+ const skippedBeflyTables = new Set;
15720
+ for (const row of dbColumns) {
15721
+ const tableName = String(row?.tableName || "");
15722
+ if (isNonEmptyString(tableName) && tableName.toLowerCase().startsWith("befly_")) {
15723
+ skippedBeflyTables.add(tableName);
15724
+ }
15725
+ }
15726
+ if (skippedBeflyTables.size > 0) {
15727
+ const skippedTables = Array.from(skippedBeflyTables).sort((left, right) => left.localeCompare(right));
15728
+ Logger.info("\u5DF2\u8DF3\u8FC7 befly_ \u524D\u7F00\u8868\uFF0C\u4E0D\u53C2\u4E0E\u5BF9\u6BD4", {
15729
+ skippedTableCount: skippedBeflyTables.size,
15730
+ skippedTables
15731
+ });
15732
+ }
15733
+ const groupedDbColumns = groupSyncDbColumns(dbColumns);
15734
+ const existingTableMap = await loadSyncDbTableDefs(tablesDir);
15735
+ const diff = buildSyncDbDiff(groupedDbColumns, existingTableMap);
15736
+ return {
15737
+ diff,
15738
+ groupedDbColumns,
15739
+ tablesDir,
15740
+ existingTableMap
15741
+ };
15742
+ }
15743
+
15744
+ // scripts/syncDb/report.js
15745
+ import { mkdir as mkdir2 } from "fs/promises";
15746
+ import { dirname as dirname2 } from "path";
15747
+ function printSyncDbDiffSummary(mode, diff) {
15748
+ const tableNames = diff.missingTables.map((item) => item.tableName);
15749
+ const missingFieldGroups = Object.values(diff.missingFieldsByTable);
15750
+ const missingFieldCount = countSyncDbMissingFields(diff);
15751
+ Logger.info("DB \u4E0E\u9879\u76EE\u6839\u76EE\u5F55 tables \u5DEE\u5F02\u68C0\u67E5\u5B8C\u6210", {
15752
+ mode,
15753
+ missingTableCount: diff.missingTables.length,
15754
+ missingFieldCount,
15755
+ missingTables: tableNames
15756
+ });
15757
+ for (const item of missingFieldGroups) {
15758
+ Logger.info("\u53D1\u73B0\u7F3A\u5931\u5B57\u6BB5", {
15759
+ tableName: item.tableName,
15760
+ tableFileName: item.tableFileName,
15761
+ fieldNames: item.fields.map((field) => field.fieldName)
15762
+ });
15763
+ }
15764
+ }
15765
+ function escapeSyncDbMdCell(value) {
15766
+ const text = String(value ?? "");
15767
+ return text.replaceAll("|", "\\|").replaceAll(`
15768
+ `, "<br>");
15769
+ }
15770
+ function formatSyncDbValue(value) {
15771
+ if (value === null || value === undefined) {
15772
+ return "-";
15773
+ }
15774
+ if (value === "") {
15775
+ return "-";
15776
+ }
15777
+ return String(value);
15778
+ }
15779
+ function buildSyncDbCheckRows(diff) {
15780
+ const rows = [];
15781
+ for (const missingTable of diff.missingTables) {
15782
+ for (const columnMeta of missingTable.columns) {
15783
+ const fieldInfo = toSyncDbFieldDef(columnMeta);
15784
+ rows.push({
15785
+ diffType: "\u7F3A\u5C11\u8868",
15786
+ tableName: missingTable.tableName,
15787
+ tableFileName: `${missingTable.tableFileName}.json`,
15788
+ dbColumnName: columnMeta.columnName,
15789
+ fieldName: fieldInfo.fieldName,
15790
+ dbDataType: columnMeta.dataType,
15791
+ dbColumnType: columnMeta.columnType,
15792
+ dbCharLength: columnMeta.charLength,
15793
+ fieldInput: fieldInfo.fieldDef.input,
15794
+ fieldMax: fieldInfo.fieldDef.max,
15795
+ fieldDisplayName: fieldInfo.fieldDef.name
15796
+ });
15797
+ }
15798
+ }
15799
+ for (const tableInfo of Object.values(diff.missingFieldsByTable)) {
15800
+ for (const fieldItem of tableInfo.fields) {
15801
+ rows.push({
15802
+ diffType: "\u7F3A\u5C11\u5B57\u6BB5",
15803
+ tableName: tableInfo.tableName,
15804
+ tableFileName: `${tableInfo.tableFileName}.json`,
15805
+ dbColumnName: fieldItem.dbColumnName,
15806
+ fieldName: fieldItem.fieldName,
15807
+ dbDataType: fieldItem.dbDataType,
15808
+ dbColumnType: fieldItem.dbColumnType,
15809
+ dbCharLength: fieldItem.dbCharLength,
15810
+ fieldInput: fieldItem.fieldDef.input,
15811
+ fieldMax: fieldItem.fieldDef.max,
15812
+ fieldDisplayName: fieldItem.fieldDef.name
15813
+ });
15814
+ }
15815
+ }
15816
+ return rows;
15817
+ }
15818
+ function groupSyncDbDiffRowsByTable(rows) {
15819
+ const grouped = new Map;
15820
+ for (const row of rows) {
15821
+ const tableName = String(row.tableName || "-");
15822
+ const tableFileName = String(row.tableFileName || "-");
15823
+ const groupKey = `${tableName}@@${tableFileName}`;
15824
+ if (!grouped.has(groupKey)) {
15825
+ grouped.set(groupKey, {
15826
+ tableName,
15827
+ tableFileName,
15828
+ rows: []
15829
+ });
15830
+ }
15831
+ grouped.get(groupKey).rows.push(row);
15832
+ }
15833
+ return Array.from(grouped.values());
15834
+ }
15835
+ function buildSyncDbAllTableRows(groupedDbColumns, existingTableMap) {
15836
+ const rows = [];
15837
+ for (const [tableName, columns] of groupedDbColumns.entries()) {
15838
+ const hasTableDef = existingTableMap.has(String(camelCase(tableName)).toLowerCase()) ? "\u662F" : "\u5426";
15839
+ const tableComment = isNonEmptyString(columns[0]?.tableComment) ? String(columns[0].tableComment).trim() : "-";
15840
+ rows.push({
15841
+ tableName,
15842
+ tableComment,
15843
+ columnCount: columns.length,
15844
+ hasTableDef
15845
+ });
15846
+ }
15847
+ return rows;
15848
+ }
15849
+ function buildSyncDbCheckMarkdown(diff, groupedDbColumns, existingTableMap) {
15850
+ const allTableRows = buildSyncDbAllTableRows(groupedDbColumns, existingTableMap);
15851
+ const rows = buildSyncDbCheckRows(diff);
15852
+ const diffGroups = groupSyncDbDiffRowsByTable(rows);
15853
+ const missingFieldCount = countSyncDbMissingFields(diff);
15854
+ const lines = [];
15855
+ lines.push("# DB \u4E0E\u9879\u76EE\u6839\u76EE\u5F55 tables \u5DEE\u5F02\u62A5\u544A");
15856
+ lines.push("");
15857
+ lines.push(`- \u751F\u6210\u65F6\u95F4\u6233: ${String(Date.now())}`);
15858
+ lines.push(`- \u626B\u63CF\u6570\u636E\u5E93\u8868\u6570\u91CF: ${String(allTableRows.length)}`);
15859
+ lines.push(`- \u7F3A\u5C11\u8868\u6570\u91CF: ${String(diff.missingTables.length)}`);
15860
+ lines.push(`- \u7F3A\u5C11\u5B57\u6BB5\u6570\u91CF: ${String(missingFieldCount)}`);
15861
+ lines.push("");
15862
+ lines.push("## \u5168\u90E8\u6570\u636E\u5E93\u8868\u4FE1\u606F");
15863
+ lines.push("");
15864
+ lines.push(`| \u6570\u636E\u5E93\u8868 | \u8868\u8BF4\u660E | \u5217\u6570\u91CF | tables\u4E2D\u662F\u5426\u5B58\u5728 |
15865
+ |---|---|---:|---|`);
15866
+ if (allTableRows.length === 0) {
15867
+ lines.push("| - | - | - | \u672A\u626B\u63CF\u5230\u53EF\u5BF9\u6BD4\u8868\uFF08\u53EF\u80FD\u5168\u90E8\u88AB\u8FC7\u6EE4\uFF09 |");
15868
+ } else {
15869
+ for (const row of allTableRows) {
15870
+ lines.push(`| ${escapeSyncDbMdCell(formatSyncDbValue(row.tableName))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.tableComment))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.columnCount))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.hasTableDef))} |`);
15871
+ }
15872
+ }
15873
+ lines.push("");
15874
+ lines.push("## \u5168\u90E8\u5DEE\u5F02\u4FE1\u606F");
15875
+ lines.push("");
15876
+ if (rows.length === 0) {
15877
+ lines.push("- \u672A\u53D1\u73B0\u5DEE\u5F02");
15878
+ lines.push("");
15879
+ return lines.join(`
15880
+ `);
15881
+ }
15882
+ for (const group of diffGroups) {
15883
+ lines.push(`### ${escapeSyncDbMdCell(formatSyncDbValue(group.tableName))}\uFF08${escapeSyncDbMdCell(formatSyncDbValue(group.tableFileName))}\uFF09`);
15884
+ lines.push("");
15885
+ lines.push("| \u5DEE\u5F02\u7C7B\u578B | \u6570\u636E\u5E93\u5B57\u6BB5\u540D | \u5B57\u6BB5\u6807\u8BC6 | \u5B57\u6BB5\u540D\u79F0 | \u5B57\u6BB5\u7C7B\u578B | \u5217\u7C7B\u578B | \u5B57\u7B26\u4E32\u957F\u5EA6 | \u63A8\u65ADinput | \u63A8\u65ADmax |");
15886
+ lines.push("|---|---|---|---|---|---|---:|---|---:|");
15887
+ for (const row of group.rows) {
15888
+ lines.push(`| ${escapeSyncDbMdCell(formatSyncDbValue(row.diffType))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.dbColumnName))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.fieldName))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.fieldDisplayName))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.dbDataType))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.dbColumnType))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.dbCharLength))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.fieldInput))} | ${escapeSyncDbMdCell(formatSyncDbValue(row.fieldMax))} |`);
15889
+ }
15890
+ lines.push("");
15891
+ }
15892
+ lines.push("");
15893
+ return lines.join(`
15894
+ `);
15895
+ }
15896
+ async function writeSyncDbCheckReport(reportPath, diff, groupedDbColumns, existingTableMap) {
15897
+ if (!isNonEmptyString(reportPath)) {
15898
+ return "";
15899
+ }
15900
+ await mkdir2(dirname2(reportPath), { recursive: true });
15901
+ const markdown = buildSyncDbCheckMarkdown(diff, groupedDbColumns, existingTableMap);
15902
+ await Bun.write(reportPath, markdown);
15903
+ Logger.info("\u5DEE\u5F02\u62A5\u544A\u5199\u5165\u5B8C\u6210", {
15904
+ reportPath
15905
+ });
15906
+ return reportPath;
15907
+ }
15908
+
15909
+ // scripts/syncDb/index.js
15910
+ async function syncDbCheck(mysqlConfig) {
15911
+ try {
15912
+ const reportPath = resolve3(join4(process.cwd(), "db.check.md"));
15913
+ await mkdir3(dirname3(reportPath), { recursive: true });
15914
+ await Bun.write(reportPath, "");
15915
+ const { diff, groupedDbColumns, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
15916
+ const missingFieldCount = countSyncDbMissingFields(diff);
15917
+ const writtenReportPath = await writeSyncDbCheckReport(reportPath, diff, groupedDbColumns, existingTableMap);
15918
+ printSyncDbDiffSummary("check", diff);
15919
+ return {
15920
+ mode: "check",
15921
+ createdTableFileCount: 0,
15922
+ appendedFieldCount: 0,
15923
+ missingTableCount: diff.missingTables.length,
15924
+ missingFieldCount,
15925
+ hasDiff: diff.missingTables.length > 0 || missingFieldCount > 0,
15926
+ reportPath: writtenReportPath
15927
+ };
15928
+ } catch (error48) {
15929
+ throw new Error("\u6267\u884C\u8868\u5B9A\u4E49\u540C\u6B65\u5931\u8D25", {
15930
+ cause: error48,
15931
+ code: "runtime",
15932
+ subsystem: "scripts",
15933
+ operation: "syncDb"
15934
+ });
15935
+ } finally {
15936
+ await Connect.disconnect();
15937
+ await Logger.flush();
15938
+ }
15939
+ }
15940
+ async function syncDbApply(mysqlConfig) {
15941
+ try {
15942
+ const { diff, tablesDir, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
15943
+ printSyncDbDiffSummary("check", diff);
15944
+ const applyResult = await applySyncDbDiff(diff, tablesDir, existingTableMap);
15945
+ printSyncDbDiffSummary("apply", diff);
15946
+ return {
15947
+ mode: "apply",
15948
+ createdTableFileCount: applyResult.createdTableFileCount,
15949
+ appendedFieldCount: applyResult.appendedFieldCount,
15950
+ missingTableCount: diff.missingTables.length,
15951
+ missingFieldCount: applyResult.appendedFieldCount,
15952
+ hasDiff: diff.missingTables.length > 0 || applyResult.appendedFieldCount > 0,
15953
+ reportPath: ""
15954
+ };
15955
+ } catch (error48) {
15956
+ throw new Error("\u6267\u884C\u8868\u5B9A\u4E49\u540C\u6B65\u5931\u8D25", {
15957
+ cause: error48,
15958
+ code: "runtime",
15959
+ subsystem: "scripts",
15960
+ operation: "syncDb"
15961
+ });
15962
+ } finally {
15963
+ await Connect.disconnect();
15964
+ await Logger.flush();
15965
+ }
15966
+ }
15967
+
15968
+ // utils/calcPerfTime.js
15969
+ var calcPerfTime = (startTime, endTime = Bun.nanoseconds()) => {
15970
+ const elapsedMs = (endTime - startTime) / 1e6;
15971
+ if (elapsedMs < 1000) {
15972
+ return `${elapsedMs.toFixed(2)} \u6BEB\u79D2`;
15973
+ } else {
15974
+ const elapsedSeconds = elapsedMs / 1000;
15975
+ return `${elapsedSeconds.toFixed(2)} \u79D2`;
15976
+ }
15977
+ };
15978
+
15979
+ // utils/scanFiles.js
15980
+ import { existsSync as existsSync2 } from "fs";
15981
+ async function scanFiles(dir, source, type, pattern) {
15982
+ try {
15983
+ const glob = new Bun.Glob(pattern);
15984
+ if (!existsSync2(dir))
15985
+ return [];
15986
+ const results = [];
15987
+ const files = await glob.scan({
15988
+ cwd: dir,
15989
+ onlyFiles: true,
15990
+ absolute: true,
15991
+ followSymlinks: true
15992
+ });
15993
+ for await (const file of files) {
15994
+ const filePath = normalize(file);
15995
+ const parsedFile = parse5(filePath);
15996
+ const fileName = parsedFile.name;
15997
+ const parsedRelativePath = parse5(relative(dir, filePath));
15998
+ const relativePath = parsedRelativePath.dir ? join(parsedRelativePath.dir, parsedRelativePath.name) : parsedRelativePath.name;
15999
+ if (relativePath.split("/").some((part) => part.startsWith("_")))
16000
+ continue;
16001
+ const content = await importDefault(filePath, {});
16002
+ const contentObj = isPlainObject2(content) ? content : {};
16003
+ const base = {
16004
+ source,
16005
+ type,
16006
+ filePath,
16007
+ relativePath,
16008
+ fileName,
16009
+ apiPath: `/api/${source}/${relativePath}`
16010
+ };
16011
+ if (type === "table") {
16012
+ base["fieldsDef"] = contentObj;
16013
+ }
16014
+ if (type === "api") {
16015
+ base["name"] = "";
16016
+ }
16017
+ if (type !== "table") {
16018
+ for (const [key, value] of Object.entries(contentObj)) {
16019
+ if (base[key])
16020
+ continue;
16021
+ base[key] = value;
16022
+ }
16023
+ }
16024
+ results.push(base);
16025
+ }
16026
+ return results;
16027
+ } catch (error48) {
16028
+ throw new Error(`\u626B\u63CF\u5931\u8D25: source=${source} type=${type} dir=${dir} pattern=${pattern}`, {
16029
+ cause: error48,
16030
+ code: "runtime"
16031
+ });
16032
+ }
16033
+ }
16034
+
16035
+ // utils/scanSources.js
16036
+ var scanSources = async () => {
16037
+ const apis = [];
16038
+ const plugins = [];
16039
+ const hooks = [];
16040
+ const tables = [];
16041
+ const allCoreTables = await scanFiles(coreTableDir, "core", "table", "*.json");
16042
+ for (const item of allCoreTables) {
16043
+ tables.push(item);
16044
+ }
16045
+ const allAppTables = await scanFiles(appTableDir, "app", "table", "*.json");
16046
+ for (const item of allAppTables) {
16047
+ tables.push(item);
16048
+ }
16049
+ const allCorePlugins = await scanFiles(corePluginDir, "core", "plugin", "*.js");
16050
+ for (const item of allCorePlugins) {
16051
+ plugins.push(item);
16052
+ }
16053
+ const allAppPlugins = await scanFiles(appPluginDir, "app", "plugin", "*.js");
16054
+ for (const item of allAppPlugins) {
16055
+ plugins.push(item);
16056
+ }
16057
+ const allCoreHooks = await scanFiles(coreHookDir, "core", "hook", "*.js");
16058
+ for (const item of allCoreHooks) {
16059
+ hooks.push(item);
16060
+ }
16061
+ const allAppHooks = await scanFiles(appHookDir, "app", "hook", "*.js");
16062
+ for (const item of allAppHooks) {
16063
+ hooks.push(item);
16064
+ }
16065
+ const allCoreApis = await scanFiles(coreApiDir, "core", "api", "**/*.js");
16066
+ for (const item of allCoreApis) {
16067
+ apis.push(item);
16068
+ }
16069
+ const allAppApis = await scanFiles(appApiDir, "app", "api", "**/*.js");
16070
+ for (const item of allAppApis) {
16071
+ apis.push(item);
16072
+ }
16073
+ return {
16074
+ hooks,
16075
+ plugins,
16076
+ apis,
16077
+ tables
16078
+ };
16079
+ };
16080
+
16081
+ // utils/deepMerge.js
16082
+ function clone2(value) {
16083
+ return structuredClone(value);
16084
+ }
16085
+ function concat(targetArr, sourceArr, cloneTargetItems) {
16086
+ const nextArr = [];
16087
+ if (cloneTargetItems) {
16088
+ for (const item of targetArr) {
16089
+ nextArr.push(clone2(item));
16090
+ }
16091
+ } else {
16092
+ for (const item of targetArr) {
16093
+ nextArr.push(item);
16094
+ }
16095
+ }
16096
+ for (const item of sourceArr) {
16097
+ nextArr.push(clone2(item));
16098
+ }
16099
+ return nextArr;
16100
+ }
16101
+ function mergeValue(baseValue, incomingValue) {
16102
+ if (Array.isArray(baseValue) && Array.isArray(incomingValue)) {
16103
+ return concat(baseValue, incomingValue, false);
16104
+ }
16105
+ if (isPlainObject2(baseValue) && isPlainObject2(incomingValue)) {
16106
+ mergeInto(baseValue, incomingValue);
16107
+ return baseValue;
16108
+ }
16109
+ return clone2(incomingValue);
16110
+ }
16111
+ function mergeInto(base, incoming) {
16112
+ for (const key of Object.keys(incoming)) {
16113
+ const incomingValue = incoming[key];
16114
+ if (incomingValue === undefined) {
16115
+ continue;
16116
+ }
16117
+ base[key] = mergeValue(base[key], incomingValue);
16118
+ }
16119
+ return base;
16120
+ }
16121
+ function deepMerge(source, target) {
16122
+ if (Array.isArray(source) && Array.isArray(target)) {
16123
+ const base = clone2(source);
16124
+ const incoming = clone2(target);
16125
+ return concat(base, incoming, false);
16126
+ }
16127
+ if (isPlainObject2(source) && isPlainObject2(target)) {
16128
+ const base = clone2(source);
16129
+ const incoming = clone2(target);
16130
+ return mergeInto(base, incoming);
16131
+ }
16132
+ throw new Error("deepMerge: source/target \u5FC5\u987B\u540C\u4E3A\u6570\u7EC4\u6216\u540C\u4E3A\u666E\u901A\u5BF9\u8C61", {
16133
+ cause: null,
16134
+ code: "validation",
16135
+ subsystem: "utils",
16136
+ operation: "deepMerge"
16137
+ });
16138
+ }
16139
+
16140
+ // utils/sortModules.js
16141
+ function sortModules(items) {
16142
+ const result = [];
16143
+ const visited = new Set;
12536
16144
  const visiting = new Set;
12537
16145
  const nameToItem = {};
12538
16146
  let isPass = true;
@@ -12655,32 +16263,58 @@ class Befly {
12655
16263
  plugins = [];
12656
16264
  hooks = [];
12657
16265
  apis = {};
12658
- constructor(env = {}, config3 = {}, menus = []) {
12659
- this.context.env = env;
12660
- this.context.config = deepMerge(beflyConfig_default, config3);
12661
- this.menus = deepMerge(prefixMenuPaths(beflyMenus_default, "core"), prefixMenuPaths(menus, "app"));
16266
+ scanApis = [];
16267
+ scanPlugins = [];
16268
+ scanHooks = [];
16269
+ createdByFactory = false;
16270
+ static async create(env = {}, config3 = {}, menus = []) {
16271
+ const instance = new Befly;
16272
+ instance.context.env = env;
16273
+ instance.context.config = deepMerge(beflyConfig_default, config3);
16274
+ instance.menus = deepMerge(prefixMenuPaths(beflyMenus_default, "core"), prefixMenuPaths(menus, "app"));
16275
+ instance.createdByFactory = true;
16276
+ const configHasError = await checkConfig(instance.context.config);
16277
+ const { apis, tables, plugins, hooks } = await scanSources();
16278
+ const apiHasError = await checkApi(apis);
16279
+ const tableHasError = await checkTable(tables);
16280
+ const pluginHasError = await checkPlugin(plugins);
16281
+ const hookHasError = await checkHook(hooks);
16282
+ const menuHasError = await checkMenu(instance.menus);
16283
+ if (configHasError || apiHasError || tableHasError || pluginHasError || hookHasError || menuHasError) {
16284
+ throw new Error("\u68C0\u67E5\u5931\u8D25\uFF1A\u5B58\u5728\u914D\u7F6E/\u7ED3\u6784\u95EE\u9898", {
16285
+ cause: null,
16286
+ code: "policy",
16287
+ subsystem: "checks",
16288
+ operation: "checkAll"
16289
+ });
16290
+ }
16291
+ instance.scanApis = apis;
16292
+ instance.scanPlugins = plugins;
16293
+ instance.scanHooks = hooks;
16294
+ return instance;
12662
16295
  }
12663
16296
  async start() {
12664
16297
  try {
16298
+ if (!this.createdByFactory) {
16299
+ throw new Error("\u8BF7\u4F7F\u7528 Befly.create(...) \u521B\u5EFA\u5B9E\u4F8B\u540E\u518D\u8C03\u7528 start", {
16300
+ cause: null,
16301
+ code: "policy",
16302
+ subsystem: "start",
16303
+ operation: "factoryGuard"
16304
+ });
16305
+ }
12665
16306
  const serverStartTime = Bun.nanoseconds();
12666
- const configHasError = await checkConfig(this.context.config);
12667
- const { apis, tables, plugins, hooks } = await scanSources();
12668
- const apiHasError = await checkApi(apis);
12669
- const tableHasError = await checkTable(tables);
12670
- const pluginHasError = await checkPlugin(plugins);
12671
- const hookHasError = await checkHook(hooks);
12672
- const menuHasError = await checkMenu(this.menus);
12673
- if (configHasError || apiHasError || tableHasError || pluginHasError || hookHasError || menuHasError) {
12674
- throw new Error("\u68C0\u67E5\u5931\u8D25\uFF1A\u5B58\u5728\u914D\u7F6E/\u7ED3\u6784\u95EE\u9898", {
16307
+ if (!Array.isArray(this.scanApis) || !Array.isArray(this.scanPlugins) || !Array.isArray(this.scanHooks)) {
16308
+ throw new Error("\u8BF7\u4F7F\u7528 Befly.create(...) \u5B8C\u6210\u9884\u68C0\u540E\u518D\u8C03\u7528 start", {
12675
16309
  cause: null,
12676
16310
  code: "policy",
12677
- subsystem: "checks",
12678
- operation: "checkAll"
16311
+ subsystem: "start",
16312
+ operation: "preflightGuard"
12679
16313
  });
12680
16314
  }
12681
16315
  await Connect.connectRedis(this.context.config.redis);
12682
16316
  await Connect.connectMysql(this.context.config.mysql);
12683
- const sortedPlugins = sortModules(plugins);
16317
+ const sortedPlugins = sortModules(this.scanPlugins);
12684
16318
  if (sortedPlugins) {
12685
16319
  for (const item of sortedPlugins) {
12686
16320
  const pluginInstance = await item.handler(this.context);
@@ -12694,12 +16328,12 @@ class Befly {
12694
16328
  }
12695
16329
  await ensureSyncPrerequisites(this.context);
12696
16330
  if (isPrimaryProcess(this.context.env)) {
12697
- await syncApi(this.context, apis);
16331
+ await syncApi(this.context, this.scanApis);
12698
16332
  await syncMenu(this.context, this.menus);
12699
16333
  await syncDev(this.context);
12700
16334
  await syncCache(this.context);
12701
16335
  }
12702
- const sortedHooks = sortModules(hooks);
16336
+ const sortedHooks = sortModules(this.scanHooks);
12703
16337
  if (sortedHooks) {
12704
16338
  this.hooks = sortedHooks;
12705
16339
  } else {
@@ -12708,7 +16342,7 @@ class Befly {
12708
16342
  code: "policy"
12709
16343
  });
12710
16344
  }
12711
- for (const api2 of apis) {
16345
+ for (const api2 of this.scanApis) {
12712
16346
  this.apis[api2.apiPath] = api2;
12713
16347
  }
12714
16348
  const apiFetch = apiHandler(this.apis, this.hooks, this.context);
@@ -12763,7 +16397,17 @@ class Befly {
12763
16397
  }
12764
16398
  }
12765
16399
  }
16400
+ async function dbCheck(config3 = {}) {
16401
+ const mergedConfig = deepMerge(beflyConfig_default, config3);
16402
+ return syncDbCheck(mergedConfig.mysql);
16403
+ }
16404
+ async function dbApply(config3 = {}) {
16405
+ const mergedConfig = deepMerge(beflyConfig_default, config3);
16406
+ return syncDbApply(mergedConfig.mysql);
16407
+ }
12766
16408
  export {
16409
+ dbCheck,
16410
+ dbApply,
12767
16411
  Logger,
12768
16412
  Befly
12769
16413
  };