letopis 0.20.3 → 1.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 (89) hide show
  1. package/AGENT-CHEATSHEET.en.md +368 -0
  2. package/AGENT-CHEATSHEET.md +354 -0
  3. package/CHANGELOG.md +348 -0
  4. package/MIGRATION.md +190 -0
  5. package/README.en.md +1937 -0
  6. package/README.md +1493 -3466
  7. package/dist/acl.d.ts +26 -50
  8. package/dist/acl.js +22 -267
  9. package/dist/admin.d.ts +138 -0
  10. package/dist/admin.js +170 -0
  11. package/dist/auth.d.ts +120 -73
  12. package/dist/auth.js +121 -306
  13. package/dist/cache.d.ts +73 -0
  14. package/dist/cache.js +148 -0
  15. package/dist/chain.d.ts +124 -191
  16. package/dist/chain.js +369 -551
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +164 -0
  19. package/dist/demo/booking.d.ts +289 -0
  20. package/dist/demo/booking.js +159 -0
  21. package/dist/errors.d.ts +29 -0
  22. package/dist/errors.js +70 -0
  23. package/dist/import.d.ts +179 -0
  24. package/dist/import.js +792 -0
  25. package/dist/index.d.ts +172 -26
  26. package/dist/index.js +304 -178
  27. package/dist/jsonschema.d.ts +22 -0
  28. package/dist/jsonschema.js +167 -0
  29. package/dist/load.d.ts +76 -0
  30. package/dist/load.js +884 -0
  31. package/dist/model.d.ts +166 -0
  32. package/dist/model.js +224 -0
  33. package/dist/ops.d.ts +7 -6
  34. package/dist/ops.js +7 -51
  35. package/dist/pglite.d.ts +22 -0
  36. package/dist/pglite.js +45 -0
  37. package/dist/registry.d.ts +57 -0
  38. package/dist/registry.js +82 -0
  39. package/dist/sql.d.ts +59 -142
  40. package/dist/sql.js +568 -654
  41. package/dist/sync.d.ts +31 -0
  42. package/dist/sync.js +108 -0
  43. package/dist/tx.d.ts +129 -8
  44. package/dist/tx.js +300 -73
  45. package/dist/typed.d.ts +97 -0
  46. package/dist/typed.js +1 -0
  47. package/dist/types.d.ts +71 -250
  48. package/dist/types.js +27 -108
  49. package/dist/up.d.ts +140 -47
  50. package/dist/up.js +339 -267
  51. package/dist/uuid.d.ts +21 -6
  52. package/dist/uuid.js +48 -64
  53. package/dist/validate.d.ts +24 -0
  54. package/dist/validate.js +251 -0
  55. package/dist/watch.d.ts +62 -0
  56. package/dist/watch.js +168 -0
  57. package/dist/write.d.ts +117 -74
  58. package/dist/write.js +658 -720
  59. package/llms.txt +26 -0
  60. package/package.json +49 -19
  61. package/sql/10-core.sql +136 -0
  62. package/sql/15-errors.sql +60 -0
  63. package/sql/20-context.sql +153 -0
  64. package/sql/30-validate.sql +423 -0
  65. package/sql/40-class.sql +259 -0
  66. package/sql/50-acl.sql +539 -0
  67. package/sql/60-write.sql +1369 -0
  68. package/sql/70-read.sql +245 -0
  69. package/sql/80-auth.sql +827 -0
  70. package/sql/90-time.sql +957 -0
  71. package/sql/95-seed.system.sql +178 -0
  72. package/sql/99-revision.sql +3 -0
  73. package/sql/README.md +56 -0
  74. package/sql/seed.booking.sql +39 -112
  75. package/dist/schema.d.ts +0 -15
  76. package/dist/schema.js +0 -351
  77. package/dist/sessions.d.ts +0 -32
  78. package/dist/sessions.js +0 -114
  79. package/dist/tables.d.ts +0 -105
  80. package/dist/tables.js +0 -248
  81. package/docker/Dockerfile +0 -40
  82. package/docker/start.sh +0 -18
  83. package/scripts/check-docs.mjs +0 -375
  84. package/scripts/gen-api-contract.mjs +0 -226
  85. package/scripts/gen-types.mjs +0 -350
  86. package/scripts/release-notes.mjs +0 -76
  87. package/scripts/schema-sync.mjs +0 -185
  88. package/sql/ddl.sql +0 -600
  89. package/sql/seed.auth.sql +0 -73
