@nitra/cursor 1.8.180 → 1.8.185
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 +45 -5
- package/mdc/ga.mdc +23 -1
- package/mdc/image.mdc +17 -20
- package/mdc/js-run.mdc +21 -1
- package/package.json +4 -4
- package/scripts/check-abie.mjs +1 -0
- package/scripts/check-changelog.mjs +49 -50
- package/scripts/check-ga.mjs +69 -8
- package/scripts/check-hasura.mjs +1 -0
- package/scripts/check-image.mjs +221 -47
- package/scripts/check-js-bun-db.mjs +3 -22
- package/scripts/check-js-lint.mjs +3 -1
- package/scripts/check-js-mssql.mjs +5 -23
- package/scripts/check-js-run.mjs +37 -3
- package/scripts/check-k8s.mjs +32 -31
- package/scripts/check-vue.mjs +17 -10
- package/scripts/claude-stop-hook.mjs +1 -0
- package/scripts/lint-ga.mjs +0 -1
- package/scripts/run-docker.mjs +1 -0
- package/scripts/run-k8s.mjs +1 -0
- package/scripts/utils/bun-sql-scan.mjs +1 -2
- package/scripts/utils/depcheck-workflow.mjs +188 -0
- package/scripts/utils/find-package-json-paths.mjs +30 -0
- package/scripts/utils/load-cursor-config.mjs +3 -1
- package/scripts/utils/oxlint-canonical.json +3 -16
- package/scripts/utils/walkDir.mjs +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.8.185] - 2026-05-06
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `image` (mdc v1.4 → v1.5): прапорець `--avif` у `lint-image` тепер **заборонений** (інакше `bun run lint` плодив би `.avif` для зображень, що ніде не вживаються); канонічний `lint-image` — `npx @nitra/minify-image --src=. --write`. AVIF-генерацію виконує **виключно** `npx @nitra/cursor check image`. Секцію «AVIF-імпорти у `.vue`» переписано: тепер вона документує триетапну логіку `check image` — (1) запуск `npx @nitra/minify-image --src=. --write --avif`, (2) авто-заміна raster-посилань у `.vue`/`.html` на `.avif` у кожному workspace-пакеті, (3) прибирання AVIF-сиріт (файли `.avif` без жодного посилання у `.vue`/`.html` видаляються — AVIF лишається лише там, де заміна реально вдалася).
|
|
12
|
+
- `check-image.mjs`: `checkLintImageScript` більше не вимагає `--avif`, натомість фейлить за його наявністю; додано `runAvifGeneration` (best-effort `npx ... --avif`, опт-аут через `NITRA_CURSOR_NO_AVIF_RUN=1` для тестів), `cleanupOrphanAvifs` (видаляє `<...>.avif` без живого посилання), `hasAnyRasterImage`, `resolveImagePath`. `checkVueAvifImportsInPackage` тепер не лише валідує, а й переписує raster-посилання на `.avif` (коли AVIF-двійник реально існує на диску); якщо `.avif` нема — фейл, як раніше. Сканування поширено на `.html` файли (раніше було тільки `.vue`).
|
|
13
|
+
- `tests/check-image.test.mjs`: `CANONICAL_LINT_IMAGE` без `--avif`; кейс «без `--avif`» перейменовано/перекинуто на «з забороненим `--avif`»; додано тести на orphan-cleanup (`.avif` без посилань видаляється) та авто-заміну raster-імпорту, коли `.avif`-сусід реально існує.
|
|
14
|
+
|
|
15
|
+
## [1.8.184] - 2026-05-06
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `check-js-run.mjs`: програмна перевірка нового правила «depcheck у GitHub Actions з path-фільтром». Для кожного backend workspace-пакета сканується `.github/workflows/*.yml`; якщо `on.push.paths` або `on.pull_request.paths` містить glob, що починається з `<rootDir>/`, у job очікується крок `npx depcheck` з `working-directory: <rootDir>` і `--ignores`, що містить мінімум `graphql,bun` (інші значення допустимі). Логіка — у новому `scripts/utils/depcheck-workflow.mjs` (парсинг `--ignores="…"` з підтримкою single/double-quote і unquoted формату; класифікація `missing` / `wrong-cwd` / `missing-ignores`).
|
|
20
|
+
- `check-js-run-fixture.test.mjs`: 9 нових кейсів — нема `.github/workflows/`, глобальні paths без скоупу пакета, scoped-paths без depcheck (fail), depcheck з неправильним `working-directory` (fail), без `--ignores` (fail), `--ignores` без `bun` (fail), валідний з extra-ignores (pass), вкладений `cron-jobs/foo/src/**` як scope (pass).
|
|
21
|
+
- `.github/workflows/npm-publish.yml`: додано власний крок `npx depcheck --ignores="graphql,bun,bun:test,@nitra/cursor"` з `working-directory: npm`, щоб репо `@nitra/cursor` саме відповідало новому правилу js-run (`paths: ['npm/**']` обмежено пакетом `npm`); extra-ignores потрібні для self-reference `@nitra/cursor` у devDependencies та для `bun:test` як bun-built-in.
|
|
22
|
+
|
|
23
|
+
## [1.8.183] - 2026-05-06
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `ga` (mdc v1.6 → v1.7): додано **універсальну** вимогу — кожен workflow у `.github/workflows/*.yml` обов'язково містить блок `concurrency` з `group: ${{ github.ref }}-${{ github.workflow }}` і `cancel-in-progress: true`. Без винятків — scheduled cleanup-воркфлоу, `pull_request: types: [closed]`, publish-воркфлоу теж. Канонічні приклади у правилі (`clean-ga-workflows.yml`, `clean-merged-branch.yml`, `git-ai.yml`) оновлено й тепер містять цей блок.
|
|
28
|
+
- `check-ga.mjs`: нова перевірка `verifyConcurrencyBlock` — запускається на кожному `*.yml` у `.github/workflows/` і структурно перевіряє рівно два поля (`concurrency.group` дорівнює канонічному рядку, `concurrency.cancel-in-progress === true`); відсутність блоку, інший `group` або `cancel-in-progress: false` — fail. Спільний `validateConcurrencyOnRoot` додано в усі канонічні структурні валідатори (clean-ga-workflows, clean-merged-branch, lint-ga, git-ai), щоб ці workflow перевірялися й через шаблонну, і через універсальну логіку.
|
|
29
|
+
|
|
30
|
+
## [1.8.182] - 2026-05-06
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- `js-run` (mdc v1.2 → v1.3): додано секцію **«depcheck у GitHub Actions з path-фільтром»** — якщо в `.github/workflows/*.yml` тригер `paths:` обмежено каталогом одного backend-пакета (наприклад `cron-jobs/refund-loyalty-points/**`), у job має бути крок `npx depcheck --ignores="graphql,bun"` з `working-directory`, що вказує на той самий каталог. Список `--ignores` обов'язково містить мінімум `graphql,bun` (peer-залежність GraphQL та рантайм Bun, які depcheck не розпізнає коректно), але може бути розширений значеннями через кому без пробілів. Не застосовується до глобальних workflow без `paths:` або з кореневими `**/*.js` патернами.
|
|
35
|
+
|
|
36
|
+
## [1.8.181] - 2026-05-06
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `scripts/utils/find-package-json-paths.mjs`: винесено спільну `findAllPackageJsonPaths(repoRoot, ignorePaths)` з `check-js-bun-db.mjs` і `check-js-mssql.mjs`, щоб усунути jscpd-дублювання. Самі check-скрипти тепер імпортують її, як інші утиліти з `utils/`.
|
|
41
|
+
- `scripts/utils/walkDir.mjs` / `scripts/utils/load-cursor-config.mjs`: trimming trailing-slash переписано з регулярки `/\\/+$/` на `while (s.endsWith('/'))`, щоб уникнути попередження `sonarjs/slow-regex` (потенційний backtracking) — поведінка не змінилась.
|
|
42
|
+
- `scripts/check-k8s.mjs`: `failIfExplicitPatchTargetsHaveRedundantGroupVersion` рефакторено — логіка одного запису винесена в новий хелпер `describePatchTargetRedundancy`, основна функція тепер просто будує повідомлення з результату (зменшено sonarjs/cognitive-complexity 24→<15, поведінка не змінилась).
|
|
43
|
+
- `scripts/claude-stop-hook.mjs`: `readStdin` і `runStopHookCli` переписані з `new Promise(resolve => …)` на `events.once(stream, 'end' | 'exit')` — підказка `eslint-plugin-promise/avoid-new`, поведінка не змінилась.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- `scripts/utils/bun-sql-scan.mjs`: у JSDoc `findBunSqlUnsafeUseWithoutAllowMarkerInText` прибрано вкладений приклад із backslash-backtick (`sql\\`...\\${value}...\\``), який ламав парсер коментарів oxlint і призводив до false-positive `eslint-plugin-jsdoc(require-param)`/`(require-returns)` на функції з валідним JSDoc — текст переписано без екранованих backtick-ів.
|
|
48
|
+
- Десятки `eslint-plugin-jsdoc` правил у `npm/scripts/**` та `npm/tests/**`: додано відсутні описи `@param` / `@returns` (включно зі спільним `ignorePaths`-аргументом у нових сигнатурах walkDir-обгорток), прибрано неприпустимі дефолтні значення в JSDoc (`[name=...]`) — без зміни поведінки.
|
|
49
|
+
|
|
7
50
|
## [1.8.180] - 2026-05-05
|
|
8
51
|
|
|
9
52
|
### Changed
|
|
@@ -55,9 +98,6 @@
|
|
|
55
98
|
### Added
|
|
56
99
|
|
|
57
100
|
- Нове правило `changelog` (`mdc/changelog.mdc` + `scripts/check-changelog.mjs`): для «звичайних» Bun-монорепо проєктів вимагає, щоб у кожному workspace, який змінився відносно базової гілки `dev`, у поточному PR було підвищено `version` у `<ws>/package.json` і додано запис `## [version] - YYYY-MM-DD` у `<ws>/CHANGELOG.md` (Keep a Changelog 1.1.0). Перевірка PR-scoped: на самій гілці `dev` пропускається; на feature-гілці bump і запис достатньо зробити **один раз — як суму по всьому PR**, без бамп-шуму в проміжних комітах. Воркспейс `npm/` пропускається — його CHANGELOG покриває окреме правило `npm-module`. У `auto-rules.md` / `auto-rules.mjs` `changelog` додано до автодетекту з умовою «у корені є `package.json`» і до `AUTO_RULE_ORDER` між `capacitor` і `docker`.
|
|
58
|
-
|
|
59
|
-
### Added
|
|
60
|
-
|
|
61
101
|
- `.n-cursor.json` поле `ignore` (`schemas/n-cursor.json`): тепер не лише сигнал для AI, а й керує обходом усіх `check-*.mjs` / `run-*.mjs` — перелічені каталоги повністю виключаються з `walkDir`, як `node_modules` чи `.git`. Дозволяє безпечно тримати vendored Helm-чарти, генеровані маніфести, legacy-дерева у репо без false-positive’ів від check-скриптів. Розширено опис у схемі (стандартні виключення додавати не треба) і README отримав секцію «Виключення цілих дерев».
|
|
62
102
|
- `scripts/utils/load-cursor-config.mjs`: нова утиліта `loadCursorIgnorePaths(root)` — читає поле `ignore` з `.n-cursor.json` і нормалізує до абсолютних posix-шляхів без trailing-slash; пропускає не-рядки та порожні елементи; повертає `[]`, якщо файлу/поля нема або JSON невалідний.
|
|
63
103
|
- `scripts/utils/walkDir.mjs`: третій аргумент `ignorePaths` (за замовчуванням `[]`) — каталоги, які пропускаються разом з усім вмістом. Збіг — за повним шляхом (точний або з префіксом `/`), а не за basename, тож `postgres-master-test/` не пропускається коли в ignore лише `postgres-master/`. Стандартні пропуски (`node_modules`, `.git`, `dist`, `coverage`, `.turbo`, `.next`) працюють як раніше.
|
|
@@ -154,8 +194,8 @@
|
|
|
154
194
|
|
|
155
195
|
### Changed
|
|
156
196
|
|
|
157
|
-
- `js-bun-db.mdc` (v1.4): `sql.unsafe(...)` тепер заборонено за замовчуванням — допустимо лише для підстановки назви таблиці/колонки чи dynamic SQL/DDL з code-controlled значенням; інакше переробляємо на tagged template `sql\`...${value}...\``. Кожен легітимний виклик має супроводжуватись
|
|
158
|
-
- `check-js-bun-db.mjs`: замість вузької перевірки `sql.unsafe
|
|
197
|
+
- `js-bun-db.mdc` (v1.4): `sql.unsafe(...)` тепер заборонено за замовчуванням — допустимо лише для підстановки назви таблиці/колонки чи dynamic SQL/DDL з code-controlled значенням; інакше переробляємо на tagged template `sql\`...${value}...\``. Кожен легітимний виклик має супроводжуватись маркером`// allow-unsafe: <причина>` на тому ж рядку або рядком вище.
|
|
198
|
+
- `check-js-bun-db.mjs`: замість вузької перевірки `sql.unsafe` із tagged-template і інтерполяцією тепер сканер `findBunSqlUnsafeUseWithoutAllowMarkerInText` падає на будь-якому `obj.unsafe(...)` без маркера-коментаря з непорожньою причиною (line- або block-коментар на тому ж рядку чи безпосередньо перед викликом).
|
|
159
199
|
- `ast-scan-utils.mjs`: додано `parseProgramAndCommentsOrNull` — окремий вхід для перевірок, яким потрібні коментарі поряд з AST.
|
|
160
200
|
|
|
161
201
|
## [1.8.159] - 2026-05-01
|
package/mdc/ga.mdc
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила форматів для .github/workflows
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.7'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
У `.github/workflows/` лише **`.yml`**. Мають бути **`clean-ga-workflows.yml`**, **`clean-merged-branch.yml`**, **`lint-ga.yml`**, **`git-ai.yml`**. Якщо є **`apply-k8s.yml`** / **`apply-nats-consumer.yml`** — paths у тригері як у фрагментах.
|
|
8
8
|
|
|
9
|
+
**Кожен** workflow у `.github/workflows/*.yml` **обов'язково** містить блок `concurrency` з фіксованим `group` та `cancel-in-progress: true`:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Без винятків — у scheduled cleanup-воркфлоу, у `pull_request: types: [closed]`, у publish-воркфлоу теж. Це уникає паралельних запусків того самого workflow на тій самій ref і скасовує попередні в чергу нових.
|
|
18
|
+
|
|
9
19
|
Повинен бути файл .github/workflows/clean-ga-workflows.yml, зі змістом:
|
|
10
20
|
|
|
11
21
|
```yaml
|
|
@@ -18,6 +28,10 @@ on:
|
|
|
18
28
|
# Allow workflow to be manually run from the GitHub UI
|
|
19
29
|
workflow_dispatch: {}
|
|
20
30
|
|
|
31
|
+
concurrency:
|
|
32
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
33
|
+
cancel-in-progress: true
|
|
34
|
+
|
|
21
35
|
jobs:
|
|
22
36
|
cleanup_old_workflows:
|
|
23
37
|
runs-on: ubuntu-latest
|
|
@@ -47,6 +61,10 @@ on:
|
|
|
47
61
|
# Allow workflow to be manually run from the GitHub UI
|
|
48
62
|
workflow_dispatch: {}
|
|
49
63
|
|
|
64
|
+
concurrency:
|
|
65
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
66
|
+
cancel-in-progress: true
|
|
67
|
+
|
|
50
68
|
jobs:
|
|
51
69
|
cleanup_old_branches:
|
|
52
70
|
runs-on: ubuntu-latest
|
|
@@ -119,6 +137,10 @@ on:
|
|
|
119
137
|
pull_request:
|
|
120
138
|
types: [closed]
|
|
121
139
|
|
|
140
|
+
concurrency:
|
|
141
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
142
|
+
cancel-in-progress: true
|
|
143
|
+
|
|
122
144
|
jobs:
|
|
123
145
|
git-ai:
|
|
124
146
|
if: github.event.pull_request.merged == true
|
package/mdc/image.mdc
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Оптимізація зображень через @nitra/minify-image у локальному lint
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.5'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (≥ **3.2.0**) запускається через `npx` (як `markdownlint-cli2` у text.mdc) і **не** додається в `dependencies` / `devDependencies`. Канонічний `lint-image` — авто-оптимізація з
|
|
7
|
+
CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (≥ **3.2.0**) запускається через `npx` (як `markdownlint-cli2` у text.mdc) і **не** додається в `dependencies` / `devDependencies`. Канонічний `lint-image` — авто-оптимізація з прапорцем `--write`: стискає raster/SVG на місці. **AVIF-генерація (`--avif`) у `lint-image` заборонена** — її виконує лише `npx @nitra/cursor check image`, який заодно прибирає AVIF-сироти (див. секцію «AVIF-імпорти у `.vue`»). Split-cache робить повторні прогони дешевими — і локально, і після `git clone`.
|
|
8
8
|
|
|
9
9
|
Перевірка лише локальна — у CI `lint-image` не запускаємо (sharp/svgo тягнуть бінарні залежності, цінність на ubuntu-runner-ах нижча за час прогону). Окремий workflow `lint-image.yml` створювати не треба.
|
|
10
10
|
|
|
@@ -14,12 +14,12 @@ CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (
|
|
|
14
14
|
{
|
|
15
15
|
"scripts": {
|
|
16
16
|
"lint": "bun run lint-js && bun run lint-text && bun run lint-ga && bun run lint-image && oxfmt .",
|
|
17
|
-
"lint-image": "npx @nitra/minify-image --src=. --write
|
|
17
|
+
"lint-image": "npx @nitra/minify-image --src=. --write"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Якщо в `package.json` уже є агрегований `lint`, додай у його ланцюжок `bun run lint-image` (як `bun run lint-text`, `bun run lint-js`, `bun run lint-ga`). Так розробник, що локально гонить `bun run lint`, перед фіксацією одразу бачить, чи зросли
|
|
22
|
+
Якщо в `package.json` уже є агрегований `lint`, додай у його ланцюжок `bun run lint-image` (як `bun run lint-text`, `bun run lint-js`, `bun run lint-ga`). Так розробник, що локально гонить `bun run lint`, перед фіксацією одразу бачить, чи зросли зображення.
|
|
23
23
|
|
|
24
24
|
## Split-cache
|
|
25
25
|
|
|
@@ -47,13 +47,19 @@ rm -f .minify-image-cache.tsv
|
|
|
47
47
|
# прибери відповідний рядок з .gitignore, якщо був
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
AVIF-двійники (`<name>.<ext>.avif`) **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від
|
|
50
|
+
AVIF-двійники (`<name>.<ext>.avif`) **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
|
|
51
51
|
|
|
52
52
|
## AVIF-імпорти у `.vue`
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor check image` — у `lint-image` прапорець `--avif` заборонений (інакше `bun run lint` плодить непотрібні `.avif` для зображень, що ніде не використовуються). Перевірка робить три кроки в порядку:
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
1. Запускає `npx @nitra/minify-image --src=. --write --avif` — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF.
|
|
57
|
+
2. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
|
|
58
|
+
- **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
|
|
59
|
+
- **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
|
|
60
|
+
3. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости».
|
|
61
|
+
|
|
62
|
+
```vue title="App.vue (після check image)"
|
|
57
63
|
<script setup>
|
|
58
64
|
import welcomeImage from './assets/welcome.png.avif'
|
|
59
65
|
</script>
|
|
@@ -63,19 +69,10 @@ import welcomeImage from './assets/welcome.png.avif'
|
|
|
63
69
|
</template>
|
|
64
70
|
```
|
|
65
71
|
|
|
66
|
-
```vue title="App.vue (неправильно — втрачає AVIF)"
|
|
67
|
-
<script setup>
|
|
68
|
-
import welcomeImage from './assets/welcome.png'
|
|
69
|
-
</script>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Перевірка `check image` сканує `.vue` файли в кожному workspace-пакеті (root + workspaces) і вимагає AVIF-двійник для двох форм:
|
|
73
|
-
|
|
74
|
-
1. **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні).
|
|
75
|
-
2. **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
|
|
76
|
-
|
|
77
72
|
Реактивне `:src="..."` (з JS-виразом — змінною, тернарником, викликом тощо) **не сканується** — значення обчислюється у рантаймі й шлях туди потрапляє через імпорт або інший резолвинг, який ловить імпорт-перевірка вище. SVG не торкаємо (vector → AVIF безглуздо). Атрибути `data-src=`, `obj.src=` у `<script>` тощо також пропускаються.
|
|
78
73
|
|
|
74
|
+
Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) — `check image` падає з помилкою на конкретний файл, як раніше.
|
|
75
|
+
|
|
79
76
|
### Опт-аут для конкретного пакета
|
|
80
77
|
|
|
81
78
|
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл, де AVIF-підтримка не гарантована), додай у `package.json` цього пакета:
|
|
@@ -92,8 +89,8 @@ import welcomeImage from './assets/welcome.png'
|
|
|
92
89
|
|
|
93
90
|
## Заборонені залежності
|
|
94
91
|
|
|
95
|
-
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` кореневого `package.json` — CLI запускається лише через `npx` (так само, як `markdownlint-cli2` у text.mdc). Якщо потрібен явний пін — закладай діапазон версій npm у самому виклику (`npx @nitra/minify-image@^3 --src=. --write
|
|
92
|
+
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` кореневого `package.json` — CLI запускається лише через `npx` (так само, як `markdownlint-cli2` у text.mdc). Якщо потрібен явний пін — закладай діапазон версій npm у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
|
|
96
93
|
|
|
97
94
|
## Перевірка
|
|
98
95
|
|
|
99
|
-
`npx @nitra/cursor check image` (охоплює `lint-image` з обовʼязковими `--src=.`, `--write
|
|
96
|
+
`npx @nitra/cursor check image` (охоплює `lint-image` з обовʼязковими `--src=.`, `--write` і **забороненим** `--avif`; агрегований `lint`; `.n-minify-image.tsv` НЕ в `.gitignore` — має бути в git; відсутність застарілого `.minify-image-cache.tsv` у корені; запуск AVIF-генерації + авто-заміна raster-посилань на `.avif` у `.vue`/`.html` кожного workspace-пакета + прибирання AVIF-сиріт).
|
package/mdc/js-run.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Це правила для backend проектів на JavaScript/Node.js, сюди входять і job і WEB сервери.
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.3'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Область застосування
|
|
@@ -136,6 +136,26 @@ console.log(env.OPTIONAL_ENV_VAR)
|
|
|
136
136
|
`// @nitra/cursor ignore-next-line checkEnv` безпосередньо перед використанням
|
|
137
137
|
(escape-hatch для legacy-коду, не для нових файлів).
|
|
138
138
|
|
|
139
|
+
## depcheck у GitHub Actions з path-фільтром
|
|
140
|
+
|
|
141
|
+
Якщо в `.github/workflows/*.yml` є тригер з `paths:`, який обмежує запуск workflow змінами в каталозі конкретного backend-пакета, в job цього workflow має бути крок `npx depcheck` з `working-directory`, який вказує на той самий каталог пакета. Це гарантує, що декларація залежностей у `package.json` пакета відповідає реальним імпортам — інакше можна випадково зламати білд після видалення «зайвої» залежності, яка насправді використовується через побічний імпорт.
|
|
142
|
+
|
|
143
|
+
Список `--ignores` **обов'язково** містить як мінімум `graphql,bun` (це ті, які `depcheck` не вміє коректно розпізнавати: `graphql` — peer-залежність, що часто використовується без прямого імпорту в коді; `bun` — рантайм, не npm-пакет). За потреби список можна розширити іншими модулями, специфічними для пакета — список значень розділяється комою без пробілів.
|
|
144
|
+
|
|
145
|
+
```yaml title="Приклад: workflow для cron-jobs/refund-loyalty-points"
|
|
146
|
+
on:
|
|
147
|
+
push:
|
|
148
|
+
paths:
|
|
149
|
+
- 'cron-jobs/refund-loyalty-points/**'
|
|
150
|
+
|
|
151
|
+
# …
|
|
152
|
+
|
|
153
|
+
- run: npx depcheck --ignores="graphql,bun"
|
|
154
|
+
working-directory: cron-jobs/refund-loyalty-points
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Правило не застосовується до workflow без `paths:` або з `paths:`, який не звужує тригер до одного backend-пакета (наприклад, кореневі `lint-*.yml` з глобальними `**/*.js`).
|
|
158
|
+
|
|
139
159
|
## Перевірка
|
|
140
160
|
|
|
141
161
|
`npx @nitra/cursor check js-run`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.185",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"oxc-parser": "^0.128.0",
|
|
45
45
|
"yaml": "^2.8.3"
|
|
46
46
|
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@nitra/cursor": "^1.8.170"
|
|
49
|
+
},
|
|
47
50
|
"engines": {
|
|
48
51
|
"bun": ">=1.3",
|
|
49
52
|
"node": ">=25"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@nitra/cursor": "^1.8.170"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/scripts/check-abie.mjs
CHANGED
|
@@ -384,6 +384,7 @@ export function ignoreBranchesIncludesRequired(ignoreBranches, required) {
|
|
|
384
384
|
/**
|
|
385
385
|
* Збирає абсолютні шляхи до **.yaml** / **.yml** під деревом, де є сегмент **k8s**.
|
|
386
386
|
* @param {string} root корінь репозиторію
|
|
387
|
+
* @param {string[]} [ignorePaths] абсолютні шляхи каталогів, повністю виключених з обходу
|
|
387
388
|
* @returns {Promise<string[]>} відсортовані шляхи
|
|
388
389
|
*/
|
|
389
390
|
async function findK8sYamlFiles(root, ignorePaths = []) {
|
|
@@ -41,7 +41,7 @@ const NPM_VIEW_TIMEOUT_MS = 10_000
|
|
|
41
41
|
/**
|
|
42
42
|
* Тихо запускає `git` і повертає stdout або `null` при будь-якій помилці.
|
|
43
43
|
* @param {string[]} args аргументи `git`
|
|
44
|
-
* @returns {Promise<string | null>}
|
|
44
|
+
* @returns {Promise<string | null>} stdout процесу або `null` при будь-якій помилці виконання
|
|
45
45
|
*/
|
|
46
46
|
async function gitOrNull(args) {
|
|
47
47
|
try {
|
|
@@ -54,7 +54,7 @@ async function gitOrNull(args) {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Чи робочий каталог — git-репозиторій.
|
|
57
|
-
* @returns {Promise<boolean>}
|
|
57
|
+
* @returns {Promise<boolean>} `true`, якщо `git rev-parse --is-inside-work-tree` повернув `true`
|
|
58
58
|
*/
|
|
59
59
|
async function isInsideGitRepo() {
|
|
60
60
|
const out = await gitOrNull(['rev-parse', '--is-inside-work-tree'])
|
|
@@ -63,7 +63,7 @@ async function isInsideGitRepo() {
|
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* Назва поточної гілки (або `HEAD` для detached state).
|
|
66
|
-
* @returns {Promise<string | null>}
|
|
66
|
+
* @returns {Promise<string | null>} назва гілки чи `'HEAD'`, або `null` (поза git / помилка)
|
|
67
67
|
*/
|
|
68
68
|
async function currentBranchName() {
|
|
69
69
|
const out = await gitOrNull(['rev-parse', '--abbrev-ref', 'HEAD'])
|
|
@@ -73,7 +73,7 @@ async function currentBranchName() {
|
|
|
73
73
|
/**
|
|
74
74
|
* Знаходить ref для базової гілки. Перевага локальному `dev`, далі `origin/dev`. Повертає `null`,
|
|
75
75
|
* якщо жоден не існує.
|
|
76
|
-
* @returns {Promise<string | null>}
|
|
76
|
+
* @returns {Promise<string | null>} назва ref-а (`dev` чи `origin/dev`) або `null`, якщо жоден не знайдено
|
|
77
77
|
*/
|
|
78
78
|
async function resolveBaseRef() {
|
|
79
79
|
for (const ref of [BASE_BRANCH, `origin/${BASE_BRANCH}`]) {
|
|
@@ -88,8 +88,8 @@ async function resolveBaseRef() {
|
|
|
88
88
|
/**
|
|
89
89
|
* Точка розгалуження поточної гілки від `baseRef`. На feature-гілці = коли вона відгалузилась;
|
|
90
90
|
* на `main` після merge `dev → main` = поточний `dev`. Повертає `null`, якщо merge-base нема.
|
|
91
|
-
* @param {string} baseRef
|
|
92
|
-
* @returns {Promise<string | null>}
|
|
91
|
+
* @param {string} baseRef SHA або ref-name бази (зазвичай `dev` / `origin/dev`)
|
|
92
|
+
* @returns {Promise<string | null>} SHA точки розгалуження або `null`, якщо merge-base нема
|
|
93
93
|
*/
|
|
94
94
|
async function resolveMergeBase(baseRef) {
|
|
95
95
|
const out = await gitOrNull(['merge-base', baseRef, 'HEAD'])
|
|
@@ -104,9 +104,9 @@ async function resolveMergeBase(baseRef) {
|
|
|
104
104
|
* Для кореня `.` — це точка плюс magic-виключення кожного підворкспейсу через `:(exclude)<sub>/`,
|
|
105
105
|
* щоб зміни всередині sub-workspace не вважалися змінами кореня.
|
|
106
106
|
* Для звичайного воркспейсу — просто `<ws>/`.
|
|
107
|
-
* @param {string} ws
|
|
108
|
-
* @param {string[]} subWorkspaces
|
|
109
|
-
* @returns {string[]}
|
|
107
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня, інакше — відносний шлях, як у `workspaces`)
|
|
108
|
+
* @param {string[]} subWorkspaces усі під-воркспейси (зокрема для `'.'` потрібно виключити їх)
|
|
109
|
+
* @returns {string[]} pathspec для git: масив, що передається після `--`
|
|
110
110
|
*/
|
|
111
111
|
function pathspecForWorkspace(ws, subWorkspaces) {
|
|
112
112
|
if (ws !== '.') return [`${ws}/`]
|
|
@@ -119,9 +119,9 @@ function pathspecForWorkspace(ws, subWorkspaces) {
|
|
|
119
119
|
* `git diff --quiet <baseRef> -- <pathspec>` ловить committed-зміни на цій гілці й незбережені
|
|
120
120
|
* правки tracked-файлів. Untracked-файли — `git ls-files --others --exclude-standard`.
|
|
121
121
|
* @param {string} baseRef SHA або ref-name (зокрема merge-base)
|
|
122
|
-
* @param {string} ws
|
|
123
|
-
* @param {string[]} subWorkspaces
|
|
124
|
-
* @returns {Promise<boolean>}
|
|
122
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
123
|
+
* @param {string[]} subWorkspaces усі під-воркспейси для коректного формування pathspec кореня
|
|
124
|
+
* @returns {Promise<boolean>} `true`, якщо в межах воркспейсу є будь-які зміни (committed або untracked)
|
|
125
125
|
*/
|
|
126
126
|
async function workspaceHasChangesAgainstBase(baseRef, ws, subWorkspaces) {
|
|
127
127
|
const pathspec = pathspecForWorkspace(ws, subWorkspaces)
|
|
@@ -129,8 +129,7 @@ async function workspaceHasChangesAgainstBase(baseRef, ws, subWorkspaces) {
|
|
|
129
129
|
await execFileAsync('git', ['diff', '--quiet', baseRef, '--', ...pathspec])
|
|
130
130
|
} catch (error) {
|
|
131
131
|
const code = /** @type {{ code?: number }} */ (error).code
|
|
132
|
-
|
|
133
|
-
return false
|
|
132
|
+
return code === 1
|
|
134
133
|
}
|
|
135
134
|
const untracked = await gitOrNull(['ls-files', '--others', '--exclude-standard', '--', ...pathspec])
|
|
136
135
|
return typeof untracked === 'string' && untracked.trim().length > 0
|
|
@@ -138,9 +137,9 @@ async function workspaceHasChangesAgainstBase(baseRef, ws, subWorkspaces) {
|
|
|
138
137
|
|
|
139
138
|
/**
|
|
140
139
|
* Версія з `<ws>/package.json` на `baseRef` або `null`.
|
|
141
|
-
* @param {string} baseRef
|
|
142
|
-
* @param {string} ws
|
|
143
|
-
* @returns {Promise<string | null>}
|
|
140
|
+
* @param {string} baseRef SHA або ref-name (зазвичай merge-base) для `git show`
|
|
141
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
142
|
+
* @returns {Promise<string | null>} значення поля `version` або `null`, якщо файла нема / JSON некоректний
|
|
144
143
|
*/
|
|
145
144
|
async function readBaseVersion(baseRef, ws) {
|
|
146
145
|
const wsPath = ws === '.' ? 'package.json' : `${ws}/package.json`
|
|
@@ -156,9 +155,9 @@ async function readBaseVersion(baseRef, ws) {
|
|
|
156
155
|
|
|
157
156
|
/**
|
|
158
157
|
* Чи містить текст `CHANGELOG.md` запис `## [version]` (з опційним `- YYYY-MM-DD`).
|
|
159
|
-
* @param {string} text
|
|
160
|
-
* @param {string} version
|
|
161
|
-
* @returns {boolean}
|
|
158
|
+
* @param {string} text вміст CHANGELOG.md
|
|
159
|
+
* @param {string} version версія, яку шукаємо у форматі Keep a Changelog
|
|
160
|
+
* @returns {boolean} `true`, якщо запис для `version` знайдено
|
|
162
161
|
*/
|
|
163
162
|
function changelogHasVersionEntry(text, version) {
|
|
164
163
|
const escaped = version.replaceAll(/[.+*?^$()[\]{}|\\]/g, String.raw`\$&`)
|
|
@@ -168,8 +167,8 @@ function changelogHasVersionEntry(text, version) {
|
|
|
168
167
|
|
|
169
168
|
/**
|
|
170
169
|
* Зчитує `<ws>/package.json`. `null`, якщо файл відсутній або JSON некоректний.
|
|
171
|
-
* @param {string} ws
|
|
172
|
-
* @returns {Promise<Record<string, unknown> | null>}
|
|
170
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
171
|
+
* @returns {Promise<Record<string, unknown> | null>} розпарсений `package.json` або `null`
|
|
173
172
|
*/
|
|
174
173
|
async function readPackageJsonOrNull(ws) {
|
|
175
174
|
const path = join(ws, 'package.json')
|
|
@@ -186,8 +185,8 @@ async function readPackageJsonOrNull(ws) {
|
|
|
186
185
|
|
|
187
186
|
/**
|
|
188
187
|
* Воркспейс публікується в npm: має непорожній `name`, не `private: true`, і має масив `files`.
|
|
189
|
-
* @param {Record<string, unknown> | null} pkg
|
|
190
|
-
* @returns {boolean}
|
|
188
|
+
* @param {Record<string, unknown> | null} pkg розпарсений `package.json` (або `null`)
|
|
189
|
+
* @returns {boolean} `true`, якщо пакет придатний для публікації в npm
|
|
191
190
|
*/
|
|
192
191
|
function isNpmPublishable(pkg) {
|
|
193
192
|
if (!pkg) return false
|
|
@@ -199,8 +198,8 @@ function isNpmPublishable(pkg) {
|
|
|
199
198
|
/**
|
|
200
199
|
* Опублікована версія пакета в npm-реєстрі. `null` — пакет не знайдено / нема мережі / помилка.
|
|
201
200
|
* Дефолтна імплементація — `npm view <name> version` із таймаутом, щоб не блокуватись офлайн.
|
|
202
|
-
* @param {string} name
|
|
203
|
-
* @returns {Promise<string | null>}
|
|
201
|
+
* @param {string} name повна назва пакета (включно зі скоупом)
|
|
202
|
+
* @returns {Promise<string | null>} опублікована версія або `null` (нема пакета / офлайн)
|
|
204
203
|
*/
|
|
205
204
|
async function defaultGetPublishedVersion(name) {
|
|
206
205
|
try {
|
|
@@ -214,10 +213,10 @@ async function defaultGetPublishedVersion(name) {
|
|
|
214
213
|
|
|
215
214
|
/**
|
|
216
215
|
* Перевіряє масив `files` у `<ws>/package.json`: якщо оголошено — має містити `"CHANGELOG.md"`.
|
|
217
|
-
* @param {Record<string, unknown> | null} pkg
|
|
218
|
-
* @param {string} ws
|
|
219
|
-
* @param {(msg: string) => void} pass
|
|
220
|
-
* @param {(msg: string) => void} fail
|
|
216
|
+
* @param {Record<string, unknown> | null} pkg розпарсений `package.json` воркспейсу
|
|
217
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
218
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
219
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
221
220
|
*/
|
|
222
221
|
function checkFilesArrayContainsChangelog(pkg, ws, pass, fail) {
|
|
223
222
|
if (!pkg || !Array.isArray(pkg.files)) return
|
|
@@ -231,10 +230,10 @@ function checkFilesArrayContainsChangelog(pkg, ws, pass, fail) {
|
|
|
231
230
|
|
|
232
231
|
/**
|
|
233
232
|
* Перевіряє наявність запису у `<ws>/CHANGELOG.md` для версії `version`.
|
|
234
|
-
* @param {string} ws
|
|
235
|
-
* @param {string} version
|
|
236
|
-
* @param {(msg: string) => void} pass
|
|
237
|
-
* @param {(msg: string) => void} fail
|
|
233
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
234
|
+
* @param {string} version версія, для якої очікується запис
|
|
235
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
236
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
238
237
|
* @returns {Promise<boolean>} `false`, якщо файл відсутній або немає запису
|
|
239
238
|
*/
|
|
240
239
|
async function verifyChangelogEntry(ws, version, pass, fail) {
|
|
@@ -257,11 +256,11 @@ async function verifyChangelogEntry(ws, version, pass, fail) {
|
|
|
257
256
|
* npm-published режим: порівнює локальну `version` з опублікованою в реєстрі. Якщо вони
|
|
258
257
|
* відрізняються — вимагає запис у CHANGELOG і `"CHANGELOG.md"` у `files`. Якщо реєстр недосяжний,
|
|
259
258
|
* правило fail-safe пасує (щоб офлайн-розробка не блокувалась).
|
|
260
|
-
* @param {string} ws
|
|
261
|
-
* @param {Record<string, unknown>} pkg
|
|
262
|
-
* @param {(name: string) => Promise<string | null>} getPublishedVersion
|
|
263
|
-
* @param {(msg: string) => void} pass
|
|
264
|
-
* @param {(msg: string) => void} fail
|
|
259
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
260
|
+
* @param {Record<string, unknown>} pkg розпарсений `package.json` воркспейсу
|
|
261
|
+
* @param {(name: string) => Promise<string | null>} getPublishedVersion стаб/реальна функція отримання опублікованої версії
|
|
262
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
263
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
265
264
|
*/
|
|
266
265
|
async function checkPublishedWorkspace(ws, pkg, getPublishedVersion, pass, fail) {
|
|
267
266
|
const label = ws === '.' ? '<root>' : ws
|
|
@@ -289,10 +288,10 @@ async function checkPublishedWorkspace(ws, pkg, getPublishedVersion, pass, fail)
|
|
|
289
288
|
* local-only режим: PR-scoped перевірка проти `dev` через `git merge-base`. Викликається лише
|
|
290
289
|
* для воркспейсів, де є реальні зміни щодо merge-base.
|
|
291
290
|
* @param {string} mergeBase SHA точки розгалуження
|
|
292
|
-
* @param {string} ws
|
|
293
|
-
* @param {Record<string, unknown> | null} pkg
|
|
294
|
-
* @param {(msg: string) => void} pass
|
|
295
|
-
* @param {(msg: string) => void} fail
|
|
291
|
+
* @param {string} ws шлях воркспейсу (`'.'` для кореня)
|
|
292
|
+
* @param {Record<string, unknown> | null} pkg розпарсений `package.json` воркспейсу (або `null`)
|
|
293
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
294
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
296
295
|
*/
|
|
297
296
|
async function checkLocalOnlyChangedWorkspace(mergeBase, ws, pkg, pass, fail) {
|
|
298
297
|
const label = ws === '.' ? '<root>' : ws
|
|
@@ -315,12 +314,12 @@ async function checkLocalOnlyChangedWorkspace(mergeBase, ws, pkg, pass, fail) {
|
|
|
315
314
|
|
|
316
315
|
/**
|
|
317
316
|
* Виконує local-only перевірку для всіх workspace-ів, у яких немає npm-published режиму.
|
|
318
|
-
* @param {string[]} localOnlyWorkspaces
|
|
319
|
-
* @param {Map<string, Record<string, unknown> | null>} pkgByWs
|
|
320
|
-
* @param {string[]} subWorkspaces
|
|
321
|
-
* @param {(msg: string) => void} pass
|
|
322
|
-
* @param {(msg: string) => void} fail
|
|
323
|
-
* @returns {Promise<void>}
|
|
317
|
+
* @param {string[]} localOnlyWorkspaces список шляхів local-only воркспейсів
|
|
318
|
+
* @param {Map<string, Record<string, unknown> | null>} pkgByWs мапа: шлях воркспейсу → розпарсений `package.json` (або `null`)
|
|
319
|
+
* @param {string[]} subWorkspaces усі під-воркспейси (для коректного pathspec кореня)
|
|
320
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
321
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
322
|
+
* @returns {Promise<void>} резолвиться по завершенню перевірок усіх local-only воркспейсів
|
|
324
323
|
*/
|
|
325
324
|
async function runLocalOnlyChecks(localOnlyWorkspaces, pkgByWs, subWorkspaces, pass, fail) {
|
|
326
325
|
if (localOnlyWorkspaces.length === 0) return
|
|
@@ -358,7 +357,7 @@ async function runLocalOnlyChecks(localOnlyWorkspaces, pkgByWs, subWorkspaces, p
|
|
|
358
357
|
|
|
359
358
|
/**
|
|
360
359
|
* Перевіряє відповідність проєкту правилу changelog.mdc.
|
|
361
|
-
* @param {object} [opts]
|
|
360
|
+
* @param {object} [opts] опції перевірки
|
|
362
361
|
* @param {(name: string) => Promise<string | null>} [opts.getPublishedVersion] перевизначення для тестів
|
|
363
362
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
364
363
|
*/
|