letopis 1.4.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/AGENT-CHEATSHEET.md +329 -281
  2. package/CHANGELOG.md +418 -0
  3. package/README.md +572 -1053
  4. package/dist/acl.d.ts +78 -1
  5. package/dist/acl.js +264 -13
  6. package/dist/admin.d.ts +10 -141
  7. package/dist/admin.js +24 -184
  8. package/dist/auth.d.ts +2 -2
  9. package/dist/auth.js +5 -15
  10. package/dist/cache.d.ts +8 -1
  11. package/dist/cache.js +30 -8
  12. package/dist/chain.d.ts +18 -41
  13. package/dist/chain.js +165 -203
  14. package/dist/cli.js +45 -90
  15. package/dist/demo/booking.d.ts +352 -36
  16. package/dist/describe.d.ts +47 -0
  17. package/dist/describe.js +195 -0
  18. package/dist/errors.d.ts +59 -13
  19. package/dist/errors.js +190 -29
  20. package/dist/index.d.ts +52 -70
  21. package/dist/index.js +189 -122
  22. package/dist/internal.d.ts +5 -0
  23. package/dist/jsonschema.js +18 -17
  24. package/dist/load.d.ts +20 -12
  25. package/dist/load.js +207 -181
  26. package/dist/mcp.d.ts +5 -7
  27. package/dist/mcp.js +168 -214
  28. package/dist/messages/access.d.ts +90 -0
  29. package/dist/messages/access.js +113 -0
  30. package/dist/messages/api.d.ts +171 -0
  31. package/dist/messages/api.js +215 -0
  32. package/dist/messages/boot.d.ts +201 -0
  33. package/dist/messages/boot.js +256 -0
  34. package/dist/messages/codes.d.ts +172 -0
  35. package/dist/messages/codes.js +173 -0
  36. package/dist/messages/index.d.ts +95 -0
  37. package/dist/messages/index.js +118 -0
  38. package/dist/messages/mcp.d.ts +306 -0
  39. package/dist/messages/mcp.js +387 -0
  40. package/dist/messages/model.d.ts +233 -0
  41. package/dist/messages/model.js +283 -0
  42. package/dist/messages/query.d.ts +131 -0
  43. package/dist/messages/query.js +158 -0
  44. package/dist/messages/sql-access.d.ts +70 -0
  45. package/dist/messages/sql-access.js +87 -0
  46. package/dist/messages/sql-core.d.ts +150 -0
  47. package/dist/messages/sql-core.js +181 -0
  48. package/dist/messages/sql-time.d.ts +71 -0
  49. package/dist/messages/sql-time.js +88 -0
  50. package/dist/messages/sql-write.d.ts +246 -0
  51. package/dist/messages/sql-write.js +270 -0
  52. package/dist/messages/time.d.ts +23 -0
  53. package/dist/messages/time.js +29 -0
  54. package/dist/messages/types.d.ts +27 -0
  55. package/dist/messages/types.js +5 -0
  56. package/dist/messages/write.d.ts +405 -0
  57. package/dist/messages/write.js +509 -0
  58. package/dist/model.d.ts +73 -60
  59. package/dist/model.js +20 -29
  60. package/dist/ops.d.ts +35 -0
  61. package/dist/ops.js +101 -0
  62. package/dist/pglite.d.ts +0 -9
  63. package/dist/pglite.js +3 -1
  64. package/dist/registry.d.ts +3 -19
  65. package/dist/registry.js +3 -16
  66. package/dist/signals.d.ts +1 -5
  67. package/dist/signals.js +1 -5
  68. package/dist/sql.d.ts +7 -1
  69. package/dist/sql.js +101 -85
  70. package/dist/sync.d.ts +9 -3
  71. package/dist/sync.js +40 -17
  72. package/dist/tx.d.ts +11 -1
  73. package/dist/tx.js +32 -11
  74. package/dist/typed.d.ts +240 -45
  75. package/dist/types.d.ts +15 -90
  76. package/dist/types.js +12 -14
  77. package/dist/up.d.ts +53 -32
  78. package/dist/up.js +180 -74
  79. package/dist/uuid.js +5 -5
  80. package/dist/validate.d.ts +13 -1
  81. package/dist/validate.js +61 -29
  82. package/dist/watch.d.ts +5 -16
  83. package/dist/watch.js +23 -77
  84. package/dist/write.d.ts +9 -16
  85. package/dist/write.js +102 -185
  86. package/llms.txt +22 -25
  87. package/package.json +2 -9
  88. package/sql/10-core.sql +7 -50
  89. package/sql/15-errors.sql +64 -10
  90. package/sql/20-context.sql +7 -6
  91. package/sql/30-validate.sql +35 -27
  92. package/sql/40-class.sql +20 -114
  93. package/sql/50-acl.sql +8 -13
  94. package/sql/60-write.sql +220 -676
  95. package/sql/70-read.sql +11 -13
  96. package/sql/80-auth.sql +23 -27
  97. package/sql/90-time.sql +77 -944
  98. package/sql/95-seed.system.sql +7 -14
  99. package/sql/99-revision.sql +2 -1
  100. package/sql/README.md +32 -24
  101. package/sql/migrate/README.md +14 -0
  102. package/AGENT-CHEATSHEET.en.md +0 -488
  103. package/MIGRATION.md +0 -190
  104. package/README.en.md +0 -2910
  105. package/dist/import.d.ts +0 -185
  106. package/dist/import.js +0 -820
  107. package/dist/project.d.ts +0 -359
  108. package/dist/project.js +0 -1258
  109. package/sql/75-history.sql +0 -436
  110. package/sql/88-derived.sql +0 -1236
  111. package/sql/migrate/0006-changed-paths.sql +0 -3
  112. package/sql/migrate/0007-secret-account-fk.sql +0 -12
