letopis 0.20.3 → 1.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 (89) hide show
  1. package/AGENT-CHEATSHEET.en.md +368 -0
  2. package/AGENT-CHEATSHEET.md +354 -0
  3. package/CHANGELOG.md +348 -0
  4. package/MIGRATION.md +190 -0
  5. package/README.en.md +1937 -0
  6. package/README.md +1493 -3466
  7. package/dist/acl.d.ts +26 -50
  8. package/dist/acl.js +22 -267
  9. package/dist/admin.d.ts +138 -0
  10. package/dist/admin.js +170 -0
  11. package/dist/auth.d.ts +120 -73
  12. package/dist/auth.js +121 -306
  13. package/dist/cache.d.ts +73 -0
  14. package/dist/cache.js +148 -0
  15. package/dist/chain.d.ts +124 -191
  16. package/dist/chain.js +369 -551
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +164 -0
  19. package/dist/demo/booking.d.ts +289 -0
  20. package/dist/demo/booking.js +159 -0
  21. package/dist/errors.d.ts +29 -0
  22. package/dist/errors.js +70 -0
  23. package/dist/import.d.ts +179 -0
  24. package/dist/import.js +792 -0
  25. package/dist/index.d.ts +172 -26
  26. package/dist/index.js +304 -178
  27. package/dist/jsonschema.d.ts +22 -0
  28. package/dist/jsonschema.js +167 -0
  29. package/dist/load.d.ts +76 -0
  30. package/dist/load.js +884 -0
  31. package/dist/model.d.ts +166 -0
  32. package/dist/model.js +224 -0
  33. package/dist/ops.d.ts +7 -6
  34. package/dist/ops.js +7 -51
  35. package/dist/pglite.d.ts +22 -0
  36. package/dist/pglite.js +45 -0
  37. package/dist/registry.d.ts +57 -0
  38. package/dist/registry.js +82 -0
  39. package/dist/sql.d.ts +59 -142
  40. package/dist/sql.js +568 -654
  41. package/dist/sync.d.ts +31 -0
  42. package/dist/sync.js +108 -0
  43. package/dist/tx.d.ts +129 -8
  44. package/dist/tx.js +300 -73
  45. package/dist/typed.d.ts +97 -0
  46. package/dist/typed.js +1 -0
  47. package/dist/types.d.ts +71 -250
  48. package/dist/types.js +27 -108
  49. package/dist/up.d.ts +140 -47
  50. package/dist/up.js +339 -267
  51. package/dist/uuid.d.ts +21 -6
  52. package/dist/uuid.js +48 -64
  53. package/dist/validate.d.ts +24 -0
  54. package/dist/validate.js +251 -0
  55. package/dist/watch.d.ts +62 -0
  56. package/dist/watch.js +168 -0
  57. package/dist/write.d.ts +117 -74
  58. package/dist/write.js +658 -720
  59. package/llms.txt +26 -0
  60. package/package.json +49 -19
  61. package/sql/10-core.sql +136 -0
  62. package/sql/15-errors.sql +60 -0
  63. package/sql/20-context.sql +153 -0
  64. package/sql/30-validate.sql +423 -0
  65. package/sql/40-class.sql +259 -0
  66. package/sql/50-acl.sql +539 -0
  67. package/sql/60-write.sql +1369 -0
  68. package/sql/70-read.sql +245 -0
  69. package/sql/80-auth.sql +827 -0
  70. package/sql/90-time.sql +957 -0
  71. package/sql/95-seed.system.sql +178 -0
  72. package/sql/99-revision.sql +3 -0
  73. package/sql/README.md +56 -0
  74. package/sql/seed.booking.sql +39 -112
  75. package/dist/schema.d.ts +0 -15
  76. package/dist/schema.js +0 -351
  77. package/dist/sessions.d.ts +0 -32
  78. package/dist/sessions.js +0 -114
  79. package/dist/tables.d.ts +0 -105
  80. package/dist/tables.js +0 -248
  81. package/docker/Dockerfile +0 -40
  82. package/docker/start.sh +0 -18
  83. package/scripts/check-docs.mjs +0 -375
  84. package/scripts/gen-api-contract.mjs +0 -226
  85. package/scripts/gen-types.mjs +0 -350
  86. package/scripts/release-notes.mjs +0 -76
  87. package/scripts/schema-sync.mjs +0 -185
  88. package/sql/ddl.sql +0 -600
  89. package/sql/seed.auth.sql +0 -73
