letopis 1.4.2 → 3.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 (123) hide show
  1. package/AGENT-CHEATSHEET.md +516 -398
  2. package/CHANGELOG.md +739 -0
  3. package/LICENSE +75 -21
  4. package/LICENSE-COMMERCIAL.md +49 -0
  5. package/README.md +1312 -1190
  6. package/api-contract.json +381 -0
  7. package/dist/acl.d.ts +81 -1
  8. package/dist/acl.js +281 -13
  9. package/dist/admin.d.ts +11 -141
  10. package/dist/admin.js +28 -186
  11. package/dist/auth.d.ts +20 -5
  12. package/dist/auth.js +80 -21
  13. package/dist/cache.d.ts +8 -1
  14. package/dist/cache.js +30 -8
  15. package/dist/chain.d.ts +46 -48
  16. package/dist/chain.js +556 -223
  17. package/dist/cli.js +73 -96
  18. package/dist/demo/booking.d.ts +352 -36
  19. package/dist/describe.d.ts +47 -0
  20. package/dist/describe.js +195 -0
  21. package/dist/errors.d.ts +77 -13
  22. package/dist/errors.js +231 -30
  23. package/dist/index.d.ts +62 -74
  24. package/dist/index.js +312 -155
  25. package/dist/internal.d.ts +7 -0
  26. package/dist/jsonschema.js +18 -17
  27. package/dist/load.d.ts +29 -17
  28. package/dist/load.js +235 -190
  29. package/dist/log.d.ts +121 -0
  30. package/dist/log.js +633 -0
  31. package/dist/mcp.d.ts +5 -7
  32. package/dist/mcp.js +194 -225
  33. package/dist/messages/access.d.ts +90 -0
  34. package/dist/messages/access.js +113 -0
  35. package/dist/messages/api.d.ts +237 -0
  36. package/dist/messages/api.js +304 -0
  37. package/dist/messages/boot.d.ts +201 -0
  38. package/dist/messages/boot.js +256 -0
  39. package/dist/messages/codes.d.ts +172 -0
  40. package/dist/messages/codes.js +173 -0
  41. package/dist/messages/index.d.ts +98 -0
  42. package/dist/messages/index.js +121 -0
  43. package/dist/messages/log.d.ts +229 -0
  44. package/dist/messages/log.js +102 -0
  45. package/dist/messages/mcp.d.ts +306 -0
  46. package/dist/messages/mcp.js +387 -0
  47. package/dist/messages/model.d.ts +256 -0
  48. package/dist/messages/model.js +314 -0
  49. package/dist/messages/query.d.ts +141 -0
  50. package/dist/messages/query.js +171 -0
  51. package/dist/messages/sql-access.d.ts +70 -0
  52. package/dist/messages/sql-access.js +87 -0
  53. package/dist/messages/sql-core.d.ts +164 -0
  54. package/dist/messages/sql-core.js +198 -0
  55. package/dist/messages/sql-time.d.ts +86 -0
  56. package/dist/messages/sql-time.js +111 -0
  57. package/dist/messages/sql-write.d.ts +246 -0
  58. package/dist/messages/sql-write.js +270 -0
  59. package/dist/messages/time.d.ts +23 -0
  60. package/dist/messages/time.js +29 -0
  61. package/dist/messages/types.d.ts +27 -0
  62. package/dist/messages/types.js +5 -0
  63. package/dist/messages/write.d.ts +452 -0
  64. package/dist/messages/write.js +572 -0
  65. package/dist/model.d.ts +77 -60
  66. package/dist/model.js +43 -34
  67. package/dist/ops.d.ts +35 -0
  68. package/dist/ops.js +101 -0
  69. package/dist/pglite.d.ts +0 -9
  70. package/dist/pglite.js +3 -1
  71. package/dist/registry.d.ts +3 -19
  72. package/dist/registry.js +3 -16
  73. package/dist/signals.d.ts +1 -5
  74. package/dist/signals.js +1 -5
  75. package/dist/sql.d.ts +9 -1
  76. package/dist/sql.js +175 -107
  77. package/dist/sync.d.ts +10 -4
  78. package/dist/sync.js +90 -17
  79. package/dist/tx.d.ts +21 -2
  80. package/dist/tx.js +76 -13
  81. package/dist/typed.d.ts +252 -53
  82. package/dist/types.d.ts +93 -90
  83. package/dist/types.js +17 -14
  84. package/dist/up.d.ts +56 -35
  85. package/dist/up.js +229 -83
  86. package/dist/uuid.d.ts +8 -0
  87. package/dist/uuid.js +14 -6
  88. package/dist/validate.d.ts +13 -1
  89. package/dist/validate.js +82 -33
  90. package/dist/watch.d.ts +10 -16
  91. package/dist/watch.js +36 -77
  92. package/dist/write.d.ts +87 -17
  93. package/dist/write.js +441 -206
  94. package/llms.txt +48 -31
  95. package/package.json +13 -14
  96. package/sql/10-core.sql +7 -50
  97. package/sql/15-errors.sql +64 -10
  98. package/sql/20-context.sql +18 -9
  99. package/sql/30-validate.sql +79 -30
  100. package/sql/40-class.sql +41 -115
  101. package/sql/50-acl.sql +8 -13
  102. package/sql/60-write.sql +220 -676
  103. package/sql/70-read.sql +11 -13
  104. package/sql/80-auth.sql +109 -42
  105. package/sql/90-time.sql +108 -947
  106. package/sql/95-seed.system.sql +7 -14
  107. package/sql/99-revision.sql +6 -1
  108. package/sql/README.md +32 -24
  109. package/sql/migrate/0009-restore-rev.tenants-name.sql +10 -0
  110. package/sql/migrate/0010-impersonation-no-account.sql +5 -0
  111. package/sql/migrate/0011-key-lock.otp-enabled.sql +7 -0
  112. package/sql/migrate/README.md +18 -0
  113. package/AGENT-CHEATSHEET.en.md +0 -488
  114. package/MIGRATION.md +0 -190
  115. package/README.en.md +0 -2910
  116. package/dist/import.d.ts +0 -185
  117. package/dist/import.js +0 -820
  118. package/dist/project.d.ts +0 -359
  119. package/dist/project.js +0 -1258
  120. package/sql/75-history.sql +0 -436
  121. package/sql/88-derived.sql +0 -1236
  122. package/sql/migrate/0006-changed-paths.sql +0 -3
  123. package/sql/migrate/0007-secret-account-fk.sql +0 -12
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * Этап 2: connect() — подключение под сессией, реестр классов, отложенный сигнал, db.sql.
6
6
  * Этап 3: цепочки чтения t.Класс(…).Связь().rows(); этап 4 — запись цепочками и загрузчик.
