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/sql/90-time.sql
CHANGED
|
@@ -109,8 +109,13 @@ end $$;
|
|
|
109
109
|
|
|
110
110
|
-- Восстановление удалённого объекта (этап 6, п. 2): последний снимок новой версией с видом restore.
|
|
111
111
|
-- Снимок перепроверяется по текущему описанию класса (данные, концы с видимостью целей, ключ), право
|
|
112
|
-
-- WRITE — на восстановленные значения. id прежний, в том числе у класса без ключа.
|
|
113
|
-
|
|
112
|
+
-- WRITE — на восстановленные значения. id прежний, в том числе у класса без ключа. p_rev (ревизия 9, план 2.1, A1) —
|
|
113
|
+
-- ожидаемый номер последней записи журнала (надгробия из ответа delete или withDeleted): другой — отказ restore_stale,
|
|
114
|
+
-- проверка под той же блокировкой записи журнала, что у параллельного restore и воскрешения. Прежняя restore(uuid)
|
|
115
|
+
-- снимается миграцией 0009: вызов restore(id) попадает сюда с p_rev = null. Отказ — по записи журнала за последним
|
|
116
|
+
-- надгробием (второй раунд 2.1, A2): restore — restore_already, create — restore_id_taken (объект живой); иначе с p_rev —
|
|
117
|
+
-- restore_stale с after (вид записи сразу за p_rev), deleted и live, без p_rev — restore_not_deleted.
|
|
118
|
+
create or replace function @SCHEMA@.restore(p_id uuid, p_rev int default null) returns uuid
|
|
114
119
|
language plpgsql volatile security definer
|
|
115
120
|
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
116
121
|
as $$
|
|
@@ -127,6 +132,9 @@ declare
|
|
|
127
132
|
own uuid;
|
|
128
133
|
ts timestamptz;
|
|
129
134
|
n @SCHEMA@.entity;
|
|
135
|
+
v_live boolean;
|
|
136
|
+
v_tomb int;
|
|
137
|
+
v_next text;
|
|
130
138
|
begin
|
|
131
139
|
select * into c from @SCHEMA@.ctx();
|
|
132
140
|
if c.account is null then
|
|
@@ -134,8 +142,28 @@ begin
|
|
|
134
142
|
end if;
|
|
135
143
|
rd := @SCHEMA@.acl_actor('READ');
|
|
136
144
|
l := @SCHEMA@.last_version_visible(p_id, c.tenant, rd);
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
-- проигравший гонку: блокировка дождалась чужой фиксации, а запись l прочитана до неё. Новая версия видна следующему
|
|
146
|
+
-- оператору (read committed) — тогда последняя запись берётся заново, под той же блокировкой и проверкой видимости
|
|
147
|
+
if exists (select 1 from @SCHEMA@.log x where x.id = p_id and x.tenant = c.tenant and x.rev > l.rev) then
|
|
148
|
+
l := @SCHEMA@.last_version_visible(p_id, c.tenant, rd);
|
|
149
|
+
end if;
|
|
150
|
+
v_live := exists (select 1 from @SCHEMA@.entity e where e.id = p_id);
|
|
151
|
+
if l.op <> 'delete' or v_live or (p_rev is not null and l.rev <> p_rev) then
|
|
152
|
+
-- что за последним надгробием: restore — уже восстановлен, create — id занял новый объект с тем же ключом
|
|
153
|
+
select max(x.rev) into v_tomb from @SCHEMA@.log x where x.id = p_id and x.tenant = c.tenant and x.op = 'delete';
|
|
154
|
+
select x.op into v_next from @SCHEMA@.log x where x.id = p_id and x.tenant = c.tenant and x.rev > v_tomb order by x.rev limit 1;
|
|
155
|
+
if v_live and v_next = 'restore' then
|
|
156
|
+
perform @SCHEMA@.lt_fail('sql-time.restore_already', jsonb_build_object('id', p_id), jsonb_build_object('id', p_id, 'rev', l.rev));
|
|
157
|
+
elsif v_live and v_next = 'create' then
|
|
158
|
+
perform @SCHEMA@.lt_fail('sql-time.restore_id_taken', jsonb_build_object('id', p_id), jsonb_build_object('id', p_id, 'rev', l.rev));
|
|
159
|
+
elsif p_rev is null then
|
|
160
|
+
perform @SCHEMA@.lt_fail('sql-time.restore_not_deleted');
|
|
161
|
+
end if;
|
|
162
|
+
-- удалён снова, не удалялся, гонка: что было сразу после показанного удаления
|
|
163
|
+
select x.op into v_next from @SCHEMA@.log x where x.id = p_id and x.tenant = c.tenant and x.rev > p_rev order by x.rev limit 1;
|
|
164
|
+
perform @SCHEMA@.lt_fail('sql-time.restore_stale',
|
|
165
|
+
jsonb_build_object('id', p_id, 'rev', l.rev, 'expected', p_rev, 'after', coalesce(v_next, '—')),
|
|
166
|
+
jsonb_build_object('id', p_id, 'rev', l.rev, 'expected', p_rev, 'after', v_next, 'deleted', l.op = 'delete', 'live', v_live));
|
|
139
167
|
end if;
|
|
140
168
|
if l.class = 'Class' then
|
|
141
169
|
perform @SCHEMA@.lt_fail('sql-time.restore_class_row');
|
|
@@ -370,13 +398,13 @@ declare f text;
|
|
|
370
398
|
begin
|
|
371
399
|
foreach f in array array[
|
|
372
400
|
'session_role()', 'admin_role()', 'letopis_allowed(text)', 'session_expires()', 'signal_mode()', 'signal_key()', 'class_stamp()',
|
|
373
|
-
'last_version_visible(uuid, uuid, jsonb)', 'restore(uuid)', 'account_purge(uuid)', 'maintain()',
|
|
401
|
+
'last_version_visible(uuid, uuid, jsonb)', 'restore(uuid, int)', 'account_purge(uuid)', 'maintain()',
|
|
374
402
|
'watch_read(xid8, bigint, text[], int, pg_snapshot)', 'watch_head()'] loop
|
|
375
403
|
execute format('alter function @SCHEMA@.%s owner to @OWNER@', f);
|
|
376
404
|
execute format('revoke all on function @SCHEMA@.%s from public', f);
|
|
377
405
|
end loop;
|
|
378
406
|
end $$;
|
|
379
407
|
-- приложению: функции владельца этапа 6 (проверяют права сами) и подписка (под RLS)
|
|
380
|
-
grant execute on function @SCHEMA@.restore(uuid), @SCHEMA@.account_purge(uuid), @SCHEMA@.maintain(),
|
|
408
|
+
grant execute on function @SCHEMA@.restore(uuid, int), @SCHEMA@.account_purge(uuid), @SCHEMA@.maintain(),
|
|
381
409
|
@SCHEMA@.session_expires(), @SCHEMA@.watch_read(xid8, bigint, text[], int, pg_snapshot), @SCHEMA@.watch_head(),
|
|
382
410
|
@SCHEMA@.signal_mode(), @SCHEMA@.signal_key(), @SCHEMA@.class_stamp() to @APP@;
|
package/sql/99-revision.sql
CHANGED
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
-- Номер совпадает с DDL_REVISION в lib/src/types.ts (проверяет check-docs). С 1.4 в штампе ещё версия пакета и хэш
|
|
3
3
|
-- файлов ядра (engine, sql): connect() предупреждает, если функции схемы ставила другая версия движка той же ревизии.
|
|
4
4
|
-- Ревизия 8 — первая у 2.0 (план 2.0, редакция 2): схему ниже неё (letopis 1.x) up() и connect() отклоняют.
|
|
5
|
-
|
|
5
|
+
-- Ревизия 9 — 2.1.0: restore(id, rev), auth_logout_token(), auth_tenants() с именем, validate_row(); миграция 0009.
|
|
6
|
+
-- Ревизия 10 — 3.0.0: ctx() — имперсонация несуществующего аккаунта — «не найден» (target_not_found); миграция 0010.
|
|
7
|
+
-- Ревизия 11 — 3.0.0: вход по ключу и отзыв ключа — под блокировкой строки ключа, отключённые OTP и TOTP не
|
|
8
|
+
-- принимаются; миграция 0011.
|
|
9
|
+
comment on schema @SCHEMA@ is 'letopis ddl_revision=11 engine=@ENGINE_VERSION@ sql=@ENGINE_SQL@';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- letopis 2.1.0 — ревизия 9 (план 2.1, A1, A3, A5). Миграция снимает функции, чью сигнатуру или вид результата меняет
|
|
2
|
+
-- ревизия: create or replace их не заменит (новый аргумент дал бы вторую перегрузку и неоднозначный вызов restore(id),
|
|
3
|
+
-- новый вид результата PostgreSQL не принимает). Новые функции и права ставит повторный накат файлов ядра сразу после
|
|
4
|
+
-- миграции (90-time.sql, 80-auth.sql); новая auth_logout_token(text) и validate_row(text, jsonb) — тоже он.
|
|
5
|
+
-- Второй раунд 2.1 (ревизия та же, 9): новые тела restore(uuid, int) — отказы по смыслу restore_already, restore_id_taken,
|
|
6
|
+
-- restore_stale с after, deleted, live — и validate_data(jsonb, jsonpath, jsonb, jsonb) — граница в тексте длины строки
|
|
7
|
+
-- (sql-core.issue_string_too_short, issue_string_too_long) — сигнатур не меняют: их ставит тот же повторный накат, а
|
|
8
|
+
-- тексты новых ключей — каталог сообщений (message_catalog), который накат пишет заново. Снимать здесь нечего.
|
|
9
|
+
drop function if exists @SCHEMA@.restore(uuid);
|
|
10
|
+
drop function if exists @SCHEMA@.auth_tenants();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- letopis 3.0.0 — ревизия 10. Новое тело ctx() (20-context.sql): имперсонация аккаунта, которого нет, — отказ
|
|
2
|
+
-- sql-core.impersonation_no_account (target_not_found) вместо sql-core.impersonation_denied (acl_denied). Сигнатура та же:
|
|
3
|
+
-- тело ставит повторный накат файлов ядра сразу после миграции, текст ключа — каталог сообщений (message_catalog),
|
|
4
|
+
-- который накат пишет заново. Снимать и переносить нечего; оператор ниже — чтобы файл не был пустым запросом.
|
|
5
|
+
select 1;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- letopis 3.0.0 — ревизия 11. Новые тела функций входа и кредов (20-context.sql, 80-auth.sql): вход по ключу
|
|
2
|
+
-- (login_apikey, auth_login_apikey, login_keysecret) берёт строку ключа for share, отзыв (auth_revoke_credential) —
|
|
3
|
+
-- for update: сессия, открытая параллельно с отзывом, гаснет вместе с ключом; отключённые (meta.enabled = false) OTP
|
|
4
|
+
-- и TOTP не принимаются (login_otp, auth_verify_totp, auth_totp_enabled); отключение креда (auth_set_flags) берёт его
|
|
5
|
+
-- строку for update и гасит его сессии, последний ключ сервиса не отключается. Сигнатуры те же: тела ставит повторный
|
|
6
|
+
-- накат файлов ядра сразу после миграции. Снимать и переносить нечего; оператор ниже — чтобы файл не был пустым запросом.
|
|
7
|
+
select 1;
|
package/sql/migrate/README.md
CHANGED
|
@@ -11,4 +11,8 @@
|
|
|
11
11
|
у файлов ядра (`@SCHEMA@`, `@OWNER@` и другие — таблица в `lib/sql/README.md`).
|
|
12
12
|
- В миграцию идёт только то, чего не догонит повторный накат: колонка или ограничение существующей таблицы, перенос
|
|
13
13
|
данных. Функции, индексы, триггеры и политики ставят файлы ядра.
|
|
14
|
-
- У 2.0 (ревизия 8) миграций нет.
|
|
14
|
+
- У 2.0 (ревизия 8) миграций нет. Ревизия 9 (2.1.0) — `0009-restore-rev.tenants-name.sql`: снимает `restore(uuid)` и
|
|
15
|
+
`auth_tenants()` прежнего вида, новые ставит повторный накат. Ревизия 10 (3.0.0) — `0010-impersonation-no-account.sql`:
|
|
16
|
+
новое тело `ctx()` ставит повторный накат, миграции переносить нечего. Ревизия 11 (3.0.0) —
|
|
17
|
+
`0011-key-lock.otp-enabled.sql`: новые тела функций входа по ключу, отзыва креда, OTP и TOTP ставит повторный накат,
|
|
18
|
+
миграции переносить нечего.
|