package/dist/admin.js ADDED
@@ -0,0 +1,170 @@
1
+ import { LetopisError } from './errors.js';
2
+ import { runTx } from './tx.js';
3
+ import { CHAIN_METHODS, DB_METHODS } from './types.js';
4
+ const SYSTEM_CLASSES = new Set(['Class', 'Account', 'member', 'Resource', 'rule']);
5
+ const one = async (a, text, params = []) => a.exec.run(async (q) => (await q.unsafe(text, params))[0]);
6
+ export async function verify(a, opts = {}) {
7
+ const r = await one(a, `select ${a.S}.verify($1::boolean) as v`, [opts.data === true]);
8
+ return r.v;
9
+ }
10
+ /** Ключ advisory-блокировки обслуживания: форма с двумя int4, своё пространство (19541). */
11
+ const MAINTAIN_LOCK = (schema) => [19541, schema];
12
+ /**
13
+ * Обслуживание: истёкшие сессии и коды, прореживание журнала пачками (каждая — своя транзакция,
14
+ * долгих блокировок нет), заполнение очереди сигналов. Из нескольких процессов работает один: пачка
15
+ * первым делом берёт транзакционную advisory-блокировку (pg_try_advisory_xact_lock), и та снимается
16
+ * с фиксацией пачки. Сеансовая блокировка за пулом в режиме транзакций (PgBouncer) оставалась бы на
17
+ * серверном соединении, которое пул отдаёт другим клиентам, — и обслуживание молча вставало бы
18
+ * (skipped у всех). Блокировку держит другой процесс: до первой пачки — skipped; между пачками —
19
+ * вызов останавливается (продолжит тот процесс), отчёт — о сделанном, more: true.
20
+ */
21
+ export async function maintain(a, opts = {}) {
22
+ if (a.exec.explicit)
23
+ throw new LetopisError('invalid_query', 'maintain() — пачками в своих транзакциях, не внутри db.begin()');
24
+ const now = opts.now === undefined ? null : opts.now instanceof Date ? opts.now.toISOString() : opts.now;
25
+ const report = { skipped: false, sessions: 0, codes: 0, thinned: 0, batches: 0, more: false, queue: 0 };
26
+ const [k1, k2] = MAINTAIN_LOCK(a.schema);
27
+ const max = opts.batches ?? 100;
28
+ for (let i = 0; i < max; i++) {
29
+ // блокировка и пачка — одним оператором: case вычисляет maintain() только при взятой блокировке
30
+ const [{ r }] = await runTx(a.txc, (q) => q.unsafe(`select case when pg_try_advisory_xact_lock($1::int, hashtext($2::text))
31
+ then ${a.S}.maintain($3::text::timestamptz, $4::int) end as r`, [k1, k2, now, opts.limit ?? 10_000]));
32
+ if (r === null) {
33
+ if (i === 0)
34
+ return { ...report, skipped: true };
35
+ break;
36
+ }
37
+ const b = r;
38
+ report.sessions += b.sessions;
39
+ report.codes += b.codes;
40
+ report.thinned += b.thinned;
41
+ report.queue = Number(b.queue);
42
+ report.more = b.more;
43
+ report.batches++;
44
+ if (!b.more)
45
+ break;
46
+ }
47
+ const warn = queueWarning(report.queue);
48
+ if (warn)
49
+ console.warn(warn);
50
+ return report;
51
+ }
52
+ /** Предупреждение о заполненной очереди сигналов (§2.12): больше половины — зависший слушатель. */
53
+ export function queueWarning(queue) {
54
+ return queue > 0.5 ? `letopis: очередь сигналов PostgreSQL заполнена на ${(queue * 100).toFixed(0)} % — проверьте зависших слушателей` : null;
55
+ }
56
+ /** Интервал расписания: число мс или '30s', '15m', '1h', '1d'. */
57
+ export function parseInterval(v) {
58
+ if (typeof v === 'number')
59
+ return v;
60
+ const m = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/.exec(v.trim());
61
+ if (!m)
62
+ throw new LetopisError('invalid_data', `интервал ${v}: число мс или '30s', '15m', '1h', '1d'`);
63
+ const n = Number(m[1]);
64
+ return n * { ms: 1, s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2]];
65
+ }
66
+ /** Запуск maintain() по расписанию (connect({ maintain: '1h' })): ошибки — предупреждением. */
67
+ export function scheduleMaintain(run, every) {
68
+ const ms = parseInterval(every);
69
+ let busy = false;
70
+ const timer = setInterval(() => {
71
+ if (busy)
72
+ return;
73
+ busy = true;
74
+ run().catch((e) => console.warn(`letopis: maintain по расписанию: ${e.message}`)).finally(() => (busy = false));
75
+ }, ms);
76
+ timer.unref?.();
77
+ return () => clearInterval(timer);
78
+ }
79
+ /**
80
+ * Сброс (этап 6, п. 6). Предохранители — в базе: флаг allowReset установки, право letopis.reset,
81
+ * подтверждение именем схемы. tenant — пачками (длинная операция не держит горизонт подписок).
82
+ */
83
+ export async function reset(a, opts) {
84
+ if (opts.level === 'tenant') {
85
+ if (a.exec.explicit)
86
+ throw new LetopisError('invalid_query', "reset({ level: 'tenant' }) — пачками в своих транзакциях, не внутри db.begin()");
87
+ let rows = 0;
88
+ let versions = 0;
89
+ for (;;) {
90
+ const [{ r }] = await runTx(a.txc, (q) => q.unsafe(`select ${a.S}.reset($1, $2, $3::uuid, 10000) as r`, [opts.level, opts.confirm, opts.tenant ?? null]));
91
+ const b = r;
92
+ rows += b.rows;
93
+ versions += b.versions;
94
+ if (b.done)
95
+ return { level: opts.level, tenant: b.tenant, rows, versions };
96
+ }
97
+ }
98
+ const r = await one(a, `select ${a.S}.reset($1, $2, $3::uuid) as r`, [opts.level, opts.confirm, opts.tenant ?? null]);
99
+ return r.r;
100
+ }
101
+ /**
102
+ * Обрезка истории (этап 6, п. 3): версии строже момента before удаляются, текущая — никогда; на место
103
+ * версии 1 встаёт запись trim. target — имя класса (с потомками), id или список id, строка ответа.
104
+ */
105
+ export async function trimHistory(a, target, before) {
106
+ const at = before instanceof Date ? before.toISOString() : before;
107
+ let ids = null;
108
+ let classes = null;
109
+ if (typeof target === 'string' && a.registry().has(target)) {
110
+ classes = a.registry().family(target).map((c) => c.name);
111
+ }
112
+ else {
113
+ const list = Array.isArray(target) ? target : [target];
114
+ ids = list.map((x) => (typeof x === 'string' ? x : x.id));
115
+ }
116
+ const r = await one(a, `select ${a.S}.trim_history($1::uuid[], $2::text[], $3::text::timestamptz) as n`, [ids, classes, at]);
117
+ return r.n;
118
+ }
119
+ /** Новый ключ класса (этап 6, п. 7): строки семейства переносятся на новые id. Возвращает их число. */
120
+ export async function rekeyClass(a, cls, key) {
121
+ const name = a.registry().resolve(cls).name;
122
+ const r = await one(a, `select ${a.S}.rekey_class($1, $2::jsonb) as n`, [name, key]);
123
+ return r.n;
124
+ }
125
+ /** Машинное описание модели арендатора вместе с метаданными классов (этап 6, п. 12). */
126
+ export function describe(a) {
127
+ const classes = a.registry().all().map((c) => ({
128
+ name: c.name,
129
+ kind: c.kind,
130
+ ...(c.alias ? { alias: c.alias } : {}),
131
+ abstract: c.abstract,
132
+ ...(c.ancestors[1] ? { parent: c.ancestors[1] } : {}),
133
+ ancestors: c.ancestors,
134
+ system: SYSTEM_CLASSES.has(c.name),
135
+ fields: (c.schema.properties ?? {}),
136
+ required: (c.schema.required ?? []),
137
+ ends: c.ends,
138
+ key: c.key,
139
+ history: c.history,
140
+ ownerDefault: c.ownerDefault,
141
+ ...(c.cache ? { cache: c.cache } : {}),
142
+ meta: c.meta,
143
+ })).sort((x, y) => Number(x.system) - Number(y.system) || x.name.localeCompare(y.name));
144
+ return { schema: a.schema, tenant: a.tenant(), classes, methods: { chain: CHAIN_METHODS, db: DB_METHODS } };
145
+ }
146
+ /** Описание модели текстом для языковых моделей (llms.txt). */
147
+ export function describeText(d) {
148
+ const out = [`# letopis: модель схемы ${d.schema}`, '',
149
+ 'Цепочка: db.Класс(фильтр).Связь().Класс().rows(); запись — create, update, upsert, delete; история — versions(), asOf(), withDeleted().', ''];
150
+ for (const c of d.classes) {
151
+ const title = typeof c.meta.title === 'string' ? ` — ${c.meta.title}` : '';
152
+ out.push(`## ${c.name}${c.alias ? ` (${c.alias})` : ''}${title}`);
153
+ out.push(`- вид: ${c.kind === 'hub' ? 'хаб (объект)' : 'связь'}${c.abstract ? ', абстрактный' : ''}${c.parent ? `, предок ${c.parent}` : ''}${c.system ? ', системный' : ''}`);
154
+ out.push(`- ключ: ${c.key.length ? c.key.join(', ') : 'нет (id выдаёт база)'}`);
155
+ const fields = Object.entries(c.fields).map(([k, v]) => {
156
+ const t = v.type;
157
+ return `${k}${c.required.includes(k) ? '*' : ''}: ${Array.isArray(t) ? t.join('|') : t ?? 'any'}`;
158
+ });
159
+ if (fields.length)
160
+ out.push(`- поля: ${fields.join(', ')}`);
161
+ const ends = Object.entries(c.ends).map(([k, v]) => {
162
+ const e = v;
163
+ return `${k}${e.required ? '*' : ''} → ${Array.isArray(e.class) ? e.class.join('|') : e.class}${e.many ? '[]' : ''}`;
164
+ });
165
+ if (ends.length)
166
+ out.push(`- концы: ${ends.join(', ')}`);
167
+ out.push('');
168
+ }
169
+ return out.join('\n');
170
+ }
package/dist/auth.d.ts CHANGED
@@ -1,123 +1,170 @@
1
- /**
2
- * db.auth: вход по таблице Credential — на аккаунт много способов:
3
- * пароль (логин/почта), api-ключ, ключ-секрет, внешние identity (oauth/sso/telegram).
4
- * Пароли — scrypt (node:crypto, без зависимостей); ключи/секреты — в БД только sha256.
5
- * Однозначность входа даёт credential_identity_udx: один живой кред на (category, identifier).
6
- * OAuth-танец, rate-limit, lockout — на приложении: здесь хранение и проверка.
7
- */
8
- import type { Account, Credential } from './types.js';
9
- import type { Tables } from './tables.js';
10
- import { type SessionStore, type Sessions } from './sessions.js';
11
- /** Код authenticator-а для секрета на момент времени (хелпер для тестов/сервисов). */
1
+ import type { Executor } from './tx.js';
2
+ /** Хэш пароля scrypt: `scrypt$N$r$p$соль$хэш` (формат 0.21). */
3
+ export declare function hashPassword(password: string): Promise<string>;
4
+ /** Проверка пароля по хэшу scrypt; другие форматы не проверяются. */
5
+ export declare function verifyHash(password: string, stored: string): Promise<boolean>;
6
+ export declare function base32Encode(buf: Uint8Array): string;
7
+ export declare function base32Decode(s: string): Buffer;
8
+ /** Код приложения-аутентификатора для секрета на момент времени (RFC 6238; помощник для тестов и сервисов). */
12
9
  export declare function totpCode(secretBase32: string, atMs?: number): string;
