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
@@ -0,0 +1,381 @@
1
+ {
2
+ "$comment": "СГЕНЕРИРОВАНО lib/scripts/gen-api-contract.mjs из исходников — не редактировать руками. Регенерация: cd lib && npm run api:contract. CI сверяет через npm run check:docs.",
3
+ "package": {
4
+ "name": "letopis",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "exports": [
10
+ ".",
11
+ "./model",
12
+ "./mcp"
13
+ ]
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "values": [
18
+ "CHAIN_METHODS",
19
+ "DB_METHODS",
20
+ "DDL_REVISION",
21
+ "LOG_EVENTS",
22
+ "LetopisError",
23
+ "RESERVED_CLASS_NAMES",
24
+ "Registry",
25
+ "ValidationError",
26
+ "applyDefaults",
27
+ "between",
28
+ "compileValidator",
29
+ "connect",
30
+ "createTenant",
31
+ "cursorOf",
32
+ "ends",
33
+ "exists",
34
+ "fromDbError",
35
+ "generateTypes",
36
+ "gt",
37
+ "gte",
38
+ "has",
39
+ "hasAll",
40
+ "hasAny",
41
+ "hashPassword",
42
+ "idOf",
43
+ "ilike",
44
+ "inList",
45
+ "inc",
46
+ "isInc",
47
+ "isNull",
48
+ "isOp",
49
+ "like",
50
+ "loose",
51
+ "lt",
52
+ "lte",
53
+ "ne",
54
+ "not",
55
+ "or",
56
+ "schemaToTs",
57
+ "starts",
58
+ "syncModels",
59
+ "totpCode",
60
+ "up",
61
+ "uuidv5",
62
+ "v5Id",
63
+ "v5Name",
64
+ "verifyHash"
65
+ ],
66
+ "types": [
67
+ "AclApi",
68
+ "AclDataDecision",
69
+ "AclDecision",
70
+ "AclOp",
71
+ "AclRule",
72
+ "AclRuleSpec",
73
+ "AclTarget",
74
+ "AnyModelLike",
75
+ "AuthApi",
76
+ "Batch",
77
+ "BatchEntry",
78
+ "CacheOptions",
79
+ "CacheStats",
80
+ "Chain",
81
+ "ChainCore",
82
+ "ChainMods",
83
+ "ChainRef",
84
+ "ClassInfo",
85
+ "ConnectOptions",
86
+ "Credential",
87
+ "Cursor",
88
+ "Db",
89
+ "DbCore",
90
+ "DbTx",
91
+ "DeleteOptions",
92
+ "EndInfo",
93
+ "Expected",
94
+ "FieldFilter",
95
+ "FieldFilterOf",
96
+ "Filter",
97
+ "FilterOf",
98
+ "IncMarker",
99
+ "Issue",
100
+ "KeyedClass",
101
+ "LeafType",
102
+ "LoadInput",
103
+ "LoadIssue",
104
+ "LoadOptions",
105
+ "LoadReport",
106
+ "LogEvent",
107
+ "LogEventName",
108
+ "LogFn",
109
+ "LogLevel",
110
+ "LogObject",
111
+ "LogOptions",
112
+ "Logger",
113
+ "LoginOptions",
114
+ "LoginResult",
115
+ "MaintainReport",
116
+ "ModelLike",
117
+ "Op",
118
+ "OrOp",
119
+ "PatchOf",
120
+ "Path",
121
+ "QueryEvent",
122
+ "RestoreOptions",
123
+ "Row",
124
+ "RowOf",
125
+ "Scalar",
126
+ "SessionInfo",
127
+ "SignalMode",
128
+ "StepFn",
129
+ "StepsOf",
130
+ "Subscription",
131
+ "SyncResult",
132
+ "SystemModels",
133
+ "TenantOptions",
134
+ "TenantResult",
135
+ "TypedChain",
136
+ "TypedDb",
137
+ "UpOptions",
138
+ "UpResult",
139
+ "Validator",
140
+ "WatchEvent",
141
+ "WatchOptions",
142
+ "WriteRow"
143
+ ]
144
+ },
145
+ "./model": {
146
+ "values": [
147
+ "End",
148
+ "hub",
149
+ "link",
150
+ "many",
151
+ "normalizeSchema",
152
+ "one",
153
+ "translatePattern"
154
+ ],
155
+ "types": [
156
+ "AnyModel",
157
+ "ClassDef",
158
+ "ClassDescription",
159
+ "ClassMeta",
160
+ "Infer",
161
+ "InferLinks",
162
+ "JsonSchema",
163
+ "LostRule",
164
+ "Model",
165
+ "OnDelete"
166
+ ]
167
+ },
168
+ "./mcp": {
169
+ "values": [
170
+ "createMcpServer"
171
+ ],
172
+ "types": [
173
+ "McpOptions"
174
+ ]
175
+ }
176
+ },
177
+ "chain": [
178
+ "add",
179
+ "after",
180
+ "alias",
181
+ "asOf",
182
+ "avg",
183
+ "cache",
184
+ "count",
185
+ "countBy",
186
+ "create",
187
+ "deep",
188
+ "delete",
189
+ "entity",
190
+ "exact",
191
+ "first",
192
+ "forUpdate",
193
+ "ids",
194
+ "limit",
195
+ "max",
196
+ "min",
197
+ "offset",
198
+ "owner",
199
+ "paths",
200
+ "remove",
201
+ "restore",
202
+ "rows",
203
+ "set",
204
+ "sort",
205
+ "sum",
206
+ "tags",
207
+ "then",
208
+ "unset",
209
+ "update",
210
+ "upsert",
211
+ "versions",
212
+ "withDeleted"
213
+ ],
214
+ "db": [
215
+ "acl",
216
+ "as",
217
+ "auth",
218
+ "batch",
219
+ "begin",
220
+ "cache",
221
+ "close",
222
+ "commit",
223
+ "flushSignals",
224
+ "idOf",
225
+ "load",
226
+ "lock",
227
+ "maintain",
228
+ "refresh",
229
+ "registry",
230
+ "rollback",
231
+ "schema",
232
+ "sql",
233
+ "tenant",
234
+ "token",
235
+ "transaction",
236
+ "watch"
237
+ ],
238
+ "batch": [
239
+ "discard",
240
+ "run",
241
+ "size"
242
+ ],
243
+ "codes": [
244
+ "acl_denied",
245
+ "admin_required",
246
+ "bypass_rls",
247
+ "class_not_loadable",
248
+ "commit_unknown",
249
+ "confirmation_required",
250
+ "conflict",
251
+ "delete_restricted",
252
+ "deleted",
253
+ "duplicate",
254
+ "exists",
255
+ "id_from_db",
256
+ "id_mismatch",
257
+ "immutable_key",
258
+ "internal",
259
+ "invalid_class",
260
+ "invalid_data",
261
+ "invalid_query",
262
+ "isolation_level",
263
+ "lock_unsupported",
264
+ "no_key",
265
+ "no_path",
266
+ "no_session",
267
+ "not_deleted",
268
+ "not_empty",
269
+ "recreated_in_statement",
270
+ "rev_ambiguous",
271
+ "target_not_found",
272
+ "tenant_denied",
273
+ "tightening_conflict",
274
+ "tx_required",
275
+ "tx_rolled_back",
276
+ "undeclared_end",
277
+ "user_session_required"
278
+ ],
279
+ "errors": [
280
+ {
281
+ "module": "15-errors",
282
+ "message": "letopis: acl_denied: отказ по правилам доступа"
283
+ },
284
+ {
285
+ "module": "15-errors",
286
+ "message": "letopis: delete_restricted: удалению мешают ссылающиеся строки"
287
+ },
288
+ {
289
+ "module": "15-errors",
290
+ "message": "letopis: id_from_db: id класса без ключа выдаёт база"
291
+ },
292
+ {
293
+ "module": "15-errors",
294
+ "message": "letopis: id_mismatch: id не равен v5 от ключа"
295
+ },
296
+ {
297
+ "module": "15-errors",
298
+ "message": "letopis: immutable_key: ключевое поле менять нельзя"
299
+ },
300
+ {
301
+ "module": "15-errors",
302
+ "message": "letopis: invalid_class: описание класса отклонено"
303
+ },
304
+ {
305
+ "module": "15-errors",
306
+ "message": "letopis: invalid_data: данные не проходят описание класса"
307
+ },
308
+ {
309
+ "module": "15-errors",
310
+ "message": "letopis: isolation_level: операция разрешена только в read committed"
311
+ },
312
+ {
313
+ "module": "15-errors",
314
+ "message": "letopis: no_session: нет действующей сессии"
315
+ },
316
+ {
317
+ "module": "15-errors",
318
+ "message": "letopis: not_deleted: объект не удалён — restore только для удалённых"
319
+ },
320
+ {
321
+ "module": "15-errors",
322
+ "message": "letopis: recreated_in_statement: id удалён и создан заново одним оператором"
323
+ },
324
+ {
325
+ "module": "15-errors",
326
+ "message": "letopis: target_not_found: цель не найдена"
327
+ },
328
+ {
329
+ "module": "15-errors",
330
+ "message": "letopis: tenant_denied: чужой арендатор"
331
+ },
332
+ {
333
+ "module": "15-errors",
334
+ "message": "letopis: tightening_conflict: ужесточению описания мешают строки"
335
+ },
336
+ {
337
+ "module": "15-errors",
338
+ "message": "letopis: undeclared_end: конец не объявлен в классе"
339
+ },
340
+ {
341
+ "module": "60-write",
342
+ "message": "letopis: invalid_data: "
343
+ },
344
+ {
345
+ "module": "errors",
346
+ "message": "letopis: <…>"
347
+ },
348
+ {
349
+ "module": "errors",
350
+ "message": "letopis: <…>: <…>"
351
+ },
352
+ {
353
+ "module": "errors",
354
+ "message": "letopis: invalid_data: <…><…>"
355
+ },
356
+ {
357
+ "module": "messages/index",
358
+ "message": "letopis: ключ каталога <…> повторяется"
359
+ },
360
+ {
361
+ "module": "messages/index",
362
+ "message": "letopis: ключ каталога <…> — не из раздела <…>"
363
+ },
364
+ {
365
+ "module": "up",
366
+ "message": "letopis.up: ${own ? seed : "
367
+ },
368
+ {
369
+ "module": "up",
370
+ "message": "letopis.up: <…>"
371
+ },
372
+ {
373
+ "module": "up",
374
+ "message": "letopis.up: <…>: <…>"
375
+ },
376
+ {
377
+ "module": "up",
378
+ "message": "letopis.up: migrate/<…>: <…>"
379
+ }
380
+ ]
381
+ }
package/dist/acl.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Executor } from './tx.js';
2
+ import type { CallSpec } from './log.js';
2
3
  export type AclOp = 'READ' | 'WRITE' | 'DELETE';
