letopis 0.5.0 → 0.16.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.
package/dist/chain.d.ts CHANGED
@@ -1,19 +1,23 @@
1
1
  /**
2
- * Dot-цепочки: db.Сотрудник({name:'Вася'}).навык().Услуга().execute()
2
+ * Dot-цепочки: db.Сотрудник({name:'Вася'}).навык().Услуга().run()
3
3
  * db и цепочка — Proxy над реестром классов (id и alias работают одинаково).
4
4
  *
5
5
  * Выборка настраивается модификаторами: .limit(n) .offset(n) .sort(field, dir?)
6
6
  * Колонки — модификаторами: .tags(…) .account(…) .owner(…); ключ путей — .alias(…).
7
- * Запись: .set(data) — связи задаются контекст-шагами цепочки; .delete().
7
+ * Запись: .create(data) / .update(data) / .delete({confirm}) / .anonymize(fields) — ЗВЕНЬЯ
8
+ * (возвращают цепочку, продолжение — от результата); исполняет ТЕРМИНАЛ, весь план
9
+ * одной транзакцией.
8
10
  */
9
11
  import type { Row, Path, Filter } from './types.js';
10
12
  import { type Ctx } from './sql.js';
11
- import { type BatchOp } from './write.js';
13
+ import { type BatchPlan } from './write.js';
12
14
  import { type Tables } from './tables.js';
15
+ import { type AuthApi } from './auth.js';
16
+ import { type AclApi } from './acl.js';
13
17
  import type { Registry } from './schema.js';
