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/typed.d.ts CHANGED
@@ -1,35 +1,171 @@
1
1
  /**
2
- * Типы цепочек по моделям (план, этап 3, п. 9): `connect({ models })` даёт типы шагов по именам и
3
- * алиасам классов, типы строк ответа и фильтров. Допустимость переходов проверяется во время
4
- * выполнения, как в 0.21. Модель описана структурно (ModelLike), чтобы основная точка входа не
5
- * зависела от Zod: модели letopis/model ей соответствуют.
2
+ * Строгие типы цепочек по моделям (план 2.0, этап 4, п. 1; типы шагов, строк и фильтров — план 1.0, этап 3, п. 9).
3
+ * `connect({ models })` — модели letopis/model — или `connect<Models>()` — объявления `letopis types` (generateTypes,
4
+ * sync.ts) — дают TypedDb без индексных сигнатур:
5
+ * - шаг от корня — только известный класс или алиас: моделей подключения и системных (встроенный набор SystemModels);
6
+ * - переходы цепочки выводятся из концов, как в resolveHop (lib/src/sql.ts): от класса C — роли его семейства (C и
7
+ * потомков) и классы, связанные с семейством C концом в любую сторону (роль, имя которой совпало с именем класса,
8
+ * во время выполнения — шаг класса, так же и здесь);
9
+ * - слоты — только роли класса C: `set(id | строка | цепочка)`, `add` и `remove` — у множественного конца, `unset` —
10
+ * у необязательного;
11
+ * - поля фильтров, `or()` и агрегатов `sum`, `avg`, `min`, `max`, `countBy` — поля класса.
12
+ * Чего типы не знают: возврат к узлу связи из пути (pivot) от класса, с которым связь не связана концом, и роль чужого
13
+ * класса шагом. Это и имена, известные только во время выполнения, — через loose(db) (index.ts): проверки во время
14
+ * выполнения те же. db.as(), db.begin(), db.batch() и entity() отдают цепочки без строгих типов (Db, Batch, Chain).
15
+ * Модель описана структурно (ModelLike), поэтому основная точка входа не зависит от Zod.
6
16
  */