13
- /** Успешный вход: чей кред и какой именно. */
14
- export interface AuthResult {
15
- account: Account;
16
- credential: Credential;
10
+ type AccountRef = string | {
11
+ id: string;
12
+ };
13
+ /** Успешный вход: аккаунт и токен новой сессии. */
14
+ export interface LoginResult {
15
+ account: string;
16
+ token: string;
17
17
  }
18
- interface VerifyOpts {
19
- /** false — пускать и неподтверждённые креды (default: подтверждённые). */
20
- requireConfirmed?: boolean;
18
+ /** Кред без секретного материала. */
19
+ export interface Credential {
20
+ id: string;
21
+ kind: string;
22
+ /** Идентификатор (у TOTP — нет). */
23
+ ident: string | null;
24
+ confirmed: boolean;
25
+ enabled: boolean;
26
+ meta: Record<string, unknown>;
27
+ created_at: string;
28
+ expires_at: string | null;
21
29
  }
22
30
  export interface AuthApi {
23
- /** Пароль (upsert): логин/пароль и почта/пароль — одна механика, различает category. */
31
+ /** Пароль (и любой «логин/пароль»: kind — PASSWORD, EMAIL, …): создать или заменить. Смена гасит остальные сессии аккаунта. */
24
32
  setPassword(a: {
25
- account: string | {
26
- id: string;
27
- };
33
+ account: AccountRef;
28
34
  identifier: string;
29
35
  password: string;
30
- category?: string;
36
+ kind?: string;
31
37
  confirmed?: boolean;
32
- }): Promise<Credential>;
38
+ keepCurrent?: boolean;
39
+ }): Promise<string>;
40
+ /**
41
+ * Вход по паролю — для сервиса с правом auth.password и auth.session. Неудача — null. С `totp` после
42
+ * верного пароля база проверяет код TOTP аккаунта (право auth.totp; счётчик неудач и блокировка — общие
43
+ * с verifyTotp): верный — сессия со способами [kind, 'TOTP'], неверный или повторный — null.
44
+ */
33
45
  verifyPassword(a: {
34
46
  identifier: string;
35
47
  password: string;
36
- category?: string;
37
- } & VerifyOpts): Promise<AuthResult | null>;
38
- /** Новый api-ключ `lts_<48hex>`: отдаётся ОДИН раз, в БД — только sha256. */
48
+ kind?: string;
49
+ requireConfirmed?: boolean;
50
+ ttl?: number;
51
+ totp?: string;
52
+ }): Promise<LoginResult | null>;
53
+ /** Ключ API `lts_…`: отдаётся один раз. Себе сервис выпускает ключ сам по сессии своего ключа. */
39
54
  issueApiKey(a: {
40
- account: string | {
41
- id: string;
42
- };
55
+ account: AccountRef;
43
56
  name?: string;
57
+ ttl?: number;
44
58
  }): Promise<{
45
59
  key: string;
46
- credential: Credential;
60
+ id: string;
47
61
  }>;
