letopis 1.0.0 → 1.0.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  Формат: [Keep a Changelog](https://keepachangelog.com/), версии — semver.
4
4
 
5
+ ## [1.0.1] — 2026-10-09
6
+
7
+ Исправления, найденные сразу после выпуска 1.0.0.
8
+
9
+ ### Fixed
10
+ - Команда `letopis` на Linux и macOS: запуск через `npx`, `node_modules/.bin` или скрипты `package.json` молча
11
+ завершался с кодом 0, ничего не сделав. Там `node_modules/.bin/letopis` — симлинк, а защита запуска сравнивала
12
+ путь симлинка (`process.argv[1]`) с реальным путём файла (`import.meta.url`). Теперь сравниваются реальные пути.
13
+ Прямой запуск `node node_modules/letopis/dist/cli.js` и Windows дефекта не имели.
14
+ - Эволюция классов: правка условий `anyOf` и `oneOf` в описании класса считалась безопасной, и строки семейства не
15
+ перепроверялись. Строка, не проходящая новое условие, оставалась в базе, а её следующая правка падала с
16
+ `invalid_data`. Теперь любая правка `anyOf` и `oneOf` — ужесточение: строки проверяются под исключительной
17
+ блокировкой семейства, мешающие перечисляет `tightening_conflict` (`class_change_safe`). Строки, оставшиеся после
18
+ такой правки в 1.0.0, находит `db.verify({ data: true })`.
19
+ - Выпуск: проверка опубликованного пакета (`check-package.mjs --registry`) повторяет установку, пока свежая версия не
20
+ появится в метаданных реестра для установки (до 5 минут). На 1.0.0 этот шаг `release.yml` упал ложно: `npm view`
21
+ уже видел версию, а `npm install` ещё нет. Сам пакет 1.0.0 исправен: та же проверка прошла после появления версии.
22
+
23
+ ### Tests
24
+ - `load`: CLI запускается через симлинк, как из `node_modules/.bin` на Linux и macOS.
25
+ - `engine-class`: правка `anyOf` и `oneOf` при мешающей строке — `tightening_conflict`, при подходящих строках —
26
+ принимается.
27
+ - Проверка пакета запускает установленную команду `letopis --help` через `npm exec` — в CI это симлинк на Linux.
28
+
5
29
  ## [1.0.0] — 2026-10-09
6
30
 
7
31
  letopis 1.0 — новая реализация хранилища по плану `reports/PLAN-LETOPIS-1.0.md` (редакция 8), основанному на
package/README.en.md CHANGED
@@ -477,7 +477,8 @@ name of the class for a chain step, for example a name in another language.
477
477
  `syncModels(db, models)` without `apply` is a dry run; with `{ apply: true }` it applies the changes. The result is
478
478
  `{ applied, created, updated, unchanged, skipped, lost }`; `skipped` lists System classes, which only the installation
479
479
  changes. `syncModels` touches only the classes passed to it and deletes nothing. Safe changes (a new optional field or
480
- end, metadata, an alias) are applied at once. A tightening checks all rows of the family; if some rows conflict with it, you get
480
+ end, metadata, an alias) are applied at once. Anything else, including any change to the `anyOf` and `oneOf` conditions, is a
481
+ tightening: it checks all rows of the family; if some rows conflict with it, you get
481
482
  `tightening_conflict` with a list. Classes are written to the session tenant, and other tenants do not see them.
482
483
  Classes shared by everyone are in System: the installation seed or the import puts them there.
483
484
 
package/README.md CHANGED
@@ -467,8 +467,8 @@ id из email подбирается перебором и остаётся в
467
467
  `syncModels(db, модели)` без `apply` — сухой прогон, с `{ apply: true }` — применение. Результат:
468
468
  `{ applied, created, updated, unchanged, skipped, lost }`; в `skipped` — классы System, их меняет только установка.
469
469
  `syncModels` трогает только переданные классы и ничего не удаляет. Безопасные изменения — новое необязательное поле или