7
7
  * Этап 5: db.auth (креды, вход, сессии), db.acl, db.as(аккаунт, { tenant }) — имперсонация.
8
- * Этап 6: история (restore, purge, rekey, trimHistory), verify, maintain, reset, watch, кэш результатов.
8
+ * Этап 6: история (versions, asOf, restore), maintain, watch, кэш результатов.
9
9
  */
10
10
  import postgres from 'postgres';
11
11
  import { Registry } from './registry.js';
@@ -13,58 +13,71 @@ import { type Tx } from './tx.js';
13
13
  import { type AuthApi } from './auth.js';
14
14
  import { type AclApi } from './acl.js';
15
15
  import { type LoadInput, type LoadOptions, type LoadReport } from './load.js';
16
- import { type Batch, type Chain, type StepFn } from './chain.js';
17
- import type { QueryEvent, Row } from './types.js';
16
+ import { type Batch, type Chain, type ChainRef, type StepFn } from './chain.js';
17
+ import type { LogOptions, QueryEvent, Row } from './types.js';
18
18
  import { type CacheOptions, type CacheStats } from './cache.js';
19
19
  import { Subscription, type WatchOptions } from './watch.js';
20
- import { Projector, type ProjectorOptions } from './project.js';
21
- import { type MaintainOptions, type MaintainReport, type ModelDescription, type ResetOptions, type ResetResult, type VerifyResult } from './admin.js';
22
- import type { AnyModelLike, StepsOf } from './typed.js';
20
+ import { type MaintainReport } from './admin.js';
21
+ import type { AnyModelLike, OrOp, StepsOf } from './typed.js';
23
22
  export { DDL_REVISION } from './types.js';