@@ -21,8 +21,6 @@ begin
21
21
  if p_class = 'Class' then
22
22
  select c.data, c.validator into d, vld from @SCHEMA@.class_prepare(d, p_tenant) c;
23
23
  end if;
24
- -- журнал раньше строки: агрегатные поля новой цели триггер после вставки досчитывает в ту же версию — и в entity,
25
- -- и в уже записанную версию журнала (план 1.1, §2.4)
26
24
  insert into @SCHEMA@.log (id, rev, class, tenant, owner, links, data, tags, at, author, op)
27
25
  values (p_id, 1, p_class, p_tenant, p_tenant, coalesce(p_links, '{}'), d, '{}', now_ts, '@SYSTEM@', 'create');
28
26
  insert into @SCHEMA@.entity (id, rev, class, tenant, owner, links, data, at, created_at, author, op, validator)
@@ -30,10 +28,6 @@ begin
30
28
  if p_class in ('rule', 'Resource', 'member', 'Account', 'Class') then
31
29
  perform @SCHEMA@.acl_bump();
32
30
  end if;
33
- -- новая цель агрегатов: метки её источников — заново
34
- if p_class = 'Class' and d->'resolved' ? 'aggregate' then
35
- perform @SCHEMA@.agg_mark(p_tenant, @SCHEMA@.agg_of(d->'resolved'));
36
- end if;
37
31
  return p_id;
38
32
  end $$;
39
33
  alter function @SCHEMA@.sys_create(uuid, text, uuid, jsonb, jsonb) owner to @OWNER@;
@@ -58,10 +52,10 @@ alter function @SCHEMA@.sys_resource(uuid, text, text, jsonb) owner to @OWNER@;
58
52
  alter function @SCHEMA@.sys_rule(uuid, uuid, uuid, text, int, jsonb) owner to @OWNER@;
59
53
  revoke all on function @SCHEMA@.sys_resource(uuid, text, text, jsonb), @SCHEMA@.sys_rule(uuid, uuid, uuid, text, int, jsonb) from public;
60
54
 
61
- -- Системный сид целиком (установка и reset({ all }), этап 6): ключ подписи и счётчик прав, System,
55
+ -- Системный сид целиком (установка, этап 6): ключ подписи и счётчик прав, System,
62
56
  -- системные классы, общие группы и системные права (этап 5, п. 8). Роль admin — роль членства в