3
4
  /** Решение по ресурсу приложения (эндпоинту): можно ли к нему обратиться. */
4
5
  export interface AclDecision {
@@ -20,6 +21,52 @@ export interface AclDataDecision {
20
21
  allow: boolean;
21
22
  filter?: Record<string, unknown>;
22
23
  }
24
+ /**
25
+ * Цель правила на строки: имя класса (правило действует и на потомков), `'*'` — все классы, или класс с условием на
26
+ * строки — шаблоном колонок `owner`, `tenant`, `tags`, `data`, `links`, `id` (`'$actor'` — тот, от чьего имени запрос,
27
+ * `'$tenant'` — рабочий арендатор): `{ class: 'Ticket', owner: '$actor' }`.
28
+ */
29
+ export type AclTarget = string | ({
30
+ class: string;
31
+ } & {
32
+ [column: string]: unknown;
33
+ });
34
+ /**
35
+ * Что разрешить, запретить или снять: кому — ровно одно из `role` и `everyone` (иначе `invalid_data`), операции — с
36
+ * классами, ресурсами приложения или с теми и другими.
37
+ */
38
+ export interface AclRuleSpec {
39
+ /** Роль членства в текущем арендаторе (как в `member.roles`). */
40
+ role?: string;
41
+ /** Все аккаунты арендатора, кроме анонимных и теневых, — группа правил «можно всё» от createTenant. */
42
+ everyone?: true;
43
+ read?: AclTarget | readonly AclTarget[];
44
+ write?: AclTarget | readonly AclTarget[];
45
+ delete?: AclTarget | readonly AclTarget[];
46
+ /** Ресурсы приложения — маски эндпоинтов, как у `check()`: `'reports.*'` — `reports.daily`, но не `reports`. */
47
+ api?: string | readonly string[];
48
+ /** Вес правила, целое от 1 до 999; по умолчанию 50 — сильнее правила «можно всё» (вес 10) от createTenant. */
49
+ weight?: number;
50
+ }
51
+ /** Правило арендатора в виде обёртки. */
52
+ export interface AclRule {
53
+ id: string;
54
+ /** Роль группы `role:<роль>:ACCOUNT`; null — группа не одной роли (все аккаунты или своя группа). */
55
+ role: string | null;
56
+ /** Группа всех аккаунтов арендатора: `authenticated:ACCOUNT` от createTenant или `everyone:ACCOUNT` обёртки. */
57
+ everyone: boolean;
58
+ /** Alias группы. */
59
+ group: string;
60
+ op: AclOp | 'API';
61
+ /** Класс (`'*'` — все классы) или маска эндпоинта. */
62
+ target: string;
63
+ /** Условие на строки — шаблон колонок. */
64
+ condition?: Record<string, unknown>;
65
+ permission: 'allow' | 'deny';
66
+ weight: number;
67
+ /** Действует ли правило: `false` — выключено полем `enabled` строки `rule`, база его не применяет. */
68
+ enabled: boolean;
69
+ }
23
70
  export interface AclApi {
24
71
  /**
25
72
  * Можно ли тому, от чьего имени идёт запрос, обратиться к ресурсу приложения (эндпоинту). Решают правила доступа
@@ -31,5 +78,38 @@ export interface AclApi {
31
78
  * что подходят под условие `filter` в ответе.
32
79
  */
33
80
  checkData(className: string, op: AclOp): Promise<AclDataDecision>;
81
+ /**
82
+ * Разрешить роли (`role`) или всем аккаунтам арендатора (`everyone: true`) операции с классами и ресурсы приложения —
83
+ * по правилу на каждую операцию и цель, с весом `weight` (по умолчанию 50). Повтор ничего не меняет; запрет той же
84
+ * группы и цели заменяется разрешением. Внутри `db.begin()` — в её транзакции, иначе — своей.
85
+ */
86
+ grant(spec: AclRuleSpec): Promise<AclRule[]>;
87
+ /** Запретить — те же правила с запретом: при равном весе запрет сильнее разрешения. */
88
+ deny(spec: AclRuleSpec): Promise<AclRule[]>;
89
+ /**
90
+ * Снять правила роли или всех аккаунтов — разрешения и запреты — для этих операций и целей; число снятых.
91
+ * `revoke({ everyone: true, read: '*', write: '*', delete: '*' })` снимает «можно всё» от createTenant: дальше права —
92
+ * только по ролям.
93
+ */
94
+ revoke(spec: Omit<AclRuleSpec, 'weight'>): Promise<number>;
95
+ /**
96
+ * Роли аккаунта в текущем арендаторе (строка `member`); null — не член. Со списком ролей — заменить их (членство
97
+ * заводится, если его нет), с null — снять членство. Сам арендатор (вход под его аккаунтом) — владелец без строки
98
+ * `member`: для него ответ null.
99
+ */
100
+ roles(account: string | {
101
+ id: string;
102
+ }, roles?: readonly string[] | null): Promise<string[] | null>;
103
+ /**
104
+ * Правила текущего арендатора — свои и «можно всё» от createTenant. На подключении без RLS (`allowBypassRls`) — тоже
105
+ * только правила арендатора сессии, без правил других арендаторов и System.
106
+ */
107
+ list(): Promise<AclRule[]>;
34
108
  }
35
- export declare function makeAcl(S: string, exec: Executor): AclApi;
109
+ /**
110
+ * db.acl фасада. db — сам фасад (корень, db.as(), db.begin()): запись правил идёт его цепочками, внутри db.begin() — в её
111
+ * транзакции.
112
+ */
113
+ export declare function makeAcl(S: string, exec: Executor, db?: () => unknown): AclApi;
114
+ /** Что пишет в лог каждый метод db.acl: правка прав — acl.change, проверки и список — acl.check. */
115
+ export declare const ACL_LOG: Readonly<Record<string, CallSpec>>;