letopis 1.4.2 → 2.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.
Files changed (112) hide show
  1. package/AGENT-CHEATSHEET.md +329 -281
  2. package/CHANGELOG.md +418 -0
  3. package/README.md +572 -1053
  4. package/dist/acl.d.ts +78 -1
  5. package/dist/acl.js +264 -13
  6. package/dist/admin.d.ts +10 -141
  7. package/dist/admin.js +24 -184
  8. package/dist/auth.d.ts +2 -2
  9. package/dist/auth.js +5 -15
  10. package/dist/cache.d.ts +8 -1
  11. package/dist/cache.js +30 -8
  12. package/dist/chain.d.ts +18 -41
  13. package/dist/chain.js +165 -203
  14. package/dist/cli.js +45 -90
  15. package/dist/demo/booking.d.ts +352 -36
  16. package/dist/describe.d.ts +47 -0
  17. package/dist/describe.js +195 -0
  18. package/dist/errors.d.ts +59 -13
  19. package/dist/errors.js +190 -29
  20. package/dist/index.d.ts +52 -70
  21. package/dist/index.js +189 -122
  22. package/dist/internal.d.ts +5 -0
  23. package/dist/jsonschema.js +18 -17
  24. package/dist/load.d.ts +20 -12
  25. package/dist/load.js +207 -181
  26. package/dist/mcp.d.ts +5 -7
  27. package/dist/mcp.js +168 -214
  28. package/dist/messages/access.d.ts +90 -0
  29. package/dist/messages/access.js +113 -0
  30. package/dist/messages/api.d.ts +171 -0
  31. package/dist/messages/api.js +215 -0
  32. package/dist/messages/boot.d.ts +201 -0
  33. package/dist/messages/boot.js +256 -0
  34. package/dist/messages/codes.d.ts +172 -0
  35. package/dist/messages/codes.js +173 -0
  36. package/dist/messages/index.d.ts +95 -0
  37. package/dist/messages/index.js +118 -0
  38. package/dist/messages/mcp.d.ts +306 -0
  39. package/dist/messages/mcp.js +387 -0
  40. package/dist/messages/model.d.ts +233 -0
  41. package/dist/messages/model.js +283 -0
  42. package/dist/messages/query.d.ts +131 -0
  43. package/dist/messages/query.js +158 -0
  44. package/dist/messages/sql-access.d.ts +70 -0
  45. package/dist/messages/sql-access.js +87 -0
  46. package/dist/messages/sql-core.d.ts +150 -0
  47. package/dist/messages/sql-core.js +181 -0
  48. package/dist/messages/sql-time.d.ts +71 -0
  49. package/dist/messages/sql-time.js +88 -0
  50. package/dist/messages/sql-write.d.ts +246 -0
  51. package/dist/messages/sql-write.js +270 -0
  52. package/dist/messages/time.d.ts +23 -0
  53. package/dist/messages/time.js +29 -0
  54. package/dist/messages/types.d.ts +27 -0
  55. package/dist/messages/types.js +5 -0
  56. package/dist/messages/write.d.ts +405 -0
  57. package/dist/messages/write.js +509 -0
  58. package/dist/model.d.ts +73 -60
  59. package/dist/model.js +20 -29
  60. package/dist/ops.d.ts +35 -0
  61. package/dist/ops.js +101 -0
  62. package/dist/pglite.d.ts +0 -9
  63. package/dist/pglite.js +3 -1
  64. package/dist/registry.d.ts +3 -19
  65. package/dist/registry.js +3 -16
  66. package/dist/signals.d.ts +1 -5
  67. package/dist/signals.js +1 -5
  68. package/dist/sql.d.ts +7 -1
  69. package/dist/sql.js +101 -85
  70. package/dist/sync.d.ts +9 -3
  71. package/dist/sync.js +40 -17
  72. package/dist/tx.d.ts +11 -1
  73. package/dist/tx.js +32 -11
  74. package/dist/typed.d.ts +240 -45
  75. package/dist/types.d.ts +15 -90
  76. package/dist/types.js +12 -14
  77. package/dist/up.d.ts +53 -32
  78. package/dist/up.js +180 -74
  79. package/dist/uuid.js +5 -5
  80. package/dist/validate.d.ts +13 -1
  81. package/dist/validate.js +61 -29
  82. package/dist/watch.d.ts +5 -16
  83. package/dist/watch.js +23 -77
  84. package/dist/write.d.ts +9 -16
  85. package/dist/write.js +102 -185
  86. package/llms.txt +22 -25
  87. package/package.json +2 -9
  88. package/sql/10-core.sql +7 -50
  89. package/sql/15-errors.sql +64 -10
  90. package/sql/20-context.sql +7 -6
  91. package/sql/30-validate.sql +35 -27
  92. package/sql/40-class.sql +20 -114
  93. package/sql/50-acl.sql +8 -13
  94. package/sql/60-write.sql +220 -676
  95. package/sql/70-read.sql +11 -13
  96. package/sql/80-auth.sql +23 -27
  97. package/sql/90-time.sql +77 -944
  98. package/sql/95-seed.system.sql +7 -14
  99. package/sql/99-revision.sql +2 -1
  100. package/sql/README.md +32 -24
  101. package/sql/migrate/README.md +14 -0
  102. package/AGENT-CHEATSHEET.en.md +0 -488
  103. package/MIGRATION.md +0 -190
  104. package/README.en.md +0 -2910
  105. package/dist/import.d.ts +0 -185
  106. package/dist/import.js +0 -820
  107. package/dist/project.d.ts +0 -359
  108. package/dist/project.js +0 -1258
  109. package/sql/75-history.sql +0 -436
  110. package/sql/88-derived.sql +0 -1236
  111. package/sql/migrate/0006-changed-paths.sql +0 -3
  112. package/sql/migrate/0007-secret-account-fk.sql +0 -12
