@7n/rules 1.38.0 → 1.39.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.39.0] - 2026-07-21
4
+
5
+ ### Added
6
+
7
+ - workspace_root: канон одного кореневого uv workspace на репозиторій (дзеркало rust/workspace_root), main.json auto-glob розширено на вкладені pyproject.toml
8
+
3
9
  ## [1.38.0] - 2026-07-21
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "description": "CLI еталонних правил і skills (префікс n-): синк у репозиторій, дельта-lint, конформність",
5
5
  "keywords": [
6
6
  "cli",
@@ -20,6 +20,7 @@ resource: npm/scripts/utils/
20
20
  | [resolve-js-root.mjs](resolve-js-root.md) | JS Module |
21
21
  | [spawn-async.mjs](spawn-async.md) | JS Module |
22
22
  | [test-helpers.mjs](test-helpers.md) | JS Module |
23
+ | [uv-workspace.mjs](uv-workspace.md) | JS Module |
23
24
  | [walk-cache.mjs](walk-cache.md) | JS Module |
24
25
  | [walkDir.mjs](walkDir.md) | JS Module |
25
26
  | [with-lock.mjs](with-lock.md) | JS Module |
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: JS Module
3
+ title: uv-workspace.mjs
4
+ resource: npm/scripts/utils/uv-workspace.mjs
5
+ docgen:
6
+ crc: b7602da3
7
+ model: openai-codex/gpt-5.4-mini
8
+ tier: cloud-min
9
+ score: 100
10
+ issues: judge:inaccurate:0.98
11
+ judgeModel: openai-codex/gpt-5.4-mini
12
+ ---
13
+
14
+ ## Огляд
15
+
16
+ `readPyprojectManifest` читає `pyproject.toml` і повертає маніфест uv workspace, а `resolveUvWorkspaceMemberDirs` резолвить каталоги з `[tool.uv.workspace].members` та `.exclude` за тією самою glob-семантикою, що й у Cargo workspaces. Це read-only, fail-safe набір спільних T0-утиліт: він не пише у ФС чи БД, не кидає винятків назовні та для частини помилок повертає порожнє значення замість помилки.
17
+
18
+ ## Поведінка
19
+
20
+ - `readPyprojectManifest` — читає `pyproject.toml` для uv workspace і повертає розпарсений manifest або `null`, якщо файл відсутній чи TOML невалідний.
21
+ - `resolveUvWorkspaceMemberDirs` — перетворює `members` і `exclude` uv workspace на список абсолютних каталогів із власним `pyproject.toml`, враховуючи літеральні шляхи та прості glob-патерни.
22
+
23
+ ## Публічний API
24
+
25
+ - readPyprojectManifest — Розпарсений pyproject.toml або null (файл відсутній чи невалідний TOML).
26
+ - resolveUvWorkspaceMemberDirs — Резолвить `[tool.uv.workspace].members`/`.exclude`-патерни (літеральні шляхи й прості
27
+ glob з `*`) відносно `rootDir` у список абсолютних каталогів, що мають власний
28
+ pyproject.toml. Без повної glob-семантики uv — лише `*`-сегменти й літерали.
29
+
30
+ ## Гарантії поведінки
31
+
32
+ - Read-only: не виконує операцій запису (ФС/БД).
33
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
34
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Спільні T0 (без spawn `uv`) утиліти для роботи з uv workspace-структурою: читання
3
+ * `pyproject.toml`, резолв `[tool.uv.workspace].members`/`.exclude`-glob-патернів у
4
+ * каталоги. Дзеркалить `cargo-workspace.mjs` (rust/workspace_root) — uv workspaces
5
+ * навмисно змодельовані на Cargo workspaces, glob-семантика `members`/`exclude` та сама.
6
+ * Не імпортується напряму з rust-боку (окремі плагіни) — дублювання свідоме.
7
+ */
8
+ import { existsSync } from 'node:fs'
9
+ import { readFile } from 'node:fs/promises'
10
+ import { dirname, resolve } from 'node:path'
11
+
12
+ import { parse as parseToml } from 'smol-toml'
13
+
14
+ import { scanGlob } from './glob-compat.mjs'
15
+
16
+ const TRAILING_SLASH_RE = /\/$/
17
+
18
+ /**
19
+ * Розпарсений pyproject.toml або null (файл відсутній чи невалідний TOML).
20
+ * @param {string} absPath абсолютний шлях до pyproject.toml
21
+ * @returns {Promise<Record<string, unknown>|null>} розпарсений маніфест або null
22
+ */
23
+ export async function readPyprojectManifest(absPath) {
24
+ if (!existsSync(absPath)) return null
25
+ try {
26
+ return parseToml(await readFile(absPath, 'utf8'))
27
+ } catch {
28
+ return null
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Резолвить `[tool.uv.workspace].members`/`.exclude`-патерни (літеральні шляхи й прості
34
+ * glob з `*`) відносно `rootDir` у список абсолютних каталогів, що мають власний
35
+ * pyproject.toml. Без повної glob-семантики uv — лише `*`-сегменти й літерали.
36
+ * @param {string} rootDir корінь workspace (каталог з pyproject.toml, де живе `[tool.uv.workspace]`)
37
+ * @param {string[]} patterns патерни з `members`/`exclude`
38
+ * @returns {Promise<string[]>} абсолютні шляхи (без дублікатів)
39
+ */
40
+ export async function resolveUvWorkspaceMemberDirs(rootDir, patterns) {
41
+ const found = new Set()
42
+ for (const pattern of patterns ?? []) {
43
+ const norm = pattern.replace(TRAILING_SLASH_RE, '')
44
+ if (norm.includes('*')) {
45
+ // Патерн для pyproject.toml напряму (не для каталогів) — деякі раннери (Bun.Glob)
46
+ // за замовчуванням матчать лише файли, тож патерн на голий каталог даватиме 0 збігів.
47
+ for await (const relManifest of scanGlob(`${norm}/pyproject.toml`, rootDir)) {
48
+ found.add(resolve(rootDir, dirname(relManifest)))
49
+ }
50
+ continue
51
+ }
52
+ const abs = resolve(rootDir, norm)
53
+ if (existsSync(`${abs}/pyproject.toml`)) found.add(abs)
54
+ }
55
+ return [...found]
56
+ }