63
57
  -- System: системные администраторы работают в арендаторе System. Права auth.* — сервису
64
- -- (имперсонация, выпуск сессий, креды), letopis.* (verify, reset, maintain) — только системному
58
+ -- (имперсонация, выпуск сессий, креды), letopis.* (maintain) — только системному
65
59
  -- администратору. Повторный вызов ничего не меняет.
66
60
  create or replace function @SCHEMA@.seed_system() returns void
67
61
  language plpgsql volatile
@@ -78,16 +72,15 @@ declare
78
72
  o_signals uuid;
79
73
  op text;
80
74
  begin
81
- -- аккаунт System — раньше строки ключа: служебные строки secret (KEY, LOCK, PROJECTOR) принадлежат ему, а внешний
75
+ -- аккаунт System — раньше строки ключа: служебные строки secret (KEY, LOCK) принадлежат ему, а внешний
82
76
  -- ключ secret.account (ревизия 7) требует строку аккаунта в entity. Счётчик прав sys_create повышает, только когда
83
- -- строка ключа уже есть, — на свежей установке и после reset({ all }) ей неоткуда взяться, и начальный v — 0
77
+ -- строка ключа уже есть, — на свежей установке ей неоткуда взяться, и начальный v — 0
84
78
  perform @SCHEMA@.sys_create('@SYSTEM@'::uuid, 'Account', '@SYSTEM@'::uuid, '{}',
85
79
  '{"name": "System", "categories": ["System"], "enabled": true}');
86
80
 
87
81
  -- Ключ подписи пропуска прав и счётчик изменений прав (50-acl.sql: acl_token, acl_bump); от ключа же
88
82
  -- выводится ключ сигналов. Режим сигналов (up({ signals })) хранится в meta этой строки: установщик
89
- -- читает его перед накатом, поэтому повторный up() без опции режим не меняет, а reset({ all }) кладёт
90
- -- его заново из константы установки.
83
+ -- читает его перед накатом, поэтому повторный up() без опции режим не меняет.
91
84
  insert into @SCHEMA@.secret as k (id, account, kind, material, meta)
92
85
  values ('@SYSTEM@'::uuid, '@SYSTEM@'::uuid, 'KEY', @PGCRYPTO@.gen_random_bytes(32), '{"v": 0, "signals": "@SIGNALS@"}')
93
86
  on conflict (id) do update set meta = k.meta || '{"signals": "@SIGNALS@"}'
@@ -148,8 +141,8 @@ begin
148
141
  foreach op in array array['READ', 'WRITE', 'DELETE'] loop
149
142
  perform @SCHEMA@.sys_rule(sys, g_admin, @SCHEMA@.sys_resource(sys, 'all:' || op, op, null), 'allow', 100);
150
143
  end loop;
151
- -- строки-замки (60-write.sql: lock_row) семейств всех классов, в том числе классов арендаторов, заведённых до 1.2, и
152
- -- обслуживания — заранее: первое обращение под нагрузкой их не вставляет
144
+ -- строки-замки (60-write.sql: lock_row) семейств всех классов — системных, из сидов (sys_create их не заводит) и
145
+ -- арендаторов — и обслуживания — заранее: первое обращение под нагрузкой их не вставляет
153
146
  perform @SCHEMA@.family_rows(c.tenant, c.data) from @SCHEMA@.entity c where c.class = 'Class';
154
147
  perform @SCHEMA@.lock_row('@SCHEMA_NAME@:maintain');
155
148
  end $$;
@@ -1,4 +1,5 @@
1
1
  -- letopis 1.0 — штамп ревизии движка (план, §2.2, этап 1, п. 11): последний оператор наката.
2
2
  -- Номер совпадает с DDL_REVISION в lib/src/types.ts (проверяет check-docs). С 1.4 в штампе ещё версия пакета и хэш
3
3
  -- файлов ядра (engine, sql): connect() предупреждает, если функции схемы ставила другая версия движка той же ревизии.