24
23
  export { uuidv5, v5Name, v5Id, idOf, type KeyedClass } from './uuid.js';
25
24
  export { LetopisError, ValidationError, fromDbError, type Issue } from './errors.js';
26
- export { Registry, type AggregateInfo, type ClassInfo, type EndInfo, type LeafType } from './registry.js';
25
+ export { Registry, type ClassInfo, type EndInfo, type LeafType } from './registry.js';
27
26
  export { compileValidator, applyDefaults, type Validator } from './validate.js';
28
27
  export { syncModels, generateTypes, schemaToTs, type SyncResult } from './sync.js';
29
- export { ne, gt, gte, lt, lte, between, inList, like, ilike, starts, ends, has, hasAny, hasAll, exists, isNull, not, or, isOp } from './ops.js';
28
+ export { ne, gt, gte, lt, lte, between, inList, like, ilike, starts, ends, has, hasAny, hasAll, exists, isNull, not, isOp } from './ops.js';
29
+ /**
30
+ * Хотя бы один из фильтров: `db.Product(or({ sku: 'K-1' }, { price: lt(500) }))` — товар K-1 или дешевле 500. Ставится
31
+ * вместо всего фильтра шага, а не значением поля; условия внутри одного фильтра соединяются через «и». В шаге подключения
32
+ * с моделями (`TypedDb`) фильтры внутри — поля и роли класса шага: тип им даёт шаг (план 2.0, этап 4, п. 1). Та же
33
+ * фабрика, что в реестре операторов (ops.ts), — другой у неё только тип.
34
+ */
35
+ export declare const or: <F extends object>(...filters: NoInfer<F>[]) => OrOp<F>;
30
36
  export { cursorOf } from './sql.js';
31
- export { inc, isInc, type IncMarker, type WriteRow } from './write.js';
37
+ export { inc, isInc, type DeleteOptions, type Expected, type IncMarker, type RestoreOptions, type WriteRow } from './write.js';
32
38
  export { totpCode, hashPassword, verifyHash, type AuthApi, type LoginResult, type Credential, type SessionInfo } from './auth.js';
33
- export type { AclApi, AclDecision, AclDataDecision, AclOp } from './acl.js';
39
+ export type { AclApi, AclDecision, AclDataDecision, AclOp, AclRule, AclRuleSpec, AclTarget } from './acl.js';
34
40
  export type { LoadOptions, LoadReport, LoadIssue, LoadInput } from './load.js';
35
- export type { Chain, ChainCore, StepFn, BatchEntry } from './chain.js';
36
- export type { ModelLike, AnyModelLike, RowOf, FilterOf, FieldFilterOf, TypedChain, StepsOf, PatchOf } from './typed.js';
41
+ export type { Chain, ChainCore, ChainRef, StepFn, BatchEntry } from './chain.js';
42
+ export type { ModelLike, AnyModelLike, RowOf, FilterOf, FieldFilterOf, TypedChain, StepsOf, PatchOf, OrOp, SystemModels } from './typed.js';
37
43
  export type { Row, Path, Filter, FieldFilter, Cursor, ChainMods, QueryEvent, Op, Scalar } from './types.js';
38
- export type { DiffChange, DiffResult, DiffOptions, BlameField, BlameResult, BlameOptions } from './types.js';
39
44
  export type { CacheOptions, CacheStats } from './cache.js';
40
45
  export type { SignalMode } from './signals.js';
41
46
  export type { WatchOptions, WatchEvent, Subscription } from './watch.js';
42
- export type { Projector, ProjectorOptions, ProjectorChange, ProjectorOut, ProjectorRow, ProjectorReport, ProjectorStatus, ProjectorFailure } from './project.js';
43
- export type { VerifyResult, VerifyIssue, MaintainOptions, MaintainReport, ResetOptions, ResetResult, ModelDescription } from './admin.js';
44
- export { describeText } from './admin.js';
45
- export { up, createTenant, type UpOptions, type UpResult, type HistoryPolicy, type TenantOptions, type TenantResult } from './up.js';
47
+ export type { MaintainReport } from './admin.js';
48
+ export { up, createTenant, type UpOptions, type UpResult, type TenantOptions, type TenantResult, type LoginOptions } from './up.js';
46
49
  export { RESERVED_CLASS_NAMES, CHAIN_METHODS, DB_METHODS } from './types.js';
