letopis 1.4.1 → 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.
- package/AGENT-CHEATSHEET.md +329 -278
- package/CHANGELOG.md +558 -1
- package/README.md +606 -1037
- package/dist/acl.d.ts +78 -1
- package/dist/acl.js +264 -13
- package/dist/admin.d.ts +10 -141
- package/dist/admin.js +24 -175
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +5 -15
- package/dist/cache.d.ts +8 -1
- package/dist/cache.js +33 -10
- package/dist/chain.d.ts +19 -41
- package/dist/chain.js +169 -201
- package/dist/cli.js +51 -88
- 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 +59 -13
- package/dist/errors.js +190 -29
- package/dist/index.d.ts +52 -71
- package/dist/index.js +245 -130
- package/dist/internal.d.ts +12 -0
- package/dist/internal.js +5 -0
- package/dist/jsonschema.js +18 -17
- package/dist/load.d.ts +20 -12
- package/dist/load.js +225 -186
- package/dist/mcp.d.ts +5 -7
- package/dist/mcp.js +171 -215
- package/dist/messages/access.d.ts +90 -0
- package/dist/messages/access.js +113 -0
- package/dist/messages/api.d.ts +171 -0
- package/dist/messages/api.js +215 -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 +95 -0
- package/dist/messages/index.js +118 -0
- package/dist/messages/mcp.d.ts +306 -0
- package/dist/messages/mcp.js +387 -0
- package/dist/messages/model.d.ts +233 -0
- package/dist/messages/model.js +283 -0
- package/dist/messages/query.d.ts +131 -0
- package/dist/messages/query.js +158 -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 +150 -0
- package/dist/messages/sql-core.js +181 -0
- package/dist/messages/sql-time.d.ts +71 -0
- package/dist/messages/sql-time.js +88 -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 +405 -0
- package/dist/messages/write.js +509 -0
- package/dist/model.d.ts +73 -60
- package/dist/model.js +26 -29
- 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 +5 -9
- package/dist/signals.js +5 -9
- package/dist/sql.d.ts +13 -6
- package/dist/sql.js +176 -83
- package/dist/sync.d.ts +9 -3
- package/dist/sync.js +51 -21
- package/dist/tx.d.ts +26 -1
- package/dist/tx.js +44 -14
- package/dist/typed.d.ts +240 -45
- package/dist/types.d.ts +23 -91
- package/dist/types.js +12 -14
- package/dist/up.d.ts +53 -32
- package/dist/up.js +184 -73
- package/dist/uuid.js +5 -5
- package/dist/validate.d.ts +13 -1
- package/dist/validate.js +61 -29
- package/dist/watch.d.ts +11 -10
- package/dist/watch.js +33 -70
- package/dist/write.d.ts +9 -16
- package/dist/write.js +118 -187
- package/llms.txt +22 -25
- package/package.json +8 -11
- package/sql/10-core.sql +7 -50
- package/sql/15-errors.sql +64 -10
- package/sql/20-context.sql +7 -6
- package/sql/30-validate.sql +35 -27
- package/sql/40-class.sql +20 -114
- package/sql/50-acl.sql +8 -13
- package/sql/60-write.sql +220 -667
- package/sql/70-read.sql +20 -16
- package/sql/80-auth.sql +24 -27
- package/sql/90-time.sql +70 -859
- package/sql/95-seed.system.sql +7 -14
- package/sql/99-revision.sql +2 -1
- package/sql/README.md +32 -24
- package/sql/migrate/README.md +14 -0
- package/AGENT-CHEATSHEET.en.md +0 -485
- package/MIGRATION.md +0 -190
- package/README.en.md +0 -2859
- package/dist/import.d.ts +0 -185
- package/dist/import.js +0 -820
- package/dist/project.d.ts +0 -350
- package/dist/project.js +0 -1224
- package/sql/75-history.sql +0 -358
- package/sql/88-derived.sql +0 -1235
- package/sql/migrate/0006-changed-paths.sql +0 -3
- 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
|
+
};
|