48
- verifyApiKey(key: string, opts?: VerifyOpts): Promise<AuthResult | null>;
49
- /** Пара key(открытый id)+secret: secret отдаётся один раз, в БД — sha256. */
62
+ verifyApiKey(key: string): Promise<LoginResult | null>;
63
+ /** Пара ключ + секрет: секрет отдаётся один раз. Себе сервис выпускает пару сам по сессии своего ключа. */
50
64
  issueKeySecret(a: {
51
- account: string | {
52
- id: string;
53
- };
65
+ account: AccountRef;
54
66
  name?: string;
55
67
  }): Promise<{
56
68
  key: string;
57
69
  secret: string;
58
- credential: Credential;
70
+ id: string;
59
71
  }>;
60
- verifyKeySecret(key: string, secret: string, opts?: VerifyOpts): Promise<AuthResult | null>;
61
- /** TOTP (authenticator): секрет + otpauth-URI для QR; активен после первой успешной проверки. */
72
+ verifyKeySecret(key: string, secret: string): Promise<LoginResult | null>;
73
+ /** TOTP: секрет в base32 и ссылка otpauth:// для QR; фактор включает первая успешная проверка. */
62
74
  enrollTotp(a: {
63
- account: string | {
64
- id: string;
65
- };
75
+ account: AccountRef;
66
76
  issuer?: string;
67
77
  label?: string;
68
78
  }): Promise<{
69
79
  secret: string;
70
80
  uri: string;
71
- credential: Credential;
72
81
  }>;
