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,509 @@
1
+ export const write = {
2
+ // write.ts — write.ts:checkReason: причина изменения (reason у глаголов, db.begin(), батча)
3
+ 'write.bad_reason': {
4
+ code: 'invalid_query',
5
+ ru: '{where}: reason — строка до 1000 символов',
6
+ },
7
+ // данные create(), upsert(): inc() внутри массива
8
+ 'write.inc_in_array': {
9
+ code: 'invalid_data',
10
+ ru: 'inc() внутри массива ({path}) не поддерживается',
11
+ },
12
+ // данные: оператор фильтра внутри вложенного значения
13
+ 'write.op_in_data': {
14
+ code: 'invalid_data',
15
+ ru: 'оператор фильтра в данных ({path}) — данные записываются значениями',
16
+ },
17
+ // патч update(): величина inc(by)
18
+ 'write.inc_by_not_finite': {
19
+ code: 'invalid_data',
20
+ ru: 'inc({by}) по пути {path}: величина — конечное число',
21
+ },
22
+ // патч update(): inc(by, { start })
23
+ 'write.inc_start_not_finite': {
24
+ code: 'invalid_data',
25
+ ru: 'inc: start по пути {path} — конечное число',
26
+ },
27
+ // патч update(): оператор фильтра вместо значения
28
+ 'write.op_in_patch': {
29
+ code: 'invalid_data',
30
+ ru: 'оператор фильтра в патче ({path}) — update записывает значения',
31
+ },
32
+ // данные create(), upsert() — не объект
33
+ 'write.data_not_object': {
34
+ code: 'invalid_data',
35
+ ru: '{verb}(): данные — объект',
36
+ },
37
+ // inc() в данных глагола, кроме update()
38
+ 'write.inc_outside_update': {
39
+ code: 'invalid_data',
40
+ ru: 'inc() работает только в update(): {verb}() по пути {path}',
41
+ },
42
+ // оператор фильтра на верхнем уровне данных глагола
43
+ 'write.op_in_verb_data': {
44
+ code: 'invalid_data',
45
+ ru: 'оператор фильтра в данных {verb}() ({path}) — данные записываются значениями',
46
+ },
47
+ // глагол после asOf()
48
+ 'write.as_of_write': {
49
+ code: 'invalid_query',
50
+ ru: '{verb}(): asOf описывает прошлое — записывать в него нельзя',
51
+ },
52
+ // глагол после withDeleted(), кроме restore()
53
+ 'write.with_deleted_write': {
54
+ code: 'invalid_query',
55
+ ru: '{verb}(): withDeleted описывает удалённые — записывать в них нельзя',
56
+ },
57
+ // create() или upsert() сразу после другого глагола; prev — прежний глагол
58
+ 'write.verb_after_verb': {
59
+ code: 'invalid_query',
60
+ ru: '{verb}() сразу после {prev}() — между глаголами нужен шаг',
61
+ },
62
+ // create() или upsert() на возврате к узлу пути
63
+ 'write.create_on_pivot': {
64
+ code: 'invalid_query',
65
+ ru: '{verb}() на возврате к узлу — он ведёт к существующей сущности, используйте update()',
66
+ },
67
+ // create() или upsert() после шага с фильтром, а не с id
68
+ 'write.create_with_filter': {
69
+ code: 'invalid_query',
70
+ ru: '{verb}() не ищет: {step}(id).{verb}(…) задаёт id, поиск — это update()',
71
+ },
72
+ // upsert() у класса без ключа
73
+ 'write.upsert_no_key': {
74
+ code: 'no_key',
75
+ ru: 'upsert(): у класса {cls} нет ключа — сравнивать не с чем, используйте create()',
76
+ },
77
+ // .tags() перед create() или upsert() — не строка и не список
78
+ 'write.bad_tags': {
79
+ code: 'invalid_query',
80
+ ru: '.tags() перед {verb}() — строка или список',
81
+ },
82
+ // патч update() — не объект
83
+ 'write.patch_not_object': {
84
+ code: 'invalid_data',
85
+ ru: 'update(): патч — объект',
86
+ },
87
+ // update(patch, { rev }) — rev не номер версии
88
+ 'write.bad_rev': {
89
+ code: 'invalid_query',
90
+ ru: 'update(patch, { rev }): rev — номер версии строки',
91
+ },
92
+ // write.ts:precheckPlan (narrowed) — update() или delete({ confirm: true }), а ни у шага глагола, ни у шагов до него нет
93
+ // фильтра, id, непустого tags(), owner() или limit() и раньше в цепочке нет create() или upsert(); у update() цель
94
+ // задаёт и data.id патча (план 2.0, этап 4, п. 2; 7.10 №2); call — сам вызов, cls — класс шага глагола
95
+ 'write.no_filter': {
96
+ code: 'invalid_query',
97
+ ru: '{call} без фильтра ({cls}): ни у шага глагола, ни у шагов до него нет фильтра, id, непустого tags(), owner() или limit(), и раньше в цепочке нет create() или upsert() — правка прошла бы по всем строкам',
98
+ hint: 'сузить цели фильтром, id, tags(), owner() или limit() (цель update() задаёт и data.id патча); правка всех строк — опция глагола { all: true }',
99
+ },
100
+ // слот после глагола, который слотов не принимает
101
+ 'write.slot_bad_verb': {
102
+ code: 'invalid_query',
103
+ ru: 'слот .{role} после {verb}() — слоты у create(), update() и upsert()',
104
+ },
105
+ // write.ts:slotRole: имя слота — не роль записываемого класса и не класс реестра
106
+ 'write.slot_unknown': {
107
+ code: 'invalid_class',
108
+ ru: 'класс или роль {name} не найдены',
109
+ },
110
+ // write.ts:slotRole: класс слота не принимает ни одна роль; ends — концы через запятую или «—»
111
+ 'write.slot_not_end': {
112
+ code: 'invalid_query',
113
+ ru: '{name} — не конец класса {cls} (концы: {ends})',
114
+ },
115
+ // write.ts:slotRole: класс слота принимают несколько ролей
116
+ 'write.slot_ambiguous': {
117
+ code: 'invalid_query',
118
+ ru: 'слот {name} у класса {cls} неоднозначен: роли {roles} — назовите роль',
119
+ },
120
+ // конец из пути: шаг подходит нескольким ролям
121
+ 'write.end_ambiguous': {
122
+ code: 'invalid_query',
123
+ ru: 'конец класса {cls} для шага {step} неоднозначен: роли {roles} — назовите шаг ролью или задайте слот',
124
+ },
125
+ // конец из пути: шаг дал не одну сущность; count — сколько найдено
126
+ 'write.end_not_single': {
127
+ code: 'invalid_query',
128
+ ru: 'шаг {step} должен дать ровно одну сущность для конца {cls} (найдено {count})',
129
+ },
130
+ // слот .роль.set() одиночного конца — значение дало несколько сущностей
131
+ 'write.slot_single_many': {
132
+ code: 'invalid_query',
133
+ ru: 'слот .{role}.set(): конец одиночный, а значение дало {count} сущностей',
134
+ },
135
+ // write.ts:existsError: create() — объект уже есть, id не вычислить
136
+ 'write.exists': {
137
+ code: 'exists',
138
+ ru: 'объект класса {cls} уже существует',
139
+ },
140
+ // write.ts:existsError: create() — объект уже есть, id известен
141
+ 'write.exists_id': {
142
+ code: 'exists',
143
+ ru: 'объект класса {cls} уже существует (id {id})',
144
+ },
145
+ // write.ts:explainMissing: одна цель не видна
146
+ 'write.target_missing': {
147
+ code: 'target_not_found',
148
+ ru: 'цель {id} не найдена',
149
+ },
150
+ // write.ts:explainMissing: несколько целей не видны; ids — через запятую
151
+ 'write.targets_missing': {
152
+ code: 'target_not_found',
153
+ ru: 'цели не найдены: {ids}',
154
+ },
155
+ // write.ts:explainMissing: update(patch, { rev }) — строку успели изменить
156
+ 'write.rev_conflict': {
157
+ code: 'conflict',
158
+ ru: 'строку {id} успели изменить: версия {rev}, а не {expected} — перечитайте и повторите',
159
+ },
160
+ // write.ts:explainMissing: строки видны, но RLS не дала их изменить; ids — через запятую
161
+ 'write.rows_denied': {
162
+ code: 'acl_denied',
163
+ ru: 'строки видны, но изменить их нельзя: {ids}',
164
+ },
165
+ // update(patch, { rev }): у шага больше одной цели; count — сколько
166
+ 'write.rev_many_targets': {
167
+ code: 'rev_ambiguous',
168
+ ru: 'update(patch, { rev }): у шага {step} {count} целей — проверить версию { rev } можно только у одной цели',
169
+ },
170
+ // update(patch, { rev }): у шага нет цели
171
+ 'write.rev_no_target': {
172
+ code: 'target_not_found',
173
+ ru: 'update(patch, { rev }): у шага {step} нет цели',
174
+ },
175
+ // restore() живого объекта
176
+ 'write.not_deleted': {
177
+ code: 'not_deleted',
178
+ ru: 'объект не удалён: {verb}() — только для удалённых',
179
+ },
180
+ // tx.ts — tx.ts:commitUnknown: связь оборвалась во время commit
181
+ 'write.commit_unknown': {
182
+ code: 'commit_unknown',
183
+ ru: 'связь оборвалась во время commit: транзакция могла зафиксироваться — проверьте исход повтором той же операции с тем же ключом',
184
+ },
185
+ // tx.ts: запрос в завершённой транзакции db.begin()
186
+ 'write.tx_done': {
187
+ code: 'tx_required',
188
+ ru: 'транзакция db.begin() уже завершена',
189
+ hint: 'транзакция уже зафиксирована или откатана — откройте новую: `const tr = await db.begin()`',
190
+ },
191
+ // tx.ts: lock() в завершённой транзакции db.begin()
192
+ 'write.lock_tx_done': {
193
+ code: 'tx_required',
194
+ ru: 'lock(): транзакция db.begin() уже завершена',
195
+ hint: 'транзакция уже зафиксирована или откатана — откройте новую: `const tr = await db.begin()`',
196
+ },
197
+ // tx.ts: commit() транзакции, в которой была ошибка, — PostgreSQL её откатил
198
+ 'write.tx_rolled_back': {
199
+ code: 'tx_rolled_back',
200
+ ru: 'commit: PostgreSQL откатил транзакцию (в ней была ошибка)',
201
+ },
202
+ // load.ts — загрузчик db.load()
203
+ // Issues.fail(): загрузка отменена, первое нарушение (message — его текст); код ошибки — код этого нарушения
204
+ // (detail.rows[0].code), а не записи: место строит LetopisError с ключом, как fromDbError
205
+ 'write.load_cancelled': {
206
+ code: 'invalid_data',
207
+ ru: 'загрузка отменена: строка {line} — {message}',
208
+ },
209
+ // Issues.fail(): … и ещё rest нарушений, проверены все строки
210
+ 'write.load_cancelled_more': {
211
+ code: 'invalid_data',
212
+ ru: 'загрузка отменена: строка {line} — {message} (и ещё {rest})',
213
+ },
214
+ // Issues.fail(): … проверены не все строки (why — причины через точку с запятой), нарушений не меньше rest + 1
215
+ 'write.load_cancelled_at_least': {
216
+ code: 'invalid_data',
217
+ ru: 'загрузка отменена: строка {line} — {message} (и ещё не меньше {rest}: {why})',
218
+ },
219
+ // Issues.fail(): … нарушение одно, но проверены не все строки
220
+ 'write.load_cancelled_maybe_more': {
221
+ code: 'invalid_data',
222
+ ru: 'загрузка отменена: строка {line} — {message} (возможно, не единственная: {why})',
223
+ },
224
+ // причина в why текста отмены (laterUnchecked): порция commitEvery не последняя
225
+ 'write.load_later_portions': {
226
+ code: 'invalid_data',
227
+ ru: 'следующие порции не проверялись',
228
+ },
229
+ // причина в why (prepareBatch, { as }): условия ACL проверили пачку, а следующие пачки — нет
230
+ 'write.load_later_batches': {
231
+ code: 'invalid_data',
232
+ ru: 'условия ACL следующих пачек не проверялись',
233
+ },
234
+ // причина в why (prepareBatch, pipeline, historyWrite): нарушение нашлось раньше, чем база проверила pattern
235
+ // (его валидатор на TypeScript оставляет базе) у строк, уже прошедших проверку в коде
236
+ 'write.load_db_rules_unchecked': {
237
+ code: 'invalid_data',
238
+ ru: 'шаблоны pattern (их проверяет база) проверены не у всех строк',
239
+ },
240
+ // причина в why (checkInDb): проверка базы называет не больше limit строк
241
+ 'write.load_db_check_limit': {
242
+ code: 'invalid_data',
243
+ ru: 'проверка базы называет не больше {limit} строк',
244
+ },
245
+ // toList: вход db.load() — не объект «класс → строки», не массив и не поток
246
+ 'write.load_bad_input': {
247
+ code: 'invalid_data',
248
+ ru: 'load(): вход — объект «класс → строки», массив или поток строк с $class',
249
+ hint: 'передать объект { Класс: [строки] }, массив строк с $class или их поток (Iterable, AsyncIterable); класс строк без $class — опция class',
250
+ },
251
+ // refOf — значение конца (текст — error нарушений write.load_end_ref и write.load_key_part_ref): строка, но не uuid
252
+ 'write.load_end_not_uuid': {
253
+ code: 'invalid_data',
254
+ ru: 'значение конца не uuid',
255
+ },
256
+ // refOf: не строка и не объект с $key или $ext
257
+ 'write.load_end_value': {
258
+ code: 'invalid_data',
259
+ ru: 'значение конца — id, { $key } или { $ext }',
260
+ },
261
+ // refOf: у полиморфного конца нет $class цели
262
+ 'write.load_end_polymorphic': {
263
+ code: 'invalid_data',
264
+ ru: 'полиморфный конец: укажите $class цели',
265
+ },
266
+ // refOf: класса цели нет в реестре или он абстрактный
267
+ 'write.load_target_class': {
268
+ code: 'invalid_data',
269
+ ru: 'класс цели {name} не найден или абстрактный',
270
+ },
271
+ // refOf: $key цели — не список строк по частям ключа (key — части через запятую)
272
+ 'write.load_target_key': {
273
+ code: 'invalid_data',
274
+ ru: '$key цели {cls} — список строк ключа [{key}]',
275
+ },
276
+ // refOf: $ext у цели класса с ключом
277
+ 'write.load_ext_keyed': {
278
+ code: 'invalid_data',
279
+ ru: '$ext — для класса без ключа, а у {cls} ключ [{key}]',
280
+ },
281
+ // refOf, idOfRow: $ext не строка
282
+ 'write.load_ext_string': {
283
+ code: 'invalid_data',
284
+ ru: '$ext — строка',
285
+ },
286
+ // shape — нарушение строки: значение конца со многими целями не массив
287
+ 'write.load_end_list': {
288
+ code: 'invalid_data',
289
+ ru: 'конец {role} — список целей',
290
+ },
291
+ // shape: значение конца не годится (error — текст refOf)
292
+ 'write.load_end_ref': {
293
+ code: 'invalid_data',
294
+ ru: 'конец {role}: {error}',
295
+ },
296
+ // shape: цель повторяется в списке конца
297
+ 'write.load_end_repeat': {
298
+ code: 'invalid_data',
299
+ ru: 'конец {role}: цель повторяется',
300
+ },
301
+ // shape: inc() в данных строки (path — путь к значению)
302
+ 'write.load_inc': {
303
+ code: 'invalid_data',
304
+ ru: 'inc() — только в update() ({path})',
305
+ },
306
+ // shape: оператор фильтра в данных строки (path — путь к значению)
307
+ 'write.load_filter_op': {
308
+ code: 'invalid_data',
309
+ ru: 'оператор фильтра в данных ({path})',
310
+ },
311
+ // shape: данные не проходят валидатор класса на TypeScript; нарушения — issues строки отчёта
312
+ 'write.load_invalid_data': {
313
+ code: 'invalid_data',
314
+ ru: 'данные не проходят описание класса {cls}',
315
+ },
316
+ // shape: у конца со многими целями меньше min
317
+ 'write.load_end_too_few': {
318
+ code: 'invalid_data',
319
+ ru: 'у конца {role} слишком мало целей',
320
+ },
321
+ // shape: обязательный конец не задан
322
+ 'write.load_end_required': {
323
+ code: 'invalid_data',
324
+ ru: 'обязательный конец {role} не задан',
325
+ },
326
+ // shape: у конца со многими целями больше max
327
+ 'write.load_end_too_many': {
328
+ code: 'invalid_data',
329
+ ru: 'у конца {role} слишком много целей',
330
+ },
331
+ // idOfRow — нарушение строки: часть ключа — конец, а его значение не годится (error — текст refOf)
332
+ 'write.load_key_part_ref': {
333
+ code: 'invalid_data',
334
+ ru: 'часть ключа {part}: {error}',
335
+ },
336
+ // idOfRow: часть ключа — поле, а значение не строка
337
+ 'write.load_key_part_string': {
338
+ code: 'invalid_data',
339
+ ru: 'часть ключа {part} — строка',
340
+ },
341
+ // load(), initialSession(): подключение не может стать владельцем схемы
342
+ 'write.load_admin_required': {
343
+ code: 'admin_required',
344
+ ru: 'загрузчику нужно администраторское подключение (connect({ allowBypassRls: true }) роли, которая может стать владельцем схемы)',
345
+ },
346
+ // load({ as }): не uuid
347
+ 'write.load_as_not_uuid': {
348
+ code: 'invalid_data',
349
+ ru: 'load({ as }): id аккаунта',
350
+ hint: 'передать в as id (uuid) аккаунта, от имени которого идёт загрузка',
351
+ },
352
+ // load({ as, withHistory })
353
+ 'write.load_as_history': {
354
+ code: 'invalid_data',
355
+ ru: 'load({ as }) и режим истории несовместимы: историю импортирует администратор',
356
+ hint: 'историю (withHistory) загружать без as; от имени пользователя — загрузка без истории',
357
+ },
358
+ // load({ withHistory, existing: 'update' })
359
+ 'write.load_history_update': {
360
+ code: 'invalid_data',
361
+ ru: "режим истории: existing — 'error' или 'skip' (новую версию существующего объекта пишет загрузка без истории)",
362
+ hint: "новые версии существующих объектов — отдельной загрузкой без withHistory с existing: 'update'",
363
+ },
364
+ // load({ initial, commitEvery })
365
+ 'write.load_initial_commit_every': {
366
+ code: 'invalid_data',
367
+ ru: 'первая заливка (initial) несовместима с commitEvery: снятые индексы и TRUNCATE требуют одной транзакции',
368
+ hint: 'убрать commitEvery: первая заливка — всегда одна транзакция; порциями пишет загрузка без initial',
369
+ },
370
+ // load({ initial, existing }): existing не 'error'
371
+ 'write.load_initial_existing': {
372
+ code: 'invalid_data',
373
+ ru: "первая заливка (initial): existing — только 'error', в пустой схеме пропускать и обновлять нечего",
374
+ hint: 'убрать existing; пропустить или обновить существующие объекты — загрузкой без initial',
375
+ },
376
+ // load({ initial, as })
377
+ 'write.load_initial_as': {
378
+ code: 'invalid_data',
379
+ ru: 'первая заливка (initial) — от администратора, без { as }',
380
+ hint: 'убрать as; от имени пользователя — загрузка без initial',
381
+ },
382
+ // load(): нет ни { tenant }, ни { as }, ни арендатора сессии
383
+ 'write.load_no_tenant': {
384
+ code: 'invalid_data',
385
+ ru: 'load(): нет арендатора — сессия без арендатора и не задан { tenant }',
386
+ hint: 'передать load(…, { tenant }) или загружать в сессии с арендатором',
387
+ },
388
+ // проход 1 — нарушение строки: класса $class нет в реестре
389
+ 'write.load_class_not_found': {
390
+ code: 'invalid_class',
391
+ ru: 'класс {name} не найден',
392
+ },
393
+ // проход 1: у строки нет $class (и нет опции class)
394
+ 'write.load_no_class': {
395
+ code: 'invalid_class',
396
+ ru: 'нет $class',
397
+ },
398
+ // проход 1: строка класса Class
399
+ 'write.load_class_rows': {
400
+ code: 'class_not_loadable',
401
+ ru: 'строки Class задают letopis sync и create — там база компилирует проверку',
402
+ },
403
+ // проход 1: класс абстрактный
404
+ 'write.load_class_abstract': {
405
+ code: 'invalid_class',
406
+ ru: 'класс {cls} абстрактный',
407
+ },
408
+ // проход 1, первая заливка: id занят строкой системного класса или прошлой загрузкой сессии
409
+ 'write.load_taken': {
410
+ code: 'exists',
411
+ ru: 'объект уже есть в схеме: строка системного класса или запись этой же первой заливки',
412
+ },
413
+ // проход 1: повтор id при duplicates: 'error' (prev — строка, где id был раньше)
414
+ 'write.load_duplicate': {
415
+ code: 'duplicate',
416
+ ru: 'id {id} уже был в строке {prev}',
417
+ },
418
+ // loadPortion ({ as }) — нарушение: у пользователя нет права WRITE на класс
419
+ 'write.load_no_write': {
420
+ code: 'acl_denied',
421
+ ru: 'нет права WRITE на класс {cls}',
422
+ },
423
+ // check, loadPortion, initialSession — нарушение: цели конца нет, она чужая, не того класса или не видна ({ as })
424
+ 'write.load_target_not_found': {
425
+ code: 'target_not_found',
426
+ ru: 'цель конца {role} не найдена',
427
+ },
428
+ // loadPortion, initialSession: владелец ($owner) — не аккаунт
429
+ 'write.load_owner_not_found': {
430
+ code: 'target_not_found',
431
+ ru: 'владелец {id} не найден',
432
+ },
433
+ // loadPortion, initialSession: арендатора нет или он отключён
434
+ 'write.load_tenant_denied': {
435
+ code: 'tenant_denied',
436
+ ru: 'арендатор {tenant} не найден или отключён',
437
+ },
438
+ // explainTaken (existing: 'error') — нарушение: id занят живым объектом
439
+ 'write.load_exists': {
440
+ code: 'exists',
441
+ ru: 'объект уже есть в базе',
442
+ },
443
+ // explainTaken: id занят удалённым объектом
444
+ 'write.load_deleted': {
445
+ code: 'deleted',
446
+ ru: 'объект с этим id жил раньше и удалён (existing: update воскресит)',
447
+ },
448
+ // explainTaken: первичный ключ отказал, а занятых id запрос не нашёл
449
+ 'write.load_objects_exist': {
450
+ code: 'exists',
451
+ ru: 'загрузка отменена: часть объектов уже есть в базе',
452
+ },
453
+ // check — нарушение строки: Account не в арендаторе System
454
+ 'write.load_account_tenant': {
455
+ code: 'tenant_denied',
456
+ ru: 'аккаунты лежат в арендаторе System',
457
+ },
458
+ // check: конец Tenant строки member — не арендатор строки
459
+ 'write.load_member_tenant': {
460
+ code: 'invalid_data',
461
+ ru: 'конец Tenant членства — арендатор строки',
462
+ },
463
+ // check: $tags — не список строк
464
+ 'write.load_tags': {
465
+ code: 'invalid_data',
466
+ ru: '$tags — список строк',
467
+ },
468
+ // check: $owner при { as }
469
+ 'write.load_as_owner': {
470
+ code: 'invalid_data',
471
+ ru: 'load({ as }): $owner не задаётся — владелец по ownerDefault класса',
472
+ },
473
+ // check: $owner не uuid
474
+ 'write.load_owner_id': {
475
+ code: 'invalid_data',
476
+ ru: '$owner — id аккаунта',
477
+ },
478
+ // prepareBatch (existing: 'update') — нарушение: id занят объектом другого класса (was)
479
+ 'write.load_other_class': {
480
+ code: 'exists',
481
+ ru: 'объект с этим id уже есть в классе {was}: строка класса {cls} его не правит',
482
+ },
483
+ // flushBatch ({ as }) — нарушение: условие ACL запрещает строку
484
+ 'write.load_acl_row': {
485
+ code: 'acl_denied',
486
+ ru: 'строку запрещает условие ACL',
487
+ },
488
+ // flushBatch ({ as }, existing: 'update'): условие ACL запрещает правку — прежние или новые значения
489
+ 'write.load_acl_update': {
490
+ code: 'acl_denied',
491
+ ru: 'правку строки запрещает условие ACL',
492
+ },
493
+ // historyWrite — нарушение: $rev версий объекта не подряд от 1 или $at не строка
494
+ 'write.load_history_versions': {
495
+ code: 'invalid_data',
496
+ ru: 'режим истории: версии с $rev подряд от 1 и $at — время версии',
497
+ },
498
+ // initialSession: в схеме есть строки, кроме системных классов
499
+ 'write.load_not_empty': {
500
+ code: 'not_empty',
501
+ ru: 'первая заливка — только в пустую схему: в entity и log, кроме строк системных классов (Class, Account, member, Resource, rule), ничего быть не должно',
502
+ },
503
+ // tx.ts, hintTxRetry: хвост к тексту взаимной блокировки или ошибки сериализации в явной транзакции (lmsg) — с
504
+ // пробелом и тире в начале; называет db.begin(), поэтому второй раз не дописывается
505
+ 'write.tx_retry_hint': {
506
+ code: 'invalid_query',
507
+ ru: ' — откатите транзакцию и повторите весь блок db.begin()',
508
+ },
509
+ };