4
- comment on schema @SCHEMA@ is 'letopis ddl_revision=7 engine=@ENGINE_VERSION@ sql=@ENGINE_SQL@';
4
+ -- Ревизия 8 — первая у 2.0 (план 2.0, редакция 2): схему ниже неё (letopis 1.x) up() и connect() отклоняют.
5
+ comment on schema @SCHEMA@ is 'letopis ddl_revision=8 engine=@ENGINE_VERSION@ sql=@ENGINE_SQL@';
package/sql/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # SQL ядра letopis 1.0
1
+ # SQL ядра letopis 2.0
2
2
 
3
3
  Файлы накатываются установщиком (`lib/src/up.ts`, функция `install`) по возрастанию номера; каждый можно применить повторно.
4
- Спецификация — `reports/PLAN-LETOPIS-1.0.md`, §2 и этап 1.
4
+ Спецификация — `reports/PLAN-LETOPIS-1.0.md` (§2 и этап 1) и `reports/PLAN-LETOPIS-2.0.md`.
5
5
 
6
6
  ## Подстановки при накате
7
7
 
@@ -10,27 +10,39 @@
10
10
  | `@SCHEMA@` | имя схемы в кавычках, например `"v2.salon"` |
11
11
  | `@SCHEMA_NAME@` | имя схемы как строковый литерал без кавычек идентификатора, например `v2.salon` |
12
12
  | `@OWNER@` | роль-владелец, по умолчанию `letopis_owner` |
13
+ | `@OWNER_NAME@` | имя роли-владельца строковым литералом без кавычек идентификатора, например `letopis_owner` (условия триггеров, `pg_has_role`) |
13
14
  | `@APP@` | роль приложения, по умолчанию `letopis_app` |
14
15
  | `@PGCRYPTO@` | схема, где установлен pgcrypto (`pg_extension.extnamespace`) |
15
- | `@SYSTEM@` | id системного арендатора System: `2eba6d0a-1edd-4bb6-a85a-a703dab49035` (как в 0.21) |
16
- | `@ALLOW_RESET@` | `true` или `false` — константа установки |
17
- | `@HISTORY_DEFAULT@` | политика хранения по умолчанию `up({ history })` литералом JSON или `'null'` — константа установки |
18
- | `@RESERVED_NAMES@` | методы цепочки — `RESERVED_CLASS_NAMES` из `lib/src/types.ts`, литералами через запятую |
19
- | `@SIGNALS@` | режим сигналов `class`, `tenant`, `opaque` или `none` (план 1.1, §2.2): опция `up({ signals })`, иначе сохранённый в `meta.signals` строки ключа `secret`, иначе `class` — константа установки |
16
+ | `@SYSTEM@` | id системного арендатора System: `2eba6d0a-1edd-4bb6-a85a-a703dab49035` |
17
+ | `@RESERVED_NAMES@` | методы цепочки, корня `db` и батча — `RESERVED_CLASS_NAMES` из `lib/src/types.ts`, литералами через запятую |
18
+ | `@MESSAGES@` | каталог сообщений базы — разделы `sql-*` каталога `lib/src/messages` (план 2.0, этап 3, п. 3): литерал JSON «ключ → `{ name, ru }`» для `message_catalog()` в `15-errors.sql` |
19
+ | `@SIGNALS@` | режим сигналов `class`, `tenant`, `opaque` или `none`: опция `up({ signals })`, иначе сохранённый в `meta.signals` строки ключа `secret`, иначе `class` — константа установки |
20
20
  | `@ENGINE_VERSION@` | версия пакета (`lib/package.json`) — штамп движка в комментарии схемы (`99-revision.sql`) |
21
- | `@ENGINE_SQL@` | 16 знаков sha256 исходного текста файлов `NN-*.sql` до подстановок (переводы строк — LF): `connect()` сверяет его с файлами своего пакета |
21
+ | `@ENGINE_SQL@` | 16 знаков sha256 исходного текста файлов `NN-*.sql` до подстановок (переводы строк — LF) и разделов `sql-*` каталога сообщений: `connect()` сверяет его с файлами своего пакета |
22
22
 
23
- ## Колонки снимка версии
23
+ ## Колонки версии
24
24
 