7
- import type { ChainCore, StepFn } from './chain.js';
8
- import type { Cursor, Op, Path, Row, Scalar } from './types.js';
9
- import type { IncMarker } from './write.js';
10
- /** То, что цепочкам нужно от модели: имя, алиас и выведенные типы данных и концов. */
11
- export interface ModelLike<N extends string = string, D = unknown, L = unknown, A extends string = string> {
17
+ import type { ChainCore, ChainRef } from './chain.js';
18
+ import type { Cursor, Op, OP, Path, Row, Scalar } from './types.js';
19
+ import type { DeleteOptions, IncMarker, RestoreOptions } from './write.js';
20
+ /**
21
+ * То, что цепочкам нужно от модели: имя, алиас и типы для вывода — данные строки, концы строки, концы класса (роль →
22
+ * `{ targets: имена классов целей; many; required }`) и имена класса и его предков. Модели letopis/model и объявления
23
+ * `letopis types` ему соответствуют.
24
+ */
25
+ export interface ModelLike<N extends string = string, D = unknown, L = unknown, A extends string = string, E = unknown, P extends string = never> {
12
26
  readonly name: N;
13
27
  readonly alias?: A;
14
28
  readonly __types?: {
15
29
  data: D;
16
30
  links: L;
31
+ ends?: E;
32
+ ancestors?: P;
17
33
  };
18
34
  }
19
- export type AnyModelLike = ModelLike<string, any, any, any>;
20
- type DataOf<C> = C extends ModelLike<string, infer D, unknown, string> ? (D extends Record<string, unknown> ? D : Record<string, unknown>) : Record<string, unknown>;
21
- type LinksOf<C> = C extends ModelLike<string, unknown, infer L, string> ? (L extends Record<string, unknown> ? L : Record<string, string>) : Record<string, string>;
35
+ export type AnyModelLike = ModelLike<string, any, any, any, any, any>;
36
+ /** Обязательный одиночный конец на класс T (системные классы). */
37
+ type One<T extends string> = {
38
+ targets: T;
39
+ many: false;
40
+ required: true;
41
+ };
42
+ type NoKeys = Record<never, never>;
43
+ /**
44
+ * Встроенный набор моделей системных классов (lib/sql/95-seed.system.sql): шаги Class, Account, member, Resource, rule
45
+ * есть у любого TypedDb. Модель с тем же именем в `models` (например, из `letopis types --system`) — вместо встроенной.
46
+ */
47
+ export type SystemModels = readonly [
48
+ ModelLike<'Class', {
49
+ name: string;
50
+ }, NoKeys, never, NoKeys>,
51
+ ModelLike<'Account', {
52
+ name: string;
53
+ enabled?: boolean;
54
+ categories?: string[];
55
+ }, NoKeys, never, NoKeys>,
56
+ ModelLike<'member', {
57
+ roles?: string[];
58
+ }, {
59
+ User: string;
60
+ Tenant: string;
61
+ }, never, {
62
+ User: One<'Account'>;
63
+ Tenant: One<'Account'>;
64
+ }>,
65
+ ModelLike<'Resource', {
66
+ alias: string;
67
+ category: 'ACCOUNT' | 'API' | 'READ' | 'WRITE' | 'DELETE';
68
+ pattern?: {
69
+ categories?: string;
70
+ roles?: string;
71
+ endpoint?: string;
72
+ class?: string;
73
+ };
74
+ description?: string;
75
+ }, NoKeys, never, NoKeys>,
76
+ ModelLike<'rule', {
77
+ permission: 'allow' | 'deny';
78
+ weight?: number;
79
+ enabled?: boolean;
80
+ code?: number;
81
+ message?: string;
82
+ description?: string;
83
+ }, {
84
+ Group: string;
85
+ Object: string;
86
+ }, never, {
87
+ Group: One<'Resource'>;
88
+ Object: One<'Resource'>;
89
+ }>
90
+ ];
91
+ type NameOf<K> = K extends {
92
+ readonly name: infer N extends string;
93
+ } ? N : never;
94
+ /** Алиас модели; нет алиаса (или он не литерал) — never. */
95
+ type AliasOf<K> = K extends {
96
+ readonly alias?: infer A;
97
+ } ? (A extends string ? (string extends A ? never : A) : never) : never;
98
+ /** Имена, которыми модель вызывается шагом: имя и алиас. */
99
+ type StepNames<K> = NameOf<K> | AliasOf<K>;
100
+ type TypesOf<K> = K extends {
101
+ readonly __types?: infer T;
102
+ } ? NonNullable<T> : NoKeys;
103
+ /** Данные строки класса C (интерфейсы `letopis types` — тоже: индексная сигнатура не требуется). */
104
+ type DataOf<C> = C extends unknown ? (TypesOf<C> extends {
105
+ data: infer D;
106
+ } ? (D extends object ? D : Record<string, unknown>) : Record<string, unknown>) : never;
107
+ type LinksOf<C> = C extends unknown ? (TypesOf<C> extends {
108
+ links: infer L;
109
+ } ? (L extends object ? L : Record<string, string>) : Record<string, string>) : never;
110
+ /** Концы класса C: роль → { targets, many, required }; модель без них — без концов. */
111
+ type EndsOf<C> = C extends unknown ? (TypesOf<C> extends {
112
+ ends?: infer E;
113
+ } ? EndsOrNone<NonNullable<E>> : NoKeys) : never;
114
+ type EndsOrNone<E> = [E] extends [object] ? E : NoKeys;
115
+ /** Имя класса и его предков (ancestors реестра). */
116
+ type AncestorsOf<K> = K extends unknown ? NameOf<K> | (TypesOf<K> extends {
117
+ ancestors?: infer P;
118
+ } ? Extract<P, string> : never) : never;
119
+ type EndTargets<S> = S extends {
120
+ targets: infer T;
121
+ } ? Extract<T, string> : never;
122
+ /** Классы целей всех концов модели. */
123
+ type TargetsOf<K> = K extends unknown ? {
124
+ [R in keyof EndsOf<K>]: EndTargets<EndsOf<K>[R]>;
125
+ }[keyof EndsOf<K>] : never;
126
+ /** Роли модели. */
127
+ type RolesOf<K> = K extends unknown ? Extract<keyof EndsOf<K>, string> : never;
128
+ /** Модели подключения и встроенные системные (своя модель системного класса — вместо встроенной). */
129
+ type AllOf<M extends readonly AnyModelLike[]> = M[number] | Exclude<SystemModels[number], {
130
+ readonly name: NameOf<M[number]>;
131
+ }>;
132
+ /** Семейство класса C: сам класс и потомки (reg.family). */
133
+ type FamilyOf<C, All> = C extends unknown ? C | DescendantsOf<NameOf<C>, All> : never;
134
+ type DescendantsOf<N, All> = All extends unknown ? (N extends AncestorsOf<All> ? All : never) : never;
135
+ type FamTargets<C, All> = TargetsOf<FamilyOf<C, All>>;
136
+ type FamAncestors<C, All> = AncestorsOf<FamilyOf<C, All>>;
137
+ /**
138
+ * Связаны ли классы C и X концом: роль семейства C принимает класс семейства X (вперёд) или роль семейства X — класс
139
+ * семейства C (назад); конец принимает класс и его потомков (accepts).
140
+ */
141
+ type Linked<C, X, All> = [FamTargets<C, All> & FamAncestors<X, All>] extends [never] ? ([FamTargets<X, All> & FamAncestors<C, All>] extends [never] ? false : true) : true;
142
+ /** Классы, в которые ведёт шаг от C. */
143
+ type HopClasses<C, All> = HopClassesOf<C, All, All>;
144
+ type HopClassesOf<C, X, All> = X extends unknown ? (Linked<C, X, All> extends true ? X : never) : never;
145
+ /** Модель по имени или алиасу шага. */
146
+ type ByStepName<P, U> = U extends unknown ? (P extends StepNames<U> ? U : never) : never;
147
+ /** Классы целей роли R: имя роли однозначно во всей схеме (проверяет база). */
148
+ type RoleTargets<R, All> = All extends unknown ? (R extends RolesOf<All> ? EndTargets<EndsOf<All>[R & keyof EndsOf<All>]> : never) : never;
149
+ /** Модели классов T; класса нет среди моделей — только имя (строки без типов данных). */
150
+ type ModelsNamed<T, All> = (All extends unknown ? (NameOf<All> extends T ? All : never) : never) | BareOf<Exclude<T, NameOf<All>>>;
151
+ type BareOf<T> = T extends string ? {
152
+ readonly name: T;
153
+ } : never;
154
+ /**
155
+ * Анонимный тип объекта вместо интерфейса: у интерфейса (`<Класс>Data` из `letopis types`) нет неявной индексной
156
+ * сигнатуры, и без этого его строка не подошла бы туда, где ждут `Row` (`entity()`, `cursorOf()`).
157
+ */
158
+ type Plain<T> = {
159
+ [K in keyof T]: T[K];
160
+ };
22
161
  /** Строка ответа класса C. */
23
- export type RowOf<C> = Row<DataOf<C>, LinksOf<C>>;
162
+ export type RowOf<C> = C extends unknown ? Row<Plain<DataOf<C>>, Plain<LinksOf<C>>> : never;
24
163
  /** Фильтр по значению поля: равенство, оператор, вложенный объект любой глубины. */
25
164
  export type FieldFilterOf<T> = Op | (T extends readonly unknown[] ? T : T extends Record<string, unknown> ? {
26
165
  [P in keyof T]?: FieldFilterOf<T[P]>;
27
166
  } : T extends Scalar ? T | null : never);
28
- /** Фильтр шага класса C: id, список id, строка ответа, or(…) или объект полей и ролей. */
29
- export type FilterOf<C> = string | string[] | Op | {
30
- id: string;
31
- class: string;
32
- } | ({
167
+ /** Фильтр-объект класса C: поля данных, роли концов и id. */
168
+ type ObjectFilterOf<C> = C extends unknown ? {
33
169
  [P in keyof DataOf<C>]?: FieldFilterOf<DataOf<C>[P]>;
34
170
  } & {
35
171
  [R in keyof LinksOf<C>]?: string | {
@@ -37,26 +173,98 @@ export type FilterOf<C> = string | string[] | Op | {
37
173
  } | null;
38
174
  } & {
39
175
  id?: string | string[];
40
- });
41
- /** Шаги по именам и алиасам моделей M. */
42
- export type StepsOf<M extends readonly AnyModelLike[]> = {
43
- [K in M[number] as K['name']]: (filter?: FilterOf<K>) => TypedChain<K, M>;
176
+ } : never;
177
+ /**
178
+ * Результат `or(…)` (index.ts): оператор `or` и фильтры, которыми он типизирован; `F` — только для типов. В шаге класса C
179
+ * фильтры внутри `or()` — фильтры-объекты класса C.
180
+ */
181
+ export type OrOp<F = Record<string, unknown>> = Op & {
182
+ readonly [K in typeof OP]: 'or';
44
183
  } & {
45
- [K in M[number] as K extends ModelLike<string, unknown, unknown, infer A> ? (string extends A ? never : A) : never]: (filter?: FilterOf<K>) => TypedChain<K, M>;
184
+ readonly __filters?: F;
46
185
  };
186
+ /** Фильтр шага класса C: id, список id, строка ответа, or(…) из фильтров класса или объект полей и ролей. */
187
+ export type FilterOf<C> = string | string[] | {
188
+ id: string;
189
+ class: string;
190
+ } | OrOp<ObjectFilterOf<C>> | ObjectFilterOf<C>;
47
191
  /** Патч update: поля любой глубины необязательны; inc() — только у числовых полей (§2.5). */
48
192
  export type PatchOf<T> = {
49
193
  [P in keyof T]?: T[P] extends number | undefined ? T[P] | IncMarker : T[P] extends readonly unknown[] | undefined ? T[P] : T[P] extends Record<string, unknown> | undefined ? PatchOf<NonNullable<T[P]>> : T[P];
50
194
  };
51
- type Retyped = 'paths' | 'rows' | 'first' | 'versions' | 'limit' | 'offset' | 'sort' | 'asOf' | 'after' | 'withDeleted' | 'deep' | 'exact' | 'alias' | 'tags' | 'owner' | 'create' | 'update' | 'upsert' | 'delete' | 'anonymize' | 'reclass' | 'forUpdate' | 'restore' | 'purge' | 'rekey' | 'cache';
52
- /** Цепочка с текущим классом C: терминалы отдают RowOf<C>, шаги — по моделям M; роли и прочие классы — без типов. */
195
+ /** Счётчик глубины путей полей. */
196
+ type Depth = [never, 0, 1, 2, 3, 4, 5];
197
+ /** Пути полей данных через точку — без элементов массивов. */
198
+ type FieldPaths<T, D extends number = 6> = [D] extends [never] ? never : T extends readonly unknown[] ? never : T extends object ? {
199
+ [K in keyof T & string]-?: K | `${K}.${FieldPaths<NonNullable<T[K]>, Depth[D]>}`;
200
+ }[keyof T & string] : never;
201
+ /** Пути числовых полей (и полей без типа). */
202
+ type NumberPaths<T, D extends number = 6> = [D] extends [never] ? never : T extends readonly unknown[] ? never : T extends object ? {
203
+ [K in keyof T & string]-?: (unknown extends T[K] ? K : NonNullable<T[K]> extends number ? K : never) | `${K}.${NumberPaths<NonNullable<T[K]>, Depth[D]>}`;
204
+ }[keyof T & string] : never;
205
+ /** Поле агрегата класса C: `'data.<путь>'`. */
206
+ type DataField<C> = `data.${FieldPaths<DataOf<C>>}`;
207
+ /** Числовое поле агрегата класса C (sum, avg). */
208
+ type NumberField<C> = `data.${NumberPaths<DataOf<C>>}`;
209
+ /** Шаг класса C: фильтр класса и цепочка с ним. */
210
+ type StepFnOf<C, M extends readonly AnyModelLike[]> = (filter?: FilterOf<C>) => TypedChain<C, M>;
211
+ /** Шаги корня: имена и алиасы моделей подключения и системных классов. */
212
+ export type StepsOf<M extends readonly AnyModelLike[]> = {
213
+ [K in AllOf<M> as NameOf<K>]: StepFnOf<K, M>;
214
+ } & {
215
+ [K in AllOf<M> as AliasOf<K>]: StepFnOf<K, M>;
216
+ };
217
+ /** Значение слота одиночного конца: id, строка ответа или цепочка. */
218
+ type SlotOne = string | {
219
+ readonly id: string;
220
+ } | ChainRef;
221
+ /** Значение слота множественного конца: ещё и список id или строк. */
222
+ type SlotMany = SlotOne | readonly (string | {
223
+ readonly id: string;
224
+ })[];
225
+ /** Слоты роли с концом S записываемой строки класса C. */
226
+ type SlotFns<S, C, M extends readonly AnyModelLike[]> = {
227
+ set(value: S extends {
228
+ many: true;
229
+ } ? SlotMany : SlotOne): TypedChain<C, M>;
230
+ } & (S extends {
231
+ many: false;
232
+ } ? unknown : {
233
+ add(value: SlotMany): TypedChain<C, M>;
234
+ remove(value: SlotMany): TypedChain<C, M>;
235
+ }) & (S extends {
236
+ required: true;
237
+ } ? unknown : {
238
+ unset(): TypedChain<C, M>;
239
+ });
240
+ /** Шаг от C по имени P: класс или алиас, связанный с C, или роль семейства C (имя роли, совпавшее с классом, — шаг класса). */
241
+ type HopStep<C, P, M extends readonly AnyModelLike[], All> = P extends StepNames<All> ? ([ByStepName<P, HopClasses<C, All>>] extends [never] ? unknown : StepFnOf<ByStepName<P, HopClasses<C, All>>, M>) : P extends RolesOf<FamilyOf<C, All>> ? StepFnOf<ModelsNamed<RoleTargets<P, All>, All>, M> : unknown;
242
+ /** Слоты имени P: только роль самого класса C. */
243
+ type HopSlots<C, P, M extends readonly AnyModelLike[]> = P extends keyof EndsOf<C> ? SlotFns<EndsOf<C>[P], C, M> : unknown;
244
+ /** Свойства-шаги цепочки класса C. */
245
+ type HopsOf<C, M extends readonly AnyModelLike[], All = AllOf<M>> = {
246
+ [P in StepNames<HopClasses<C, All>> | Exclude<RolesOf<FamilyOf<C, All>>, StepNames<All>> | Extract<keyof EndsOf<C>, string>]: HopStep<C, P, M, All> & HopSlots<C, P, M>;
247
+ };
248
+ type Retyped = 'paths' | 'rows' | 'first' | 'versions' | 'limit' | 'offset' | 'sort' | 'asOf' | 'after' | 'withDeleted' | 'deep' | 'exact' | 'alias' | 'tags' | 'owner' | 'create' | 'update' | 'upsert' | 'delete' | 'forUpdate' | 'restore' | 'cache' | 'sum' | 'avg' | 'min' | 'max' | 'countBy';
249
+ /**
250
+ * Цепочка с текущим классом C (у шага-роли с несколькими целями — их союз): терминалы отдают RowOf<C>, шаги — переходы
251
+ * из концов, слоты — роли C, агрегаты — поля C.
252
+ */
53
253
  export type TypedChain<C, M extends readonly AnyModelLike[]> = Omit<ChainCore, Retyped> & {
54
254
  paths(): Promise<Path[]>;
55
255
  rows(): Promise<RowOf<C>[]>;
56
256
  first(): Promise<RowOf<C> | null>;
57
- versions(opts?: {
58
- follow?: boolean;
59
- }): Promise<RowOf<C>[]>;
257
+ versions(): Promise<RowOf<C>[]>;
258
+ /** Сумма числового поля `'data.<путь>'` класса; нет ни одного значения — `null`. */
259
+ sum(field: NumberField<C>): Promise<number | null>;
260
+ /** Среднее числового поля `'data.<путь>'` класса; нет ни одного значения — `null`. */
261
+ avg(field: NumberField<C>): Promise<number | null>;
262
+ /** Наименьшее значение поля `'data.<путь>'` класса. */
263
+ min(field: DataField<C>): Promise<unknown>;
264
+ /** Наибольшее значение поля `'data.<путь>'` класса. */
265
+ max(field: DataField<C>): Promise<unknown>;
266
+ /** Сколько объектов с каждым значением поля `'data.<путь>'` класса. */
267
+ countBy(field: DataField<C>): Promise<Record<string, number>>;
60
268
  limit(n: number): TypedChain<C, M>;
61
269
  offset(n: number): TypedChain<C, M>;
62
270
  sort(field: 'at' | 'rev' | 'id' | `data.${string}`, dir?: 'asc' | 'desc' | boolean): TypedChain<C, M>;
@@ -73,40 +281,31 @@ export type TypedChain<C, M extends readonly AnyModelLike[]> = Omit<ChainCore, R
73
281
  create(data: Partial<DataOf<C>>, opts?: {
74
282
  reason?: string;
75
283
  }): TypedChain<C, M>;
284
+ /** Без фильтра, id, `tags()`, `owner()` и `limit()` у шагов — `invalid_query`; правка всех строк — `{ all: true }`. */
76
285
  update(patch?: PatchOf<DataOf<C>>, opts?: {
77
286
  rev?: number;
287
+ all?: boolean;
78
288
  reason?: string;
79
289
  }): TypedChain<C, M>;
80
290
  upsert(data: Partial<DataOf<C>>, opts?: {
81
291
  reason?: string;
82
292
  }): TypedChain<C, M>;
83
- delete(opts?: {
84
- confirm?: boolean;
85
- reason?: string;
86
- }): TypedChain<C, M>;
87
- anonymize(fields: (keyof DataOf<C> & string)[], opts?: {
88
- reason?: string;
89
- }): TypedChain<C, M>;
90
- reclass(cls: string, data?: Record<string, unknown>, opts?: {
91
- reason?: string;
92
- }): TypedChain<unknown, M>;
293
+ /**
294
+ * Без опций — превью. `{ confirm: true, expect: превью }` — удалить ровно показанное (цели сужены фильтром, id, `tags()`,
295
+ * `owner()` или `limit()`); `{ confirm: true }` без `expect` — `invalid_query` (`write.confirm_without_expect`);
296
+ * осознанно удалить текущий состав без сверки — `{ confirm: true, all: true }`.
297
+ */
298
+ delete(opts?: DeleteOptions): TypedChain<C, M>;
93
299
  forUpdate(): TypedChain<C, M>;
94
- restore(opts?: {
95
- reason?: string;
96
- }): TypedChain<C, M>;
97
- purge(opts?: {
98
- confirm?: boolean;
99
- reason?: string;
100
- }): TypedChain<C, M>;
101
- rekey(patch: Partial<DataOf<C>> & Partial<Record<keyof LinksOf<C>, string | {
102
- id: string;
103
- }>>, opts?: {
104
- reason?: string;
105
- }): TypedChain<C, M>;
300
+ /**
301
+ * `{ expect }` — надгробия `{ id, rev }`: восстановить ровно то, что ушло удалением; расхождение — отказ по смыслу
302
+ * (`write.restore_already`, `write.restore_id_taken`, `write.restore_stale`). `{ expect, partial: true }` — что можно, а
303
+ * остальное — строки `$action: 'skip'` с причиной `$why`. Без `expect` — `invalid_query` (`write.restore_without_expect`);
304
+ * осознанно вернуть все удалённые цели шага без сверки — `{ all: true }`.
305
+ */
306
+ restore(opts: RestoreOptions): TypedChain<C, M>;
106
307
  cache(opts?: {
107
308
  ttl?: number;
108
309
  }): TypedChain<C, M>;
109
- } & StepsOf<M> & {
110
- [step: string]: StepFn;
111
- };
310
+ } & HopsOf<C, M>;
112
311
  export {};
package/dist/types.d.ts CHANGED
@@ -3,35 +3,41 @@
3
3
  */
4
4
  /**
5
5
  * Ревизия схемы, которую знает эта версия пакета, — номер версии SQL-части letopis (таблиц и функций в схеме установки).
6
- * После `up()` у схемы та же ревизия (`r.revision`); схему отставшей ревизии `up()` обновляет только с `upgrade: true`,
7
- * а `connect()` о ней предупреждает.
6
+ * После `up()` у схемы та же ревизия (`r.revision`); схему отставшей ревизии 2.x `up()` обновляет только с
7
+ * `upgrade: true`, а `connect()` о ней предупреждает.
8
8
  */
9
- export declare const DDL_REVISION = 7;
9
+ export declare const DDL_REVISION = 11;
10
+ /**
11
+ * Префикс версии в имени схемы `v<N>.<имя>`: `up({ schema: 'salon' })` и `connect({ schema: 'salon' })` дописывают его к
12
+ * имени без точки — `v2.salon`.
13
+ */
14
+ export declare const SCHEMA_VERSION = "v2";
10
15
  /**
11
16
  * Классы, строки которых меняют права и схему: их запись повышает счётчик прав (загрузчик), а сигнал
12
17
  * сбрасывает весь кэш результатов и пропуска прав. Тот же список — в триггерах базы (60-write.sql).
13
18
  */
14
19
  export declare const ACL_CLASS_NAMES: readonly string[];
15
20
  /**
16
- * Имена методов цепочки: модификаторы, терминалы, глаголы записи и слоты концов, а также `then` и снятые методы прежних
17
- * версий. После шага цепочки эти имена библиотека не ищет среди классов; все они входят в `RESERVED_CLASS_NAMES`.
21
+ * Имена методов цепочки: модификаторы, терминалы, глаголы записи и слоты концов, а также `then`. После шага цепочки эти
22
+ * имена библиотека не ищет среди классов; все они входят в `RESERVED_CLASS_NAMES`.
18
23
  */
19
- export declare const CHAIN_METHODS: readonly ["then", "entity", "paths", "rows", "first", "ids", "count", "limit", "offset", "sort", "asOf", "withDeleted", "deep", "exact", "sum", "avg", "min", "max", "countBy", "after", "versions", "create", "update", "upsert", "set", "unset", "add", "remove", "delete", "anonymize", "reclass", "restore", "purge", "alias", "tags", "owner", "forUpdate", "cache", "rekey", "run", "execute", "account", "diff", "blame"];
24
+ export declare const CHAIN_METHODS: readonly ["then", "entity", "paths", "rows", "first", "ids", "count", "limit", "offset", "sort", "asOf", "withDeleted", "deep", "exact", "sum", "avg", "min", "max", "countBy", "after", "versions", "create", "update", "upsert", "set", "unset", "add", "remove", "delete", "restore", "alias", "tags", "owner", "forUpdate", "cache"];
20
25
  /** Свойства корня db (и транзакции db.begin()). */
21
- export declare const DB_METHODS: readonly ["as", "begin", "commit", "rollback", "lock", "batch", "watch", "close", "registry", "sql", "load", "verify", "maintain", "reset", "describe", "auth", "acl", "idOf", "refresh", "transaction", "flushSignals", "schema", "token", "tenant", "reloadSchema", "accounts", "credentials", "resources", "rules", "compact", "trimHistory", "rekeyClass", "cache", "projector", "rebuildAggregates"];
26
+ export declare const DB_METHODS: readonly ["as", "begin", "commit", "rollback", "lock", "batch", "watch", "close", "registry", "sql", "load", "maintain", "auth", "acl", "idOf", "refresh", "transaction", "flushSignals", "schema", "token", "tenant", "cache"];
22
27
  /** Свойства батча db.batch(name). */
23
- export declare const BATCH_METHODS: readonly ["discard", "size"];
28
+ export declare const BATCH_METHODS: readonly ["run", "discard", "size"];
24
29
  /**
25
30
  * Имена методов цепочки, корня `db` и батча. Там, где имя занято методом, библиотека не ищет его среди классов, и шаг с
26
- * таким именем до класса не дойдёт; класс-связь (`link`) с таким именем база не создаст (`invalid_class`). Не давайте
27
- * классам эти имена.
31
+ * таким именем до класса не дойдёт. Хаб с таким именем отклоняют `hub()`, `syncModels` и база, связь (`link`) — база
32
+ * (`invalid_class`; план 2.0, этап 4, п. 2); хаб, заведённый мимо проверки базы, `up()` оставляет и называет в
33
+ * предупреждении.
28
34
  */
29
35
  export declare const RESERVED_CLASS_NAMES: readonly string[];
30
36
  /** Строка ответа: текущая версия (или версия журнала для asOf, versions, withDeleted). */
31
37
  export interface Row<D = Record<string, unknown>, L = Record<string, string | string[]>> {
32
38
  id: string;
33
39
  class: string;
34
- /** Номер версии — для update(patch, { rev }) (этап 4). */
40
+ /** Номер версии — для update(patch, { rev }). */
35
41
  rev: number;
36
42
  tenant: string;
37
43
  owner: string;
@@ -42,23 +48,25 @@ export interface Row<D = Record<string, unknown>, L = Record<string, string | st
42
48
  at: string;
43
49
  author: string;
44
50
  agent?: string;
45
- /** Вид операции версии: create, update, delete, reclass, … */
51
+ /** Вид операции версии: create, update, delete, restore, … */
46
52
  op: string;
47
53
  reason?: string;
48
- /** Связь со старым или новым id при reclass и rekey. */
49
- moved?: string;
50
54
  /** Надгробие: удалённая сущность (withDeleted) или версия удаления (versions). */
51
55
  $deleted?: true;
52
56
  /** Глубина узла при deep() (1 — прямой потомок); в превью удаления — уровень каскада (0 — цель). */
53
57
  $depth?: number;
54
- /** Что сделал upsert (этап 4). */
58
+ /** Что сделал upsert. */
55
59
  $upsert?: 'created' | 'updated' | 'unchanged';
56
- /** Превью удаления (delete() без confirm): удалится, снимется конец или помешает (restrict). */
57
- $action?: 'delete' | 'unset' | 'restrict';
58
- /** purge(): число версий в журнале (превью) или стёртых. */
59
- $versions?: number;
60
- /** purge({ confirm: true }): история стёрта. */
61
- $purged?: true;
60
+ /**
61
+ * Превью удаления (delete() без confirm): удалится, снимется конец или помешает (restrict). В ответе
62
+ * `restore({ expect, partial: true })` — `skip`: цель не восстановлена, причина — `$why`.
63
+ */
64
+ $action?: 'delete' | 'unset' | 'restrict' | 'skip';
65
+ /**
66
+ * Почему цель `restore({ expect, partial: true })` не восстановлена: `restored` — её уже восстановили, `reissued` — id
67
+ * занял новый объект с тем же ключом, `changed` — после показанного удаления её меняли (удалили снова и т. п.).
68
+ */
69
+ $why?: 'restored' | 'reissued' | 'changed';
62
70
  }
63
71
  /** Путь paths(): узел на каждый шаг цепочки. */
64
72
  export type Path = Record<string, Row>;
@@ -107,28 +115,21 @@ export interface ChainMods {
107
115
  /** Срез «как было на момент T» (ISO). */
108
116
  asOf?: string;
109
117
  after?: Cursor;
110
- /** Последний шаг включает удалённые. */
118
+ /**
119
+ * Не действует: с 2.1 `withDeleted()` — модификатор своего шага (флаг шага цепочки), а не всей выборки. Поле оставлено,
120
+ * чтобы тип не менялся.
121
+ */
111
122
  withDeleted?: boolean;
112
123
  /** Чтение с блокировкой строк последнего шага (только в db.begin()). */
113
124
  forUpdate?: boolean;
114
- /** versions({ follow: true }): история и через смену id (reclass, rekey) по колонке moved. */
115
- follow?: boolean;
116
125
  /** Кэш результатов (§2.12): .cache() — срок по умолчанию, .cache({ ttl }) — свой, секунды. */
117
126
  cache?: {
118
127
  ttl?: number;
119
128
  };
120
- /** Параметры терминалов diff() и blame() (план 1.1, §2.1): номера версий или моменты (ISO). */
121
- hist?: {
122
- from?: number | string;
123
- to?: number | string;
124
- at?: string;
125
- follow?: boolean;
126
- limit?: number;
127
- };
128
129
  }
129
130
  /** Событие хука onQuery: один запрос цепочки. */
130
131
  export interface QueryEvent {
131
- mode: 'paths' | 'rows' | 'ids' | 'count' | 'versions' | 'agg' | 'diff' | 'blame';
132
+ mode: 'paths' | 'rows' | 'ids' | 'count' | 'versions' | 'agg';
132
133
  /** Классы шагов цепочки. */
133
134
  classes: string[];
134
135
  ms: number;
@@ -136,63 +137,65 @@ export interface QueryEvent {
136
137
  /** ms > slowMs. */
137
138
  slow: boolean;
138
139
  }
139
- /** Изменение пути: added — не было, removed — не стало, changed — другое значение. */
140
- export interface DiffChange {
141
- /** Путь: первый сегмент — раздел строки (data, links, tags, owner, class). */
142
- path: string[];
143
- kind: 'added' | 'removed' | 'changed';
144
- before?: unknown;
145
- after?: unknown;
146
- }
147
- /** Различия объекта между двумя версиями (diff()). */
148
- export interface DiffResult {
149
- id: string;
150
- class: string | null;
151
- from: {
152
- rev: number;
153
- at: string;
154
- id?: string;
155
- } | null;
156
- to: {
157
- rev: number;
158
- at: string;
159
- id?: string;
160
- deleted?: true;
161
- } | null;
162
- changes: DiffChange[];
163
- }
164
- /** Кто и почему последним изменил лист версии (blame()). */
165
- export interface BlameField {
166
- path: string[];
167
- rev: number;
168
- at: string;
169
- author: string;
170
- agent?: string;
171
- op: string;
172
- reason?: string;
173
- /** Другой id (история прошла через смену id при reclass или rekey). */
174
- id?: string;
175
- /** false — до найденной версии история неполная: изменение могло быть раньше. */
176
- exact: boolean;
177
- }
178
- /** Поля версии объекта с версиями, которые их изменили (blame()). */
179
- export interface BlameResult {
180
- id: string;
181
- class: string;
182
- rev: number;
183
- deleted?: true;
184
- /** История просмотрена целиком и без пропусков. */
185
- complete: boolean;
186
- fields: BlameField[];
140
+ /** Порог логирования: события этого уровня и выше; `'off'` — ничего. */
141
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
142
+ /** Событие логирования (каталог событий — lib/src/messages/log.ts). Поля без значения в событии отсутствуют. */
143
+ export interface LogEvent {
144
+ /** Время события, ISO 8601 (UTC). */
145
+ ts: string;
146
+ level: Exclude<LogLevel, 'off'>;
147
+ /** Имя события: connect, read, write, delete, error, sql и др. */
148
+ event: string;
149
+ /** Операция: терминал или глагол цепочки, метод db, auth, acl, инструмент MCP. */
150
+ op?: string;
151
+ /** Длительность операции, мс (с точностью до 0,1). */
152
+ ms?: number;
153
+ /** Арендатор сессии фасада. */
154
+ tenant?: string;
155
+ /** Аккаунт фасада: аккаунт сессии или тот, от чьего имени действует db.as(). */
156
+ account?: string;
157
+ /** Метка транзакции db.begin() или db.transaction(). */
158
+ tx?: string;
159
+ /** Цепочка в читаемом виде: `Shop({"name":"A"}).Product.limit(10).rows()`; данные записи — только именами полей. */
160
+ chain?: string;
161
+ /** Строк в ответе или затронуто. */
162
+ rows?: number;
163
+ /** Код ошибки letopis (или SQLSTATE ошибки базы) — у error. */
164
+ code?: string;
165
+ /** Ключ каталога сообщений — у error и warning. */
166
+ key?: string;
167
+ /** Текст ошибки или предупреждения. */
168
+ msg?: string;
169
+ /** Подробности события; секреты замаскированы. */
170
+ detail?: Record<string, unknown>;
171
+ /** Причина ошибки (исходная ошибка базы и т. п.) — у error. */
172
+ cause?: {
173
+ name?: string;
174
+ message: string;
175
+ code?: string;
176
+ };
187
177
  }
188
- /** diff(): «до» и «после» — номер версии или момент (ISO-строка, Date); follow — через смену id. */
189
- export interface DiffOptions {
190
- from?: number | string | Date;
191
- to?: number | string | Date;
192
- follow?: boolean;
178
+ /** Логгер-функция. */
179
+ export type LogFn = (event: LogEvent) => unknown;
180
+ /** Логгер-объект: pino, console и подобные — вызов `logger[уровень](событие, событие.event)`. */
181
+ export interface LogObject {
182
+ debug?(event: LogEvent, msg: string): unknown;
183
+ info?(event: LogEvent, msg: string): unknown;
184
+ warn?(event: LogEvent, msg: string): unknown;
185
+ error?(event: LogEvent, msg: string): unknown;
186
+ log?(event: LogEvent, msg: string): unknown;
193
187
  }
194
- /** blame(): версия на момент at (по умолчанию текущая); limit — сколько версий просматривать (1000). */
195
- export interface BlameOptions {
196
- at?: string | Date;
197
- limit?: number;
188
+ /** Логгер: функция или объект с методами уровней. */
189
+ export type Logger = LogFn | LogObject;
190
+ /** Опции логирования connect() и up(). */
191
+ export interface LogOptions {
192
+ /**
193
+ * Логгер: функция `(событие) => void` или объект с методами `debug`, `info`, `warn`, `error` (pino, console). Без него
194
+ * и с переменной окружения `LETOPIS_LOG=<уровень>` события идут JSON-строками в stderr.
195
+ */
196
+ log?: Logger;
197
+ /** Порог: `'debug' | 'info' | 'warn' | 'error' | 'off'`, по умолчанию `'info'` (или уровень из `LETOPIS_LOG`). */
198
+ logLevel?: LogLevel;
199
+ /** Ещё имена полей, значения которых маскируются в событиях (пароли, токены и ключи маскируются всегда). */
200
+ redact?: readonly string[];
198
201
  }