@@ -0,0 +1,90 @@
1
+ export declare const access: {
2
+ 'access.switch_not_root': {
3
+ code: "invalid_query";
4
+ ru: string;
5
+ hint: string;
6
+ };
7
+ 'access.role_not_string': {
8
+ code: "invalid_data";
9
+ ru: string;
10
+ hint: string;
11
+ };
12
+ 'access.bad_role_name': {
13
+ code: "invalid_data";
14
+ ru: string;
15
+ hint: string;
16
+ };
17
+ 'access.bad_condition_column': {
18
+ code: "invalid_data";
19
+ ru: string;
20
+ hint: string;
21
+ };
22
+ 'access.bad_rule_target': {
23
+ code: "invalid_data";
24
+ ru: string;
25
+ hint: string;
26
+ };
27
+ 'access.bad_endpoint_mask': {
28
+ code: "invalid_data";
29
+ ru: string;
30
+ hint: string;
31
+ };
32
+ 'access.rule_spec_not_object': {
33
+ code: "invalid_data";
34
+ ru: string;
35
+ hint: string;
36
+ };
37
+ 'access.unknown_rule_field': {
38
+ code: "invalid_data";
39
+ ru: string;
40
+ hint: string;
41
+ };
42
+ 'access.bad_rule_weight': {
43
+ code: "invalid_data";
44
+ ru: string;
45
+ hint: string;
46
+ };
47
+ 'access.rule_who': {
48
+ code: "invalid_data";
49
+ ru: string;
50
+ hint: string;
51
+ };
52
+ 'access.no_rule_operations': {
53
+ code: "invalid_data";
54
+ ru: string;
55
+ hint: string;
56
+ };
57
+ 'access.no_facade': {
58
+ code: "invalid_query";
59
+ ru: string;
60
+ };
61
+ 'access.bad_account': {
62
+ code: "invalid_data";
63
+ ru: string;
64
+ hint: string;
65
+ };
66
+ 'access.no_session_tenant': {
67
+ code: "no_session";
68
+ ru: string;
69
+ };
70
+ 'access.roles_not_array': {
71
+ code: "invalid_data";
72
+ ru: string;
73
+ hint: string;
74
+ };
75
+ 'access.duplicate_roles': {
76
+ code: "invalid_data";
77
+ ru: string;
78
+ hint: string;
79
+ };
80
+ 'access.bad_scrypt_params': {
81
+ code: "invalid_data";
82
+ ru: string;
83
+ hint: string;
84
+ };
85
+ 'access.bad_totp_secret': {
86
+ code: "invalid_data";
87
+ ru: string;
88
+ hint: string;
89
+ };
90
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Подсказка неверного вызова db.acl: общая подсказка invalid_data — про issues, а у этих ошибок их нет. Параметр what в
3
+ * записях db.acl — метод (grant, deny, revoke, roles), с которого начинается текст.
4
+ */
5
+ const ACL_CALL = 'исправить аргумент по тексту ошибки; вызовы db.acl — README, «Выдать право»';
6
+ export const access = {
7
+ // auth.ts, switch(): фасад db.as() арендатора не меняет
8
+ 'access.switch_not_root': {
9
+ code: 'invalid_query',
10
+ ru: 'auth.switch() — от корня подключения: при имперсонации арендатора задаёт db.as(…, { tenant })',
11
+ hint: 'сменить арендатора корня — db.auth.switch(); фасаду db.as() арендатора задают при создании: db.as(аккаунт, { tenant })',
12
+ },
13
+ // acl.ts, roleName: роль в grant, deny, revoke или в списке db.acl.roles — не строка; type — её тип
14
+ 'access.role_not_string': {
15
+ code: 'invalid_data',
16
+ ru: 'db.acl.{what}: role — строка, а не {type}',
17
+ hint: ACL_CALL,
18
+ },
19
+ // acl.ts, roleName: имя роли пустое, с запятой или фигурной скобкой, с «!» в начале или с пробелом по краям
20
+ 'access.bad_role_name': {
21
+ code: 'invalid_data',
22
+ ru: 'db.acl.{what}: роль {role} — непустая строка без запятых и фигурных скобок, без «!» в начале и пробелов по краям',
23
+ hint: ACL_CALL,
24
+ },
25
+ // acl.ts, classItem: условие цели правила — на колонку не из списка
26
+ 'access.bad_condition_column': {
27
+ code: 'invalid_data',
28
+ ru: 'db.acl.{what}: условие на колонку {column} — условие ставят на {columns}',
29
+ hint: ACL_CALL,
30
+ },
31
+ // acl.ts, classItem: цель read, write или delete — не имя класса, не звёздочка и не объект с полем class
32
+ 'access.bad_rule_target': {
33
+ code: 'invalid_data',
34
+ ru: "db.acl.{what}: цель {target} — имя класса, '*' (все классы) или { class, …условие на строки }",
35
+ hint: ACL_CALL,
36
+ },
37
+ // acl.ts, apiItem: цель api — не маска эндпоинта
38
+ 'access.bad_endpoint_mask': {
39
+ code: 'invalid_data',
40
+ ru: 'db.acl.{what}: api {endpoint} — маска эндпоинта: сегменты через «.», {a,b} — любой из, * — хвост',
41
+ hint: ACL_CALL,
42
+ },
43
+ // acl.ts, parse: описание правила grant, deny или revoke — не объект
44
+ 'access.rule_spec_not_object': {
45
+ code: 'invalid_data',
46
+ ru: 'db.acl.{what}: нужен объект { role, read | write | delete | api }',
47
+ hint: ACL_CALL,
48
+ },
49
+ // acl.ts, parse: лишнее поле в описании правила (у revoke нет и weight)
50
+ 'access.unknown_rule_field': {
51
+ code: 'invalid_data',
52
+ ru: 'db.acl.{what}: поле {field} — допустимы {fields}',
53
+ hint: ACL_CALL,
54
+ },
55
+ // acl.ts, parse: weight — не целое от 1 до 999
56
+ 'access.bad_rule_weight': {
57
+ code: 'invalid_data',
58
+ ru: 'db.acl.{what}: weight — целое от 1 до 999: вес правила арендатора (от 1000 — административный слой System)',
59
+ hint: ACL_CALL,
60
+ },
61
+ // acl.ts, parse: кому правило — нет ни role, ни everyone, есть оба или everyone не true
62
+ 'access.rule_who': {
63
+ code: 'invalid_data',
64
+ ru: 'db.acl.{what}: кому правило — ровно одно из role (роль членства) и everyone: true (все аккаунты арендатора)',
65
+ hint: ACL_CALL,
66
+ },
67
+ // acl.ts, parse: в описании правила нет ни read, ни write, ни delete, ни api
68
+ 'access.no_rule_operations': {
69
+ code: 'invalid_data',
70
+ ru: 'db.acl.{what}: нужна хотя бы одна операция: read, write, delete или api',
71
+ hint: ACL_CALL,
72
+ },
73
+ // acl.ts, makeAcl: db.acl без фасада подключения, а grant, deny, revoke, roles и list идут его цепочками
74
+ 'access.no_facade': {
75
+ code: 'invalid_query',
76
+ ru: 'db.acl: запись правил — от фасада подключения',
77
+ },
78
+ // acl.ts, roles(): аккаунт — не id и не строка с полем id
79
+ 'access.bad_account': {
80
+ code: 'invalid_data',
81
+ ru: 'db.acl.roles: аккаунт — id или строка с полем id',
82
+ hint: ACL_CALL,
83
+ },
84
+ // acl.ts, roles() и list(): у фасада нет рабочего арендатора — подключение без сессии; what — метод
85
+ 'access.no_session_tenant': {
86
+ code: 'no_session',
87
+ ru: 'db.acl.{what}: нет арендатора сессии',
88
+ },
89
+ // acl.ts, roles(): роли — не массив и не null
90
+ 'access.roles_not_array': {
91
+ code: 'invalid_data',
92
+ ru: 'db.acl.roles: роли — массив строк или null (снять членство)',
93
+ hint: ACL_CALL,
94
+ },
95
+ // acl.ts, roles(): роль в списке повторяется
96
+ 'access.duplicate_roles': {
97
+ code: 'invalid_data',
98
+ ru: 'db.acl.roles: роли повторяются',
99
+ hint: ACL_CALL,
100
+ },
101
+ // auth.ts, verifyHash: scrypt отверг N, r, p из строки хэша — недопустимы или требуют больше 64 МБ памяти
102
+ 'access.bad_scrypt_params': {
103
+ code: 'invalid_data',
104
+ ru: 'хэш пароля: параметры scrypt N={N}, r={r}, p={p} недопустимы или требуют больше 64 МБ памяти',
105
+ hint: 'хэш с такими параметрами не проверить: задать пароль заново — auth.setPassword()',
106
+ },
107
+ // auth.ts, base32Decode: в секрете TOTP (totpCode) знак не из алфавита base32
108
+ 'access.bad_totp_secret': {
109
+ code: 'invalid_data',
110
+ ru: 'секрет TOTP — не base32',
111
+ hint: 'передать секрет из auth.enrollTotp() — строку base32 (A–Z, 2–7)',
112
+ },
113
+ };
@@ -0,0 +1,171 @@
1
+ export declare const api: {
2
+ 'api.validation_failed': {
3
+ code: "invalid_data";
4
+ ru: string;
5
+ };
6
+ 'api.unknown_step': {
7
+ code: "invalid_class";
8
+ ru: string;
9
+ };
10
+ 'api.connect_schema': {
11
+ code: "invalid_data";
12
+ ru: string;
13
+ hint: string;
14
+ };
15
+ 'api.unknown_option': {
16
+ code: "invalid_data";
17
+ ru: string;
18
+ hint: string;
19
+ };
20
+ 'api.row_check_denied': {
21
+ code: "acl_denied";
22
+ ru: string;
23
+ };
24
+ 'api.no_history': {
25
+ code: "invalid_query";
26
+ ru: string;
27
+ };
28
+ 'api.slow_query': {
29
+ code: "invalid_query";
30
+ ru: string;
31
+ };
32
+ 'api.batch_queued': {
33
+ code: "invalid_query";
34
+ ru: string;
35
+ };
36
+ 'api.mod_after_verb': {
37
+ code: "invalid_query";
38
+ ru: string;
39
+ };
40
+ 'api.after_not_paged': {
41
+ code: "invalid_query";
42
+ ru: string;
43
+ hint: string;
44
+ };
45
+ 'api.for_update_outside_tx': {
46
+ code: "tx_required";
47
+ ru: string;
48
+ };
49
+ 'api.for_update_unsupported': {
50
+ code: "lock_unsupported";
51
+ ru: string;
52
+ };
53
+ 'api.lock_denied': {
54
+ code: "acl_denied";
55
+ ru: string;
56
+ };
57
+ 'api.slot_value': {
58
+ code: "invalid_query";
59
+ ru: string;
60
+ };
61
+ 'api.slot_without_write': {
62
+ code: "invalid_query";
63
+ ru: string;
64
+ };
65
+ 'api.slot_single': {
66
+ code: "invalid_query";
67
+ ru: string;
68
+ };
69
+ 'api.slot_required': {
70
+ code: "invalid_query";
71
+ ru: string;
72
+ };
73
+ 'api.slot_twice': {
74
+ code: "invalid_query";
75
+ ru: string;
76
+ };
77
+ 'api.step_mod_after_verb': {
78
+ code: "invalid_query";
79
+ ru: string;
80
+ };
81
+ 'api.verb_without_step': {
82
+ code: "invalid_query";
83
+ ru: string;
84
+ };
85
+ 'api.entity_with_verbs': {
86
+ code: "invalid_query";
87
+ ru: string;
88
+ };
89
+ 'api.entity_arg': {
90
+ code: "invalid_query";
91
+ ru: string;
92
+ };
93
+ 'api.slot_method': {
94
+ code: "invalid_query";
95
+ ru: string;
96
+ };
97
+ 'api.cache_ttl': {
98
+ code: "invalid_query";
99
+ ru: string;
100
+ };
101
+ 'api.tx_only': {
102
+ code: "tx_required";
103
+ ru: string;
104
+ };
105
+ 'api.id_of_no_tenant': {
106
+ code: "no_session";
107
+ ru: string;
108
+ };
109
+ 'api.as_in_tx': {
110
+ code: "invalid_query";
111
+ ru: string;
112
+ };
113
+ 'api.as_no_session': {
114
+ code: "no_session";
115
+ ru: string;
116
+ };
117
+ 'api.as_context_changing': {
118
+ code: "no_session";
119
+ ru: string;
120
+ hint: string;
121
+ };
122
+ 'api.nested_begin': {
123
+ code: "invalid_query";
124
+ ru: string;
125
+ };
126
+ 'api.load_in_tx': {
127
+ code: "invalid_query";
128
+ ru: string;
129
+ };
130
+ 'api.connect_no_dsn': {
131
+ code: "invalid_data";
132
+ ru: string;
133
+ hint: string;
134
+ };
135
+ 'api.connect_admin_type': {
136
+ code: "invalid_data";
137
+ ru: string;
138
+ hint: string;
139
+ };
140
+ 'api.connect_admin_role': {
141
+ code: "admin_required";
142
+ ru: string;
143
+ hint: string;
144
+ };
145
+ 'api.connect_no_owner': {
146
+ code: "invalid_data";
147
+ ru: string;
148
+ hint: string;
149
+ };
150
+ 'api.connect_bypass_rls': {
151
+ code: "bypass_rls";
152
+ ru: string;
153
+ };
154
+ 'api.admin_connection': {
155
+ code: "bypass_rls";
156
+ ru: string;
157
+ };
158
+ 'api.connect_no_schema': {
159
+ code: "invalid_data";
160
+ ru: string;
161
+ hint: string;
162
+ };
163
+ 'api.schema_older': {
164
+ code: "invalid_data";
165
+ ru: string;
166
+ };
167
+ 'api.schema_newer': {
168
+ code: "invalid_data";
169
+ ru: string;
170
+ };
171
+ };
@@ -0,0 +1,215 @@
1
+ export const api = {
2
+ // errors.ts, ValidationError: данные не проходят проверку на TypeScript (из базы — sql-write.invalid_data)
3
+ 'api.validation_failed': {
4
+ code: 'invalid_data',
5
+ ru: 'данные не проходят описание класса {cls}',
6
+ },
7
+ // chain.ts: шаг цепочки — не класс, не алиас и не роль реестра сессии; похожие имена — params.similar
8
+ 'api.unknown_step': {
9
+ code: 'invalid_class',
10
+ ru: 'класс или роль {name} не найдены',
11
+ },
12
+ // index.ts: connect({ schema })
13
+ 'api.connect_schema': {
14
+ code: 'invalid_data',
15
+ ru: 'connect: схема {schema} — нужно v<N>.<имя>',
16
+ hint: 'имя схемы — v<N>.<имя>, как у up({ schema }): например, v2.salon',
17
+ },
18
+ // errors.ts, checkOptions: неизвестная опция connect(), up(), load(), watch(), createMcpServer() — в 1.5
19
+ // было предупреждение, с 2.0 — ошибка (план 2.0, этап 4, п. 2; 7.10 №4)
20
+ 'api.unknown_option': {
21
+ code: 'invalid_data',
22
+ ru: 'неизвестная опция {where}({ {name} })',
23
+ hint: 'проверить имя опции: похожие названы в тексте, все опции — в README, «Справочник API»',
24
+ },
25
+ // errors.ts, fromDbError: новые значения строки не проходят условие ACL (политика RLS WITH CHECK)
26
+ 'api.row_check_denied': {
27
+ code: 'acl_denied',
28
+ ru: 'новые значения строки запрещают правила доступа',
29
+ },
30
+ // chain.ts, warnNoHistory: versions(), asOf() у класса без журнала — предупреждение (lwarn), один раз
31
+ // на класс и метод; код записи не печатается
32
+ 'api.no_history': {
33
+ code: 'invalid_query',
34
+ ru: '.{method}() у класса {cls} без журнала (history: false) — истории нет',
35
+ },
36
+ // chain.ts, report: медленный запрос без хука onQuery — предупреждение (lwarn); ms — целое, classes — классы шагов через →
37
+ 'api.slow_query': {
38
+ code: 'invalid_query',
39
+ ru: 'медленный запрос {ms} мс — {mode} {classes}',
40
+ },
41
+ // chain.ts: терминал у цепочки батча с глаголами
42
+ 'api.batch_queued': {
43
+ code: 'invalid_query',
44
+ ru: 'план стоит в очереди батча — его исполнит batch.run()',
45
+ },
46
+ // chain.ts, afterVerbError: модификатор выборки сразу после глагола перед rows, first, ids, count, paths (и в batch.run())
47
+ 'api.mod_after_verb': {
48
+ code: 'invalid_query',
49
+ ru: '{name}() после {verb}() ни на что не действует: глагол проходит по всем целям шага, а ответ — его строки. Цели сужают limit(), offset() и sort() до глагола: db.Класс(…).limit(n).{verb}(…)',
50
+ },
51
+ // chain.ts, afterNotPaged: after() перед count() или агрегатом чтения (sum, avg, min, max, countBy) — с 2.0 отказ, в 1.x
52
+ // курсор там молча не учитывался (план 2.0, этап 4, п. 2; 7.10 №7); fn — имя терминала
53
+ 'api.after_not_paged': {
54
+ code: 'invalid_query',
55
+ ru: '{fn}() не листается: after() сдвигает начало страницы rows(), ids(), versions() и paths(), а {fn}() считает по всем объектам шага',
56
+ hint: 'убрать after() перед счётом и агрегатом; сузить то, что считается, — фильтром шага',
57
+ },
58
+ // chain.ts, lockedRead: forUpdate() вне db.begin()
59
+ 'api.for_update_outside_tx': {
60
+ code: 'tx_required',
61
+ ru: 'forUpdate() — только внутри db.begin(): вне явной транзакции блокировка снялась бы сразу',
62
+ },
63
+ // chain.ts, lockedRead: forUpdate() с другим терминалом, агрегатом, срезом, глаголом
64
+ 'api.for_update_unsupported': {
65
+ code: 'lock_unsupported',
66
+ ru: 'forUpdate() — с терминалами rows, first, ids, без агрегатов, asOf, versions, withDeleted, deep и глаголов',
67
+ },
68
+ // chain.ts, lockedRead: строки видны, но политика правки не даёт их заблокировать; ids — через запятую (detail.ids — списком)
69
+ 'api.lock_denied': {
70
+ code: 'acl_denied',
71
+ ru: 'строки видны, но заблокировать их нельзя: {ids}',
72
+ },
73
+ // chain.ts, slotValue: значение .Роль.set(…), .add(…), .remove(…)
74
+ 'api.slot_value': {
75
+ code: 'invalid_query',
76
+ ru: 'значение слота — id, список id, строка ответа или цепочка',
77
+ },
78
+ // chain.ts: слот не сразу после create(), update() или upsert(); name — роль, как вызвана, op — set, unset, add, remove
79
+ 'api.slot_without_write': {
80
+ code: 'invalid_query',
81
+ ru: 'слот .{name}.{op}() — после create(), update() или upsert(): db.Класс(…).update(…).{name}.{op}(…)',
82
+ },
83
+ // chain.ts: add() или remove() у одиночного конца
84
+ 'api.slot_single': {
85
+ code: 'invalid_query',
86
+ ru: 'слот .{name}.{op}(): конец {role} одиночный — set() или unset()',
87
+ },
88
+ // chain.ts: unset() у обязательного конца
89
+ 'api.slot_required': {
90
+ code: 'invalid_query',
91
+ ru: 'слот .{name}.unset(): конец {role} обязателен',
92
+ },
93
+ // chain.ts: set() или unset() одного конца дважды в одной записи
94
+ 'api.slot_twice': {
95
+ code: 'invalid_query',
96
+ ru: 'слот {role} задан дважды в одной записи',
97
+ },
98
+ // chain.ts, withLast: deep(), exact(), alias(), tags(), owner() сразу после глагола; verb — глагол
99
+ 'api.step_mod_after_verb': {
100
+ code: 'invalid_query',
101
+ ru: 'модификатор шага после {verb}() — сначала шаг класса',
102
+ },
103
+ // chain.ts, withOp: глагол без шага класса
104
+ 'api.verb_without_step': {
105
+ code: 'invalid_query',
106
+ ru: '{verb}() — нужен шаг класса: db.Класс(…).{verb}(…)',
107
+ },
108
+ // chain.ts, entity(): цепочка с глаголами
109
+ 'api.entity_with_verbs': {
110
+ code: 'invalid_query',
111
+ ru: 'entity() принимает читающий паттерн — цепочку с глаголами вклеить нельзя',
112
+ },
113
+ // chain.ts, entity(): не цепочка и не строка ответа
114
+ 'api.entity_arg': {
115
+ code: 'invalid_query',
116
+ ru: 'entity() принимает ленивую цепочку или строку ответа',
117
+ },
118
+ // chain.ts: set, unset, add, remove у цепочки, а не у слота роли; method — имя метода
119
+ 'api.slot_method': {
120
+ code: 'invalid_query',
121
+ ru: '{method}() — метод слота: db.Класс(…).update(…).Роль.{method}(…)',
122
+ },
123
+ // chain.ts: cache({ ttl })
124
+ 'api.cache_ttl': {
125
+ code: 'invalid_query',
126
+ ru: 'cache({ ttl }): срок — положительное число секунд',
127
+ },
128
+ // index.ts, noTx: lock(), commit(), rollback() вне db.begin(); what — вызов
129
+ 'api.tx_only': {
130
+ code: 'tx_required',
131
+ ru: '{what} — только внутри db.begin()',
132
+ },
133
+ // index.ts: db.idOf() без арендатора сессии
134
+ 'api.id_of_no_tenant': {
135
+ code: 'no_session',
136
+ ru: 'idOf: нет арендатора сессии',
137
+ },
138
+ // index.ts: db.as() внутри db.begin()
139
+ 'api.as_in_tx': {
140
+ code: 'invalid_query',
141
+ ru: 'db.as() — от корня подключения, а не внутри db.begin()',
142
+ },
143
+ // index.ts: db.as() — сессии нет (первое чтение контекста и повторное, если контекст ушёл из арендатора)
144
+ 'api.as_no_session': {
145
+ code: 'no_session',
146
+ ru: 'db.as(): нет действующей сессии',
147
+ },
148
+ // index.ts: db.as() — контекст сессии трижды ушёл из арендатора, пока грузился реестр
149
+ 'api.as_context_changing': {
150
+ code: 'no_session',
151
+ ru: 'db.as(): контекст сессии меняется — реестр арендатора не загружен',
152
+ hint: 'повторить вызов: между запросами db.as() сессия сменила арендатора или кончилась',
153
+ },
154
+ // index.ts: db.begin() внутри db.begin()
155
+ 'api.nested_begin': {
156
+ code: 'invalid_query',
157
+ ru: 'вложенных транзакций нет: db.begin() внутри db.begin()',
158
+ },
159
+ // index.ts: db.load() внутри db.begin()
160
+ 'api.load_in_tx': {
161
+ code: 'invalid_query',
162
+ ru: 'load() — своей транзакцией, не внутри db.begin()',
163
+ },
164
+ // index.ts: connect() без dsn и sql
165
+ 'api.connect_no_dsn': {
166
+ code: 'invalid_data',
167
+ ru: 'connect: нужен dsn или sql',
168
+ hint: 'передать dsn — строку подключения — или sql — готовый пул postgres.js',
169
+ },
170
+ // index.ts: connect({ admin }) — не строка и не пул
171
+ 'api.connect_admin_type': {
172
+ code: 'invalid_data',
173
+ ru: 'connect: admin — строка подключения администратора или пул postgres.js',
174
+ hint: 'передать в admin строку подключения администратора или пул postgres.js либо убрать опцию',
175
+ },
176
+ // index.ts, adminReady: роль строки admin не может стать владельцем схемы; owner — роль-владелец
177
+ 'api.connect_admin_role': {
178
+ code: 'admin_required',
179
+ ru: 'connect({ admin }): роль администраторской строки подключения не может стать владельцем схемы — нужен суперпользователь или роль с SET на {owner}',
180
+ hint: 'для admin взять строку подключения суперпользователя или роли с правом SET на роль-владельца',
181
+ },
182
+ // index.ts: роли владельца (опция owner) в базе нет
183
+ 'api.connect_no_owner': {
184
+ code: 'invalid_data',
185
+ ru: 'connect: роли владельца {owner} в базе нет — проверьте опцию owner (по умолчанию letopis_owner)',
186
+ hint: 'указать в owner роль-владельца, с которой up() ставил схему (по умолчанию letopis_owner)',
187
+ },
188
+ // index.ts: роль подключения обходит RLS, а allowBypassRls нет
189
+ 'api.connect_bypass_rls': {
190
+ code: 'bypass_rls',
191
+ ru: 'роль подключения обходит RLS (суперпользователь, BYPASSRLS или член роли-владельца); администраторское подключение — только с опцией allowBypassRls',
192
+ },
193
+ // index.ts: подключение с allowBypassRls — предупреждение (lwarn)
194
+ 'api.admin_connection': {
195
+ code: 'bypass_rls',
196
+ ru: 'администраторское подключение — RLS не действует, данные всех арендаторов видны',
197
+ },
198
+ // index.ts: схемы нет
199
+ 'api.connect_no_schema': {
200
+ code: 'invalid_data',
201
+ ru: 'connect: схемы {schema} нет — установите её: up({ schema })',
202
+ hint: 'установить схему up({ schema }) или исправить имя в connect({ schema })',
203
+ },
204
+ // index.ts: ревизия движка схемы 2.x старше библиотеки — предупреждение (lwarn); want — DDL_REVISION. Схема 1.x — отказ
205
+ // boot.schema_1x (refuse1x в up.ts)
206
+ 'api.schema_older': {
207
+ code: 'invalid_data',
208
+ ru: "движок схемы {schema} ревизии {rev}, библиотека ждёт {want} — обновите: up({ schema: '{schema}', upgrade: true })",
209
+ },
210
+ // index.ts: ревизия движка схемы новее библиотеки — предупреждение (lwarn); known — DDL_REVISION
211
+ 'api.schema_newer': {
212
+ code: 'invalid_data',
213
+ ru: 'движок схемы {schema} ревизии {rev} новее библиотеки (она знает {known}) — обновите пакет letopis',
214
+ },
215
+ };