25
- Одинаковы в `entity` и `log` (§2.2): `id`, `rev`, `class`, `tenant`, `owner`, `links`, `data`, `tags`, `at` (время версии),
26
- `author`, `agent`, `op`, `reason`, `moved`. Хэш — генерируемая колонка: `sha256(prev_hash || snapshot(...))`, где
27
- `snapshot()` — неизменяемая функция, время входит числом микросекунд.
25
+ Одинаковы в `entity` и `log` (§2.2): `id`, `rev`, `class`, `cv`, `tenant`, `owner`, `links`, `data`, `tags`, `at` (время
26
+ версии), `author`, `agent`, `op`, `reason`. У `entity` ещё `ends` (генерируемая: все цели концов),
27
+ `created_at` и `validator` (у строк `Class`), у `log` — `seq` и `tx` (курсор подписки).
28
28
 
29
29
  ## Коды ошибок
30
30
 
31
31
  Собственный класс SQLSTATE `LT`; текст — `letopis: <имя>: <сообщение>`, детали — JSON в `DETAIL`.
32
32
  Источник истины — функция `error_catalog()` в `15-errors.sql`.
33
33
 
34
+ Тексты — по ключам каталога сообщений (план 2.0, этап 3, п. 3): `lt_fail(ключ, параметры jsonb, detail jsonb)` берёт имя
35
+ ошибки и русский шаблон из `message_catalog()` (`{имя}` — параметр, подставляет `lt_text()`), а в `DETAIL` кладёт поля
36
+ `detail` и ещё `key`, `params` — библиотека переносит их в `err.key` и `err.params`; текст тот же, что видит сырой SQL.
37
+ `LetopisError.detail`: без `detail` — `{}`; с `p_detail => 'null'` в `DETAIL` — признак `nodetail`, и у ошибки `detail` нет.
38
+ Свой SQLSTATE — `lt_raise(код, ключ, параметры, detail)`. Функции, которые исполняет приложение (`inc`, `links_patch`),
39
+ бросают `raise` сами: текст — `lt_text(ключ, параметры)`, она и `message_catalog()` выданы приложению; `DETAIL` без своих
40
+ полей — с `'nodetail', true`. Все места бросают ошибки по ключам: перегрузки `lt_fail(имя, текст, detail)` и
41
+ `lt_raise(код, имя, текст, detail)` — сам механизм, вне `15-errors.sql` их не зовут (`lt_raise` с текстом — единственный
42
+ `raise`). Внутренняя ошибка — `lt_raise('XX000', ключ, …)` с кодом `internal`; строки отказа ужесточения и нарушения данных несут `key` и `params`, а
43
+ текст `problem` и `message` собирает `lt_text`. Литеральный текст в `raise` и в `problem` ловит `check:docs`
44
+ (`literals`).
45
+
34
46
  | Код | Имя | Когда |
35
47
  |---|---|---|
36
48
  | LT001 | `no_session` | нет действующей сессии |
@@ -41,22 +53,18 @@
41
53
  | LT006 | `acl_denied` | отказ по правилам доступа |
42
54
  | LT007 | `id_mismatch` | id не равен v5 от ключа; в деталях `expected` |
43
55
  | LT008 | `id_from_db` | id класса без ключа выдаёт база |
44
- | LT009 | `immutable_key` | ключевое поле, id, арендатор, владелец менять нельзя |
56
+ | LT009 | `immutable_key` | ключевое поле, класс, id, арендатор, владелец менять нельзя |
45
57
  | LT010 | `invalid_class` | описание класса отклонено, класс не найден или абстрактный |
46
- | LT011 | `use_reclass` | класс с ключом меняется функцией `reclass()` |
47
- | LT012 | `reclass_denied` | роли ссылающихся строк не принимают новый класс; в деталях `rows`, `others` |
48
58
  | LT013 | `tightening_conflict` | ужесточению мешают строки; в деталях `rows`, `others` |
49
- | LT014 | `isolation_level` | удаление и смена класса — только в `read committed` |
59
+ | LT014 | `isolation_level` | удаление — только в `read committed` |
50
60
  | LT015 | `recreated_in_statement` | id удалён и создан заново одним оператором |
