befly 3.21.2 → 3.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/dbUtil.js CHANGED
@@ -1,72 +1,6 @@
1
1
  import { isFiniteNumber, isNonEmptyString, isNullable, isString } from "../utils/is.js";
2
2
  import { snakeCase } from "../utils/util.js";
3
3
 
4
- export const SAFE_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
5
-
6
- export function assertNoUndefinedParam(value, label) {
7
- if (value === undefined) {
8
- throw new Error(`${label} 不能为 undefined`, {
9
- cause: null,
10
- code: "validation"
11
- });
12
- }
13
- }
14
-
15
- export function assertSafeIdentifierPart(part, kind) {
16
- if (!SAFE_IDENTIFIER_RE.test(part)) {
17
- throw new Error(`无效的 ${kind} 标识符: ${part}`, {
18
- cause: null,
19
- code: "validation"
20
- });
21
- }
22
- }
23
-
24
- export function validateQuoteIdentifierInput(identifier) {
25
- if (!isString(identifier)) {
26
- throw new Error(`quoteIdent 需要字符串类型标识符 (identifier: ${String(identifier)})`, {
27
- cause: null,
28
- code: "validation"
29
- });
30
- }
31
-
32
- const trimmed = identifier.trim();
33
- if (!trimmed) {
34
- throw new Error("SQL 标识符不能为空", {
35
- cause: null,
36
- code: "validation"
37
- });
38
- }
39
-
40
- return trimmed;
41
- }
42
-
43
- export function validateLimitValue(count) {
44
- if (typeof count !== "number" || count < 0) {
45
- throw new Error(`LIMIT 数量必须是非负数 (count: ${String(count)})`, {
46
- cause: null,
47
- code: "validation"
48
- });
49
- }
50
- }
51
-
52
- export function validateOffsetValue(offset, label = "OFFSET") {
53
- if (typeof offset !== "number" || offset < 0) {
54
- throw new Error(`${label} 必须是非负数 (offset: ${String(offset)})`, {
55
- cause: null,
56
- code: "validation"
57
- });
58
- }
59
- }
60
-
61
- export function validateExcludeFieldsResult(resultFields, table, excludeSnakeFields) {
62
- if (resultFields.length === 0) {
63
- throw new Error(`排除字段后没有剩余字段可查询。表: ${table}, 排除: ${excludeSnakeFields.join(", ")}`, {
64
- cause: null,
65
- code: "validation"
66
- });
67
- }
68
- }
69
-
70
4
  export function assertNoExprField(field) {
71
5
  if (!isString(field)) {
72
6
  return;
@@ -84,93 +18,6 @@ export function assertNoExprField(field) {
84
18
  }
85
19
  }
86
20
 
87
- export function assertNoUndefinedInRecord(row, label) {
88
- for (const [key, value] of Object.entries(row)) {
89
- if (value === undefined) {
90
- throw new Error(`${label} 存在 undefined 字段值 (field: ${key})`, {
91
- cause: null,
92
- code: "validation"
93
- });
94
- }
95
- }
96
- }
97
-
98
- export function assertBatchInsertRowsConsistent(rows, options) {
99
- if (!Array.isArray(rows)) {
100
- throw new Error("批量插入 rows 必须是数组", {
101
- cause: null,
102
- code: "validation"
103
- });
104
- }
105
- if (rows.length === 0) {
106
- throw new Error(`插入数据不能为空 (table: ${options.table})`, {
107
- cause: null,
108
- code: "validation"
109
- });
110
- }
111
-
112
- const first = rows[0];
113
- if (!first || typeof first !== "object" || Array.isArray(first)) {
114
- throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: 0)`, {
115
- cause: null,
116
- code: "validation"
117
- });
118
- }
119
-
120
- const fields = Object.keys(first);
121
- if (fields.length === 0) {
122
- throw new Error(`插入数据必须至少有一个字段 (table: ${options.table})`, {
123
- cause: null,
124
- code: "validation"
125
- });
126
- }
127
-
128
- const fieldSet = new Set(fields);
129
- for (let i = 0; i < rows.length; i++) {
130
- const row = rows[i];
131
- if (!row || typeof row !== "object" || Array.isArray(row)) {
132
- throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: ${i})`, {
133
- cause: null,
134
- code: "validation"
135
- });
136
- }
137
-
138
- const rowKeys = Object.keys(row);
139
- if (rowKeys.length !== fields.length) {
140
- throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i})`, {
141
- cause: null,
142
- code: "validation"
143
- });
144
- }
145
-
146
- for (const key of rowKeys) {
147
- if (!fieldSet.has(key)) {
148
- throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i}, extraField: ${key})`, {
149
- cause: null,
150
- code: "validation"
151
- });
152
- }
153
- }
154
-
155
- for (const field of fields) {
156
- if (!(field in row)) {
157
- throw new Error(`批量插入缺少字段 (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
158
- cause: null,
159
- code: "validation"
160
- });
161
- }
162
- if (row[field] === undefined) {
163
- throw new Error(`批量插入字段值不能为 undefined (table: ${options.table}, rowIndex: ${i}, field: ${field})`, {
164
- cause: null,
165
- code: "validation"
166
- });
167
- }
168
- }
169
- }
170
-
171
- return fields;
172
- }
173
-
174
21
  export function parseTableRef(tableRef) {
175
22
  if (!isString(tableRef)) {
176
23
  throw new Error(`tableRef 必须是字符串 (tableRef: ${String(tableRef)})`, {
@@ -180,7 +27,7 @@ export function parseTableRef(tableRef) {
180
27
  }
181
28
 
182
29
  const trimmed = tableRef.trim();
183
- if (!isNonEmptyString(tableRef)) {
30
+ if (!trimmed) {
184
31
  throw new Error("tableRef 不能为空", {
185
32
  cause: null,
186
33
  code: "validation"
@@ -188,12 +35,6 @@ export function parseTableRef(tableRef) {
188
35
  }
189
36
 
190
37
  const parts = trimmed.split(/\s+/).filter((part) => part.length > 0);
191
- if (parts.length === 0) {
192
- throw new Error("tableRef 不能为空", {
193
- cause: null,
194
- code: "validation"
195
- });
196
- }
197
38
  if (parts.length > 2) {
198
39
  throw new Error(`不支持的表引用格式(包含过多片段)。请使用最简形式:table 或 table alias 或 schema.table 或 schema.table alias (tableRef: ${trimmed})`, {
199
40
  cause: null,
@@ -258,156 +99,6 @@ export function parseTableRef(tableRef) {
258
99
  return { schema: null, table: table, alias: aliasPart };
259
100
  }
260
101
 
261
- export function validateAndClassifyFields(fields) {
262
- if (!fields || fields.length === 0) {
263
- return { type: "all", fields: [] };
264
- }
265
-
266
- if (fields.some((field) => field === "*")) {
267
- throw new Error("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段", {
268
- cause: null,
269
- code: "validation"
270
- });
271
- }
272
-
273
- if (fields.some((field) => !isNonEmptyString(field))) {
274
- throw new Error("fields 不能包含空字符串或无效值", {
275
- cause: null,
276
- code: "validation"
277
- });
278
- }
279
-
280
- for (const rawField of fields) {
281
- const checkField = rawField.startsWith("!") ? rawField.substring(1) : rawField;
282
- assertNoExprField(checkField);
283
- }
284
-
285
- const includeFields = fields.filter((field) => !field.startsWith("!"));
286
- const excludeFields = fields.filter((field) => field.startsWith("!"));
287
-
288
- if (includeFields.length > 0 && excludeFields.length === 0) {
289
- return { type: "include", fields: includeFields };
290
- }
291
- if (excludeFields.length > 0 && includeFields.length === 0) {
292
- return { type: "exclude", fields: excludeFields.map((field) => field.substring(1)) };
293
- }
294
-
295
- throw new Error('fields 不能同时包含普通字段和排除字段(! 开头)。只能使用以下3种方式之一:\n1. 空数组 [] 或不传(查询所有)\n2. 全部指定字段 ["id", "name"]\n3. 全部排除字段 ["!password", "!token"]', {
296
- cause: null,
297
- code: "validation"
298
- });
299
- }
300
-
301
- export function validateExplicitReadFields(fields) {
302
- return validateAndClassifyFields(fields);
303
- }
304
-
305
- function getTableQualifier(tableRef) {
306
- const parsed = parseTableRef(tableRef);
307
- if (parsed.alias) {
308
- return snakeCase(parsed.alias);
309
- }
310
-
311
- return snakeCase(parsed.table);
312
- }
313
-
314
- export function validateExplicitLeftJoinFields(fields, mainTableRef) {
315
- if (!fields || fields.length === 0) {
316
- throw new Error("leftJoin 查询必须显式传 fields,不支持空 fields 或查询全部字段", {
317
- cause: null,
318
- code: "validation"
319
- });
320
- }
321
-
322
- const mainQualifier = getTableQualifier(mainTableRef);
323
- const mainWildcard = `${mainQualifier}.*`;
324
- const includeFields = [];
325
- const mainExcludeFields = [];
326
- let mainAllIncluded = false;
327
-
328
- for (const rawField of fields) {
329
- if (!isNonEmptyString(rawField)) {
330
- throw new Error("fields 不能包含空字符串或无效值", {
331
- cause: null,
332
- code: "validation"
333
- });
334
- }
335
-
336
- const trimmed = rawField.trim();
337
- if (trimmed === "*") {
338
- throw new Error("fields 不支持 * 星号,请使用空数组 [] 或不传参数表示查询所有字段", {
339
- cause: null,
340
- code: "validation"
341
- });
342
- }
343
-
344
- if (trimmed.startsWith("!")) {
345
- const fieldPart = trimmed.substring(1).trim();
346
- if (!isNonEmptyString(fieldPart)) {
347
- throw new Error("fields 不能包含空字符串或无效值", {
348
- cause: null,
349
- code: "validation"
350
- });
351
- }
352
- if (/\s/.test(fieldPart)) {
353
- throw new Error("leftJoin 排除字段不支持别名", {
354
- cause: null,
355
- code: "validation"
356
- });
357
- }
358
-
359
- assertNoExprField(fieldPart);
360
- const normalizedField = normalizeQualifierField(fieldPart);
361
- if (normalizedField === mainWildcard) {
362
- throw new Error("leftJoin 主表排除字段不能使用 *", {
363
- cause: null,
364
- code: "validation"
365
- });
366
- }
367
- if (!normalizedField.startsWith(`${mainQualifier}.`)) {
368
- throw new Error("leftJoin 排除字段仅支持主表字段,请使用主表限定符", {
369
- cause: null,
370
- code: "validation"
371
- });
372
- }
373
-
374
- mainExcludeFields.push(normalizedField.substring(mainQualifier.length + 1));
375
- continue;
376
- }
377
-
378
- assertNoExprField(trimmed);
379
- const aliasParts = parseFieldAliasParts(trimmed);
380
- const normalizedField = normalizeQualifierField(aliasParts ? aliasParts.fieldPart.trim() : trimmed);
381
- if (normalizedField === mainWildcard) {
382
- if (aliasParts) {
383
- throw new Error("leftJoin 主表全字段不支持别名", {
384
- cause: null,
385
- code: "validation"
386
- });
387
- }
388
- mainAllIncluded = true;
389
- continue;
390
- }
391
-
392
- includeFields.push(trimmed);
393
- }
394
-
395
- if (mainExcludeFields.length > 0 && !mainAllIncluded) {
396
- throw new Error("leftJoin 使用主表排除字段时,必须同时传主表限定符.*", {
397
- cause: null,
398
- code: "validation"
399
- });
400
- }
401
-
402
- return {
403
- type: "join",
404
- includeFields: includeFields,
405
- mainAllIncluded: mainAllIncluded,
406
- mainExcludeFields: mainExcludeFields,
407
- mainQualifier: mainQualifier
408
- };
409
- }
410
-
411
102
  function isQuotedIdentPaired(value) {
412
103
  const trimmed = value.trim();
413
104
  if (trimmed.length < 2) {
@@ -424,53 +115,19 @@ function startsWithIdentifierQuote(value) {
424
115
  return trimmed.startsWith("`") || trimmed.startsWith('"');
425
116
  }
426
117
 
427
- function assertPairedQuotedIdent(value, label) {
428
- if (startsWithIdentifierQuote(value) && !isQuotedIdentPaired(value)) {
429
- throw new Error(`${label} 引用不完整,请使用成对的 \`...\` 或 "..." (value: ${value})`, {
430
- cause: null,
431
- code: "validation"
432
- });
433
- }
434
- }
435
-
436
- function escapeIdentifierPart(part, kind, quoteIdent, unpairedErrorFactory) {
437
- if (isQuotedIdent(part)) {
438
- return part;
439
- }
440
-
441
- if (startsWithQuote(part)) {
442
- throw new Error(unpairedErrorFactory(part), {
443
- cause: null,
444
- code: "validation"
445
- });
446
- }
447
-
448
- assertSafeIdentifierPart(part, kind);
449
- return quoteIdent(part);
450
- }
451
-
452
118
  export function resolveQuoteIdent(options) {
453
119
  if (options && options.quoteIdent) {
454
120
  return options.quoteIdent;
455
121
  }
456
122
 
457
123
  return (identifier) => {
458
- const trimmed = validateQuoteIdentifierInput(identifier);
459
-
124
+ const trimmed = isString(identifier) ? identifier.trim() : String(identifier).trim();
460
125
  const escaped = trimmed.replace(/`/g, "``");
461
126
  return `\`${escaped}\``;
462
127
  };
463
128
  }
464
129
 
465
- export function isQuotedIdent(value) {
466
- return isQuotedIdentPaired(value);
467
- }
468
-
469
- export function startsWithQuote(value) {
470
- return startsWithIdentifierQuote(value);
471
- }
472
-
473
- function parseFieldAliasParts(field) {
130
+ export function parseFieldAliasParts(field) {
474
131
  if (!isString(field)) {
475
132
  return null;
476
133
  }
@@ -512,34 +169,25 @@ export function escapeField(field, quoteIdent) {
512
169
 
513
170
  const trimmed = field.trim();
514
171
 
515
- assertPairedQuotedIdent(trimmed, "字段标识符");
172
+ if (!trimmed) {
173
+ return trimmed;
174
+ }
516
175
 
517
- if (trimmed === "*" || isQuotedIdent(trimmed)) {
176
+ if (trimmed === "*" || isQuotedIdentPaired(trimmed)) {
518
177
  return trimmed;
519
178
  }
520
179
 
521
- try {
522
- assertNoExprField(trimmed);
523
- } catch {
524
- throw new Error(`字段包含函数/表达式,请使用 selectRaw/whereRaw (field: ${trimmed})`, {
525
- cause: null,
526
- code: "validation"
527
- });
180
+ if (startsWithIdentifierQuote(trimmed) && !isQuotedIdentPaired(trimmed) && !trimmed.includes(".")) {
181
+ return trimmed;
528
182
  }
529
183
 
530
- const aliasParts = parseFieldAliasParts(trimmed);
531
- if (aliasParts) {
532
- const cleanFieldPart = aliasParts.fieldPart.trim();
533
- const cleanAliasPart = aliasParts.aliasPart.trim();
534
- if (!isQuotedIdent(cleanAliasPart)) {
535
- if (!SAFE_IDENTIFIER_RE.test(cleanAliasPart)) {
536
- throw new Error(`无效的字段别名: ${cleanAliasPart}`, {
537
- cause: null,
538
- code: "validation"
539
- });
540
- }
541
- }
542
- return `${escapeField(cleanFieldPart, quoteIdent)} ${cleanAliasPart}`;
184
+ if (trimmed.includes("(") || trimmed.includes(")") || /\s+AS\s+/i.test(trimmed)) {
185
+ return trimmed;
186
+ }
187
+
188
+ const aliasMatch = trimmed.match(/^([^\s]+)\s+([^\s]+)$/);
189
+ if (aliasMatch) {
190
+ return `${escapeField(aliasMatch[1].trim(), quoteIdent)} ${aliasMatch[2].trim()}`;
543
191
  }
544
192
 
545
193
  if (trimmed.includes(".")) {
@@ -547,10 +195,12 @@ export function escapeField(field, quoteIdent) {
547
195
  return parts
548
196
  .map((part) => {
549
197
  const cleanPart = part.trim();
550
- if (cleanPart === "*" || isQuotedIdent(cleanPart)) {
198
+ if (!cleanPart) {
199
+ return cleanPart;
200
+ }
201
+ if (cleanPart === "*" || isQuotedIdentPaired(cleanPart) || startsWithIdentifierQuote(cleanPart)) {
551
202
  return cleanPart;
552
203
  }
553
- assertPairedQuotedIdent(cleanPart, "字段标识符");
554
204
  return quoteIdent(cleanPart);
555
205
  })
556
206
  .join(".");
@@ -566,129 +216,51 @@ export function escapeTable(table, quoteIdent) {
566
216
 
567
217
  const trimmed = table.trim();
568
218
 
569
- if (isQuotedIdent(trimmed)) {
219
+ if (!trimmed) {
570
220
  return trimmed;
571
221
  }
572
222
 
573
- const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
574
- if (parts.length === 0) {
575
- throw new Error("FROM 表名不能为空", {
576
- cause: null,
577
- code: "validation"
578
- });
223
+ if (isQuotedIdentPaired(trimmed)) {
224
+ return trimmed;
225
+ }
226
+
227
+ if (startsWithIdentifierQuote(trimmed) && !isQuotedIdentPaired(trimmed) && !trimmed.includes(".")) {
228
+ return trimmed;
579
229
  }
580
230
 
231
+ const parts = trimmed.split(/\s+/).filter((p) => p.length > 0);
581
232
  if (parts.length > 2) {
582
- throw new Error(`不支持的表引用格式(包含过多片段)。请使用 fromRaw 显式传入复杂表达式 (table: ${trimmed})`, {
583
- cause: null,
584
- code: "validation"
585
- });
233
+ return trimmed;
586
234
  }
587
235
 
588
236
  const namePart = parts[0].trim();
589
237
 
590
- const aliasPart = parts.length === 2 ? parts[1].trim() : null;
591
-
592
- const nameSegments = namePart.split(".");
593
- if (nameSegments.length > 2) {
594
- throw new Error(`不支持的表引用格式(schema 层级过深)。请使用 fromRaw (table: ${trimmed})`, {
595
- cause: null,
596
- code: "validation"
597
- });
238
+ if (namePart.split(".").some((part) => startsWithIdentifierQuote(part.trim()) && !isQuotedIdentPaired(part.trim()))) {
239
+ return trimmed;
598
240
  }
599
241
 
600
- let escapedName = "";
601
- if (nameSegments.length === 2) {
602
- const schemaRaw = nameSegments[0];
603
- const tableNameRaw = nameSegments[1];
604
- const schema = schemaRaw.trim();
605
- const tableName = tableNameRaw.trim();
606
-
607
- const escapedSchema = escapeIdentifierPart(schema, "schema", quoteIdent, (part) => `schema 标识符引用不完整,请使用成对的 \`...\` 或 "..." (schema: ${part})`);
608
- const escapedTableName = escapeIdentifierPart(tableName, "table", quoteIdent, (part) => `table 标识符引用不完整,请使用成对的 \`...\` 或 "..." (table: ${part})`);
609
-
610
- escapedName = `${escapedSchema}.${escapedTableName}`;
611
- } else {
612
- const tableNameRaw = nameSegments[0];
613
- const tableName = tableNameRaw.trim();
242
+ const aliasPart = parts.length === 2 ? parts[1].trim() : null;
614
243
 
615
- escapedName = escapeIdentifierPart(tableName, "table", quoteIdent, (part) => `table 标识符引用不完整,请使用成对的 \`...\` 或 "..." (table: ${part})`);
616
- }
244
+ const nameSegments = namePart.split(".");
245
+ const escapedName = nameSegments
246
+ .map((part) => {
247
+ const cleanPart = part.trim();
248
+ if (!cleanPart) {
249
+ return cleanPart;
250
+ }
251
+ if (isQuotedIdentPaired(cleanPart) || startsWithIdentifierQuote(cleanPart)) {
252
+ return cleanPart;
253
+ }
254
+ return quoteIdent(cleanPart);
255
+ })
256
+ .join(".");
617
257
 
618
258
  if (aliasPart) {
619
- assertSafeIdentifierPart(aliasPart, "alias");
620
259
  return `${escapedName} ${aliasPart}`;
621
260
  }
622
261
 
623
262
  return escapedName;
624
263
  }
625
-
626
- export function validateParam(value) {
627
- assertNoUndefinedParam(value, "SQL 参数值");
628
- }
629
-
630
- export function normalizeLimitValue(count, offset) {
631
- validateLimitValue(count);
632
- const limitValue = Math.floor(count);
633
-
634
- let offsetValue = null;
635
- if (!isNullable(offset)) {
636
- validateOffsetValue(offset);
637
- offsetValue = Math.floor(offset);
638
- }
639
-
640
- return { limitValue: limitValue, offsetValue: offsetValue };
641
- }
642
-
643
- export function normalizeOffsetValue(count) {
644
- validateOffsetValue(count, "OFFSET");
645
- return Math.floor(count);
646
- }
647
-
648
- export function toSqlParams(params) {
649
- if (isNullable(params)) {
650
- return [];
651
- }
652
-
653
- if (!Array.isArray(params)) {
654
- throw new Error(`SQL 参数必须是数组,当前类型: ${typeof params}`, {
655
- cause: null,
656
- code: "validation"
657
- });
658
- }
659
-
660
- const out = [];
661
- for (const value of params) {
662
- if (value === undefined) {
663
- throw new Error("SQL 参数不能为 undefined", {
664
- cause: null,
665
- code: "validation"
666
- });
667
- }
668
-
669
- if (typeof value === "bigint") {
670
- out.push(value.toString());
671
- continue;
672
- }
673
-
674
- out.push(value);
675
- }
676
-
677
- return out;
678
- }
679
-
680
- function normalizeTableRef(tableRef) {
681
- const parsed = parseTableRef(tableRef);
682
- const schemaPart = parsed.schema ? snakeCase(parsed.schema) : null;
683
- const tablePart = snakeCase(parsed.table);
684
- const aliasPart = parsed.alias ? snakeCase(parsed.alias) : null;
685
- let result = schemaPart ? `${schemaPart}.${tablePart}` : tablePart;
686
- if (aliasPart) {
687
- result = `${result} ${aliasPart}`;
688
- }
689
- return result;
690
- }
691
-
692
264
  function shouldExcludeFieldValue(field, value, excludeValueMap) {
693
265
  if (!excludeValueMap || excludeValueMap.size === 0) {
694
266
  return false;
@@ -830,13 +402,18 @@ export async function fieldsToSnake(table, classified, getTableColumns) {
830
402
  const allColumns = await getTableColumns(table);
831
403
  const excludeSnakeFields = classified.fields.map((field) => snakeCase(field));
832
404
  const resultFields = allColumns.filter((column) => !excludeSnakeFields.includes(column));
833
- validateExcludeFieldsResult(resultFields, table, excludeSnakeFields);
405
+ if (resultFields.length === 0) {
406
+ throw new Error(`排除字段后没有剩余字段可查询。表: ${table}, 排除: ${excludeSnakeFields.join(", ")}`, {
407
+ cause: null,
408
+ code: "validation"
409
+ });
410
+ }
834
411
  return resultFields;
835
412
  }
836
413
  return ["*"];
837
414
  }
838
415
 
839
- function normalizeQualifierField(field) {
416
+ export function normalizeQualifierField(field) {
840
417
  const parts = field.split(".");
841
418
  if (parts.length === 1) {
842
419
  return snakeCase(field);
@@ -997,39 +574,6 @@ export function processJoinOn(on) {
997
574
  return result;
998
575
  }
999
576
 
1000
- export function parseLeftJoinItem(joinTable, joinItem) {
1001
- const parts = joinItem.split(" ");
1002
- return {
1003
- type: "left",
1004
- table: normalizeTableRef(joinTable),
1005
- on: processJoinOn(`${parts[0]} = ${parts[1]}`)
1006
- };
1007
- }
1008
-
1009
- export function addDefaultStateFilter(where = {}, table, hasLeftJoin = false, beflyMode = "auto") {
1010
- if (beflyMode === "manual") {
1011
- return where;
1012
- }
1013
-
1014
- const hasStateCondition = Object.keys(where).some((key) => key.startsWith("state") || key.includes(".state"));
1015
- if (hasStateCondition) {
1016
- return where;
1017
- }
1018
-
1019
- const result = {};
1020
- for (const [key, value] of Object.entries(where)) {
1021
- result[key] = value;
1022
- }
1023
-
1024
- if (hasLeftJoin && table) {
1025
- result[`${table}.state$gt`] = 0;
1026
- return result;
1027
- }
1028
-
1029
- result.state$gt = 0;
1030
- return result;
1031
- }
1032
-
1033
577
  export function whereKeysToSnake(where) {
1034
578
  return mapWhereKeys(where, (key) => {
1035
579
  assertNoExprField(key);