14
18
  export interface ChainCore {
15
- /** Пути: [{шаг1: Row, шаг2: Row, …}, …] — все узлы каждого варианта. */
16
- execute(): Promise<Path[]>;
19
+ /** Пути: [{шаг1: Row, шаг2: Row, …}, …] — все узлы каждого варианта (бывший execute). */
20
+ run(): Promise<Path[]>;
17
21
  /** Уникальные сущности последнего шага. */
18
22
  rows(): Promise<Row[]>;
19
23
  first(): Promise<Row | null>;
@@ -39,53 +43,88 @@ export interface ChainCore {
39
43
  min(field: string): Promise<unknown>;
40
44
  max(field: string): Promise<unknown>;
41
45
  countBy(field: string): Promise<Record<string, number>>;
42
- /** GDPR: новая версия с затёртыми string-полями ('[erased]') + тег 'anonymized'. История остаётся (см. README). */
43
- anonymize(fields: string[]): Promise<Row[]>;
46
+ /**
47
+ * GDPR-звено: новая версия с затёртыми string-полями ('[erased]') + тег 'anonymized'.
48
+ * История остаётся. Возвращает цепочку — исполняет терминал; продолжение — от затёртых версий.
49
+ */
50
+ anonymize(fields: string[]): Chain;
44
51
  /** Переименовать ключ ТЕКУЩЕГО шага в выводе путей. */
45
52
  alias(name: string): Chain;
46
- /** Колонка tags: строка | string[] (все) | has/hasAny/hasAll. В set() — значение тегов. */
53
+ /** Колонка tags: строка | string[] (все) | has/hasAny/hasAll. В записи — значение тегов. */
47
54
  tags(v: string | string[] | object): Chain;
48
- /** Колонка account (uuid или Row/Account). В set()-insert — значение. */
55
+ /** Колонка account (uuid или Row/Account). В create() — значение. */
49
56
  account(v: string | {
50
57
  id: string;
51
58
  }): Chain;
52
- /** Колонка owner (uuid или Row/Account). В set()-insert — значение. */
59
+ /** Колонка owner (uuid или Row/Account). В create() — значение. */
53
60
  owner(v: string | {
54
61
  id: string;
55
62
  }): Chain;
56
63
  /**
57
- * Записать. Связи — контекст-шагами до set() И/ИЛИ класс-вызовами после:
58
- * await db.Сотрудник(s).занятость(id).set({kind}).Окно(w)
59
- * const z = db.занятость(id).set({kind}); z.Сотрудник(s); z.Окно(w); await z
60
- * Исполняется ОДНИМ INSERT при первом await (промис кешируется).
61
- * Пустой фильтр без data.id → INSERT; id (фильтр или data.id) → UPSERT;
62
- * фильтр-объект → версия каждого найденного.
64
+ * Вставить узел в путь: x — ленивая цепочка-паттерн (стык проверяется правилами
65
+ * переходов) либо Row (шаг его класса по id). ТА ЖЕ переменная-цепочка повторно
66
+ * в одном пути — возврат к её узлу (ветвление); разные переменные — разные узлы.
67
+ */
68
+ entity(x: Chain | Row): Chain;
69
+ /**
70
+ * Создать сущность (ЗВЕНО — вернёт цепочку, исполняет терминал):
71
+ * await db.Организация(org).Сотрудник().create({ name: 'Вася' }).rows()
72
+ * Связи — контекст-шагами до create() + слотами .Класс.set(…) после. id — по Schema
73
+ * (attributes.id: v4 | v7 | v5-вычисляемый); известный id (Класс(id) / data.id / v5)
74
+ * уже существует → новая версия (идемпотентно). Фильтр-объект — ошибка: это update().
63
75
  */
64
- set(data?: Record<string, unknown>): SetChain;
65
- /** Серверный tombstone + каскад; возвращает всё удалённое с $deleted: true. */
66
- delete(): Promise<Row[]>;
76
+ create(data?: Record<string, unknown>): Chain;
77
+ /**
78
+ * Новая версия КАЖДОГО найденного путём (deep-merge листьев data):
79
+ * await db.Запись(з).позиция({ qty: 1 }).update({ qty: 2 }).rows()
80
+ * Класс() ≡ Класс({}) — все в границах контекста. Не найдено → [] (НИКОГДА не создаёт).
81
+ * Продолжение цепочки — от записанных строк (fan-out при множестве).
82
+ */
83
+ update(data?: Record<string, unknown>): Chain;
84
+ /**
85
+ * Удаление-звено. { confirm: true } — серверный tombstone + каскад (продолжение — от
86
+ * затомбстоуненных, $deleted: true); БЕЗ confirm — превью: терминал вернёт кандидатов
87
+ * (цели + каскад), БД не тронута.
88
+ */
89
+ delete(opts?: {
90
+ confirm?: boolean;
91
+ }): Chain;
67
92
  }
68
- /** Ленивый билдер записи: довешивание связей + thenable (исполнение при await). */
69
- export type SetChain = PromiseLike<Row[]> & {
70
- [className: string]: (target: string | {
93
+ /**
94
+ * Свойство-класс на цепочке:
95
+ * ВЫЗОВ `Класс(фильтр?)` — шаг-навигация; повтор LINK-класса в пути = возврат
96
+ * к его узлу (pivot: ветвление к другому концу, дофильтровка AND);
97
+ * СЛОТ `Класс` без скобок — конец links записываемой версии:
98
+ * .set(target) — установить конец (id | Row | вложенная цепочка: та же транзакция,
99
+ * ровно одна сущность класса конца); союз-конец замещается целиком;
100
+ * .unset() — снять optional-конец.
101
+ * Слот валиден только для конца из Schema.links владельца и ТОЛЬКО после операции
102
+ * записи: …create(…).Класс.set(x) / …update(…).Класс.unset() — та же версия строки.
103
+ */
104
+ export type StepProp = ((filter?: Filter) => Chain) & {
105
+ set(target: string | {
71
106
  id: string;
72
- }) => SetChain;
107
+ } | Chain): Chain;
108
+ unset(): Chain;
73
109
  };
74
110
  export type Chain = ChainCore & {
75
- [className: string]: (filter?: Filter) => Chain;
111
+ [className: string]: StepProp;
76
112
  };
77
113
  export interface DbCore {
78
114
  /** Открыть транзакцию: tr — тот же API + commit/rollback/lock. */
79
115
  begin(): Promise<EntityTx>;
80
116
  commit(tr: EntityTx): Promise<void>;
81
117
  rollback(tr: EntityTx): Promise<void>;
82
- /** Именованный батч: те же цепочки, set/delete копятся до execute(). */
118
+ /** Именованный батч: те же цепочки, планы записи копятся до run(). */
83
119
  batch(name: string): Batch;
84
120
  /**
85
121
  * Realtime: события каждой вставленной версии (insert/update/delete-tombstone).
86
122
  * watch(cb) — все классы; watch('Запись', cb) — один. Возврат — stop-функция.
123
+ * LISTEN-соединение переживает обрывы (re-listen автоматом), но NOTIFY за время
124
+ * разрыва потеряны — opts.onReconnect зовётся после восстановления: дочитайте пропущенное.
87
125
  */
88
- watch(classOrCb: string | ((e: WatchEvent) => void), cb?: (e: WatchEvent) => void): Promise<() => void>;
126
+ watch(cb: (e: WatchEvent) => void, opts?: WatchOpts): Promise<() => void>;
127
+ watch(cls: string, cb: (e: WatchEvent) => void, opts?: WatchOpts): Promise<() => void>;
89
128
  close(): Promise<void>;
90
129
  registry: Registry;
91
130
  /** Голый postgres-клиент (тесты, EXPLAIN). */
@@ -95,6 +134,12 @@ export interface DbCore {
95
134
  credentials: Tables['credentials'];
96
135
  resources: Tables['resources'];
97
136
  rules: Tables['rules'];
137
+ /** Вход по кредам (пароль/api-key/key-secret/внешние identity) + сессии в Redis. */
138
+ auth: AuthApi;
139
+ /** ACL по Resource/Rule: check(эндпоинт) / checkData(класс, READ|WRITE|DELETE) / reload. */
140
+ acl: AclApi;
141
+ /** Начать путь с готового узла/паттерна: db.entity(pos).… (pos — ленивая цепочка или Row). */
142
+ entity(x: Chain | Row): Chain;
98
143
  }
99
144
  export type EntityDb = DbCore & {
100
145
  [className: string]: (filter?: Filter) => Chain;
@@ -112,15 +157,23 @@ export interface WatchEvent {
112
157
  updated: string;
113
158
  deleted: boolean;
114
159
  }
160
+ /** Опции watch(): реакция на восстановление LISTEN-соединения. */
161
+ export interface WatchOpts {
162
+ /** Зовётся после каждого re-listen (не на первом подключении): NOTIFY за разрыв потеряны. */
163
+ onReconnect?: () => void;
164
+ }
115
165
  export type Batch = {
116
- execute(): Promise<Row[][]>;
166
+ /** Исполнить всю очередь планов одной транзакцией (бывший execute). */
167
+ run(): Promise<Row[][]>;
117
168
  discard(): void;
118
169
  size(): number;
119
170
  } & {
120
171
  [className: string]: (filter?: Filter) => Chain;
121
172
  };
122
173
  interface RootState {
123
- batches: Map<string, BatchOp[]>;
174
+ batches: Map<string, BatchPlan[]>;
124
175
  }
176
+ /** Внутренний канал: план чужой ленивой цепочки (слот-значения, entity()). */
177
+ export declare const PLAN: unique symbol;
125
178
  export declare function makeDb(ctx: Ctx, root?: RootState): EntityDb;
126
179
  export {};