Шаги 1.1 и 1.2 плана production-готовности: - getDataTableRowsPaged: фильтры/поиск/сортировка/пагинация в SQL (values->>N) - getDataTablesWithAccess и list_directories без N+1 - /column-values: limit (дефолт 20, кап 1000), убраны debug-логи с горячего пути - tree-режим и legacy-путь TableEditor сохранены 1:1 - 10 новых тестов (88/88)
40 lines
1.7 KiB
TypeScript
40 lines
1.7 KiB
TypeScript
// Хелперы для SQL-пагинации строк справочников (data_table_rows).
|
||
// Чистые функции без доступа к БД — покрыты юнит-тестами.
|
||
|
||
/** Дефолтный лимит строк на страницу */
|
||
export const DIRECTORY_ROWS_DEFAULT_LIMIT = 100;
|
||
/** Максимальный лимит строк на страницу */
|
||
export const DIRECTORY_ROWS_MAX_LIMIT = 1000;
|
||
|
||
/**
|
||
* Экранирует спецсимволы LIKE/ILIKE (\, %, _) в пользовательской подстроке,
|
||
* чтобы поиск работал как «содержит подстроку», а не как паттерн.
|
||
* Используется вместе с `ESCAPE '\'` в SQL.
|
||
*/
|
||
export function escapeIlikePattern(value: string): string {
|
||
return value.replace(/[\\%_]/g, (ch) => '\\' + ch);
|
||
}
|
||
|
||
/**
|
||
* Приводит лимит к допустимому диапазону.
|
||
* Невалидные/неположительные значения → defaultLimit, сверху капается maxLimit.
|
||
*/
|
||
export function clampDirectoryRowsLimit(
|
||
raw: unknown,
|
||
defaultLimit: number = DIRECTORY_ROWS_DEFAULT_LIMIT,
|
||
maxLimit: number = DIRECTORY_ROWS_MAX_LIMIT,
|
||
): number {
|
||
const n = typeof raw === 'number' ? raw : parseInt(String(raw ?? ''), 10);
|
||
if (!Number.isFinite(n) || n <= 0) return defaultLimit;
|
||
return Math.min(Math.floor(n), maxLimit);
|
||
}
|
||
|
||
/**
|
||
* Приводит offset к неотрицательному целому. Невалидное → 0.
|
||
*/
|
||
export function clampDirectoryRowsOffset(raw: unknown): number {
|
||
const n = typeof raw === 'number' ? raw : parseInt(String(raw ?? ''), 10);
|
||
if (!Number.isFinite(n) || n < 0) return 0;
|
||
return Math.floor(n);
|
||
}
|