47
- /** Снесено в 1.0.0: id v7 больше не выдаются (§2.3). */
48
- export declare function uuidv7(): never;
49
- /** Снесено в 1.0.0: пространство имён v5 — id арендатора. Любое использование значения — ошибка. */
50
- export declare const LETOPIS_NS: string;
51
- export interface ConnectOptions {
50
+ export type { LogEvent, LogFn, LogLevel, LogObject, LogOptions, Logger } from './types.js';
51
+ export { LOG_EVENTS, type LogEventName } from './messages/index.js';
52
+ export interface ConnectOptions extends LogOptions {
52
53
  /** Строка подключения; либо готовый пул postgres.js в `sql`. */
53
54
  dsn?: string;
54
55
  sql?: postgres.Sql;
55
- /** Схема установки, например `v2.salon`. */
56
+ /** Схема установки, например `v2.salon`; имя без точки (`salon`) — как у `up()`: `v2.salon`. */
56
57
  schema: string;
57
58
  /** Токен сессии; либо ключ `APIKEY` в `apiKey` (он же `service` — ключ сервиса) — его обменяют на сессию. */
58
59
  token?: string;
59
60
  apiKey?: string;
60
61
  service?: string;
61
- /** Продлевать сессию раз в минуту работы (по умолчанию да, §2.9). */
62
+ /**
63
+ * Продлевать сессию раз в минуту работы (по умолчанию да, §2.9); после простоя дольше минуты — до операции: истёкшая
64
+ * сессия тогда даёт `no_session`, а не анонимное чтение с пустым ответом, а подключение по `apiKey` обменивает ключ
65
+ * заново. `false` — без продления и без этой проверки.
66
+ */
62
67
  refreshSession?: boolean;
63
68
  /** Разрешить роль, которая обходит RLS (суперпользователь, `BYPASSRLS`, член роли-владельца), — администраторское подключение (§2.9). */
64
69
  allowBypassRls?: boolean;
70
+ /**
71
+ * Один процесс малого приложения (план 2.0, этап 3): строка подключения администратора (или пул postgres.js). Второй
72
+ * пул — не больше двух соединений — открывается при первой надобности и служит только администраторским вызовам корня
73
+ * подключения: `load`, `maintain` и `syncModels`, с той же сессией.
74
+ * Цепочки и `db.sql` идут через пул приложения (`dsn`) под RLS. Роль строки — суперпользователь или роль с `SET` на
75
+ * роль-владельца, иначе `admin_required`. Пароль администратора в процессе приложения — только для малых приложений.
76
+ */
77
+ admin?: string | postgres.Sql;
65
78
  /** Имя роли-владельца (по умолчанию `letopis_owner`). */
66
79
  owner?: string;
67
- /** Интервал отправки отложенного сигнала, мс (по умолчанию 100; решение 9 точки А). */
80
+ /** Интервал отправки отложенного сигнала, мс (по умолчанию 100). */
68
81
  notifyIntervalMs?: number;
69
82
  /** Размер пула, если подключение создаёт сам `connect()`. */
70
83
  max?: number;
@@ -87,7 +100,7 @@ export interface ConnectOptions {
87
100
  * Запускать `maintain()` по расписанию: интервал — число миллисекунд или строка вроде `'15m'`, `'1h'`, от 1 мс до
88
101
  * 2³¹−1 мс (около 24 суток); иначе `connect()` отказывает с `invalid_data`, не открывая соединений. Если обслуживание в
89
102
  * этот момент уже ведёт другой процесс или ещё идёт прошлый запуск, запуск пропускается. `close()` ждёт, пока идущий
90
- * запуск доделает текущую пачку.
103
+ * запуск закончится.
91
104
  */
92
105
  maintain?: string | number;
93
106
  }
@@ -119,37 +132,10 @@ export interface DbCore {
119
132
  /** Отправить накопленные сигналы сейчас. */
120
133
  flushSignals(): Promise<void>;
121
134
  /**
122
- * Проверяет целостность журнала всей установки: сходится ли хэш каждой версии с хэшем предыдущей и текущая строка
123
- * объекта — с его последней версией; с `{ data: true }` проверяет ещё каждую строку. Якорь в ответе — один хэш текущих
124
- * версий всех объектов: храните его вне базы, тогда заметна и подмена самых свежих версий. Нужно право `letopis.verify`
125
- * (по умолчанию оно у системного администратора) или администраторское подключение.
126
- */
127
- verify(opts?: {
128
- data?: boolean;
129
- }): Promise<VerifyResult>;
130
- /** Обслуживание: истёкшие сессии и коды, прореживание журнала по политике хранения, очередь сигналов. */
131
- maintain(opts?: MaintainOptions): Promise<MaintainReport>;
132
- /**
133
- * Сбрасывает тестовую установку: сессии, данные одного арендатора или всё — смотря по `level`. Работает, только если
134
- * установка поставлена с `up({ allowReset: true })`, у вызывающего есть право `letopis.reset` (или подключение
135
- * администраторское), а `confirm` совпадает с именем схемы.
136
- */
137
- reset(opts: ResetOptions): Promise<ResetResult>;
138
- /**
139
- * Стирает версии, сделанные раньше момента `before`; текущая версия остаётся всегда. Вместо стёртого начала истории
140
- * остаётся служебная запись `trim` со временем появления объекта.
141
- */
142
- trimHistory(target: string | string[] | Row | Row[], before: string | Date): Promise<number>;
143
- /**
144
- * Меняет ключ класса. Объекты его семейства (сам класс и потомки), у которых по новому ключу выходит другой id,
145
- * переезжают на новый id, а ссылки на них переводятся.
146
- */
147
- rekeyClass(cls: string, key: string[]): Promise<number>;
148
- /**
149
- * Описание модели для программ и языковых моделей: классы арендатора сессии и общие классы System с полями, ключом,
150
- * ссылками и метаданными `meta`. `describeText(db.describe())` превращает его в текст.
135
+ * Обслуживание: истёкшие и погашенные сессии, одноразовые коды, строки-замки удалённых классов, очередь сигналов. Нужно
136
+ * право `letopis.maintain` (по умолчанию оно у системного администратора) или администраторское подключение.
151
137
  */
152
- describe(): ModelDescription;
138
+ maintain(): Promise<MaintainReport>;
153
139
  /** Подписка по курсору: `for await (const ev of db.watch({ from })) …`; `sub.cursor` — откуда продолжить. */
154
140
  watch(opts?: WatchOptions): Subscription;
155
141
  /** Кэш результатов подключения. */
@@ -158,7 +144,7 @@ export interface DbCore {
158
144
  stats(): CacheStats;
159
145
  };
160
146
  /** Начать путь с готового узла: `db.entity(row | цепочка)`. */
161
- entity(x: Chain | Row): Chain;
147
+ entity(x: ChainRef | Row): Chain;
162
148
  /**
163
149
  * Подключение на том же пуле от имени другого аккаунта: запросы идут с его правами и в его арендаторе. Журнал
164
150
  * записывает автором версий этот аккаунт, а исполнителем (`agent`) — аккаунт сессии, например сервис. Нужно право
@@ -170,7 +156,7 @@ export interface DbCore {
170
156
  owner?: string;
171
157
  tenant?: string;
172
158
  }): Promise<Db>;
173
- /** Явная транзакция `read committed`: те же цепочки, `commit()`, `rollback()`, `lock()`, `forUpdate()`; `reason` — причина изменения для всех операций блока (план 1.1). */
159
+ /** Явная транзакция `read committed`: те же цепочки, `commit()`, `rollback()`, `lock()`, `forUpdate()`; `reason` — причина изменения для всех операций блока. */
174
160
  begin(opts?: {
175
161
  reason?: string;
176
162
  }): Promise<DbTx>;
@@ -181,18 +167,6 @@ export interface DbCore {
181
167
  * от имени пользователя (опция `as`). Работает только на администраторском подключении, иначе `admin_required`.
182
168
  */
183
169
  load(input: LoadInput, opts?: LoadOptions): Promise<LoadReport>;
184
- /**
185
- * Проектор из журнала (план 1.1, §2.4): строит строки производных классов (`derivedBy: имя`) из изменений
186
- * классов `from`; `run()` — догнать журнал и следовать за ним, `rebuild()` — перестроить с текущего состояния.
187
- * Только на администраторском подключении (иначе `admin_required`).
188
- */
189
- projector(name: string, opts: ProjectorOptions): Projector;
190
- /**
191
- * Ремонт агрегатов в базе (план 1.1, §2.4): поля класса (с потомками) во всех арендаторах — заново из живых строк
192
- * источников, под исключительными блокировками; версия — только у исправленных. Возвращает их число. Только на
193
- * администраторском подключении (иначе `admin_required`).
194
- */
195
- rebuildAggregates(cls: string): Promise<number>;
196
170
  /**
197
171
  * Именованная очередь планов фасада: `batch.Класс(…).create(…)`; `batch.run()` — одной транзакцией. У корня,
198
172
  * у каждого `db.as()` и у каждой `db.begin()` очереди свои: план исполняется от имени фасада, который его поставил.
@@ -206,6 +180,10 @@ export interface DbCore {
206
180
  commit(tr?: DbTx): Promise<void>;
207
181
  /** Откатить транзакцию `tr` (то же, что `tr.rollback()`). */
208
182
  rollback(tr?: DbTx): Promise<void>;
183
+ /**
184
+ * Закрыть подключение: у корня — пул и подписки, в `db.begin()` — откат транзакции; у фасада `db.as()` ничего не делает
185
+ * (пул общий с корнем).
186
+ */
209
187
  close(): Promise<void>;
210
188
  }
211
189
  /** Подключение: служебные методы и классы как стартовые шаги цепочек — `db.Класс(фильтр)`. */
@@ -214,12 +192,22 @@ export type Db = DbCore & {
214
192
  };
215
193
  /** Явная транзакция `db.begin()`: тот же API; ошибка любого запроса её ломает (§2.5). */
216
194
  export type DbTx = Db;
217
- /** Подключение с типами шагов, строк и фильтров по моделям: `connect({ models })`. */
218
- export type TypedDb<M extends readonly AnyModelLike[]> = DbCore & StepsOf<M> & {
219
- [className: string]: StepFn;
220
- };
195
+ /**
196
+ * Подключение со строгими типами по моделям: `connect({ models })` или `connect<Models>()` с объявлениями
197
+ * `letopis types`. Шаг — только известный класс или алиас (модели и системные классы), переходы — по концам, слоты — роли
198
+ * класса; имена, известные только во время выполнения, — `loose(db)` (план 2.0, этап 4, п. 1). Модели без литеральных
199
+ * имён (`AnyModel[]` вместо списка `as const`) строгим типам не помогут — тогда это `Db`.
200
+ */
201
+ export type TypedDb<M extends readonly AnyModelLike[]> = string extends M[number]['name'] ? Db : DbCore & StepsOf<M>;
221
202
  export type { Batch };
203
+ /**
204
+ * То же подключение без строгих типов: шагом может быть любое имя, в том числе известное только во время выполнения, —
205
+ * `loose(db)[имя]()`. Объект тот же: проверки во время выполнения (неизвестное имя или поле, переход, слот) не меняются.
206
+ */
207
+ export declare function loose(db: DbCore): Db;
222
208
  export declare function connect<const M extends readonly AnyModelLike[]>(opts: ConnectOptions & {
223
209
  models: M;
224
210
  }): Promise<TypedDb<M>>;
225
211
  export declare function connect(opts: ConnectOptions): Promise<Db>;
212
+ /** Строгие типы без моделей Zod — из объявлений `letopis types`: `connect<Models>({ … })`. */
213
+ export declare function connect<M extends readonly AnyModelLike[]>(opts: ConnectOptions): Promise<TypedDb<M>>;