letopis 2.0.0 → 3.0.1
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 +491 -414
- package/CHANGELOG.md +361 -0
- package/LICENSE +75 -21
- package/LICENSE-COMMERCIAL.md +49 -0
- package/README.md +969 -210
- package/api-contract.json +385 -0
- package/dist/acl.d.ts +7 -2
- package/dist/acl.js +19 -2
- package/dist/admin.d.ts +7 -1
- package/dist/admin.js +16 -9
- package/dist/auth.d.ts +47 -11
- package/dist/auth.js +129 -15
- package/dist/chain.d.ts +52 -25
- package/dist/chain.js +445 -64
- package/dist/cli.js +72 -20
- package/dist/errors.d.ts +23 -6
- package/dist/errors.js +52 -13
- package/dist/index.d.ts +32 -15
- package/dist/index.js +209 -68
- package/dist/internal.d.ts +2 -0
- package/dist/load.d.ts +10 -6
- package/dist/load.js +30 -11
- package/dist/log.d.ts +121 -0
- package/dist/log.js +654 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +34 -16
- package/dist/messages/access.d.ts +10 -0
- package/dist/messages/access.js +13 -0
- package/dist/messages/api.d.ts +76 -0
- package/dist/messages/api.js +109 -7
- package/dist/messages/boot.d.ts +5 -0
- package/dist/messages/boot.js +11 -4
- package/dist/messages/codes.js +4 -4
- package/dist/messages/index.d.ts +5 -2
- package/dist/messages/index.js +5 -2
- package/dist/messages/log.d.ts +232 -0
- package/dist/messages/log.js +105 -0
- package/dist/messages/mcp.js +7 -7
- package/dist/messages/model.d.ts +40 -1
- package/dist/messages/model.js +52 -3
- package/dist/messages/query.d.ts +10 -0
- package/dist/messages/query.js +13 -0
- package/dist/messages/sql-core.d.ts +15 -1
- package/dist/messages/sql-core.js +20 -3
- package/dist/messages/sql-time.d.ts +15 -0
- package/dist/messages/sql-time.js +23 -0
- package/dist/messages/write.d.ts +52 -0
- package/dist/messages/write.js +77 -6
- package/dist/model.d.ts +10 -2
- package/dist/model.js +28 -6
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +6 -0
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +77 -25
- package/dist/sync.d.ts +4 -5
- package/dist/sync.js +53 -4
- package/dist/tx.d.ts +10 -1
- package/dist/tx.js +44 -2
- package/dist/typed.d.ts +18 -14
- package/dist/types.d.ts +91 -8
- package/dist/types.js +8 -3
- package/dist/up.d.ts +8 -5
- package/dist/up.js +71 -23
- package/dist/uuid.d.ts +8 -0
- package/dist/uuid.js +9 -1
- package/dist/validate.js +24 -7
- package/dist/watch.d.ts +5 -0
- package/dist/watch.js +13 -0
- package/dist/write.d.ts +84 -3
- package/dist/write.js +427 -40
- package/llms.txt +38 -17
- package/package.json +12 -6
- package/sql/20-context.sql +11 -3
- package/sql/30-validate.sql +51 -10
- package/sql/40-class.sql +21 -1
- package/sql/60-write.sql +18 -6
- package/sql/80-auth.sql +88 -17
- package/sql/90-time.sql +34 -6
- package/sql/99-revision.sql +5 -1
- 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 +5 -1
package/dist/mcp.js
CHANGED
|
@@ -27,9 +27,11 @@ import { createRequire } from 'node:module';
|
|
|
27
27
|
import { describeModel } from './describe.js';
|
|
28
28
|
import { LetopisError, checkOptions, lerr, lmsg, similar } from './errors.js';
|
|
29
29
|
import { internalsOf } from './internal.js';
|
|
30
|
+
import { NOLOG } from './log.js';
|
|
30
31
|
import { OPERATORS, opHint, or } from './ops.js';
|
|
31
32
|
import { cursorOf } from './sql.js';
|
|
32
33
|
import { ACL_CLASS_NAMES, RESERVED_CLASS_NAMES } from './types.js';
|
|
34
|
+
import { UUID_RE } from './uuid.js';
|
|
33
35
|
/** Все опции createMcpServer: неизвестная — invalid_data с похожими именами (checkOptions; план 2.0, 7.10 №4). */
|
|
34
36
|
const MCP_OPTIONS = {
|
|
35
37
|
write: true, maxRows: true, maxBytes: true, allowAclWrite: true, allowSystemAdmin: true,
|
|
@@ -43,7 +45,7 @@ const STATE_KEY = randomBytes(32);
|
|
|
43
45
|
const CONFIRM_TTL_MS = 10 * 60_000;
|
|
44
46
|
const RESERVED = new Set(RESERVED_CLASS_NAMES);
|
|
45
47
|
const SYSTEM_ID = '2eba6d0a-1edd-4bb6-a85a-a703dab49035';
|
|
46
|
-
const UUID =
|
|
48
|
+
const UUID = UUID_RE;
|
|
47
49
|
const TERMINALS = ['rows', 'first', 'ids', 'count', 'sum', 'avg', 'min', 'max', 'countBy'];
|
|
48
50
|
const PAGED = new Set(['rows', 'first', 'ids']);
|
|
49
51
|
/** Шагов цепочки и условий $or в одном фильтре не больше (ревью безопасности №2): запрос растёт с каждым. */
|
|
@@ -375,6 +377,8 @@ function stepFilter(a, where) {
|
|
|
375
377
|
return undefined;
|
|
376
378
|
}
|
|
377
379
|
const step = (x, name) => x[name];
|
|
380
|
+
/** Шаг с фильтром или без: без фильтра — вызов без аргумента (X(undefined) цепочка отмечает предупреждением, A13). */
|
|
381
|
+
const stepWith = (fn, f) => (f === undefined ? fn() : fn(f));
|
|
378
382
|
const encodeCursor = (c) => Buffer.from(JSON.stringify(c)).toString('base64url');
|
|
379
383
|
function decodeCursor(s) {
|
|
380
384
|
let c;
|
|
@@ -425,7 +429,7 @@ async function query(db, a, o) {
|
|
|
425
429
|
if (typeof terminal !== 'string' || !TERMINALS.includes(terminal)) {
|
|
426
430
|
throw lerr('mcp.unknown_terminal', { terminal: String(terminal), similar: similar(String(terminal), TERMINALS) });
|
|
427
431
|
}
|
|
428
|
-
let chain = step(db, stepName(reg, a.class, 'letopis_query.class'))
|
|
432
|
+
let chain = stepWith(step(db, stepName(reg, a.class, 'letopis_query.class')), stepFilter(a, 'letopis_query'));
|
|
429
433
|
if (a.steps !== undefined) {
|
|
430
434
|
if (!Array.isArray(a.steps))
|
|
431
435
|
throw lerr('mcp.steps_not_list');
|
|
@@ -436,7 +440,7 @@ async function query(db, a, o) {
|
|
|
436
440
|
if (!isObj(s))
|
|
437
441
|
throw lerr('mcp.step_not_object', { where });
|
|
438
442
|
checkKeys(s, ['step', 'id', 'ids', 'filter'], where);
|
|
439
|
-
chain = step(chain, stepName(reg, s.step, `${where}.step`))
|
|
443
|
+
chain = stepWith(step(chain, stepName(reg, s.step, `${where}.step`)), stepFilter(s, where));
|
|
440
444
|
});
|
|
441
445
|
}
|
|
442
446
|
if (a.asOf !== undefined) {
|
|
@@ -541,7 +545,7 @@ function withLinks(db, cls, c, links) {
|
|
|
541
545
|
}
|
|
542
546
|
/** Хэш операции удаления: схема, арендатор, класс, цели и превью каскада — подтверждение человека привязано к нему. */
|
|
543
547
|
function operationOf(db, cls, targets, preview) {
|
|
544
|
-
const rows = preview.map((r) => [r.id, r.class, r.rev, r.$action ?? null, r.$depth ?? null]).sort((x, y) => (String(x[0]) < String(y[0]) ? -1 : 1));
|
|
548
|
+
const rows = preview.map((r) => [r.id, r.class, r.rev, r.$action ?? null, r.$depth ?? null, r.$count ?? null]).sort((x, y) => (String(x[0]) < String(y[0]) ? -1 : 1));
|
|
545
549
|
return createHash('sha256').update(JSON.stringify({ schema: db.schema, tenant: db.tenant, cls, targets: [...targets].sort(), rows })).digest('hex');
|
|
546
550
|
}
|
|
547
551
|
const hmacOf = (who, exp, op) => createHmac('sha256', STATE_KEY).update(`letopis:mcp:delete:${who.account}:${who.tenant}:${exp}:${op}`).digest('hex');
|
|
@@ -658,15 +662,18 @@ async function write(db, a, ctx, sdk, server, o, who) {
|
|
|
658
662
|
throw lerr('mcp.delete_operation_changed', undefined, { detail: { operation: op } });
|
|
659
663
|
if (!preview.some((r) => (r.$depth ?? 0) === 0))
|
|
660
664
|
throw lerr('mcp.delete_nothing');
|
|
661
|
-
|
|
665
|
+
// мешают и скрытые правами строки: сводка $hidden 'restrict' (3.0.1)
|
|
666
|
+
if (preview.some((r) => r.$action === 'restrict' || r.$hidden === 'restrict'))
|
|
662
667
|
throw lerr('mcp.delete_restricted', undefined, { detail: { preview } });
|
|
663
|
-
|
|
668
|
+
// сводка скрытых строк считается их числом ($count)
|
|
669
|
+
const count = (act) => preview.filter((r) => act === undefined || r.$action === act || r.$hidden === act)
|
|
670
|
+
.reduce((n, r) => n + (r.$action === 'hidden' ? r.$count ?? 0 : 1), 0);
|
|
664
671
|
// окно видит человек: каноническое имя класса (имя — идентификатор по метасхеме) и id целей; алиас и meta — текст
|
|
665
672
|
// арендатора, в окно они не попадают (ревью безопасности №2)
|
|
666
673
|
const first = targets.slice(0, CONFIRM_IDS).join(', ');
|
|
667
674
|
const shown = targets.length > CONFIRM_IDS ? lmsg('mcp.delete_ids_more', { ids: first, more: targets.length - CONFIRM_IDS }) : first;
|
|
668
675
|
const message = lmsg('mcp.delete_message', {
|
|
669
|
-
count: targets.length, cls: db.registry.resolve(cls).name, ids: shown, rows:
|
|
676
|
+
count: targets.length, cls: db.registry.resolve(cls).name, ids: shown, rows: count(),
|
|
670
677
|
deleted: count('delete'), unset: count('unset'), operation: op.slice(0, 12),
|
|
671
678
|
});
|
|
672
679
|
const c = await confirmDelete(sdk, server, ctx, op, message, who);
|
|
@@ -674,13 +681,14 @@ async function write(db, a, ctx, sdk, server, o, who) {
|
|
|
674
681
|
return c.ask;
|
|
675
682
|
if (!c.granted)
|
|
676
683
|
throw lerr(c.reason);
|
|
677
|
-
// превью и удаление — одной транзакцией: подтверждено ровно то, что
|
|
684
|
+
// превью и удаление — одной транзакцией: подтверждено ровно то, что удаляется; delete сверяет превью сам (expect,
|
|
685
|
+
// 3.0) — под блокировкой замыкания каскада
|
|
678
686
|
const tx = await db.begin();
|
|
679
687
|
try {
|
|
680
688
|
const again = await step(tx, cls)(targets).delete(opts).rows();
|
|
681
689
|
if (operationOf(db, cls, targets, again) !== op)
|
|
682
690
|
throw lerr('mcp.delete_data_changed');
|
|
683
|
-
const rows = await step(tx, cls)(targets).delete({ confirm: true, ...opts }).rows();
|
|
691
|
+
const rows = await step(tx, cls)(targets).delete({ confirm: true, expect: again, ...opts }).rows();
|
|
684
692
|
await tx.commit();
|
|
685
693
|
return ok({ deleted: rows, operation: op });
|
|
686
694
|
}
|
|
@@ -733,7 +741,7 @@ async function checkCredentials(db, o) {
|
|
|
733
741
|
}
|
|
734
742
|
/**
|
|
735
743
|
* Сервер MCP для одного пользователя letopis: инструменты `letopis_model`, `letopis_query`, `letopis_history`, а с
|
|
736
|
-
* `{ write: true }` — ещё `letopis_write`. Подключение — сессия пользователя (`connect({ token })`) или
|
|
744
|
+
* `{ write: true }` — ещё `letopis_write`. Подключение — сессия пользователя (`connect({ token })`) или подключение
|
|
737
745
|
* `db.as(пользователь)`. Транспорт выбирает приложение: `server.connect(транспорт)`, `serveStdio(() => createMcpServer(db))`
|
|
738
746
|
* или обработчик HTTP `createMcpHandler(() => createMcpServer(db))` из SDK; токен запроса HTTP проверяет приложение.
|
|
739
747
|
*/
|
|
@@ -748,19 +756,29 @@ async function serve(db, opts) {
|
|
|
748
756
|
const who = await checkCredentials(db, o);
|
|
749
757
|
const sdk = await loadSdk();
|
|
750
758
|
const server = new sdk.McpServer({ name: 'letopis', version: VERSION }, { instructions: instructions(o), capabilities: { tools: {} } });
|
|
751
|
-
|
|
759
|
+
// логирование 3.0: вызов инструмента — mcp.call (имя, время, исход), отказ — ещё error; логгер — фасада db
|
|
760
|
+
const log = internalsOf(db)?.log ?? NOLOG;
|
|
761
|
+
const guard = (tool, fn) => async (args, ctx) => {
|
|
762
|
+
const t0 = performance.now();
|
|
752
763
|
try {
|
|
753
|
-
|
|
764
|
+
const r = await fn(isObj(args) ? args : {}, ctx);
|
|
765
|
+
if (log.wants('mcp.call')) {
|
|
766
|
+
log.emit('mcp.call', { op: tool, ms: performance.now() - t0, detail: { ok: !r.isError } });
|
|
767
|
+
}
|
|
768
|
+
return r;
|
|
754
769
|
}
|
|
755
770
|
catch (e) {
|
|
771
|
+
log.error(e, { op: tool, ms: performance.now() - t0 });
|
|
772
|
+
if (log.wants('mcp.call'))
|
|
773
|
+
log.emit('mcp.call', { op: tool, ms: performance.now() - t0, detail: { ok: false } });
|
|
756
774
|
return failure(e);
|
|
757
775
|
}
|
|
758
776
|
};
|
|
759
|
-
server.registerTool('letopis_model', { title: 'Модель letopis', description: DESCRIBE_MODEL, inputSchema: sdk.fromJsonSchema(MODEL_SCHEMA), annotations: READ_ONLY }, guard((a) => model(db, a, o)));
|
|
760
|
-
server.registerTool('letopis_query', { title: 'Чтение letopis', description: describeQuery(o), inputSchema: sdk.fromJsonSchema(QUERY_SCHEMA), annotations: READ_ONLY }, guard((a) => query(db, a, o)));
|
|
761
|
-
server.registerTool('letopis_history', { title: 'История letopis', description: describeHistory(o), inputSchema: sdk.fromJsonSchema(HISTORY_SCHEMA), annotations: READ_ONLY }, guard((a) => history(db, a, o)));
|
|
777
|
+
server.registerTool('letopis_model', { title: 'Модель letopis', description: DESCRIBE_MODEL, inputSchema: sdk.fromJsonSchema(MODEL_SCHEMA), annotations: READ_ONLY }, guard('letopis_model', (a) => model(db, a, o)));
|
|
778
|
+
server.registerTool('letopis_query', { title: 'Чтение letopis', description: describeQuery(o), inputSchema: sdk.fromJsonSchema(QUERY_SCHEMA), annotations: READ_ONLY }, guard('letopis_query', (a) => query(db, a, o)));
|
|
779
|
+
server.registerTool('letopis_history', { title: 'История letopis', description: describeHistory(o), inputSchema: sdk.fromJsonSchema(HISTORY_SCHEMA), annotations: READ_ONLY }, guard('letopis_history', (a) => history(db, a, o)));
|
|
762
780
|
if (o.write) {
|
|
763
|
-
server.registerTool('letopis_write', { title: 'Запись letopis', description: describeWrite(o), inputSchema: sdk.fromJsonSchema(WRITE_SCHEMA), annotations: DESTRUCTIVE }, guard((a, ctx) => write(db, a, ctx, sdk, server, o, who)));
|
|
781
|
+
server.registerTool('letopis_write', { title: 'Запись letopis', description: describeWrite(o), inputSchema: sdk.fromJsonSchema(WRITE_SCHEMA), annotations: DESTRUCTIVE }, guard('letopis_write', (a, ctx) => write(db, a, ctx, sdk, server, o, who)));
|
|
764
782
|
}
|
|
765
783
|
return server;
|
|
766
784
|
}
|
|
@@ -87,4 +87,14 @@ export declare const access: {
|
|
|
87
87
|
ru: string;
|
|
88
88
|
hint: string;
|
|
89
89
|
};
|
|
90
|
+
'access.bad_max_attempts': {
|
|
91
|
+
code: "invalid_data";
|
|
92
|
+
ru: string;
|
|
93
|
+
hint: string;
|
|
94
|
+
};
|
|
95
|
+
'access.bad_ttl': {
|
|
96
|
+
code: "invalid_data";
|
|
97
|
+
ru: string;
|
|
98
|
+
hint: string;
|
|
99
|
+
};
|
|
90
100
|
};
|
package/dist/messages/access.js
CHANGED
|
@@ -110,4 +110,17 @@ export const access = {
|
|
|
110
110
|
ru: 'секрет TOTP — не base32',
|
|
111
111
|
hint: 'передать секрет из auth.enrollTotp() — строку base32 (A–Z, 2–7)',
|
|
112
112
|
},
|
|
113
|
+
// auth.ts, issueOtp (3.0.1): maxAttempts не целое или вне 1–10 (раньше база молча приводила к пределу); value — как передан
|
|
114
|
+
'access.bad_max_attempts': {
|
|
115
|
+
code: 'invalid_data',
|
|
116
|
+
ru: 'issueOtp: maxAttempts {value} — целое от 1 до 10',
|
|
117
|
+
hint: 'maxAttempts — число неверных попыток до того, как код сгорит: целое от 1 до 10 (по умолчанию 5)',
|
|
118
|
+
},
|
|
119
|
+
// auth.ts, ttlOf (3.0.1): срок ttl (sessionFor, issueOtp, issueApiKey, verifyPassword, lookup, refresh, createTenant) — не
|
|
120
|
+
// число секунд, не короткая форма и не интервал PostgreSQL; where — вызов, value — как передан
|
|
121
|
+
'access.bad_ttl': {
|
|
122
|
+
code: 'invalid_data',
|
|
123
|
+
ru: "{where}: ttl {value} — нужно число секунд, '15m' или интервал PostgreSQL '15 minutes'",
|
|
124
|
+
hint: "срок ttl больше нуля: число секунд (600), короткая форма '30s', '15m', '2h', '1d' или интервал PostgreSQL '15 minutes'",
|
|
125
|
+
},
|
|
113
126
|
};
|
package/dist/messages/api.d.ts
CHANGED
|
@@ -6,6 +6,56 @@ export declare const api: {
|
|
|
6
6
|
'api.unknown_step': {
|
|
7
7
|
code: "invalid_class";
|
|
8
8
|
ru: string;
|
|
9
|
+
hint: string;
|
|
10
|
+
};
|
|
11
|
+
'api.token_not_active': {
|
|
12
|
+
code: "no_session";
|
|
13
|
+
ru: string;
|
|
14
|
+
hint: string;
|
|
15
|
+
};
|
|
16
|
+
'api.unknown_step_no_classes': {
|
|
17
|
+
code: "invalid_class";
|
|
18
|
+
ru: string;
|
|
19
|
+
hint: string;
|
|
20
|
+
};
|
|
21
|
+
'api.unknown_step_empty_tenant': {
|
|
22
|
+
code: "invalid_class";
|
|
23
|
+
ru: string;
|
|
24
|
+
hint: string;
|
|
25
|
+
};
|
|
26
|
+
'api.chain_not_rows': {
|
|
27
|
+
code: "invalid_query";
|
|
28
|
+
ru: string;
|
|
29
|
+
hint: string;
|
|
30
|
+
};
|
|
31
|
+
'api.step_undefined_filter': {
|
|
32
|
+
code: "invalid_query";
|
|
33
|
+
ru: string;
|
|
34
|
+
};
|
|
35
|
+
'api.sort_not_last': {
|
|
36
|
+
code: "invalid_query";
|
|
37
|
+
ru: string;
|
|
38
|
+
};
|
|
39
|
+
'api.with_deleted_step': {
|
|
40
|
+
code: "invalid_query";
|
|
41
|
+
ru: string;
|
|
42
|
+
};
|
|
43
|
+
'api.db_bad_value': {
|
|
44
|
+
code: "invalid_query";
|
|
45
|
+
ru: string;
|
|
46
|
+
hint: string;
|
|
47
|
+
};
|
|
48
|
+
'api.validation_issue': {
|
|
49
|
+
code: "invalid_data";
|
|
50
|
+
ru: string;
|
|
51
|
+
};
|
|
52
|
+
'api.validation_root': {
|
|
53
|
+
code: "invalid_data";
|
|
54
|
+
ru: string;
|
|
55
|
+
};
|
|
56
|
+
'api.validation_more': {
|
|
57
|
+
code: "invalid_data";
|
|
58
|
+
ru: string;
|
|
9
59
|
};
|
|
10
60
|
'api.connect_schema': {
|
|
11
61
|
code: "invalid_data";
|
|
@@ -49,6 +99,7 @@ export declare const api: {
|
|
|
49
99
|
'api.for_update_unsupported': {
|
|
50
100
|
code: "lock_unsupported";
|
|
51
101
|
ru: string;
|
|
102
|
+
hint: string;
|
|
52
103
|
};
|
|
53
104
|
'api.lock_denied': {
|
|
54
105
|
code: "acl_denied";
|
|
@@ -119,6 +170,16 @@ export declare const api: {
|
|
|
119
170
|
ru: string;
|
|
120
171
|
hint: string;
|
|
121
172
|
};
|
|
173
|
+
'api.begin_arg': {
|
|
174
|
+
code: "invalid_query";
|
|
175
|
+
ru: string;
|
|
176
|
+
hint: string;
|
|
177
|
+
};
|
|
178
|
+
'api.closed': {
|
|
179
|
+
code: "invalid_query";
|
|
180
|
+
ru: string;
|
|
181
|
+
hint: string;
|
|
182
|
+
};
|
|
122
183
|
'api.nested_begin': {
|
|
123
184
|
code: "invalid_query";
|
|
124
185
|
ru: string;
|
|
@@ -132,6 +193,11 @@ export declare const api: {
|
|
|
132
193
|
ru: string;
|
|
133
194
|
hint: string;
|
|
134
195
|
};
|
|
196
|
+
'api.connect_empty_credential': {
|
|
197
|
+
code: "invalid_data";
|
|
198
|
+
ru: string;
|
|
199
|
+
hint: string;
|
|
200
|
+
};
|
|
135
201
|
'api.connect_admin_type': {
|
|
136
202
|
code: "invalid_data";
|
|
137
203
|
ru: string;
|
|
@@ -168,4 +234,14 @@ export declare const api: {
|
|
|
168
234
|
code: "invalid_data";
|
|
169
235
|
ru: string;
|
|
170
236
|
};
|
|
237
|
+
'api.create_tenant_admin': {
|
|
238
|
+
code: "invalid_query";
|
|
239
|
+
ru: string;
|
|
240
|
+
hint: string;
|
|
241
|
+
};
|
|
242
|
+
'api.versions_terminal': {
|
|
243
|
+
code: "invalid_query";
|
|
244
|
+
ru: string;
|
|
245
|
+
hint: string;
|
|
246
|
+
};
|
|
171
247
|
};
|
package/dist/messages/api.js
CHANGED
|
@@ -8,6 +8,74 @@ export const api = {
|
|
|
8
8
|
'api.unknown_step': {
|
|
9
9
|
code: 'invalid_class',
|
|
10
10
|
ru: 'класс или роль {name} не найдены',
|
|
11
|
+
hint: 'классы арендатора видны только в его сессии: арендаторы пользователя — db.auth.tenants(), переход — db.auth.switch(tenant) или db.as(аккаунт, { tenant }); имя — как в модели (классы записывает syncModels)',
|
|
12
|
+
},
|
|
13
|
+
// chain.ts: шаг не найден, а в реестре сессии нет ни одного класса, кроме классов System — сессия, похоже, в
|
|
14
|
+
// личном арендаторе (вход без switch); похожие имена — params.similar
|
|
15
|
+
// index.ts: connect({ token }) — токен не даёт сессии (погашен, истёк, неверен); chain.ts: шаг корня без сессии
|
|
16
|
+
// (сессия погасла после connect) — отказ терминала, а не синхронный
|
|
17
|
+
'api.token_not_active': {
|
|
18
|
+
code: 'no_session',
|
|
19
|
+
ru: 'нет действующей сессии: токен погашен, истёк или неверен',
|
|
20
|
+
hint: 'войти заново и подключиться с новым токеном; веб-сервису — одно подключение сервиса и svc.as(account, { tenant }) на запрос, а не connect({ token }) на каждый вход',
|
|
21
|
+
},
|
|
22
|
+
'api.unknown_step_no_classes': {
|
|
23
|
+
code: 'invalid_class',
|
|
24
|
+
ru: 'класс или роль {name} не найдены: в реестре сессии нет ни одного класса арендатора — сессия, похоже, в личном арендаторе',
|
|
25
|
+
hint: 'после входа сессия работает в личном арендаторе пользователя: найти нужного — db.auth.tenants(), перейти — db.auth.switch(tenant) или db.as(аккаунт, { tenant }); классы в арендатор записывает syncModels',
|
|
26
|
+
},
|
|
27
|
+
// chain.ts (3.0.1): шаг не найден, в реестре сессии только классы System, а арендатор сессии не личный — в нём просто
|
|
28
|
+
// нет классов; похожие имена — params.similar
|
|
29
|
+
'api.unknown_step_empty_tenant': {
|
|
30
|
+
code: 'invalid_class',
|
|
31
|
+
ru: 'класс или роль {name} не найдены: в этом арендаторе нет классов — вызовите syncModels в нём',
|
|
32
|
+
hint: 'классы в арендатор записывает syncModels — в сессии этого арендатора (подключение арендатора или db.as(аккаунт, { tenant })); имя — как в модели',
|
|
33
|
+
},
|
|
34
|
+
// chain.ts: у цепочки спросили свойство массива (length, map, filter, 0 …) или перебрали её — она не массив строк
|
|
35
|
+
'api.chain_not_rows': {
|
|
36
|
+
code: 'invalid_query',
|
|
37
|
+
ru: 'цепочка — не массив: {name} есть у строк ответа, а не у цепочки',
|
|
38
|
+
hint: 'получить строки терминалом с await: const rows = await db.Класс(…).rows(), одну строку — await ….first()',
|
|
39
|
+
},
|
|
40
|
+
// chain.ts, A13: шаг вызван с аргументом undefined — фильтра нет, шаг находит все строки класса (как без аргумента);
|
|
41
|
+
// предупреждение (lwarn), один раз на имя шага
|
|
42
|
+
'api.step_undefined_filter': {
|
|
43
|
+
code: 'invalid_query',
|
|
44
|
+
ru: '{name}(undefined) — фильтра нет: шаг находит все строки, как {name}(). Проверьте переменную с id',
|
|
45
|
+
},
|
|
46
|
+
// chain.ts, A13: шаг после sort() — сортировка относится к последнему шагу цепочки, а не к тому, после которого
|
|
47
|
+
// стоит; предупреждение (lwarn), один раз на пару
|
|
48
|
+
'api.sort_not_last': {
|
|
49
|
+
code: 'invalid_query',
|
|
50
|
+
ru: "sort('{field}') перед шагом {step} сортирует строки последнего шага ({step}), а не прежнего: ставьте sort() в конец цепочки",
|
|
51
|
+
},
|
|
52
|
+
// chain.ts, A9: withDeleted() не у последнего шага, а у последнего — нет; предупреждение (lwarn), один раз на пару:
|
|
53
|
+
// с 2.1 withDeleted() — модификатор своего шага
|
|
54
|
+
'api.with_deleted_step': {
|
|
55
|
+
code: 'invalid_query',
|
|
56
|
+
ru: 'withDeleted() у шага {step} относится к нему, а не к последнему шагу {last}: удалённые {last} — .{last}().withDeleted()',
|
|
57
|
+
},
|
|
58
|
+
// errors.ts, fromDbError: подстраховка — значение не того типа (SQLSTATE 22P02), которое не поймала проверка до
|
|
59
|
+
// запроса; text — текст сервера
|
|
60
|
+
'api.db_bad_value': {
|
|
61
|
+
code: 'invalid_query',
|
|
62
|
+
ru: 'база не приняла значение: оно не того типа, что поле или параметр запроса (ответ PostgreSQL: {text})',
|
|
63
|
+
hint: 'передавайте id строкой uuid, числа — числами, даты — строками ISO 8601; проверьте значения фильтра, курсора, опций и параметров db.sql и повторите запрос',
|
|
64
|
+
},
|
|
65
|
+
// errors.ts, ValidationError: первое нарушение в тексте ошибки — путь и сообщение; текст не для ошибки (lmsg)
|
|
66
|
+
'api.validation_issue': {
|
|
67
|
+
code: 'invalid_data',
|
|
68
|
+
ru: '{path} — {issue}',
|
|
69
|
+
},
|
|
70
|
+
// errors.ts, ValidationError: путь нарушения на верхнем уровне данных (пустой путь); текст не для ошибки (lmsg)
|
|
71
|
+
'api.validation_root': {
|
|
72
|
+
code: 'invalid_data',
|
|
73
|
+
ru: 'данные',
|
|
74
|
+
},
|
|
75
|
+
// errors.ts, ValidationError: число нарушений, если их больше одного; текст не для ошибки (lmsg)
|
|
76
|
+
'api.validation_more': {
|
|
77
|
+
code: 'invalid_data',
|
|
78
|
+
ru: ' (всего нарушений: {n}, все — err.issues)',
|
|
11
79
|
},
|
|
12
80
|
// index.ts: connect({ schema })
|
|
13
81
|
'api.connect_schema': {
|
|
@@ -61,9 +129,11 @@ export const api = {
|
|
|
61
129
|
ru: 'forUpdate() — только внутри db.begin(): вне явной транзакции блокировка снялась бы сразу',
|
|
62
130
|
},
|
|
63
131
|
// chain.ts, lockedRead: forUpdate() с другим терминалом, агрегатом, срезом, глаголом
|
|
132
|
+
// chain.ts, lockedRead: what — что мешает блокировке (терминал, asOf(), withDeleted(), deep(), глагол)
|
|
64
133
|
'api.for_update_unsupported': {
|
|
65
134
|
code: 'lock_unsupported',
|
|
66
|
-
ru: 'forUpdate()
|
|
135
|
+
ru: 'forUpdate() нельзя с {what}: блокировка — только у чтения rows(), first(), ids()',
|
|
136
|
+
hint: 'forUpdate() — в db.begin(), перед rows(), first() или ids(); без asOf(), versions(), withDeleted(), deep(), count(), paths(), агрегатов и глаголов. Правка — отдельной цепочкой после чтения: await tr.X(id).forUpdate().first(); await tr.X(id).update(…).rows()',
|
|
67
137
|
},
|
|
68
138
|
// chain.ts, lockedRead: строки видны, но политика правки не даёт их заблокировать; ids — через запятую (detail.ids — списком)
|
|
69
139
|
'api.lock_denied': {
|
|
@@ -73,27 +143,27 @@ export const api = {
|
|
|
73
143
|
// chain.ts, slotValue: значение .Роль.set(…), .add(…), .remove(…)
|
|
74
144
|
'api.slot_value': {
|
|
75
145
|
code: 'invalid_query',
|
|
76
|
-
ru: 'значение
|
|
146
|
+
ru: 'значение шага роли (.Роль.set(), .add(), .remove()) — id, список id, строка ответа или цепочка',
|
|
77
147
|
},
|
|
78
148
|
// chain.ts: слот не сразу после create(), update() или upsert(); name — роль, как вызвана, op — set, unset, add, remove
|
|
79
149
|
'api.slot_without_write': {
|
|
80
150
|
code: 'invalid_query',
|
|
81
|
-
ru: '
|
|
151
|
+
ru: 'шаг роли .{name}.{op}() — после create(), update() или upsert(): db.Класс(…).update(…).{name}.{op}(…)',
|
|
82
152
|
},
|
|
83
153
|
// chain.ts: add() или remove() у одиночного конца
|
|
84
154
|
'api.slot_single': {
|
|
85
155
|
code: 'invalid_query',
|
|
86
|
-
ru: '
|
|
156
|
+
ru: 'шаг роли .{name}.{op}(): конец {role} одиночный — set() или unset()',
|
|
87
157
|
},
|
|
88
158
|
// chain.ts: unset() у обязательного конца
|
|
89
159
|
'api.slot_required': {
|
|
90
160
|
code: 'invalid_query',
|
|
91
|
-
ru: '
|
|
161
|
+
ru: 'шаг роли .{name}.unset(): конец {role} обязателен',
|
|
92
162
|
},
|
|
93
163
|
// chain.ts: set() или unset() одного конца дважды в одной записи
|
|
94
164
|
'api.slot_twice': {
|
|
95
165
|
code: 'invalid_query',
|
|
96
|
-
ru: '
|
|
166
|
+
ru: 'конец {role} задан дважды в одной записи',
|
|
97
167
|
},
|
|
98
168
|
// chain.ts, withLast: deep(), exact(), alias(), tags(), owner() сразу после глагола; verb — глагол
|
|
99
169
|
'api.step_mod_after_verb': {
|
|
@@ -118,7 +188,7 @@ export const api = {
|
|
|
118
188
|
// chain.ts: set, unset, add, remove у цепочки, а не у слота роли; method — имя метода
|
|
119
189
|
'api.slot_method': {
|
|
120
190
|
code: 'invalid_query',
|
|
121
|
-
ru: '{method}() — метод
|
|
191
|
+
ru: '{method}() — метод шага роли после глагола: db.Класс(…).update(…).Роль.{method}(…)',
|
|
122
192
|
},
|
|
123
193
|
// chain.ts: cache({ ttl })
|
|
124
194
|
'api.cache_ttl': {
|
|
@@ -152,6 +222,19 @@ export const api = {
|
|
|
152
222
|
hint: 'повторить вызов: между запросами db.as() сессия сменила арендатора или кончилась',
|
|
153
223
|
},
|
|
154
224
|
// index.ts: db.begin() внутри db.begin()
|
|
225
|
+
// index.ts: db.begin(аргумент) — не объект опций (функция, строка, число); функцию begin() не вызывает; type — typeof
|
|
226
|
+
// аргумента (array — массив)
|
|
227
|
+
'api.begin_arg': {
|
|
228
|
+
code: 'invalid_query',
|
|
229
|
+
ru: 'db.begin() принимает только объект опций с полем reason или ничего; передано значение типа {type}. Функцию begin() не вызывает: транзакцию открывает begin(), закрывают commit() и rollback()',
|
|
230
|
+
hint: 'откройте транзакцию без функции и закройте её сами: const tr = await db.begin(); try { …запросы через tr…; await tr.commit() } catch (e) { await tr.rollback(); throw e }',
|
|
231
|
+
},
|
|
232
|
+
// index.ts: вызов цепочки или метода подключения после db.close() корня (и у db.as(), db.begin() этого подключения)
|
|
233
|
+
'api.closed': {
|
|
234
|
+
code: 'invalid_query',
|
|
235
|
+
ru: 'подключение закрыто (db.close()) — откройте новое connect()',
|
|
236
|
+
hint: 'db.close() — в конце работы процесса; цепочки, db.as() и db.begin() закрытого подключения не работают — для новой работы const db = await connect({ … })',
|
|
237
|
+
},
|
|
155
238
|
'api.nested_begin': {
|
|
156
239
|
code: 'invalid_query',
|
|
157
240
|
ru: 'вложенных транзакций нет: db.begin() внутри db.begin()',
|
|
@@ -167,6 +250,12 @@ export const api = {
|
|
|
167
250
|
ru: 'connect: нужен dsn или sql',
|
|
168
251
|
hint: 'передать dsn — строку подключения — или sql — готовый пул postgres.js',
|
|
169
252
|
},
|
|
253
|
+
// index.ts: connect({ apiKey | service | token }) передан, но пуст (null, '', не строка) — опция есть, а значения нет
|
|
254
|
+
'api.connect_empty_credential': {
|
|
255
|
+
code: 'invalid_data',
|
|
256
|
+
ru: 'connect: {name} передан, но пуст ({value}) — подключение без сессии не открывается',
|
|
257
|
+
hint: 'передать ключ сервиса или токен строкой (process.env.SERVICE_KEY не задана?) — либо убрать опцию, если сессия не нужна; ключ сервиса up() выдаёт один раз — храните его сами',
|
|
258
|
+
},
|
|
170
259
|
// index.ts: connect({ admin }) — не строка и не пул
|
|
171
260
|
'api.connect_admin_type': {
|
|
172
261
|
code: 'invalid_data',
|
|
@@ -212,4 +301,17 @@ export const api = {
|
|
|
212
301
|
code: 'invalid_data',
|
|
213
302
|
ru: 'движок схемы {schema} ревизии {rev} новее библиотеки (она знает {known}) — обновите пакет letopis',
|
|
214
303
|
},
|
|
304
|
+
// up.ts, createTenant(): первый аргумент — не пул postgres() (строка DSN, подключение connect(), объект без begin и
|
|
305
|
+
// unsafe); type — typeof значения или null
|
|
306
|
+
'api.create_tenant_admin': {
|
|
307
|
+
code: 'invalid_query',
|
|
308
|
+
ru: 'createTenant(admin, …): admin — пул postgres() с правами администратора, а не {type}',
|
|
309
|
+
hint: "const admin = postgres(dsnАдминистратора); await createTenant(admin, 'salon', { name }); await admin.end(). DSN-строку и подключение connect() letopis createTenant не принимает",
|
|
310
|
+
},
|
|
311
|
+
// chain.ts, versions(): у результата versions() вызвали метод цепочки (rows, first, sort…); method — его имя
|
|
312
|
+
'api.versions_terminal': {
|
|
313
|
+
code: 'invalid_query',
|
|
314
|
+
ru: 'versions() — терминал: отдаёт строки версий, у результата нет .{method}()',
|
|
315
|
+
hint: "const v = await db.Класс(id).versions(); модификаторы — до versions(): db.Класс(id).sort('at', 'desc').limit(10).versions()",
|
|
316
|
+
},
|
|
215
317
|
};
|
package/dist/messages/boot.d.ts
CHANGED
package/dist/messages/boot.js
CHANGED
|
@@ -3,7 +3,7 @@ const LOGIN = 'передать login: { user, password } — имя обычн
|
|
|
3
3
|
/** Подсказка отказов user.roles в createTenant() и up({ tenant }). */
|
|
4
4
|
const ROLES = "передать user.roles — список разных имён ролей членства, например ['staff'], или не передавать: по умолчанию ['owner']";
|
|
5
5
|
/** Подсказка отказов createTenant() об удалённой строке. */
|
|
6
|
-
const GONE = 'вернуть удалённое restore() (текст называет, кто это может) или выбрать другое имя или логин: id вычисляется из них';
|
|
6
|
+
const GONE = 'вернуть удалённое restore({ expect }) — надгробие из withDeleted().rows() (текст называет, кто это может) или выбрать другое имя или логин: id вычисляется из них';
|
|
7
7
|
export const boot = {
|
|
8
8
|
// up.ts, substitute(): в тексте SQL (ядро, миграция, свой сид) маркер, которого установщик не знает
|
|
9
9
|
'boot.unknown_marker': {
|
|
@@ -83,6 +83,13 @@ export const boot = {
|
|
|
83
83
|
ru: 'up({ login }): роль {user} — привилегированная (суперпользователь, CREATEROLE, CREATEDB, REPLICATION, BYPASSRLS, роль-владелец или её член) или сама роль приложения: роль приложения — обычная роль входа, привилегированную up() не трогает',
|
|
84
84
|
hint: 'выбрать для login другое имя — роль без этих атрибутов и без членства в роли владельца',
|
|
85
85
|
},
|
|
86
|
+
// up.ts, ensureLogin (3.0.1): установщик не может выдать роль приложения (PostgreSQL 16+: нужен ADMIN OPTION, SQLSTATE
|
|
87
|
+
// 42501 «permission denied to grant role»); app — роль приложения, installer — роль установщика (current_user)
|
|
88
|
+
'boot.login_grant_denied': {
|
|
89
|
+
code: 'invalid_data',
|
|
90
|
+
ru: 'up({ login }): роль {installer} не может выдать роль {app} роли {user} — нужен ADMIN OPTION на роль {app} (grant {app} to {installer} with admin option) или суперпользователь',
|
|
91
|
+
hint: 'выполнить от суперпользователя grant <роль приложения> to <установщик> with admin option (имена — в тексте ошибки) и повторить up({ login }) — или ставить от суперпользователя',
|
|
92
|
+
},
|
|
86
93
|
// up.ts, createTenant() и up({ tenant }): нет имени арендатора
|
|
87
94
|
'boot.tenant_name_required': {
|
|
88
95
|
code: 'invalid_data',
|
|
@@ -123,19 +130,19 @@ export const boot = {
|
|
|
123
130
|
// up.ts, createTenant(): строки арендатора нет, а его история в журнале есть
|
|
124
131
|
'boot.tenant_deleted': {
|
|
125
132
|
code: 'invalid_data',
|
|
126
|
-
ru: 'createTenant: арендатор «{name}» ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore() от имени системного администратора или выберите другое имя',
|
|
133
|
+
ru: 'createTenant: арендатор «{name}» ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore({ expect: await db.Account(id).withDeleted().rows() }) от имени системного администратора или выберите другое имя',
|
|
127
134
|
hint: GONE,
|
|
128
135
|
},
|
|
129
136
|
// up.ts, createTenant(): строки аккаунта пользователя нет, а его история в журнале есть
|
|
130
137
|
'boot.tenant_user_deleted': {
|
|
131
138
|
code: 'invalid_data',
|
|
132
|
-
ru: 'createTenant: аккаунт пользователя {login} ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore() от имени системного администратора или выберите другой логин',
|
|
139
|
+
ru: 'createTenant: аккаунт пользователя {login} ({id}) удалён, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.Account(id).restore({ expect: await db.Account(id).withDeleted().rows() }) от имени системного администратора или выберите другой логин',
|
|
133
140
|
hint: GONE,
|
|
134
141
|
},
|
|
135
142
|
// up.ts, createTenant(): строки членства нет, а его история в журнале есть
|
|
136
143
|
'boot.tenant_member_deleted': {
|
|
137
144
|
code: 'invalid_data',
|
|
138
|
-
ru: 'createTenant: членство {login} в арендаторе «{name}» ({id}) удалено, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.member(id).restore() в этом арендаторе или выберите другой логин',
|
|
145
|
+
ru: 'createTenant: членство {login} в арендаторе «{name}» ({id}) удалено, а его история осталась в журнале — повтор удалённое не воскрешает: верните его db.member(id).restore({ expect: await db.member(id).withDeleted().rows() }) в этом арендаторе или выберите другой логин',
|
|
139
146
|
hint: GONE,
|
|
140
147
|
},
|
|
141
148
|
// up.ts, refuse1x(): схема letopis 1.x — ревизия из комментария схемы ниже первой ревизии 2.0 (FIRST_REVISION) или
|
package/dist/messages/codes.js
CHANGED
|
@@ -27,7 +27,7 @@ export const codes = {
|
|
|
27
27
|
'codes.acl_denied': {
|
|
28
28
|
code: 'acl_denied',
|
|
29
29
|
ru: 'правила доступа запрещают это действие',
|
|
30
|
-
hint: '
|
|
30
|
+
hint: 'выдать право `db.acl.grant` (правила — класс `rule`) или выполнить действие от пользователя, которому оно разрешено; проверить заранее — `db.acl.checkData`, `db.acl.check`',
|
|
31
31
|
},
|
|
32
32
|
'codes.id_mismatch': {
|
|
33
33
|
code: 'id_mismatch',
|
|
@@ -72,7 +72,7 @@ export const codes = {
|
|
|
72
72
|
'codes.not_deleted': {
|
|
73
73
|
code: 'not_deleted',
|
|
74
74
|
ru: '`restore` работает только для удалённых объектов',
|
|
75
|
-
hint: 'сначала `delete()`, потом `restore()`',
|
|
75
|
+
hint: 'сначала `delete({ confirm: true, expect })`, потом `restore({ expect })` с надгробиями из ответа `delete()` или `withDeleted().rows()`',
|
|
76
76
|
},
|
|
77
77
|
'codes.exists': {
|
|
78
78
|
code: 'exists',
|
|
@@ -91,8 +91,8 @@ export const codes = {
|
|
|
91
91
|
},
|
|
92
92
|
'codes.conflict': {
|
|
93
93
|
code: 'conflict',
|
|
94
|
-
ru: '`update` с `{ rev }
|
|
95
|
-
hint: 'перечитать
|
|
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
96
|
},
|
|
97
97
|
'codes.rev_ambiguous': {
|
|
98
98
|
code: 'rev_ambiguous',
|
package/dist/messages/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 1. Раздел — по файлу, где брошена ошибка:
|
|
10
10
|
* api — index, chain, typed, errors, internal; write — write, tx, load; query — sql, ops;
|
|
11
11
|
* time — watch, admin, cache, signals; access — auth, acl; model — model, registry, sync, cli, validate,
|
|
12
|
-
* jsonschema, uuid; boot — up, pglite; mcp — mcp, describe;
|
|
12
|
+
* jsonschema, uuid; boot — up, pglite; mcp — mcp, describe; log — log (и тексты логирования из index, tx);
|
|
13
13
|
* sql-core — sql/10, 15, 20, 30, 40; sql-write — sql/60; sql-access — sql/50, 80;
|
|
14
14
|
* sql-time — sql/70, 90, 95, 99 и sql/migrate.
|
|
15
15
|
* codes.ts — запись на каждый код: заголовок и подсказка. Новый код — ещё ErrorCode (types.ts), для кода базы —
|
|
@@ -56,6 +56,7 @@ import { access } from './access.js';
|
|
|
56
56
|
import { api } from './api.js';
|
|
57
57
|
import { boot } from './boot.js';
|
|
58
58
|
import { codes } from './codes.js';
|
|
59
|
+
import { log } from './log.js';
|
|
59
60
|
import { mcp } from './mcp.js';
|
|
60
61
|
import { model } from './model.js';
|
|
61
62
|
import { query } from './query.js';
|
|
@@ -67,9 +68,11 @@ import { time } from './time.js';
|
|
|
67
68
|
import { write } from './write.js';
|
|
68
69
|
import type { ErrorCode, Msg, Params } from './types.js';
|
|
69
70
|
export type { ErrorCode, Msg, Params, Section } from './types.js';
|
|
71
|
+
/** Каталог событий логирования (не сообщения об ошибках — в MESSAGES не входит). */
|
|
72
|
+
export { LOG_EVENTS, type LogEventInfo, type LogEventLevel, type LogEventName } from './log.js';
|
|
70
73
|
/** Разделы, которые установщик кладёт в базу (message_catalog() в lib/sql/15-errors.sql). */
|
|
71
74
|
export declare const SQL_SECTIONS: readonly ["sql-core", "sql-write", "sql-access", "sql-time"];
|
|
72
|
-
type All = typeof codes & typeof api & typeof write & typeof query & typeof time & typeof access & typeof model & typeof boot & typeof mcp & typeof sqlCore & typeof sqlWrite & typeof sqlAccess & typeof sqlTime;
|
|
75
|
+
type All = typeof codes & typeof api & typeof write & typeof query & typeof time & typeof access & typeof model & typeof boot & typeof mcp & typeof log & typeof sqlCore & typeof sqlWrite & typeof sqlAccess & typeof sqlTime;
|
|
73
76
|
/** Ключ каталога: опечатку в lerr('…') ловит проверка типов. */
|
|
74
77
|
export type MsgKey = keyof All;
|
|
75
78
|
/** Все записи каталога по ключу. */
|
package/dist/messages/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 1. Раздел — по файлу, где брошена ошибка:
|
|
10
10
|
* api — index, chain, typed, errors, internal; write — write, tx, load; query — sql, ops;
|
|
11
11
|
* time — watch, admin, cache, signals; access — auth, acl; model — model, registry, sync, cli, validate,
|
|
12
|
-
* jsonschema, uuid; boot — up, pglite; mcp — mcp, describe;
|
|
12
|
+
* jsonschema, uuid; boot — up, pglite; mcp — mcp, describe; log — log (и тексты логирования из index, tx);
|
|
13
13
|
* sql-core — sql/10, 15, 20, 30, 40; sql-write — sql/60; sql-access — sql/50, 80;
|
|
14
14
|
* sql-time — sql/70, 90, 95, 99 и sql/migrate.
|
|
15
15
|
* codes.ts — запись на каждый код: заголовок и подсказка. Новый код — ещё ErrorCode (types.ts), для кода базы —
|
|
@@ -56,6 +56,7 @@ import { access } from './access.js';
|
|
|
56
56
|
import { api } from './api.js';
|
|
57
57
|
import { boot } from './boot.js';
|
|
58
58
|
import { codes } from './codes.js';
|
|
59
|
+
import { log } from './log.js';
|
|
59
60
|
import { mcp } from './mcp.js';
|
|
60
61
|
import { model } from './model.js';
|
|
61
62
|
import { query } from './query.js';
|
|
@@ -65,9 +66,11 @@ import { sqlTime } from './sql-time.js';
|
|
|
65
66
|
import { sqlWrite } from './sql-write.js';
|
|
66
67
|
import { time } from './time.js';
|
|
67
68
|
import { write } from './write.js';
|
|
69
|
+
/** Каталог событий логирования (не сообщения об ошибках — в MESSAGES не входит). */
|
|
70
|
+
export { LOG_EVENTS } from './log.js';
|
|
68
71
|
/** Разделы каталога: имя — префикс ключа и имя файла. */
|
|
69
72
|
const SECTIONS = {
|
|
70
|
-
codes, api, write, query, time, access, model, boot, mcp,
|
|
73
|
+
codes, api, write, query, time, access, model, boot, mcp, log,
|
|
71
74
|
'sql-core': sqlCore, 'sql-write': sqlWrite, 'sql-access': sqlAccess, 'sql-time': sqlTime,
|
|
72
75
|
};
|
|
73
76
|
/** Разделы, которые установщик кладёт в базу (message_catalog() в lib/sql/15-errors.sql). */
|