package/dist/errors.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Ошибки letopis 1.0 (план, §7.1, этап 2, п. 10). Текст — «letopis: <код>: <сообщение>», как у ошибок
3
+ * базы; код — имя из каталога (`error_catalog()` в lib/sql/15-errors.sql) или код самой библиотеки.
4
+ */
5
+ export class LetopisError extends Error {
6
+ code;
7
+ detail;
8
+ constructor(code, message, detail) {
9
+ super(`letopis: ${code}: ${message}`);
10
+ this.name = 'LetopisError';
11
+ this.code = code;
12
+ this.detail = detail;
13
+ }
14
+ }
15
+ /** Охранник снесённого API (план, этап 3, п. 11): понятная ошибка с версией и заменой. */
16
+ export function removed(name, replacement) {
17
+ return new LetopisError('removed', `${name} снят в 1.0.0 — ${replacement}`);
18
+ }
19
+ /** Метод зарезервирован, но появится на следующем этапе плана. */
20
+ export function notYet(name, stage) {
21
+ return new LetopisError('not_implemented', `${name} появится на этапе ${stage} плана 1.0`);
22
+ }
23
+ /** Данные не проходят описание класса (`invalid_data`). */
24
+ export class ValidationError extends LetopisError {
25
+ issues;
26
+ constructor(cls, issues) {
27
+ super('invalid_data', `данные не проходят описание класса ${cls}`, { issues });
28
+ this.name = 'ValidationError';
29
+ this.issues = issues;
30
+ }
31
+ }
32
+ /** Код SQLSTATE класса LT → имя ошибки letopis (каталог error_catalog() в базе). */
33
+ const LT_NAMES = {
34
+ LT001: 'no_session', LT002: 'target_not_found', LT003: 'invalid_data', LT004: 'undeclared_end', LT005: 'tenant_denied',
35
+ LT006: 'acl_denied', LT007: 'id_mismatch', LT008: 'id_from_db', LT009: 'immutable_key', LT010: 'invalid_class',
36
+ LT011: 'use_reclass', LT012: 'reclass_denied', LT013: 'tightening_conflict', LT014: 'isolation_level',
37
+ LT015: 'recreated_in_statement', LT016: 'cursor_expired', LT017: 'delete_restricted', LT018: 'not_deleted',
38
+ LT019: 'reset_disabled', LT020: 'confirm_mismatch',
39
+ };
40
+ /**
41
+ * Ошибка базы → LetopisError (ValidationError для invalid_data с нарушениями). Детали из DETAIL
42
+ * (JSON) переносятся в detail; прочие ошибки возвращаются как есть.
43
+ */
44
+ export function fromDbError(e) {
45
+ const err = e;
46
+ // новые значения строки не проходят условие ACL (политика RLS WITH CHECK) — отказ по правам; upsert,
47
+ // которому существующую строку менять нельзя («USING expression»), остаётся 42501 (§2.5). Признак —
48
+ // функция сервера, а не текст: текст зависит от языка сервера
49
+ if (err.code === '42501' && err.routine === 'ExecWithCheckOptions' && !/USING/.test(err.message ?? '')) {
50
+ return Object.assign(new LetopisError('acl_denied', 'новые значения строки запрещают правила доступа'), { cause: e });
51
+ }
52
+ const name = err.code ? LT_NAMES[err.code] : undefined;
53
+ if (!name)
54
+ return e;
55
+ let detail;
56
+ try {
57
+ detail = err.detail ? JSON.parse(err.detail) : undefined;
58
+ }
59
+ catch {
60
+ detail = err.detail;
61
+ }
62
+ const text = (err.message ?? '').replace(/^letopis: [a-z_]+: /, '');
63
+ const issues = detail?.issues;
64
+ if (name === 'invalid_data' && Array.isArray(issues)) {
65
+ const cls = /класса (\S+)/.exec(text)?.[1] ?? '?';
66
+ const v = new ValidationError(cls, issues);
67
+ return Object.assign(v, { cause: e });
68
+ }
69
+ return Object.assign(new LetopisError(name, text, detail), { cause: e });
70
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Импорт из letopis 0.21 (план, этап 7, п. 1): `letopis import --from <строка 0.21> --from-schema v1.x`.
3
+ *
4
+ * Читает таблицы 0.21 (Schema, Entity, Account, Credential, Resource, Rule) из её собственной базы —
5
+ * PostgreSQL 17 с TimescaleDB или обычный дамп таблиц — и пишет в установку 1.0:
6
+ * - Schema → строки Class арендатора System (их видят все арендаторы): правила fastest-validator →
7
+ * JSON Schema, концы — роли по имени класса цели, союз всех конкретных потомков класса —
8
+ * полиморфный конец этого класса; корни без своих полей и концов (Entity, link) опускаются;
9
+ * description, appearance, order → meta; ссылка на класс в данных, не объявленная концом, —
10
+ * необязательный конец (в отчёте);
11
+ * - Entity → загрузчиком в режиме истории (existing: 'skip', агент «letopis import»): версии по
12
+ * порядку, надгробия — op delete, следующая за ним версия — create; id классов с ключом — v5 от
13
+ * ключа (§2.3), без ключа — v5 от старого id ($ext); данные System — в арендатора --tenant;
14
+ * - Account → строки Account (id — v5 от старого; System → System), Credential → secret (хэши scrypt
15
+ * и ключей как есть, OTP — нет), Resource и Rule → строки Resource и rule в System.
16
+ * До первой записи импорт проверяет всё, что его остановит: два объекта с одним id 1.0 (нарушение
17
+ * ключа — опция keyless), ключ, менявшийся между версиями, ключ со ссылкой на отсутствующий объект.
18
+ * Висячие ссылки живых строк останавливает загрузчик (target_not_found) — отчёт называет строки 0.21.
19
+ * Шаги (классы, аккаунты, креды, права, данные арендатора) — отдельные транзакции; повтор дописывает
20
+ * недостающее и дублей не создаёт.
21
+ */
22
+ import postgres from 'postgres';
23
+ import { type JsonSchema } from './jsonschema.js';
24
+ type Raw = Record<string, unknown>;
25
+ type Sql = postgres.Sql<Record<string, unknown>>;
26
+ export interface ImportOptions {
27
+ /** Строка подключения к базе 0.21 (или готовый пул). */
28
+ from: string | Sql;
29
+ /** Схема 0.21: v1.<имя>. */
30
+ fromSchema: string;
31
+ /** Партиция 0.21 (по умолчанию entity). */
32
+ partition?: string;
33
+ /** Администраторская строка подключения к установке 1.0. */
34
+ dsn: string;
35
+ /** Схема 1.0: v2.<имя>. */
36
+ schema: string;
37
+ /** Роль-владелец схемы 1.0 (по умолчанию letopis_owner). */
38
+ owner?: string;
39
+ /** Имя арендатора для данных аккаунта System (полигоны 0.21 записаны от его имени). */
40
+ tenant?: string;
41
+ /** Классы, которые импортируются без ключа: старый id — внешний ключ (объявленное расхождение). */
42
+ keyless?: string[];
43
+ /** Переименования классов: имена, занятые методами 1.0 и системными классами (иначе — имя + «_»). */
44
+ rename?: Record<string, string>;
45
+ /** Существующим объектам — новая версия из последней версии 0.21, если содержимое отличается. */
46
+ update?: boolean;
47
+ /** Порции загрузчика (§2.6, п. 8). */
48
+ commitEvery?: number;
49
+ /** Без сообщений в консоль. */
50
+ quiet?: boolean;
51
+ }
52
+ /** Непереведённое правило или иное расхождение описания класса. */
53
+ export interface ImportNote {
54
+ class: string;
55
+ field?: string;
56
+ note: string;
57
+ }
58
+ export interface ClassCount {
59
+ objects: number;
60
+ versions: number;
61
+ live: number;
62
+ }
63
+ export interface ImportReport {
64
+ classes: {
65
+ created: string[];
66
+ existing: string[];
67
+ omitted: string[];
68
+ renamed: Record<string, string>;
69
+ keyless: string[];
70
+ };
71
+ /** Правила fastest-validator и концы, перенесённые не один к одному. */
72
+ notes: ImportNote[];
73
+ /** Число объектов, версий и живых строк по классам: в 0.21 и в 1.0 после импорта. */
74
+ source: Record<string, ClassCount>;
75
+ target: Record<string, ClassCount>;
76
+ tenants: {
77
+ old: string;
78
+ id: string;
79
+ name: string;
80
+ }[];
81
+ accounts: {
82
+ loaded: number;
83
+ skipped: number;
84
+ };
85
+ credentials: {
86
+ imported: number;
87
+ skipped: number;
88
+ otp: number;
89
+ conflicts: {
90
+ account: string;
91
+ kind: string;
92
+ ident: string;
93
+ }[];
94
+ };
95
+ resources: {
96
+ loaded: number;
97
+ skipped: number;
98
+ };
99
+ rules: {
100
+ loaded: number;
101
+ skipped: number;
102
+ };
103
+ objects: {
104
+ loaded: number;
105
+ skipped: number;
106
+ updated: number;
107
+ };
108
+ /** Поля data живых строк, в которых лежат uuid, совпадающие со старыми id (импорт их не переписывает). */
109
+ uuidFields: Record<string, number>;
110
+ /** Отброшенные поля данных, имя которых начинается с $ (метасхема 1.0 их не принимает). */
111
+ droppedFields: Record<string, number>;
112
+ /** Строк, у которых владелец не найден среди аккаунтов (владелец — арендатор). */
113
+ ownerLost: number;
114
+ verify: {
115
+ ok: boolean;
116
+ issues: number;
117
+ anchor: string;
118
+ };
119
+ ms: number;
120
+ }
121
+ /** Строка ошибки импорта: класс и id 0.21 и, для ошибок загрузчика, версия. */
122
+ export interface ImportIssue {
123
+ code: string;
124
+ message: string;
125
+ class: string;
126
+ id: string;
127
+ rev?: number;
128
+ }
129
+ interface Conv {
130
+ s: JsonSchema;
131
+ optional: boolean;
132
+ dflt: boolean;
133
+ }
134
+ declare function conv(rule: unknown, cls: string, path: string, notes: ImportNote[]): Conv;
135
+ interface SchemaRow {
136
+ id: string;
137
+ alias: string;
138
+ category: 'HUB' | 'LINK';
139
+ ancestor: string | null;
140
+ attributes: Raw;
141
+ links: unknown[];
142
+ meta: Raw;
143
+ order: number;
144
+ ancestors: string[];
145
+ }
146
+ /** Часть ключа 1.0: роль конца (классы 0.21) или поле данных. */
147
+ type KeyPart = {
148
+ end: string;
149
+ classes: string[];
150
+ } | {
151
+ field: string;
152
+ };
153
+ /** Класс 1.0 после перевода: описание и правила id для данных. */
154
+ interface Cls {
155
+ old: string;
156
+ name: string;
157
+ desc: Raw;
158
+ /** Роль по классу цели 0.21 (конкретный класс → роль). */
159
+ roleOf: Map<string, string>;
160
+ /** Собственный ключ (undefined — наследуется). */
161
+ ownKey?: KeyPart[];
162
+ /** Поля на $ — отбрасываются из данных. */
163
+ dropped: string[];
164
+ history: boolean;
165
+ }
166
+ interface Built {
167
+ classes: Map<string, Cls>;
168
+ omitted: string[];
169
+ renamed: Record<string, string>;
170
+ keyless: string[];
171
+ notes: ImportNote[];
172
+ /** Эффективный ключ класса (собственный или унаследованный; [] — без ключа). */
173
+ keyOf(old: string): KeyPart[];
174
+ /** Конкретные (не абстрактные) классы, принимаемые концом из классов 0.21 (с потомками). */
175
+ family(old: string): string[];
176
+ }
177
+ declare function buildClasses(rows: SchemaRow[], observed: Map<string, Set<string>>, opts: ImportOptions): Built;
178
+ export declare function import021(opts: ImportOptions): Promise<ImportReport>;
179
+ export { buildClasses as _buildClasses, conv as _convRule };