umbot-ydb-adapter 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 max36895
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maxim-M <maximco36895@yandex.ru>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # umbot-ydb-adapter
2
+
3
+ [![npm version](https://img.shields.io/npm/v/umbot-ydb-adapter.svg)](https://www.npmjs.com/package/umbot-ydb-adapter)
4
+ [![npm downloads](https://img.shields.io/npm/dm/umbot-ydb-adapter.svg)](https://www.npmjs.com/package/umbot-ydb-adapter)
5
+ [![license](https://img.shields.io/npm/l/umbot-ydb-adapter.svg)](https://github.com/max36895/umbot-ydb-adapter/blob/main/LICENSE.md)
6
+ [![umbot](https://img.shields.io/badge/umbot-adapter-blue)](https://github.com/max36895/universal_bot-ts)
7
+
8
+ > YDB (Yandex Database) storage adapter for the [umbot](https://github.com/max36895/universal_bot-ts) bot framework:
9
+ > Alice skills and chat bots in Yandex Cloud Functions without a separate database server.
10
+
11
+ Адаптер [YDB](https://ydb.tech) для фреймворка [umbot](https://github.com/max36895/universal_bot-ts).
12
+
13
+ ## Зачем
14
+
15
+ Главный сценарий — бот в **Yandex Cloud Functions**:
16
+
17
+ - у функции нет постоянного диска, поэтому `FileAdapter` там не работает;
18
+ - без адаптера БД `userData` на Telegram, VK, MAX и Viber живёт в памяти экземпляра функции и пропадает, когда
19
+ запрос попадает в другой экземпляр (Алиса, Маруся и SmartApp хранят состояние в самом запросе — им БД нужна
20
+ реже);
21
+ - кластер MongoDB или PostgreSQL — отдельный платный сервер, к которому из функций нужно ходить по сети с паролем.
22
+
23
+ YDB в режиме Serverless оплачивается за запросы, имеет бесплатный лимит и пускает функцию по её сервисному аккаунту —
24
+ паролей и токенов в коде нет.
25
+
26
+ ## Возможности
27
+
28
+ - Хранит `UsersData`, `ImageTokens`, `SoundTokens` и данные ваших моделей.
29
+ - **Таблицы создаются сами.** Встроенные — всегда, ваши — по описанию в опции `tables`. Если схема уже готова, на
30
+ холодном старте функции выполняется один запрос-проверка.
31
+ - Все значения уходят в запрос параметрами (`$p0`, `$p1`, ...), имена таблиц и колонок проверяются. `UPDATE` и
32
+ `DELETE` без условий не выполняются.
33
+ - Ограниченные таймауты на подключение и на каждый запрос.
34
+ - Ошибки не бросаются: `select` возвращает `{ status: false, error }`, `insert`/`update`/`remove` — `false`.
35
+
36
+ ## Требования
37
+
38
+ - Node.js `>= 20.19.0`
39
+ - `umbot >= 3.1.4` (peer-зависимость)
40
+
41
+ ## Установка
42
+
43
+ ```bash
44
+ npm install umbot umbot-ydb-adapter
45
+ ```
46
+
47
+ ## Быстрый старт: Yandex Cloud Functions
48
+
49
+ 1. Создайте базу YDB в режиме **Serverless** и скопируйте её эндпоинт — строку вида
50
+ `grpcs://ydb.serverless.yandexcloud.net:2135/?database=/ru-central1/b1g.../etn...`.
51
+ 2. Создайте сервисный аккаунт с ролью `ydb.editor` (чтение, запись и создание таблиц).
52
+ 3. В версии функции укажите этот сервисный аккаунт и переменную окружения `YDB_CONNECTION_STRING` с эндпоинтом.
53
+
54
+ ```ts
55
+ import { Bot } from 'umbot';
56
+ import { fullPlatforms } from 'umbot/plugins';
57
+ import { YdbAdapter } from 'umbot-ydb-adapter';
58
+
59
+ const bot = new Bot();
60
+ bot.use(fullPlatforms);
61
+ // Строка подключения — из YDB_CONNECTION_STRING, токен — от сервисного аккаунта функции
62
+ bot.use(new YdbAdapter({ options: { credentials: 'metadata' } }));
63
+
64
+ export const handler = async (event: Record<string, unknown>) => {
65
+ const rawBody = typeof event.body === 'string' ? event.body : '';
66
+ const content =
67
+ typeof event.body !== 'string'
68
+ ? JSON.stringify(event.body ?? '')
69
+ : event.isBase64Encoded === true
70
+ ? Buffer.from(rawBody, 'base64').toString('utf8')
71
+ : rawBody;
72
+ const headers = (event.headers ?? {}) as Record<string, unknown>;
73
+ const requestContext = event.requestContext as { identity?: { sourceIp?: string } } | undefined;
74
+ const result = await bot.webhookEvent(content, headers, requestContext?.identity?.sourceIp);
75
+ return {
76
+ statusCode: result.statusCode,
77
+ headers: { 'Content-Type': 'application/json' },
78
+ body: typeof result.body === 'string' ? result.body : JSON.stringify(result.body ?? ''),
79
+ };
80
+ };
81
+ ```
82
+
83
+ Код `handler` повторяет раздел [Serverless](https://www.maxim-m.ru/docs/umbot/v-3.1/guides/deployment) документации
84
+ umbot. Проект с готовым `handler` и скриптом деплоя создаёт `npx umbot create from-flow flow.json --usecloud` —
85
+ останется добавить строку `bot.use(new YdbAdapter(...))`.
86
+
87
+ При первом запросе адаптер подключится к базе и создаст таблицы `UsersData`, `ImageTokens`, `SoundTokens`.
88
+
89
+ ## Локальная разработка
90
+
91
+ **С облачной базой:** подставьте IAM-токен своего аккаунта Yandex Cloud.
92
+
93
+ ```bash
94
+ export YDB_CONNECTION_STRING="grpcs://ydb.serverless.yandexcloud.net:2135/?database=/ru-central1/..."
95
+ export YDB_ACCESS_TOKEN_CREDENTIALS="$(yc iam create-token)"
96
+ ```
97
+
98
+ ```ts
99
+ bot.use(new YdbAdapter()); // авторизация по умолчанию — из переменных окружения
100
+ ```
101
+
102
+ IAM-токен живёт ограниченное время — когда он истечёт, получите новый через `yc iam create-token`.
103
+
104
+ **С локальным YDB в Docker:**
105
+
106
+ ```bash
107
+ docker run -d --rm --name ydb-local -h localhost -p 2135:2135 -p 2136:2136 -p 8765:8765 \
108
+ -e GRPC_TLS_PORT=2135 -e GRPC_PORT=2136 -e MON_PORT=8765 ydbplatform/local-ydb:latest
109
+ ```
110
+
111
+ ```ts
112
+ bot.use(
113
+ new YdbAdapter({
114
+ host: 'grpc://localhost:2136',
115
+ database: '/local',
116
+ options: { credentials: 'anonymous' },
117
+ }),
118
+ );
119
+ ```
120
+
121
+ Веб-интерфейс локальной базы — `http://localhost:8765`.
122
+
123
+ ## Подключение
124
+
125
+ Строка подключения выбирается так (первое заданное):
126
+
127
+ 1. `options.connectionString`;
128
+ 2. `host` + `database` — из конструктора или из `appConfig.db` (umbot заполняет его и из `DB_HOST` / `DB_NAME`);
129
+ 3. переменная окружения `YDB_CONNECTION_STRING`.
130
+
131
+ ```ts
132
+ new YdbAdapter({
133
+ host: 'grpcs://ydb.serverless.yandexcloud.net:2135', // эндпоинт
134
+ database: '/ru-central1/b1g.../etn...', // путь базы
135
+ options: { credentials: 'metadata', queryTimeout: 3000 },
136
+ });
137
+ ```
138
+
139
+ ### Опции (`options`)
140
+
141
+ | Опция | По умолчанию | Описание |
142
+ | ------------------ | ------------------------------------------- | ---------------------------------------------------------------------- |
143
+ | `connectionString` | — | Полная строка подключения |
144
+ | `credentials` | `user`/`pass`, если заданы, иначе `environ` | Способ авторизации (см. ниже) |
145
+ | `tables` | — | Таблицы ваших моделей (см. «Свои таблицы») |
146
+ | `ensureSchema` | `true` | Создавать недостающие таблицы, колонки и индексы после подключения |
147
+ | `connectTimeout` | `10000` | Сколько ждать подключения (discovery, получение токена), мс |
148
+ | `queryTimeout` | `5000` | Ограничение на запрос вместе с повторами SDK, мс; DDL — не меньше 30 с |
149
+ | `poolSize` | `50` (значение SDK) | Максимум одновременных сессий YDB |
150
+
151
+ ### Авторизация (`credentials`)
152
+
153
+ | Значение | Когда использовать |
154
+ | --------------- | -------------------------------------------------------------------------------------------------- |
155
+ | `'metadata'` | Cloud Functions и виртуальные машины Yandex Cloud: токен сервисного аккаунта из сервиса метаданных |
156
+ | `'environ'` | Настройка переменными окружения YDB SDK (см. ниже) |
157
+ | `'anonymous'` | Локальный YDB без авторизации |
158
+ | `user` + `pass` | Логин и пароль пользователя YDB (не аккаунта Yandex Cloud) |
159
+ | свой провайдер | Любой `CredentialsProvider` из `@ydbjs/auth` |
160
+
161
+ Переменные для `'environ'` (берётся первая найденная):
162
+
163
+ - `YDB_ANONYMOUS_CREDENTIALS=1` — без авторизации;
164
+ - `YDB_METADATA_CREDENTIALS=1` — сервис метаданных;
165
+ - `YDB_ACCESS_TOKEN_CREDENTIALS=<токен>` — IAM-токен;
166
+ - `YDB_STATIC_CREDENTIALS_USER` / `YDB_STATIC_CREDENTIALS_PASSWORD` — логин и пароль YDB.
167
+
168
+ Не храните токены и пароли в коде, `serverless.yml` и `package.json`: передавайте их переменными окружения или через
169
+ Yandex Lockbox.
170
+
171
+ ## Таблицы
172
+
173
+ ### Встроенные
174
+
175
+ Создавать вручную не нужно: после подключения umbot вызывает `ensureSchema`, и адаптер создаёт недостающее.
176
+
177
+ | Таблица | Первичный ключ | Индекс |
178
+ | ------------- | -------------------- | ----------------------------------------- |
179
+ | `UsersData` | `(userId, platform)` | — |
180
+ | `ImageTokens` | `imageToken` | `idx_platform_path` по `(platform, path)` |
181
+ | `SoundTokens` | `soundToken` | `idx_platform_path` по `(platform, path)` |
182
+
183
+ Все колонки — `Utf8`, колонки ключа — `NOT NULL`. Индексы синхронные (`GLOBAL SYNC`): токен, сохранённый через
184
+ `UPSERT`, сразу находится поиском по пути. YDB использует вторичный индекс только при явном `VIEW`, поэтому адаптер
185
+ сам добавляет `VIEW idx_platform_path` в поиск токена по `(platform, path)`.
186
+
187
+ Если таблица уже есть, адаптер добавляет недостающие колонки и индексы, но не меняет и не удаляет существующие.
188
+
189
+ ### Свои таблицы
190
+
191
+ В отличие от MongoDB, YDB не создаёт таблицу при первой записи: у таблицы должны быть первичный ключ и типы колонок.
192
+ Опишите таблицы своих моделей в опции `tables` — адаптер создаст их вместе со встроенными:
193
+
194
+ ```ts
195
+ new YdbAdapter({
196
+ options: {
197
+ credentials: 'metadata',
198
+ tables: [
199
+ {
200
+ tableName: 'Orders',
201
+ primaryKey: ['orderId'],
202
+ columns: { orderId: 'Utf8', userId: 'Utf8', total: 'Double', paid: 'Bool' },
203
+ indexes: [['userId']], // поиск по userId пойдёт через VIEW idx_userId
204
+ },
205
+ ],
206
+ },
207
+ });
208
+ ```
209
+
210
+ Типы колонок: `Utf8`, `Int64`, `Double`, `Bool`. Значения приводятся к типу колонки: число `42` в колонке `Utf8`
211
+ запишется строкой `'42'`.
212
+
213
+ Для таблиц, не описанных в `tables` (созданных миграцией или вручную), тип значения берётся из правил модели
214
+ (`rules()`), а если правила нет — из самого значения:
215
+
216
+ | Правило модели | Без правила | Колонка YDB |
217
+ | ---------------- | --------------------- | ----------- |
218
+ | `string`, `text` | строка | `Utf8` |
219
+ | `int`, `integer` | целое число, `bigint` | `Int64` |
220
+ | — | дробное число | `Double` |
221
+ | `bool` | `boolean` | `Bool` |
222
+
223
+ Строки обрезаются по `max` из правил модели — так же, как во встроенных адаптерах umbot.
224
+
225
+ ### Без прав на создание таблиц
226
+
227
+ Если у сервисного аккаунта нет прав на DDL, создайте таблицы заранее (например, миграцией) и выключите проверку:
228
+ `options: { ensureSchema: false }`. Описания из `tables` при этом всё равно используются для типов и индексов.
229
+
230
+ ## Условия
231
+
232
+ | Оператор | Пример | YQL |
233
+ | ---------------------------- | ------------------------------------------- | ------------------- |
234
+ | равенство | `{ platform: 'vk' }` | `platform = $p0` |
235
+ | `null` | `{ meta: null }` | `meta IS NULL` |
236
+ | `$gt`, `$gte`, `$lt`, `$lte` | `{ age: { $gte: 18 } }` | `age >= $p0` |
237
+ | `$ne` | `{ status: { $ne: 'banned' } }` | `status != $p0` |
238
+ | `$in`, `$nin` | `{ platform: { $in: ['vk', 'telegram'] } }` | `platform IN $p0` |
239
+ | `$like` | `{ name: { $like: 'Ив%' } }` | `name LIKE $p0` |
240
+ | `$null` | `{ deletedAt: { $null: true } }` | `deletedAt IS NULL` |
241
+
242
+ `$ne` следует правилам SQL: записи, где поле `NULL`, под условие не попадают. Неизвестный оператор не превращается в
243
+ равенство — запрос отклоняется с ошибкой.
244
+
245
+ ## Произвольный запрос
246
+
247
+ `query()` даёт клиент запросов SDK и драйвер. Значения в шаблонной строке `sql` тоже передаются параметрами:
248
+
249
+ ```ts
250
+ const stats = await new UsersData(appContext).query(async (sql) => {
251
+ const [[row]] = await sql`SELECT COUNT(*) AS cnt FROM UsersData WHERE platform = ${'vk'}`;
252
+ return { status: true, data: row };
253
+ });
254
+ ```
255
+
256
+ ## Поведение и ограничения
257
+
258
+ - **Вставка — `UPSERT`.** Повторная запись с тем же ключом перезаписывает строку, поэтому SDK может безопасно
259
+ повторить запрос после временной ошибки сети.
260
+ - **Запись — в Serializable-транзакции**: так YDB требует для таблиц со вторичными индексами.
261
+ - **`UPDATE` не меняет колонки ключа**: YDB этого не позволяет, а модель umbot передаёт `platform` и в данных, и в
262
+ условии.
263
+ - **`UPDATE` и `DELETE` по полям вне ключа** читают таблицу целиком: YDB не использует вторичный индекс в этих
264
+ запросах. Встроенные модели umbot меняют и удаляют записи только по ключу.
265
+ - **`Int64` из ответа** приходит числом; значения больше `Number.MAX_SAFE_INTEGER` — строкой.
266
+ - **Таймауты.** Если база не ответила за `queryTimeout`, `select` вернёт `{ status: false, error }`, и umbot не
267
+ перезапишет `userData` этого запроса.
268
+ - **Типы колонок** ограничены `Utf8`, `Int64`, `Double`, `Bool`. Даты, JSON и другие типы храните строкой или
269
+ работайте с ними через `query()`.
270
+
271
+ ## Проверка на настоящей базе
272
+
273
+ Юнит-тесты работают с моками SDK. Проверить адаптер на своей базе:
274
+
275
+ ```bash
276
+ YDB_CONNECTION_STRING="grpcs://ydb.serverless.yandexcloud.net:2135/?database=/ru-central1/..." \
277
+ YDB_ACCESS_TOKEN_CREDENTIALS="$(yc iam create-token)" \
278
+ npm run live
279
+ ```
280
+
281
+ Скрипт создаёт встроенные таблицы, если их нет, пишет, читает и удаляет тестовые записи с платформой
282
+ `umbot-ydb-live-check`. Другие данные не трогает.
283
+
284
+ ## Ссылки
285
+
286
+ - [История изменений](CHANGELOG.md)
287
+ - [Документация umbot](https://www.maxim-m.ru/docs/umbot/)
288
+ - [Адаптеры БД в umbot](https://www.maxim-m.ru/docs/umbot/v-3.1/guides/adapter/dbAdapter)
289
+ - [Документация YDB](https://ydb.tech/docs/ru/)
290
+
291
+ ## Лицензия
292
+
293
+ [MIT](LICENSE.md)
@@ -0,0 +1,199 @@
1
+ import { BaseDbAdapter } from 'umbot/plugins';
2
+ import type { IDatabaseInfo, IDbTableSchema, IModelRes, IQuery, IQueryData, TQueryCb } from 'umbot';
3
+ import { Driver } from '@ydbjs/core';
4
+ import type { QueryClient } from '@ydbjs/query';
5
+ import type { CredentialsProvider } from '@ydbjs/auth';
6
+ import type { IYdbTableDefinition } from './yql';
7
+ /**
8
+ * Способ авторизации в YDB.
9
+ * - `environ` — по переменным окружения YDB SDK (`YDB_METADATA_CREDENTIALS=1`,
10
+ * `YDB_ACCESS_TOKEN_CREDENTIALS`, `YDB_STATIC_CREDENTIALS_USER`/`_PASSWORD`, `YDB_ANONYMOUS_CREDENTIALS=1`);
11
+ * - `metadata` — сервисный аккаунт из сервиса метаданных (Cloud Functions, виртуальные машины Yandex Cloud);
12
+ * - `anonymous` — без авторизации (локальный YDB в Docker);
13
+ * - свой провайдер из `@ydbjs/auth`.
14
+ */
15
+ export type TYdbCredentials = 'environ' | 'metadata' | 'anonymous' | CredentialsProvider;
16
+ /**
17
+ * Опции адаптера, передаются в `IAppDB.options`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * new YdbAdapter({
22
+ * host: 'grpcs://ydb.serverless.yandexcloud.net:2135',
23
+ * database: '/ru-central1/b1g.../etn...',
24
+ * options: { credentials: 'metadata', queryTimeout: 3000 },
25
+ * });
26
+ * ```
27
+ */
28
+ export interface IYdbOptions {
29
+ /**
30
+ * Полная строка подключения (`grpcs://host:2135/?database=/ru-central1/...`).
31
+ * Если задана, `host` и `database` не используются.
32
+ */
33
+ connectionString?: string;
34
+ /**
35
+ * Способ авторизации. По умолчанию: `user`/`pass` из конфигурации — логин и пароль YDB,
36
+ * иначе `environ`.
37
+ */
38
+ credentials?: TYdbCredentials;
39
+ /** Сколько ждать подключения (discovery и получение токена), мс. По умолчанию 10000 */
40
+ connectTimeout?: number;
41
+ /** Ограничение на один запрос вместе с повторами SDK, мс. По умолчанию 5000 */
42
+ queryTimeout?: number;
43
+ /**
44
+ * Создавать недостающие таблицы, колонки и индексы после подключения. По умолчанию true.
45
+ * Выключите, если схему создаёт миграция, а у сервисного аккаунта нет прав на DDL.
46
+ */
47
+ ensureSchema?: boolean;
48
+ /** Максимум одновременных сессий YDB. По умолчанию — значение SDK (50) */
49
+ poolSize?: number;
50
+ /**
51
+ * Таблицы ваших моделей. Адаптер создаёт их вместе со встроенными (`ensureSchema`),
52
+ * приводит значения к типам колонок и ищет по их индексам через `VIEW`.
53
+ */
54
+ tables?: readonly IYdbTableDefinition[];
55
+ }
56
+ /**
57
+ * Параметры конструктора адаптера. Все поля необязательны: без них строка подключения
58
+ * берётся из `appConfig.db` или из переменной окружения `YDB_CONNECTION_STRING`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const config: IYdbAdapterConfig = { options: { credentials: 'metadata' } };
63
+ * ```
64
+ */
65
+ export interface IYdbAdapterConfig {
66
+ /** Эндпоинт: `grpcs://ydb.serverless.yandexcloud.net:2135` */
67
+ host?: string;
68
+ /** Путь базы: `/ru-central1/b1g.../etn...` */
69
+ database?: string;
70
+ /** Логин пользователя YDB (не Yandex Cloud) */
71
+ user?: string;
72
+ /** Пароль пользователя YDB */
73
+ pass?: string;
74
+ /** Опции адаптера */
75
+ options?: IYdbOptions;
76
+ }
77
+ /**
78
+ * Информация о подключении, которую адаптер хранит в `appContext.database.databaseInfo`.
79
+ */
80
+ export interface IYdbDbInfo extends IDatabaseInfo {
81
+ /** Драйвер YDB */
82
+ driver?: Driver;
83
+ /** Клиент запросов: `` sql`SELECT 1` `` */
84
+ sql?: QueryClient;
85
+ }
86
+ /**
87
+ * Адаптер umbot для YDB (Yandex Database).
88
+ *
89
+ * Хранит `UsersData`, `ImageTokens`, `SoundTokens` и данные своих моделей. Таблицы
90
+ * встроенных моделей создаёт сам (`ensureSchema`). Все значения уходят в запрос
91
+ * параметрами (`$p0`, `$p1`, ...): конкатенации пользовательских данных в YQL нет.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { Bot } from 'umbot';
96
+ * import { YdbAdapter } from 'umbot-ydb-adapter';
97
+ *
98
+ * // Строка подключения — из YDB_CONNECTION_STRING, авторизация — из окружения
99
+ * const bot = new Bot().use(new YdbAdapter());
100
+ * ```
101
+ */
102
+ export declare class YdbAdapter extends BaseDbAdapter<IYdbDbInfo> {
103
+ #private;
104
+ dbFormat: string;
105
+ /**
106
+ * @param config Параметры подключения (см. {@link IYdbAdapterConfig}). Без них строка подключения
107
+ * берётся из `appConfig.db` или из `YDB_CONNECTION_STRING`.
108
+ */
109
+ constructor(config?: IYdbAdapterConfig);
110
+ /**
111
+ * Подключается к YDB: создаёт драйвер и ждёт discovery не дольше `connectTimeout`.
112
+ * Повторный вызов закрывает предыдущее подключение.
113
+ * @returns true при успешном подключении
114
+ */
115
+ connect(): Promise<boolean>;
116
+ /**
117
+ * Создаёт недостающие таблицы, колонки и индексы встроенных моделей и таблиц из опции `tables`.
118
+ *
119
+ * Сначала выполняется один запрос-проверка (`SELECT ... LIMIT 0` по всем таблицам и индексам).
120
+ * Если схема уже готова — на этом всё: в Cloud Functions метод вызывается на каждом холодном
121
+ * старте, и DDL на каждый старт стоил бы заметного времени.
122
+ * @param tables Описание таблиц (`DB_TABLES_SCHEMA`)
123
+ * @returns true, если схема готова
124
+ */
125
+ ensureSchema(tables: readonly IDbTableSchema[]): Promise<boolean>;
126
+ /**
127
+ * Выполняет SELECT.
128
+ *
129
+ * Контракт umbot: при `isOne` в `data` — сама запись (не массив); пустая выборка —
130
+ * `{ status: false }` без `error`; сбой — `{ status: false, error }`.
131
+ * @param selectData Информация о таблице
132
+ * @param where Условия выборки
133
+ * @param isOne Вернуть только одну запись
134
+ * @returns Результат выборки
135
+ */
136
+ _select(selectData: IQuery, where: IQueryData | null, isOne: boolean): Promise<IModelRes>;
137
+ /**
138
+ * Добавляет запись (UPSERT): повторная запись с тем же ключом перезаписывает строку,
139
+ * поэтому SDK может безопасно повторить запрос после временной ошибки сети.
140
+ * @param insertData Запрос с данными в `data`
141
+ * @returns true при успехе
142
+ */
143
+ _insert(insertData: IQuery): Promise<boolean>;
144
+ /**
145
+ * Обновляет записи по условию `query`. Без условий запрос не выполняется.
146
+ * @param updateData Запрос: условия в `query`, новые значения в `data`
147
+ * @returns true при успехе (в том числе если под условие не попало ни одной записи)
148
+ */
149
+ _update(updateData: IQuery): Promise<boolean>;
150
+ /**
151
+ * Удаляет записи по условию `query`. Без условий запрос не выполняется.
152
+ * @param removeData Запрос: условия в `query`
153
+ * @returns true при успехе
154
+ */
155
+ _remove(removeData: IQuery): Promise<boolean>;
156
+ /**
157
+ * Выполняет произвольный запрос: callback получает клиент запросов и драйвер.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * const count = await usersData.query(async (sql) => {
162
+ * const [[row]] = await sql`SELECT COUNT(*) AS cnt FROM UsersData`;
163
+ * return { status: true, data: row };
164
+ * });
165
+ * ```
166
+ * @param callback Функция с запросом
167
+ * @returns `data` из результата callback или null при ошибке
168
+ */
169
+ _query(callback: TQueryCb<QueryClient, Driver>): Promise<unknown>;
170
+ /**
171
+ * Выполняет произвольный запрос (см. `_query`).
172
+ * @param callback Функция с запросом
173
+ * @returns `data` из результата callback или null при ошибке
174
+ */
175
+ query(callback: TQueryCb<QueryClient, Driver>): Promise<unknown>;
176
+ /**
177
+ * Экранирует строку для своего запроса: результат можно поставить внутрь `'...'` в YQL.
178
+ * Адаптер этим методом не пользуется — значения уходят параметрами; для своих запросов
179
+ * тоже лучше параметры (`` sql`... ${value}` `` в `query()`).
180
+ * @param str Значение
181
+ * @returns Экранированная строка
182
+ */
183
+ escapeString(str: string | number): string;
184
+ /**
185
+ * Проверяет соединение запросом `SELECT 1`.
186
+ * @returns true, если база отвечает
187
+ */
188
+ isConnected(): Promise<boolean>;
189
+ /**
190
+ * Закрывает драйвер и очищает `databaseInfo`. Безопасен к повторному вызову.
191
+ */
192
+ destroy(): Promise<void>;
193
+ /**
194
+ * Пишет ошибку в лог umbot (секреты маскирует логгер фреймворка).
195
+ * @param message Текст ошибки
196
+ * @param error Исключение
197
+ */
198
+ protected _saveLog(message: string, error?: unknown): void;
199
+ }