73
- /** Проверка 6-значного кода: окно ±window шагов (default 1), replay того же шага отбит. */
74
82
  verifyTotp(a: {
75
- account: string | {
76
- id: string;
77
- };
83
+ account: AccountRef;
78
84
  code: string;
79
85
  window?: number;
80
86
  }): Promise<boolean>;
81
- /** true — TOTP-фактор включён (enroll подтверждён хотя бы одной проверкой). */
82
- totpEnabled(account: string | {
83
- id: string;
84
- }): Promise<boolean>;
85
- /** Одноразовый код (email/SMS/reset — доставка на приложении): в БД sha256 + TTL + лимит попыток. */
87
+ totpEnabled(account: AccountRef): Promise<boolean>;
88
+ /** Одноразовый код для доставки сервисом (право auth.otp). */
86
89
  issueOtp(a: {
87
- account: string | {
88
- id: string;
89
- };
90
+ account: AccountRef;
90
91
  identifier: string;
91
- category?: string;
92
- ttlSec?: number;
92
+ kind?: string;
93
+ ttl?: number;
93
94
  digits?: number;
95
+ maxAttempts?: number;
94
96
  }): Promise<{
95
97
  code: string;
96
- credential: Credential;
97
98
  }>;
98
- /** Успех сжигает код (одноразовость); maxAttempts (default 5) неудач тоже сжигают. */
99
+ /** Вход по одноразовому коду: успех и перебор (лимит задал выпуск, 1–10, по умолчанию 5) сжигают код. */
99
100
  verifyOtp(a: {
100
101
  identifier: string;
101
102
  code: string;
102
- category?: string;
103
- maxAttempts?: number;
104
- }): Promise<AuthResult | null>;
105
- /** Внешняя identity (TELEGRAM / GOOGLE / SSO-провайдер…): токен проверяет приложение, тут — связка. */
103
+ kind?: string;
104
+ }): Promise<LoginResult | null>;
105
+ /** Внешняя identity (TELEGRAM, GOOGLE, SSO): токен проверяет сервис, здесь — связка. */
106
106
  link(a: {
107
- account: string | {
108
- id: string;
109
- };
110
- category: string;
107
+ account: AccountRef;
108
+ kind: string;
111
109
  identifier: string;
112
110
  meta?: Record<string, unknown>;
113
111
  confirmed?: boolean;
114
- }): Promise<Credential>;
112
+ }): Promise<string>;
113
+ /** Вход по identity — для сервиса с правами auth.identity и auth.session; `totp` — как у verifyPassword. */
115
114
  lookup(a: {
116
- category: string;
115
+ kind: string;
117
116
  identifier: string;
118
- } & VerifyOpts): Promise<AuthResult | null>;
119
- /** Сессии во внешнем KV (Redis): store инжектирует приложение. */
120
- sessions(store: SessionStore): Sessions;
117
+ requireConfirmed?: boolean;
118
+ ttl?: number;
119
+ totp?: string;
120
+ }): Promise<LoginResult | null>;
121
+ /** Креды аккаунта без секретов. */
122
+ credentials(account: AccountRef): Promise<Credential[]>;
123
+ /**
124
+ * Удалить кред. Ключ API и пара ключ + секрет гасят только свои сессии, прочие креды — остальные сессии
125
+ * аккаунта; текущая гаснет, если keepCurrent: false. Свои ключи сервис отзывает сам; последний
126
+ * действующий ключ сервиса не отзывается (acl_denied).
127
+ */
128
+ revokeCredential(id: string, opts?: {
129
+ keepCurrent?: boolean;
130
+ }): Promise<boolean>;
131
+ /** Признаки креда confirmed и enabled (право auth.credential). */
132
+ setFlags(id: string, flags: {
133
+ confirmed?: boolean;
134
+ enabled?: boolean;
135
+ }): Promise<boolean>;
136
+ /** Короткая сессия пользователя для другого процесса (право auth.session). */
137
+ sessionFor(account: AccountRef, opts?: {
138
+ ttl?: number;
139
+ }): Promise<string>;
140
+ /** Продлить текущую сессию; новый срок — от сейчас. */
141
+ refresh(opts?: {
142
+ ttl?: number;
143
+ }): Promise<string>;
144
+ /** Выход: погасить текущую сессию. */
145
+ revoke(): Promise<boolean>;
146
+ /** Выход везде: все сессии аккаунта (чужого — с правом auth.session). */
147
+ revokeAll(account?: AccountRef): Promise<number>;
148
+ /** Переключить арендатора сессии: собственный или по членству. */
149
+ switch(tenant: string): Promise<void>;
150
+ /** Арендаторы, доступные аккаунту сессии: собственный (owner) и по членству. */
151
+ tenants(): Promise<{
152
+ tenant: string;
153
+ roles: string[];
154
+ }[]>;
155
+ /**
156
+ * Удалить аккаунт (этап 6; accounts.purge 0.21): системный администратор или владелец его
157
+ * арендатора; себя и последнего владельца арендатора — нельзя. Данные арендатора стираются,
158
+ * владение строками в чужих арендаторах переходит им, сессии гаснут, креды удаляются.
159
+ */
160
+ purgeAccount(account: AccountRef): Promise<{
161
+ account: string;
162
+ rows: number;
163
+ versions: number;
164
+ memberships: number;
165
+ owned: number;
166
+ }>;
121
167
  }
122
- export declare function makeAuth(tables: Tables): AuthApi;
168
+ /** Фасад db.auth. onSwitch — подключение перечитывает арендатора, реестр и пропуска прав. */
169
+ export declare function makeAuth(S: string, exec: Executor, onSwitch: () => Promise<void>, canSwitch: boolean): AuthApi;
123
170
  export {};