51
- | LT016 | `cursor_expired` | курсор подписки старше яруса `all` (этап 6) |
52
61
  | LT017 | `delete_restricted` | удалению мешают ссылающиеся строки (`onDelete: restrict`); в деталях `rows`, `others` |
53
- | LT018 | `not_deleted` | `restore` и `purge` — только для удалённых объектов (этап 6) |
54
- | LT019 | `reset_disabled` | флаг `allowReset` установки выключен: `reset` и `maintain(now)` недоступны (этап 6) |
55
- | LT020 | `confirm_mismatch` | подтверждение сброса не совпадает с именем схемы (этап 6) |
56
- | LT021 | `derived_class` | строки производного класса (`derivedBy`) пишет и удаляет только его проектор; на них не ссылаются концы (план 1.1, §2.4) |
57
- | LT022 | `aggregate_field` | агрегатное поле (`aggregate`) считает база: приложение и сырой SQL его не передают и не меняют (план 1.1, §2.4) |
58
- | LT023 | `projector_pending` | `purge` удалённого объекта, чьё надгробие ещё не прочёл проектор его класса (ревью безопасности №3, №5), или `trim_history`, чью первую оставшуюся версию он ещё не прочёл (хвост ревью №3) |
62
+ | LT018 | `not_deleted` | `restore` — только для удалённых объектов |
59
63
 
60
64
  ## Порядок файлов
61
65
 
62
- `10-core` → `15-errors` → `20-context` → `30-validate` → `40-class` → `50-acl` → `60-write` → `70-read` → `75-history` → `80-auth` → `88-derived` → `90-time` → `95-seed.system` → `99-revision`. Миграции между ревизиями — `migrate/NNNN-*.sql` (номер — ревизия, к которой ведёт), их применяет `up({ upgrade: true })` до повторного наката.
66
+ `10-core` → `15-errors` → `20-context` → `30-validate` → `40-class` → `50-acl` → `60-write` → `70-read` → `80-auth` → `90-time` → `95-seed.system` → `99-revision`. Миграции между ревизиями — `migrate/NNNN-*.sql` (номер — ревизия, к которой ведёт; правило — `migrate/README.md`), их применяет `up({ upgrade: true })` до повторного наката. У 2.0 (ревизия 8) миграций нет.
67
+
68
+ Файлы описывают только нынешнюю ревизию: `drop … if exists` в них — лишь там, где без него не пройдёт повторный накат
69
+ той же ревизии (политики RLS в `50-acl.sql`: у `create policy` нет `or replace`). Смена сигнатуры функции в пределах
70
+ ревизии — `drop function if exists` прежней в том же файле, иначе повторный накат оставит лишнюю перегрузку.
@@ -0,0 +1,14 @@
1
+ # Миграции схемы letopis 2.x
2
+
3
+ `up({ upgrade: true })` применяет файлы этого каталога к схеме отставшей ревизии 2.x: в транзакции наката, под
4
+ блокировкой схемы и от роли владельца, до повторного наката файлов ядра (`lib/src/up.ts`: `install`, `migrationFiles`).
5
+
6
+ - Имя — `NNNN-описание.sql`: четыре цифры — номер ревизии, к которой ведёт миграция, затем дефис и строчные латинские
7
+ буквы, цифры, точки и дефисы (`^\d{4}-[a-z0-9.-]+\.sql$`). Файлы с другим именем, как этот, установщик пропускает.
8
+ - Номер миграции — новая ревизия движка: тот же номер — `DDL_REVISION` в `lib/src/types.ts` и `ddl_revision` в
9
+ `lib/sql/99-revision.sql` (их сверяет `npm run check:docs`).
10
+ - Применяются миграции с номером выше ревизии схемы и не выше `DDL_REVISION`, по возрастанию номера. Подстановки — как
11
+ у файлов ядра (`@SCHEMA@`, `@OWNER@` и другие — таблица в `lib/sql/README.md`).
12
+ - В миграцию идёт только то, чего не догонит повторный накат: колонка или ограничение существующей таблицы, перенос
13
+ данных. Функции, индексы, триггеры и политики ставят файлы ядра.
14
+ - У 2.0 (ревизия 8) миграций нет.