@7n/rules 1.49.14 → 1.49.16
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 +12 -0
- package/package.json +1 -1
- package/rules/test/coverage/fix-worker.mjs +3 -1
- package/rules/test/coverage/main.mjs +1 -1
- package/scripts/lib/lint-surface/docs/run-detectors.md +4 -2
- package/scripts/lib/lint-surface/run-detectors.mjs +46 -3
- package/scripts/lib/lint-surface/run-fix.mjs +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.49.16] - 2026-07-27
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Обмежено npm-module репозиторіями з npm publisher topology
|
|
8
|
+
|
|
9
|
+
## [1.49.15] - 2026-07-27
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- refresh canonical mutation results after generated coverage tests
|
|
14
|
+
|
|
3
15
|
## [1.49.14] - 2026-07-27
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/package.json
CHANGED
|
@@ -88,6 +88,7 @@ export async function fixWorker(violations, ctx, deps = {}) {
|
|
|
88
88
|
|
|
89
89
|
/** @type {string[]} */
|
|
90
90
|
const touchedFiles = []
|
|
91
|
+
const mutationRefreshFiles = []
|
|
91
92
|
/** @type {Array<{provider: string, hook: string, files: string[], error: string}>} */
|
|
92
93
|
const failed = []
|
|
93
94
|
let feedback = null
|
|
@@ -105,6 +106,7 @@ export async function fixWorker(violations, ctx, deps = {}) {
|
|
|
105
106
|
try {
|
|
106
107
|
const res = await provider[hook]({ ...args, cwd: ctx.cwd, ctx: hookCtx(ctx, deadlineAt) })
|
|
107
108
|
touchedFiles.push(...(res?.touchedFiles ?? []))
|
|
109
|
+
mutationRefreshFiles.push(...(res?.mutationRefreshFiles ?? []))
|
|
108
110
|
if (res?.feedback?.previousError) feedback = res.feedback
|
|
109
111
|
deferred.push(...(res?.deferred ?? []))
|
|
110
112
|
for (const failure of res?.failed ?? []) {
|
|
@@ -137,5 +139,5 @@ export async function fixWorker(violations, ctx, deps = {}) {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
return { touchedFiles, failed, deferred, feedback }
|
|
142
|
+
return { touchedFiles, mutationRefreshFiles: [...new Set(mutationRefreshFiles)], failed, deferred, feedback }
|
|
141
143
|
}
|
|
@@ -113,7 +113,7 @@ export async function lint(ctx) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
if (!(await provider.detect(cwd))) continue
|
|
116
|
-
let rows = await provider.collect(cwd, {})
|
|
116
|
+
let rows = await provider.collect(cwd, { mutationRefreshFiles: ctx.mutationRefreshFiles ?? [] })
|
|
117
117
|
|
|
118
118
|
// LLM-класифікація survived-мутантів (allowed gaps): verdict-и
|
|
119
119
|
// equivalent/defensive/glue/wrapper з confidence ≥ порогу виключаються зі
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: run-detectors.mjs
|
|
4
4
|
resource: npm/scripts/lib/lint-surface/run-detectors.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 9f044303
|
|
7
7
|
model: openai-codex/gpt-5.4-mini
|
|
8
8
|
tier: cloud-min
|
|
9
9
|
score: 100
|
|
@@ -19,7 +19,7 @@ docgen:
|
|
|
19
19
|
|
|
20
20
|
DEFAULT_RULES_DIR задає базовий корінь правил, від якого стартує discovery, коли споживач не передав власні каталоги.
|
|
21
21
|
|
|
22
|
-
buildDetectPlan спочатку визначає набір rules-каталогів,
|
|
22
|
+
buildDetectPlan спочатку визначає набір rules-каталогів, відбирає concern-и за capability і застосовує опційний rule-level `applies` gate. Лише після цього він будує план виконання за режимом прогону: scoped, delta, full або repo-wide. Сам план фіксує, які concern-и запускаються whole-repo, а які лише по перетину з файлами, щоб detect і fix-pipeline працювали з однаковою картиною.
|
|
23
23
|
|
|
24
24
|
loadEnabledLintRules використовує той самий discovery-ланцюжок, але повертає не план, а повну мапу concern-и за rule-id разом із множиною активних правил. Це потрібно зовнішнім споживачам, які мають знати, що реально доступно для прогону, не запускаючи сам detector-цикл.
|
|
25
25
|
|
|
@@ -42,6 +42,8 @@ concern тригериться на цих файлах (та сама табл
|
|
|
42
42
|
full-scope перевірки — справа `--repo-wide`).
|
|
43
43
|
- detectAll — Запускає detect-only прохід. Повертає всі violations і похідний exitCode.
|
|
44
44
|
|
|
45
|
+
Rule-level `applies` у `<rule>/applies/main.mjs` може повернути `false`, щоб виключити всі concern-и правила з плану для поточного репозиторію.
|
|
46
|
+
|
|
45
47
|
## Гарантії поведінки
|
|
46
48
|
|
|
47
49
|
- Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* @typedef {import('../concern-meta.mjs').ConcernMeta} ConcernMeta
|
|
10
10
|
* @typedef {{ ruleId: string, concern: ConcernMeta }} LintEntry
|
|
11
11
|
*/
|
|
12
|
+
import { existsSync } from 'node:fs'
|
|
12
13
|
import { dirname, join } from 'node:path'
|
|
13
14
|
import { env } from 'node:process'
|
|
14
|
-
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
16
|
|
|
16
17
|
import picomatch from 'picomatch'
|
|
17
18
|
|
|
@@ -126,6 +127,46 @@ async function filterByCapabilities(byRule, opts) {
|
|
|
126
127
|
return out
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Застосовує опційний rule-level gate з `<rule>/applies/main.mjs` до всіх
|
|
132
|
+
* concern-ів правила. Це потрібно для доменних правил, чий канон має сенс
|
|
133
|
+
* лише за наявності конкретної topology в репозиторії: один gate не дає
|
|
134
|
+
* policy- й JS-concern-ам розійтися у власних евристиках застосовності.
|
|
135
|
+
*
|
|
136
|
+
* @param {Record<string, ConcernMeta[]>} byRule concerns за rule-id.
|
|
137
|
+
* @param {string} cwd корінь репозиторію, який лінтиться.
|
|
138
|
+
* @returns {Promise<Record<string, ConcernMeta[]>>} лише застосовні правила.
|
|
139
|
+
*/
|
|
140
|
+
async function filterByRuleApplies(byRule, cwd) {
|
|
141
|
+
/** @type {Record<string, ConcernMeta[]>} */
|
|
142
|
+
const out = {}
|
|
143
|
+
for (const [ruleId, concerns] of Object.entries(byRule)) {
|
|
144
|
+
const firstConcern = concerns[0]
|
|
145
|
+
if (!firstConcern) continue
|
|
146
|
+
const appliesPath = join(dirname(firstConcern.dir), 'applies', 'main.mjs')
|
|
147
|
+
if (!existsSync(appliesPath)) {
|
|
148
|
+
out[ruleId] = concerns
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
let applies
|
|
152
|
+
try {
|
|
153
|
+
const mod = await import(pathToFileURL(appliesPath).href)
|
|
154
|
+
applies = mod.applies
|
|
155
|
+
} catch (error) {
|
|
156
|
+
throw new Error(`rule ${ruleId}: не вдалося завантажити applies gate: ${error.message}`)
|
|
157
|
+
}
|
|
158
|
+
if (applies === undefined) {
|
|
159
|
+
out[ruleId] = concerns
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (typeof applies !== 'function') {
|
|
163
|
+
throw new Error(`rule ${ruleId}: applies/main.mjs має експортувати applies(cwd)`)
|
|
164
|
+
}
|
|
165
|
+
if (await applies(cwd)) out[ruleId] = concerns
|
|
166
|
+
}
|
|
167
|
+
return out
|
|
168
|
+
}
|
|
169
|
+
|
|
129
170
|
/**
|
|
130
171
|
* Мердж concerns кількох rules-каталогів: правила зливаються за id, концерни — за іменем
|
|
131
172
|
* (перший власник виграє: ядро → плагіни у порядку списку). Плагін може ДОДАВАТИ концерни
|
|
@@ -267,7 +308,8 @@ export async function buildDetectPlan(opts) {
|
|
|
267
308
|
*/
|
|
268
309
|
export async function loadEnabledLintRules(opts) {
|
|
269
310
|
const rulesDirs = await effectiveRulesDirs(opts)
|
|
270
|
-
const
|
|
311
|
+
const capable = await filterByCapabilities(await readLintConcernsByRuleMulti(rulesDirs), opts)
|
|
312
|
+
const byRule = await filterByRuleApplies(capable, opts.cwd)
|
|
271
313
|
const enabledSet = new Set(await enabledRuleIds(byRule, opts.cwd, rulesDirs))
|
|
272
314
|
return { byRule, enabledSet }
|
|
273
315
|
}
|
|
@@ -618,7 +660,8 @@ export async function detectAll(opts) {
|
|
|
618
660
|
const baseLog = opts.log ?? (s => process.stdout.write(s))
|
|
619
661
|
|
|
620
662
|
const rulesDirs = await effectiveRulesDirs(opts)
|
|
621
|
-
const
|
|
663
|
+
const capable = await filterByCapabilities(await readLintConcernsByRuleMulti(rulesDirs), opts)
|
|
664
|
+
const byRule = await filterByRuleApplies(capable, cwd)
|
|
622
665
|
const plan = await buildPlan({
|
|
623
666
|
byRule,
|
|
624
667
|
full,
|
|
@@ -126,10 +126,18 @@ function isStandaloneConcern(patterns) {
|
|
|
126
126
|
* @param {string} cwd Робоча директорія для запуску детектора.
|
|
127
127
|
* @param {import('./progress.mjs').ProgressReporter|null} [progress] Reporter прогресу.
|
|
128
128
|
* @param {boolean} [verbose] Детальний вивід (прокидається у ctx concern-а).
|
|
129
|
+
* @param {string[]} [mutationRefreshFiles] Source-файли з cache-independent mutation verdict-ом.
|
|
129
130
|
* @returns {Promise<LintViolation[]>} Актуальні порушення concern-а після re-detect.
|
|
130
131
|
*/
|
|
131
|
-
async function reDetect(item, cwd, progress = null, verbose = false) {
|
|
132
|
-
const ctx = {
|
|
132
|
+
async function reDetect(item, cwd, progress = null, verbose = false, mutationRefreshFiles = []) {
|
|
133
|
+
const ctx = {
|
|
134
|
+
cwd,
|
|
135
|
+
ruleId: item.entry.ruleId,
|
|
136
|
+
concernId: item.entry.concern.name,
|
|
137
|
+
files: item.files,
|
|
138
|
+
verbose,
|
|
139
|
+
mutationRefreshFiles
|
|
140
|
+
}
|
|
133
141
|
const res = await runConcernDetector(item.entry.concern, ctx)
|
|
134
142
|
progress?.detectSnapshot(progressKey(item), res.violations.length)
|
|
135
143
|
return res.violations
|
|
@@ -416,7 +424,7 @@ async function runRung(rung, worker, violations, feedback, rungDeps) {
|
|
|
416
424
|
// ніколи не виконується.
|
|
417
425
|
let after
|
|
418
426
|
try {
|
|
419
|
-
after = await reDetect(item, cwd, progress, verbose)
|
|
427
|
+
after = await reDetect(item, cwd, progress, verbose, workerResult?.mutationRefreshFiles ?? [])
|
|
420
428
|
} catch (detectError) {
|
|
421
429
|
snapshot.rollback()
|
|
422
430
|
throw detectError
|