470
- конец, метаданные, алиас — применяются сразу. Ужесточение проверяет все строки семейства, и если им мешают строки,
471
- приходит `tightening_conflict` со списком. Классы пишутся в арендатора сессии, и другие арендаторы их не видят.
470
+ конец, метаданные, алиас — применяются сразу. Всё остальное, в том числе любая правка условий `anyOf` и `oneOf`, —
471
+ ужесточение: оно проверяет все строки семейства, и если им мешают строки, приходит `tightening_conflict` со списком. Классы пишутся в арендатора сессии, и другие арендаторы их не видят.
472
472
  Классы, общие для всех, лежат в System: их ставит сид установки или импорт.
473
473
 
474
474
  То же из командной строки:
package/dist/cli.js CHANGED
@@ -15,7 +15,8 @@
15
15
  */
16
16
  import { parseArgs } from 'node:util';
17
17
  import { readFile, writeFile } from 'node:fs/promises';
18
- import { pathToFileURL } from 'node:url';
18
+ import { realpathSync } from 'node:fs';
19
+ import { fileURLToPath, pathToFileURL } from 'node:url';
19
20
  import { resolve } from 'node:path';
20
21
  import { connect, describeText } from './index.js';
21
22
  import { syncModels, generateTypes } from './sync.js';
@@ -153,7 +154,20 @@ export async function main(argv) {
153
154
  await db.close();
154
155
  }
155
156
  }
156
- if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
157
+ // Запущен ли файл как программа. Сравниваются реальные пути: на Linux и macOS `node_modules/.bin/letopis`
158
+ // (npx, скрипты package.json) — симлинк, `process.argv[1]` указывает на него, а `import.meta.url` — на сам файл.
159
+ function isMain() {
160
+ const arg = process.argv[1];
161
+ if (!arg)
162
+ return false;
163
+ try {
164
+ return realpathSync(arg) === realpathSync(fileURLToPath(import.meta.url));
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
170
+ if (isMain()) {
157
171
  main(process.argv.slice(2)).then((code) => process.exit(code), (e) => {
158
172
  console.error(e.message);
159
173
  const rows = e.detail?.rows;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letopis",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Letopis (летопись): versioned entity store on PostgreSQL 18 — integrity, history, tenants and access control enforced by the database itself, dot-notation chains on top",
5
5
  "keywords": [
6
6
  "postgres",
package/sql/60-write.sql CHANGED
@@ -383,13 +383,16 @@ begin
383
383
  end $$;
384
384
 
385
385
  -- Безопасно ли изменение описания класса (этап 1, п. 10): новые необязательные поля и концы,
386
- -- метаданные, алиас, политика истории, кэш. Всё остальное — ужесточение с проверкой строк.
386
+ -- метаданные, алиас, политика истории, кэш. Всё остальное — ужесточение с проверкой строк, в том числе
387
+ -- любая правка условий anyOf и oneOf (1.0.1: прежде они не сравнивались, и строки не перепроверялись).
387
388
  create or replace function @SCHEMA@.class_change_safe(o jsonb, n jsonb) returns boolean
388
389
  language sql immutable
389
390
  return
390
391
  o->'key' = n->'key'
391
392
  and o->'ancestors' = n->'ancestors'
392
393
  and (o->'abstract' = n->'abstract' or n->>'abstract' = 'false')
394
+ and o->'schema'->'anyOf' is not distinct from n->'schema'->'anyOf'
395
+ and o->'schema'->'oneOf' is not distinct from n->'schema'->'oneOf'
393
396
  and coalesce(o->'schema'->'required', '[]'::jsonb) @> coalesce(n->'schema'->'required', '[]'::jsonb)
394
397
  and not exists (select 1 from jsonb_each(coalesce(o->'schema'->'properties', '{}'::jsonb)) p
395
398
  where n->'schema'->'properties'->p.key is distinct from p.value)