letopis 1.4.2 → 3.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/AGENT-CHEATSHEET.md +516 -398
- package/CHANGELOG.md +739 -0
- package/LICENSE +75 -21
- package/LICENSE-COMMERCIAL.md +49 -0
- package/README.md +1312 -1190
- package/api-contract.json +381 -0
- package/dist/acl.d.ts +81 -1
- package/dist/acl.js +281 -13
- package/dist/admin.d.ts +11 -141
- package/dist/admin.js +28 -186
- package/dist/auth.d.ts +20 -5
- package/dist/auth.js +80 -21
- package/dist/cache.d.ts +8 -1
- package/dist/cache.js +30 -8
- package/dist/chain.d.ts +46 -48
- package/dist/chain.js +556 -223
- package/dist/cli.js +73 -96
- package/dist/demo/booking.d.ts +352 -36
- package/dist/describe.d.ts +47 -0
- package/dist/describe.js +195 -0
- package/dist/errors.d.ts +77 -13
- package/dist/errors.js +231 -30
- package/dist/index.d.ts +62 -74
- package/dist/index.js +312 -155
- package/dist/internal.d.ts +7 -0
- package/dist/jsonschema.js +18 -17
- package/dist/load.d.ts +29 -17
- package/dist/load.js +235 -190
- package/dist/log.d.ts +121 -0
- package/dist/log.js +633 -0
- package/dist/mcp.d.ts +5 -7
- package/dist/mcp.js +194 -225
- package/dist/messages/access.d.ts +90 -0
- package/dist/messages/access.js +113 -0
- package/dist/messages/api.d.ts +237 -0
- package/dist/messages/api.js +304 -0
- package/dist/messages/boot.d.ts +201 -0
- package/dist/messages/boot.js +256 -0
- package/dist/messages/codes.d.ts +172 -0
- package/dist/messages/codes.js +173 -0
- package/dist/messages/index.d.ts +98 -0
- package/dist/messages/index.js +121 -0
- package/dist/messages/log.d.ts +229 -0
- package/dist/messages/log.js +102 -0
- package/dist/messages/mcp.d.ts +306 -0
- package/dist/messages/mcp.js +387 -0
- package/dist/messages/model.d.ts +256 -0
- package/dist/messages/model.js +314 -0
- package/dist/messages/query.d.ts +141 -0
- package/dist/messages/query.js +171 -0
- package/dist/messages/sql-access.d.ts +70 -0
- package/dist/messages/sql-access.js +87 -0
- package/dist/messages/sql-core.d.ts +164 -0
- package/dist/messages/sql-core.js +198 -0
- package/dist/messages/sql-time.d.ts +86 -0
- package/dist/messages/sql-time.js +111 -0
- package/dist/messages/sql-write.d.ts +246 -0
- package/dist/messages/sql-write.js +270 -0
- package/dist/messages/time.d.ts +23 -0
- package/dist/messages/time.js +29 -0
- package/dist/messages/types.d.ts +27 -0
- package/dist/messages/types.js +5 -0
- package/dist/messages/write.d.ts +452 -0
- package/dist/messages/write.js +572 -0
- package/dist/model.d.ts +77 -60
- package/dist/model.js +43 -34
- package/dist/ops.d.ts +35 -0
- package/dist/ops.js +101 -0
- package/dist/pglite.d.ts +0 -9
- package/dist/pglite.js +3 -1
- package/dist/registry.d.ts +3 -19
- package/dist/registry.js +3 -16
- package/dist/signals.d.ts +1 -5
- package/dist/signals.js +1 -5
- package/dist/sql.d.ts +9 -1
- package/dist/sql.js +175 -107
- package/dist/sync.d.ts +10 -4
- package/dist/sync.js +90 -17
- package/dist/tx.d.ts +21 -2
- package/dist/tx.js +76 -13
- package/dist/typed.d.ts +252 -53
- package/dist/types.d.ts +93 -90
- package/dist/types.js +17 -14
- package/dist/up.d.ts +56 -35
- package/dist/up.js +229 -83
- package/dist/uuid.d.ts +8 -0
- package/dist/uuid.js +14 -6
- package/dist/validate.d.ts +13 -1
- package/dist/validate.js +82 -33
- package/dist/watch.d.ts +10 -16
- package/dist/watch.js +36 -77
- package/dist/write.d.ts +87 -17
- package/dist/write.js +441 -206
- package/llms.txt +48 -31
- package/package.json +13 -14
- package/sql/10-core.sql +7 -50
- package/sql/15-errors.sql +64 -10
- package/sql/20-context.sql +18 -9
- package/sql/30-validate.sql +79 -30
- package/sql/40-class.sql +41 -115
- package/sql/50-acl.sql +8 -13
- package/sql/60-write.sql +220 -676
- package/sql/70-read.sql +11 -13
- package/sql/80-auth.sql +109 -42
- package/sql/90-time.sql +108 -947
- package/sql/95-seed.system.sql +7 -14
- package/sql/99-revision.sql +6 -1
- package/sql/README.md +32 -24
- package/sql/migrate/0009-restore-rev.tenants-name.sql +10 -0
- package/sql/migrate/0010-impersonation-no-account.sql +5 -0
- package/sql/migrate/0011-key-lock.otp-enabled.sql +7 -0
- package/sql/migrate/README.md +18 -0
- package/AGENT-CHEATSHEET.en.md +0 -488
- package/MIGRATION.md +0 -190
- package/README.en.md +0 -2910
- package/dist/import.d.ts +0 -185
- package/dist/import.js +0 -820
- package/dist/project.d.ts +0 -359
- package/dist/project.js +0 -1258
- package/sql/75-history.sql +0 -436
- package/sql/88-derived.sql +0 -1236
- package/sql/migrate/0006-changed-paths.sql +0 -3
- package/sql/migrate/0007-secret-account-fk.sql +0 -12
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/** Подсказка отказов up({ login }): что передать. */
|
|
2
|
+
const LOGIN = 'передать login: { user, password } — имя обычной роли входа (непустое, до 63 байт, не pg_…) и её пароль (непустая строка)';
|
|
3
|
+
/** Подсказка отказов user.roles в createTenant() и up({ tenant }). */
|
|
4
|
+
const ROLES = "передать user.roles — список разных имён ролей членства, например ['staff'], или не передавать: по умолчанию ['owner']";
|
|
5
|
+
/** Подсказка отказов createTenant() об удалённой строке. */
|
|
6
|
+
const GONE = 'вернуть удалённое restore({ expect }) — надгробие из withDeleted().rows() (текст называет, кто это может) или выбрать другое имя или логин: id вычисляется из них';
|
|
7
|
+
export const boot = {
|
|
8
|
+
// up.ts, substitute(): в тексте SQL (ядро, миграция, свой сид) маркер, которого установщик не знает
|
|
9
|
+
'boot.unknown_marker': {
|
|
10
|
+
code: 'invalid_data',
|
|
11
|
+
ru: 'up: неизвестный маркер {marker} в SQL',
|
|
12
|
+
hint: 'в своём сиде — только маркеры установщика (@SCHEMA@, @OWNER@, @APP@, @SYSTEM@ и другие — таблица в sql/README.md пакета)',
|
|
13
|
+
},
|
|
14
|
+
// up.ts, install(): имя схемы не вида v<N>.<имя>
|
|
15
|
+
'boot.install_schema_name': {
|
|
16
|
+
code: 'invalid_data',
|
|
17
|
+
ru: 'up: bad schema name "{schema}" (нужно v<N>.<имя>)',
|
|
18
|
+
hint: 'полное имя схемы — v<N>.<имя>: имя из строчных латинских букв, цифр и «_», не с цифры, например v2.salon',
|
|
19
|
+
},
|
|
20
|
+
// up.ts, install(): имя роли владельца или приложения (опции owner, app) не по правилу
|
|
21
|
+
'boot.role_name': {
|
|
22
|
+
code: 'invalid_data',
|
|
23
|
+
ru: 'up: bad role name "{role}"',
|
|
24
|
+
hint: 'имя роли (owner, app) — строчные латинские буквы, цифры и «_», не с цифры, до 63 знаков; по умолчанию letopis_owner и letopis_app',
|
|
25
|
+
},
|
|
26
|
+
// up.ts, install(): режим сигналов не из списка; modes — допустимые через запятую
|
|
27
|
+
'boot.signals_mode': {
|
|
28
|
+
code: 'invalid_data',
|
|
29
|
+
ru: 'up: signals — {modes}, а не {signals}',
|
|
30
|
+
hint: "передать signals: 'class' (по умолчанию у новой схемы), 'tenant', 'opaque' или 'none' — или не передавать: up() оставит режим схемы",
|
|
31
|
+
},
|
|
32
|
+
// up.ts, install(): роль владельца создал кто-то другой, а у установщика нет на неё ADMIN OPTION (план 2.0, 7.5 №3)
|
|
33
|
+
'boot.owner_no_admin': {
|
|
34
|
+
code: 'admin_required',
|
|
35
|
+
ru: 'up: роль {owner} уже есть, а у установщика нет на неё ADMIN OPTION — установку ведёт роль, создавшая {owner}, или роль с этим правом (grant {owner} to <установщик> with admin option)',
|
|
36
|
+
hint: 'запустить up() ролью, создавшей роль владельца, или суперпользователем — либо выдать установщику grant <владелец> to <установщик> with admin option',
|
|
37
|
+
},
|
|
38
|
+
// up.ts, install(): имя сида пакета (не путь к .sql) не по правилу
|
|
39
|
+
'boot.seed_name': {
|
|
40
|
+
code: 'invalid_data',
|
|
41
|
+
ru: 'up: bad seed name "{seed}"',
|
|
42
|
+
hint: "сид пакета — имя из строчных латинских букв, цифр и «-» (например, 'booking'), свой сид — путь к файлу .sql",
|
|
43
|
+
},
|
|
44
|
+
// up.ts, install(): свой ключ сервиса (serviceKey) не по формату
|
|
45
|
+
'boot.service_key_format': {
|
|
46
|
+
code: 'invalid_data',
|
|
47
|
+
ru: 'up: serviceKey — lts_ и не меньше 32 шестнадцатеричных знаков',
|
|
48
|
+
hint: 'передать serviceKey вида lts_ и 32–128 шестнадцатеричных знаков в нижнем регистре — или не передавать: up() выпустит ключ сам',
|
|
49
|
+
},
|
|
50
|
+
// up.ts, up(): имя схемы ни <имя>, ни v<N>.<имя>
|
|
51
|
+
'boot.up_schema_name': {
|
|
52
|
+
code: 'invalid_data',
|
|
53
|
+
ru: 'up: bad schema name "{schema}" (нужно <имя> или v<N>.<имя>)',
|
|
54
|
+
hint: 'имя схемы — <имя> (версия структуры подставится сама) или v<N>.<имя>; имя — строчные латинские буквы, цифры и «_», не с цифры: например, salon или v2.salon',
|
|
55
|
+
},
|
|
56
|
+
// up.ts, up(): нет ни dsn, ни sql, ни pglite
|
|
57
|
+
'boot.no_connection': {
|
|
58
|
+
code: 'invalid_data',
|
|
59
|
+
ru: 'up: нужен dsn или sql',
|
|
60
|
+
hint: 'передать dsn — строку подключения администратора, sql — готовый пул postgres.js или pglite: true для разработки без своего сервера',
|
|
61
|
+
},
|
|
62
|
+
// up.ts, up({ login }): login — не объект
|
|
63
|
+
'boot.login_not_object': {
|
|
64
|
+
code: 'invalid_data',
|
|
65
|
+
ru: 'up({ login }): нужен объект { user, password }',
|
|
66
|
+
hint: LOGIN,
|
|
67
|
+
},
|
|
68
|
+
// up.ts, up({ login }): имя роли пустое, длиннее 63 байт, с pg_ или нулевым байтом; user — значение в JSON
|
|
69
|
+
'boot.login_user': {
|
|
70
|
+
code: 'invalid_data',
|
|
71
|
+
ru: 'up({ login }): user {user} — имя роли: непустое, до 63 байт, не pg_…',
|
|
72
|
+
hint: LOGIN,
|
|
73
|
+
},
|
|
74
|
+
// up.ts, up({ login }): пароль — не строка, пустой или с нулевым байтом
|
|
75
|
+
'boot.login_password': {
|
|
76
|
+
code: 'invalid_data',
|
|
77
|
+
ru: 'up({ login }): password — непустая строка',
|
|
78
|
+
hint: LOGIN,
|
|
79
|
+
},
|
|
80
|
+
// up.ts, up({ login }): роль привилегированная, роль-владелец или сама роль приложения — её пароль не меняется
|
|
81
|
+
'boot.login_privileged': {
|
|
82
|
+
code: 'invalid_data',
|
|
83
|
+
ru: 'up({ login }): роль {user} — привилегированная (суперпользователь, CREATEROLE, CREATEDB, REPLICATION, BYPASSRLS, роль-владелец или её член) или сама роль приложения: роль приложения — обычная роль входа, привилегированную up() не трогает',
|
|
84
|
+
hint: 'выбрать для login другое имя — роль без этих атрибутов и без членства в роли владельца',
|
|
85
|
+
},
|
|
86
|
+
// up.ts, createTenant() и up({ tenant }): нет имени арендатора
|
|
87
|
+
'boot.tenant_name_required': {
|
|
88
|
+
code: 'invalid_data',
|
|
89
|
+
ru: 'createTenant: имя арендатора обязательно',
|
|
90
|
+
hint: 'передать name — имя арендатора: из него вычисляется id, поэтому одно имя — всегда один и тот же арендатор',
|
|
91
|
+
},
|
|
92
|
+
// up.ts, createTenant() и up({ tenant }): вид пароля user.kind не по правилу функций кредов; kind — значение в JSON,
|
|
93
|
+
// kinds — служебные виды через запятую
|
|
94
|
+
'boot.tenant_kind': {
|
|
95
|
+
code: 'invalid_data',
|
|
96
|
+
ru: 'createTenant: user.kind {kind} — вид пароля: заглавная латинская буква, затем заглавные буквы, цифры или «_», всего от 2 до 32 знаков; служебные виды {kinds} заняты (по умолчанию — PASSWORD)',
|
|
97
|
+
hint: "не передавать user.kind (вход по паролю — вид PASSWORD) или передать вид по правилу auth.setPassword, например 'PIN'",
|
|
98
|
+
},
|
|
99
|
+
// up.ts, createTenant() и up({ tenant }): user.roles — не список или пустой
|
|
100
|
+
'boot.tenant_roles_empty': {
|
|
101
|
+
code: 'invalid_data',
|
|
102
|
+
ru: "createTenant: user.roles — непустой список ролей, например ['staff']",
|
|
103
|
+
hint: ROLES,
|
|
104
|
+
},
|
|
105
|
+
// up.ts, createTenant() и up({ tenant }): роль в user.roles — не строка; type — null или typeof значения
|
|
106
|
+
'boot.tenant_role_type': {
|
|
107
|
+
code: 'invalid_data',
|
|
108
|
+
ru: 'createTenant: user.roles — роль — строка, а не {type}',
|
|
109
|
+
hint: ROLES,
|
|
110
|
+
},
|
|
111
|
+
// up.ts, createTenant() и up({ tenant }): роль — маска группы, пустая или с пробелами по краям; role — значение в JSON
|
|
112
|
+
'boot.tenant_role_mask': {
|
|
113
|
+
code: 'invalid_data',
|
|
114
|
+
ru: 'createTenant: user.roles — роль {role}: нужно имя роли, а не маска группы — не пустое, без пробелов по краям, запятых, фигурных скобок и «!» в начале',
|
|
115
|
+
hint: ROLES,
|
|
116
|
+
},
|
|
117
|
+
// up.ts, createTenant() и up({ tenant }): роли в user.roles повторяются
|
|
118
|
+
'boot.tenant_roles_repeat': {
|
|
119
|
+
code: 'invalid_data',
|
|
120
|
+
ru: 'createTenant: user.roles — роли повторяются',
|
|
121
|
+
hint: ROLES,
|
|
122
|
+
},
|
|
123
|
+
// up.ts, createTenant(): строки арендатора нет, а его история в журнале есть
|
|
124
|
+
'boot.tenant_deleted': {
|
|
125
|
+
code: 'invalid_data',
|
|
126
|
+
ru: 'createTenant: арендатор «{name}» ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore({ expect: await db.Account(id).withDeleted().rows() }) от имени системного администратора или выберите другое имя',
|
|
127
|
+
hint: GONE,
|
|
128
|
+
},
|
|
129
|
+
// up.ts, createTenant(): строки аккаунта пользователя нет, а его история в журнале есть
|
|
130
|
+
'boot.tenant_user_deleted': {
|
|
131
|
+
code: 'invalid_data',
|
|
132
|
+
ru: 'createTenant: аккаунт пользователя {login} ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore({ expect: await db.Account(id).withDeleted().rows() }) от имени системного администратора или выберите другой логин',
|
|
133
|
+
hint: GONE,
|
|
134
|
+
},
|
|
135
|
+
// up.ts, createTenant(): строки членства нет, а его история в журнале есть
|
|
136
|
+
'boot.tenant_member_deleted': {
|
|
137
|
+
code: 'invalid_data',
|
|
138
|
+
ru: 'createTenant: членство {login} в арендаторе «{name}» ({id}) удалено, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.member(id).restore({ expect: await db.member(id).withDeleted().rows() }) в этом арендаторе или выберите другой логин',
|
|
139
|
+
hint: GONE,
|
|
140
|
+
},
|
|
141
|
+
// up.ts, refuse1x(): схема letopis 1.x — ревизия из комментария схемы ниже первой ревизии 2.0 (FIRST_REVISION) или
|
|
142
|
+
// штампа ревизии нет (rev 0); отказ up() — до любых изменений, connect() — до входа по ключу (план 2.0, 7.10 №5).
|
|
143
|
+
// first — FIRST_REVISION
|
|
144
|
+
'boot.schema_1x': {
|
|
145
|
+
code: 'invalid_data',
|
|
146
|
+
ru: "схема {schema} ревизии {rev}, а 2.0 — с ревизии {first}: это схема letopis 1.x (или схема без штампа ревизии), 2.0 ставится только заново — up({ schema: '{schema}', fresh: true }) пересоздаст схему, данные 1.x не переносятся",
|
|
147
|
+
hint: 'схема letopis 1.x: 2.0 ставится только заново — up({ fresh: true }) пересоздаст схему, данные 1.x не переносятся',
|
|
148
|
+
},
|
|
149
|
+
// up.ts и pglite.ts: обычный Error с префиксом letopis.up (подходящего кода нет) — текст по записи (lmsg), код записи при
|
|
150
|
+
// броске не берётся
|
|
151
|
+
// up.ts, install(): сервер ниже PostgreSQL 18; version — server_version_num
|
|
152
|
+
'boot.install_pg_version': {
|
|
153
|
+
code: 'invalid_query',
|
|
154
|
+
ru: 'нужен PostgreSQL 18 или новее, а сервер — {version}',
|
|
155
|
+
},
|
|
156
|
+
// up.ts, checkServer(): сервер ниже PostgreSQL 18; major — основная версия, num — server_version_num
|
|
157
|
+
'boot.pg_version': {
|
|
158
|
+
code: 'invalid_query',
|
|
159
|
+
ru: 'нужен PostgreSQL 18 или новее, а сервер — {major} ({num})',
|
|
160
|
+
},
|
|
161
|
+
// up.ts, checkServer(): база не в UTF8
|
|
162
|
+
'boot.encoding': {
|
|
163
|
+
code: 'invalid_query',
|
|
164
|
+
ru: 'база в кодировке {encoding}, нужна UTF8 (casefold и коллация pg_unicode_fast)',
|
|
165
|
+
},
|
|
166
|
+
// up.ts, up(): база не ответила за waitTimeoutMs; host — хост строки подключения, seconds — срок в секундах
|
|
167
|
+
'boot.wait_timeout': {
|
|
168
|
+
code: 'invalid_query',
|
|
169
|
+
ru: 'база {host} не готова за {seconds} с',
|
|
170
|
+
},
|
|
171
|
+
// up.ts, up(): ревизия схемы новее пакета; rev — ревизия схемы, known — DDL_REVISION
|
|
172
|
+
'boot.schema_newer': {
|
|
173
|
+
code: 'invalid_data',
|
|
174
|
+
ru: 'схема {schema} ревизии {rev}, а библиотека знает только {known} — обновите пакет',
|
|
175
|
+
},
|
|
176
|
+
// up.ts, up(): ревизия схемы 2.x старше пакета, а upgrade нет; want — DDL_REVISION
|
|
177
|
+
'boot.schema_older': {
|
|
178
|
+
code: 'invalid_data',
|
|
179
|
+
ru: "схема {schema} ревизии {rev}, библиотека — {want}: обновите её up({ schema: '{schema}', upgrade: true })",
|
|
180
|
+
},
|
|
181
|
+
// pglite.ts, startPglite(): нет пакета PGlite; name — имя пакета
|
|
182
|
+
'boot.pglite_package': {
|
|
183
|
+
code: 'invalid_query',
|
|
184
|
+
ru: 'для pglite нужен пакет {name} — npm i -D @electric-sql/pglite @electric-sql/pglite-socket',
|
|
185
|
+
},
|
|
186
|
+
// up.ts, checkServer(): предупреждение (UpResult.warnings, lmsg) — сервер ниже 18.2; version — вида 18.1
|
|
187
|
+
'boot.pg_clog': {
|
|
188
|
+
code: 'invalid_query',
|
|
189
|
+
ru: 'PostgreSQL {version}: до 18.2 сервер мог усечь CLOG, который ещё нужен непрочитанным сигналам NOTIFY; обновите сервер до 18.2 или новее',
|
|
190
|
+
},
|
|
191
|
+
// up.ts, up(): предупреждение (UpResult.warnings) — после ANALYZE от роли владельца у таблиц всё ещё нет статистики;
|
|
192
|
+
// tables — схема.таблица через запятую
|
|
193
|
+
'boot.analyze_skipped': {
|
|
194
|
+
code: 'invalid_query',
|
|
195
|
+
ru: 'таблицы без статистики после ANALYZE от роли владельца {owner}: {tables} — проверьте, что они принадлежат ей; иначе статистику соберёт автоочистка или ANALYZE их владельца',
|
|
196
|
+
},
|
|
197
|
+
// up.ts, up(): предупреждение (UpResult.warnings) — в схеме есть хабы с именами из RESERVED_CLASS_NAMES, заведённые
|
|
198
|
+
// мимо проверки базы (план 2.0, этап 4, п. 2; 7.10 №3): up() их оставляет; hubs — «имя (арендатор)» через запятую
|
|
199
|
+
'boot.reserved_hubs': {
|
|
200
|
+
code: 'invalid_class',
|
|
201
|
+
ru: 'хабы с зарезервированными именами: {hubs} — имя занято методом цепочки или корня db, шаг с ним до класса может не дойти; с 2.0 такой хаб не создаётся, а его описание не меняется: переименуйте класс — заведите класс с другим именем и создайте в нём объекты заново',
|
|
202
|
+
},
|
|
203
|
+
// up.ts, stampDrift(): предупреждение connect() (его печатает index.ts) — схему той же ревизии обновила более новая
|
|
204
|
+
// версия; was — версия из штампа схемы, version — версия пакета
|
|
205
|
+
'boot.stamp_newer': {
|
|
206
|
+
code: 'invalid_data',
|
|
207
|
+
ru: 'схему {schema} обновила letopis {was}, библиотека старее ({version}) — обновите пакет: up() этой версией вернул бы прежние функции',
|
|
208
|
+
},
|
|
209
|
+
// up.ts, stampDrift(): функции схемы той же ревизии поставила другая версия — по записи на того, кто поставил:
|
|
210
|
+
// неизвестная версия (в штампе нет engine=: комментарий правили руками), другая сборка той же версии, другая версия
|
|
211
|
+
'boot.stamp_unknown': {
|
|
212
|
+
code: 'invalid_data',
|
|
213
|
+
ru: "функции схемы {schema} поставила неизвестная версия letopis (в штампе нет версии движка), библиотека — {version}: обновите схему — вызовите up({ schema: '{schema}' })",
|
|
214
|
+
},
|
|
215
|
+
'boot.stamp_other_build': {
|
|
216
|
+
code: 'invalid_data',
|
|
217
|
+
ru: "функции схемы {schema} поставила другая сборка letopis {was}, библиотека — {version}: обновите схему — вызовите up({ schema: '{schema}' })",
|
|
218
|
+
},
|
|
219
|
+
'boot.stamp_other_version': {
|
|
220
|
+
code: 'invalid_data',
|
|
221
|
+
ru: "функции схемы {schema} поставила letopis {was}, библиотека — {version}: обновите схему — вызовите up({ schema: '{schema}' })",
|
|
222
|
+
},
|
|
223
|
+
// up.ts: ход установки в консоли (lmsg, префикс letopis.up добавляет up()) — тексты не для ошибки, код не печатается.
|
|
224
|
+
// ensureDatabase(): база создана
|
|
225
|
+
'boot.log_database_created': {
|
|
226
|
+
code: 'invalid_query',
|
|
227
|
+
ru: 'база {name} создана',
|
|
228
|
+
},
|
|
229
|
+
// up({ fresh })
|
|
230
|
+
'boot.log_fresh': {
|
|
231
|
+
code: 'invalid_query',
|
|
232
|
+
ru: 'схема {schema} удалена и поставлена заново (fresh)',
|
|
233
|
+
},
|
|
234
|
+
// итог наката — по записи на исход; revision — ревизия после наката, from — до (у обновления)
|
|
235
|
+
'boot.log_installed': {
|
|
236
|
+
code: 'invalid_query',
|
|
237
|
+
ru: 'схема поставлена: {schema}, ревизия {revision}',
|
|
238
|
+
},
|
|
239
|
+
'boot.log_upgraded': {
|
|
240
|
+
code: 'invalid_query',
|
|
241
|
+
ru: 'схема обновлена с ревизии {from}: {schema}, ревизия {revision}',
|
|
242
|
+
},
|
|
243
|
+
'boot.log_reapplied': {
|
|
244
|
+
code: 'invalid_query',
|
|
245
|
+
ru: 'повторный накат: {schema}, ревизия {revision}',
|
|
246
|
+
},
|
|
247
|
+
// up({ login }): роль приложения заведена или получила новый пароль
|
|
248
|
+
'boot.log_login_created': {
|
|
249
|
+
code: 'invalid_query',
|
|
250
|
+
ru: 'роль приложения {user}: заведена',
|
|
251
|
+
},
|
|
252
|
+
'boot.log_login_updated': {
|
|
253
|
+
code: 'invalid_query',
|
|
254
|
+
ru: 'роль приложения {user}: пароль обновлён',
|
|
255
|
+
},
|
|
256
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
export declare const codes: {
|
|
2
|
+
'codes.no_session': {
|
|
3
|
+
code: "no_session";
|
|
4
|
+
ru: string;
|
|
5
|
+
hint: string;
|
|
6
|
+
};
|
|
7
|
+
'codes.target_not_found': {
|
|
8
|
+
code: "target_not_found";
|
|
9
|
+
ru: string;
|
|
10
|
+
hint: string;
|
|
11
|
+
};
|
|
12
|
+
'codes.invalid_data': {
|
|
13
|
+
code: "invalid_data";
|
|
14
|
+
ru: string;
|
|
15
|
+
hint: string;
|
|
16
|
+
};
|
|
17
|
+
'codes.undeclared_end': {
|
|
18
|
+
code: "undeclared_end";
|
|
19
|
+
ru: string;
|
|
20
|
+
hint: string;
|
|
21
|
+
};
|
|
22
|
+
'codes.tenant_denied': {
|
|
23
|
+
code: "tenant_denied";
|
|
24
|
+
ru: string;
|
|
25
|
+
hint: string;
|
|
26
|
+
};
|
|
27
|
+
'codes.acl_denied': {
|
|
28
|
+
code: "acl_denied";
|
|
29
|
+
ru: string;
|
|
30
|
+
hint: string;
|
|
31
|
+
};
|
|
32
|
+
'codes.id_mismatch': {
|
|
33
|
+
code: "id_mismatch";
|
|
34
|
+
ru: string;
|
|
35
|
+
hint: string;
|
|
36
|
+
};
|
|
37
|
+
'codes.id_from_db': {
|
|
38
|
+
code: "id_from_db";
|
|
39
|
+
ru: string;
|
|
40
|
+
hint: string;
|
|
41
|
+
};
|
|
42
|
+
'codes.immutable_key': {
|
|
43
|
+
code: "immutable_key";
|
|
44
|
+
ru: string;
|
|
45
|
+
hint: string;
|
|
46
|
+
};
|
|
47
|
+
'codes.invalid_class': {
|
|
48
|
+
code: "invalid_class";
|
|
49
|
+
ru: string;
|
|
50
|
+
hint: string;
|
|
51
|
+
};
|
|
52
|
+
'codes.tightening_conflict': {
|
|
53
|
+
code: "tightening_conflict";
|
|
54
|
+
ru: string;
|
|
55
|
+
hint: string;
|
|
56
|
+
};
|
|
57
|
+
'codes.isolation_level': {
|
|
58
|
+
code: "isolation_level";
|
|
59
|
+
ru: string;
|
|
60
|
+
hint: string;
|
|
61
|
+
};
|
|
62
|
+
'codes.recreated_in_statement': {
|
|
63
|
+
code: "recreated_in_statement";
|
|
64
|
+
ru: string;
|
|
65
|
+
hint: string;
|
|
66
|
+
};
|
|
67
|
+
'codes.delete_restricted': {
|
|
68
|
+
code: "delete_restricted";
|
|
69
|
+
ru: string;
|
|
70
|
+
hint: string;
|
|
71
|
+
};
|
|
72
|
+
'codes.not_deleted': {
|
|
73
|
+
code: "not_deleted";
|
|
74
|
+
ru: string;
|
|
75
|
+
hint: string;
|
|
76
|
+
};
|
|
77
|
+
'codes.exists': {
|
|
78
|
+
code: "exists";
|
|
79
|
+
ru: string;
|
|
80
|
+
hint: string;
|
|
81
|
+
};
|
|
82
|
+
'codes.duplicate': {
|
|
83
|
+
code: "duplicate";
|
|
84
|
+
ru: string;
|
|
85
|
+
hint: string;
|
|
86
|
+
};
|
|
87
|
+
'codes.deleted': {
|
|
88
|
+
code: "deleted";
|
|
89
|
+
ru: string;
|
|
90
|
+
hint: string;
|
|
91
|
+
};
|
|
92
|
+
'codes.conflict': {
|
|
93
|
+
code: "conflict";
|
|
94
|
+
ru: string;
|
|
95
|
+
hint: string;
|
|
96
|
+
};
|
|
97
|
+
'codes.rev_ambiguous': {
|
|
98
|
+
code: "rev_ambiguous";
|
|
99
|
+
ru: string;
|
|
100
|
+
hint: string;
|
|
101
|
+
};
|
|
102
|
+
'codes.tx_required': {
|
|
103
|
+
code: "tx_required";
|
|
104
|
+
ru: string;
|
|
105
|
+
hint: string;
|
|
106
|
+
};
|
|
107
|
+
'codes.lock_unsupported': {
|
|
108
|
+
code: "lock_unsupported";
|
|
109
|
+
ru: string;
|
|
110
|
+
hint: string;
|
|
111
|
+
};
|
|
112
|
+
'codes.commit_unknown': {
|
|
113
|
+
code: "commit_unknown";
|
|
114
|
+
ru: string;
|
|
115
|
+
hint: string;
|
|
116
|
+
};
|
|
117
|
+
'codes.tx_rolled_back': {
|
|
118
|
+
code: "tx_rolled_back";
|
|
119
|
+
ru: string;
|
|
120
|
+
hint: string;
|
|
121
|
+
};
|
|
122
|
+
'codes.no_key': {
|
|
123
|
+
code: "no_key";
|
|
124
|
+
ru: string;
|
|
125
|
+
hint: string;
|
|
126
|
+
};
|
|
127
|
+
'codes.no_path': {
|
|
128
|
+
code: "no_path";
|
|
129
|
+
ru: string;
|
|
130
|
+
hint: string;
|
|
131
|
+
};
|
|
132
|
+
'codes.invalid_query': {
|
|
133
|
+
code: "invalid_query";
|
|
134
|
+
ru: string;
|
|
135
|
+
hint: string;
|
|
136
|
+
};
|
|
137
|
+
'codes.admin_required': {
|
|
138
|
+
code: "admin_required";
|
|
139
|
+
ru: string;
|
|
140
|
+
hint: string;
|
|
141
|
+
};
|
|
142
|
+
'codes.not_empty': {
|
|
143
|
+
code: "not_empty";
|
|
144
|
+
ru: string;
|
|
145
|
+
hint: string;
|
|
146
|
+
};
|
|
147
|
+
'codes.class_not_loadable': {
|
|
148
|
+
code: "class_not_loadable";
|
|
149
|
+
ru: string;
|
|
150
|
+
hint: string;
|
|
151
|
+
};
|
|
152
|
+
'codes.bypass_rls': {
|
|
153
|
+
code: "bypass_rls";
|
|
154
|
+
ru: string;
|
|
155
|
+
hint: string;
|
|
156
|
+
};
|
|
157
|
+
'codes.user_session_required': {
|
|
158
|
+
code: "user_session_required";
|
|
159
|
+
ru: string;
|
|
160
|
+
hint: string;
|
|
161
|
+
};
|
|
162
|
+
'codes.confirmation_required': {
|
|
163
|
+
code: "confirmation_required";
|
|
164
|
+
ru: string;
|
|
165
|
+
hint: string;
|
|
166
|
+
};
|
|
167
|
+
'codes.internal': {
|
|
168
|
+
code: "internal";
|
|
169
|
+
ru: string;
|
|
170
|
+
hint: string;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
export const codes = {
|
|
2
|
+
'codes.no_session': {
|
|
3
|
+
code: 'no_session',
|
|
4
|
+
ru: 'нет действующей сессии',
|
|
5
|
+
hint: 'войти заново: истёкшую или отозванную сессию не вернуть',
|
|
6
|
+
},
|
|
7
|
+
'codes.target_not_found': {
|
|
8
|
+
code: 'target_not_found',
|
|
9
|
+
ru: 'цель не найдена — одинаково для несуществующей, чужой и невидимой',
|
|
10
|
+
hint: 'проверить id и права: чужой и невидимый объект неотличим от несуществующего',
|
|
11
|
+
},
|
|
12
|
+
'codes.invalid_data': {
|
|
13
|
+
code: 'invalid_data',
|
|
14
|
+
ru: 'данные не подходят к описанию класса; подробности — в `issues`',
|
|
15
|
+
hint: 'исправить данные по `issues` (путь, правило, сообщение); проверить до отправки — `compileValidator`',
|
|
16
|
+
},
|
|
17
|
+
'codes.undeclared_end': {
|
|
18
|
+
code: 'undeclared_end',
|
|
19
|
+
ru: 'у класса нет такого конца (ссылки)',
|
|
20
|
+
hint: 'объявить конец в модели класса (`ends`) или исправить имя роли',
|
|
21
|
+
},
|
|
22
|
+
'codes.tenant_denied': {
|
|
23
|
+
code: 'tenant_denied',
|
|
24
|
+
ru: 'чужой арендатор или нет членства в нём',
|
|
25
|
+
hint: 'работать в арендаторе, где у пользователя есть членство, — сменить его `auth.switch()`',
|
|
26
|
+
},
|
|
27
|
+
'codes.acl_denied': {
|
|
28
|
+
code: 'acl_denied',
|
|
29
|
+
ru: 'правила доступа запрещают это действие',
|
|
30
|
+
hint: 'выдать право `db.acl.grant` (правила — класс `rule`) или выполнить действие от пользователя, которому оно разрешено; проверить заранее — `db.acl.checkData`, `db.acl.check`',
|
|
31
|
+
},
|
|
32
|
+
'codes.id_mismatch': {
|
|
33
|
+
code: 'id_mismatch',
|
|
34
|
+
ru: 'переданный id не совпадает с id, вычисленным из ключа',
|
|
35
|
+
hint: 'не передавать `id` у класса с ключом — база вычислит его из ключа; узнать заранее — `db.idOf(класс, ключ)`',
|
|
36
|
+
},
|
|
37
|
+
'codes.id_from_db': {
|
|
38
|
+
code: 'id_from_db',
|
|
39
|
+
ru: 'у класса без ключа id выдаёт база — свой id передавать нельзя',
|
|
40
|
+
hint: 'не передавать `id` — база выдаст его сама; нужен постоянный id — дать классу ключ',
|
|
41
|
+
},
|
|
42
|
+
'codes.immutable_key': {
|
|
43
|
+
code: 'immutable_key',
|
|
44
|
+
ru: 'ключевые поля, класс, id, арендатора и владельца правкой не изменить',
|
|
45
|
+
hint: 'ключ, класс, id, арендатор и владелец строки неизменны: объект с другим ключом или классом — новый объект (`create`)',
|
|
46
|
+
},
|
|
47
|
+
'codes.invalid_class': {
|
|
48
|
+
code: 'invalid_class',
|
|
49
|
+
ru: 'описание класса отклонено, класс не найден или абстрактный, либо удаляют класс, у которого есть строки, потомки или история',
|
|
50
|
+
hint: 'проверить имя (`db.registry.has`) и описание класса (`syncModels` без `apply` покажет план); удалить класс можно только без строк, потомков и истории',
|
|
51
|
+
},
|
|
52
|
+
'codes.tightening_conflict': {
|
|
53
|
+
code: 'tightening_conflict',
|
|
54
|
+
ru: 'ужесточению модели мешают строки, которые ей не подходят',
|
|
55
|
+
hint: 'исправить или удалить строки из списка ошибки и снова вызвать `syncModels`',
|
|
56
|
+
},
|
|
57
|
+
'codes.isolation_level': {
|
|
58
|
+
code: 'isolation_level',
|
|
59
|
+
ru: 'удаление работает только на уровне `read committed`',
|
|
60
|
+
hint: 'удалять в транзакции уровня `read committed` — он стоит по умолчанию',
|
|
61
|
+
},
|
|
62
|
+
'codes.recreated_in_statement': {
|
|
63
|
+
code: 'recreated_in_statement',
|
|
64
|
+
ru: 'объект удалён и создан заново одним оператором',
|
|
65
|
+
hint: 'удалить и создать объект разными операторами',
|
|
66
|
+
},
|
|
67
|
+
'codes.delete_restricted': {
|
|
68
|
+
code: 'delete_restricted',
|
|
69
|
+
ru: 'удалить нельзя: на объект ссылаются строки с действием при удалении `restrict`',
|
|
70
|
+
hint: 'сначала удалить или перевести ссылающиеся строки — или сменить у их конца действие при удалении (`onDelete`)',
|
|
71
|
+
},
|
|
72
|
+
'codes.not_deleted': {
|
|
73
|
+
code: 'not_deleted',
|
|
74
|
+
ru: '`restore` работает только для удалённых объектов',
|
|
75
|
+
hint: 'сначала `delete({ confirm: true, expect })`, потом `restore({ expect })` с надгробиями из ответа `delete()` или `withDeleted().rows()`',
|
|
76
|
+
},
|
|
77
|
+
'codes.exists': {
|
|
78
|
+
code: 'exists',
|
|
79
|
+
ru: 'объект с таким id уже существует',
|
|
80
|
+
hint: 'при повторе операции с ключом это ответ «уже записано»; иначе — `update`, `upsert` или другой ключ',
|
|
81
|
+
},
|
|
82
|
+
'codes.duplicate': {
|
|
83
|
+
code: 'duplicate',
|
|
84
|
+
ru: 'загрузчик: id повторяется во входных данных',
|
|
85
|
+
hint: 'убрать повторы id из входных данных: первые 100 — в `detail.rows`',
|
|
86
|
+
},
|
|
87
|
+
'codes.deleted': {
|
|
88
|
+
code: 'deleted',
|
|
89
|
+
ru: 'загрузчик: объект с этим id уже жил раньше и был удалён',
|
|
90
|
+
hint: "загрузить с `existing: 'update'` — объект оживёт новой версией — или дать ему другой id",
|
|
91
|
+
},
|
|
92
|
+
'codes.conflict': {
|
|
93
|
+
code: 'conflict',
|
|
94
|
+
ru: 'то, что вы видели, уже изменилось — ничего не записано: `update` с `{ rev }` (строку изменили после чтения, `write.rev_conflict`), `delete({ confirm: true, expect })` (превью изменилось, `write.preview_changed`; в том числе цели уже удалены или не найдены — `detail.preview: []`), `restore({ expect })` (объект меняли после удаления, `write.restore_stale`)',
|
|
95
|
+
hint: 'перечитать и повторить с новыми данными: `update` — с новым `rev` (для чисел — `inc` вместо записи итога); `delete` — показать новое превью `err.detail.preview` и удалить с ним; `restore` — сообщить пользователю (вещь уже у другого) или `restore({ expect, partial: true })`',
|
|
96
|
+
},
|
|
97
|
+
'codes.rev_ambiguous': {
|
|
98
|
+
code: 'rev_ambiguous',
|
|
99
|
+
ru: '`{ rev }` указан у шага, который выбрал больше одного объекта',
|
|
100
|
+
hint: 'сузить шаг до одной строки (id или ключ) или убрать `{ rev }`',
|
|
101
|
+
},
|
|
102
|
+
'codes.tx_required': {
|
|
103
|
+
code: 'tx_required',
|
|
104
|
+
ru: '`forUpdate()`, `lock()`, `commit()` и `rollback()` работают только внутри `db.begin()`',
|
|
105
|
+
hint: 'вызывать внутри транзакции: `const tr = await db.begin()` … `await tr.commit()`',
|
|
106
|
+
},
|
|
107
|
+
'codes.lock_unsupported': {
|
|
108
|
+
code: 'lock_unsupported',
|
|
109
|
+
ru: '`forUpdate()` нельзя сочетать с агрегатом, `asOf`, `versions`, `withDeleted`, `deep` или глаголом',
|
|
110
|
+
hint: 'брать `forUpdate()` в простом чтении строк, а итоги, срезы и правки делать отдельными шагами',
|
|
111
|
+
},
|
|
112
|
+
'codes.commit_unknown': {
|
|
113
|
+
code: 'commit_unknown',
|
|
114
|
+
ru: 'связь оборвалась во время `COMMIT` — неизвестно, записалась ли транзакция',
|
|
115
|
+
hint: 'повторить ту же операцию с тем же ключом: `exists` — она уже записана, успех — в первый раз не записалась; без ключа исход не выяснить, поэтому операциям с деньгами нужен ключ',
|
|
116
|
+
},
|
|
117
|
+
'codes.tx_rolled_back': {
|
|
118
|
+
code: 'tx_rolled_back',
|
|
119
|
+
ru: 'PostgreSQL откатил транзакцию на `COMMIT`: ошибку перехватили внутри `tr.transaction(fn)`',
|
|
120
|
+
hint: 'не глушить ошибки внутри `tr.transaction(fn)`: устранить причину и повторить весь блок',
|
|
121
|
+
},
|
|
122
|
+
'codes.no_key': {
|
|
123
|
+
code: 'no_key',
|
|
124
|
+
ru: 'у класса нет ключа, а `upsert` и `idOf` без ключа не работают',
|
|
125
|
+
hint: 'дать классу ключ (`key` в модели) или работать по id: `update` вместо `upsert`',
|
|
126
|
+
},
|
|
127
|
+
'codes.no_path': {
|
|
128
|
+
code: 'no_path',
|
|
129
|
+
ru: 'между классами соседних шагов цепочки нет перехода',
|
|
130
|
+
hint: 'переходить по объявленным концам: между классами шагов нужна ссылка, роль конца — имя шага',
|
|
131
|
+
},
|
|
132
|
+
'codes.invalid_query': {
|
|
133
|
+
code: 'invalid_query',
|
|
134
|
+
ru: 'цепочка или вызов API построены неверно',
|
|
135
|
+
hint: 'исправить цепочку или вызов: текст ошибки называет неверный шаг или аргумент',
|
|
136
|
+
},
|
|
137
|
+
'codes.admin_required': {
|
|
138
|
+
code: 'admin_required',
|
|
139
|
+
ru: 'нужно подключение администратора: его требует загрузчик',
|
|
140
|
+
hint: 'запускать загрузчик на администраторском подключении (`allowBypassRls: true`) в отдельном процессе',
|
|
141
|
+
},
|
|
142
|
+
'codes.not_empty': {
|
|
143
|
+
code: 'not_empty',
|
|
144
|
+
ru: 'первая заливка возможна только в пустую схему',
|
|
145
|
+
hint: 'первую заливку делать в новую схему (`up({ fresh: true })`), в заполненную — загрузка без `initial`',
|
|
146
|
+
},
|
|
147
|
+
'codes.class_not_loadable': {
|
|
148
|
+
code: 'class_not_loadable',
|
|
149
|
+
ru: 'загрузчик не принимает строки классов (`Class`) — классы записывает `syncModels`',
|
|
150
|
+
hint: 'классы записывать `syncModels`, а загрузчику передавать только строки данных',
|
|
151
|
+
},
|
|
152
|
+
'codes.bypass_rls': {
|
|
153
|
+
code: 'bypass_rls',
|
|
154
|
+
ru: 'роль подключения обходит RLS: `connect()` без `allowBypassRls` или сервер MCP поверх такого подключения',
|
|
155
|
+
hint: 'подключать приложение отдельной ролью из группы `letopis_app`; администраторскому подключению — `allowBypassRls: true`; сервер MCP — только поверх подключения приложения',
|
|
156
|
+
},
|
|
157
|
+
'codes.user_session_required': {
|
|
158
|
+
code: 'user_session_required',
|
|
159
|
+
ru: 'серверу MCP нужна сессия пользователя, а не ключ сервиса или пустое подключение',
|
|
160
|
+
hint: 'создавать сервер MCP на подключении пользователя (`svc.as(…)` или `connect({ token })`), а не на корне сервиса',
|
|
161
|
+
},
|
|
162
|
+
'codes.confirmation_required': {
|
|
163
|
+
code: 'confirmation_required',
|
|
164
|
+
ru: 'MCP: удаление не подтвердил человек',
|
|
165
|
+
hint: 'подтвердить удаление в клиенте MCP; клиенту, который не умеет спрашивать человека, удаление недоступно',
|
|
166
|
+
},
|
|
167
|
+
// SQLSTATE XX000 с ключом каталога в DETAIL: база не держит условие, которое обязана держать сама (строка-замок)
|
|
168
|
+
'codes.internal': {
|
|
169
|
+
code: 'internal',
|
|
170
|
+
ru: 'внутренняя ошибка letopis: база не держит условие, которое обязана держать сама (например, нет строки-замка)',
|
|
171
|
+
hint: 'это ошибка letopis: сообщите о ней с текстом, кодом и версией пакета',
|
|
172
|
+
},
|
|
173
|
+
};
|