@rt-tools/agent-kit 0.8.3 → 0.9.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/README.md +12 -0
- package/assets/checks/board.github.mjs +48 -1
- package/assets/checks/check-board.github.mjs +84 -1
- package/assets/checks/check-lib-layers.mjs +13 -524
- package/assets/checks/check-specs.mjs +11 -782
- package/assets/checks/check-styles.mjs +185 -15
- package/assets/checks/lib-boundaries.mjs +143 -0
- package/assets/checks/lib-common.mjs +149 -0
- package/assets/checks/lib-domains.mjs +205 -0
- package/assets/checks/lib-manifests.mjs +60 -0
- package/assets/checks/lib-reexports.mjs +101 -0
- package/assets/checks/rt-kit-checks.config.mjs +17 -0
- package/assets/checks/spec-anchors.mjs +297 -0
- package/assets/checks/spec-common.mjs +222 -0
- package/assets/checks/spec-contract.mjs +152 -0
- package/assets/checks/spec-scenarios.mjs +201 -0
- package/assets/defaults/project.sh +8 -0
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/skill-gate.sh +1 -1
- package/assets/hooks/sql-guard-parse.sh +187 -0
- package/assets/hooks/sql-guard-request.sh +117 -0
- package/assets/hooks/sql-guard-target.sh +134 -0
- package/assets/hooks/sql-guard-write.sh +212 -0
- package/assets/hooks/sql-guard.sh +26 -596
- package/assets/hooks/waiting-turn-guard.sh +42 -13
- package/assets/laws/delivery.md +7 -0
- package/assets/laws/work-conduct.md +9 -0
- package/assets/patterns/admin-lists-screen.md +25 -14
- package/assets/patterns/admin-nav-item.md +1 -1
- package/assets/patterns/component-structure-new.md +1 -1
- package/assets/patterns/entity-aside.md +4 -2
- package/assets/patterns/observability-record.md +9 -0
- package/assets/patterns/shared-code-new.md +2 -2
- package/assets/patterns/task-flow-close.md +7 -1
- package/assets/rules/git-workflow.azure.md +7 -0
- package/assets/rules/git-workflow.github.md +41 -0
- package/assets/rules/git-workflow.gitlab.md +7 -0
- package/assets/rules/lib-layers.md +4 -0
- package/assets/rules/lists.md +10 -10
- package/assets/rules/shared-code.md +1 -1
- package/assets/rules/task-flow.md +47 -7
- package/assets/rules/testing.md +31 -0
- package/assets/rules/typescript-conventions.md +7 -0
- package/assets/skills/agent-kit.md +36 -0
- package/assets/templates/proposal.md +21 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +115 -87
- package/bin/agent-kit.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/lib/argv.d.ts.map +1 -1
- package/lib/argv.js +6 -4
- package/lib/argv.js.map +1 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +2 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +20 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts +55 -0
- package/lib/cascade.d.ts.map +1 -0
- package/lib/cascade.js +131 -0
- package/lib/cascade.js.map +1 -0
- package/lib/catalog.d.ts +0 -75
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +44 -127
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +152 -85
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts.map +1 -1
- package/lib/companion.js +5 -5
- package/lib/companion.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +7 -5
- package/lib/config.js.map +1 -1
- package/lib/enroll.d.ts +56 -0
- package/lib/enroll.d.ts.map +1 -0
- package/lib/enroll.js +123 -0
- package/lib/enroll.js.map +1 -0
- package/lib/freshness.d.ts.map +1 -1
- package/lib/freshness.js +31 -17
- package/lib/freshness.js.map +1 -1
- package/lib/hooks-map.d.ts +30 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +80 -18
- package/lib/hooks-map.js.map +1 -1
- package/lib/integrity.d.ts +1 -2
- package/lib/integrity.d.ts.map +1 -1
- package/lib/integrity.js +0 -1
- package/lib/integrity.js.map +1 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +25 -12
- package/lib/observations.js.map +1 -1
- package/lib/order.d.ts +10 -0
- package/lib/order.d.ts.map +1 -0
- package/lib/order.js +14 -0
- package/lib/order.js.map +1 -0
- package/lib/picker.d.ts.map +1 -1
- package/lib/picker.js +8 -2
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +1 -1
- package/lib/plan.js.map +1 -1
- package/lib/proposals.d.ts.map +1 -1
- package/lib/proposals.js +25 -8
- package/lib/proposals.js.map +1 -1
- package/lib/sections.js +1 -1
- package/lib/sections.js.map +1 -1
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +9 -1
- package/lib/ship.js.map +1 -1
- package/lib/shipment.d.ts.map +1 -1
- package/lib/shipment.js +14 -10
- package/lib/shipment.js.map +1 -1
- package/lib/snapshot.d.ts.map +1 -1
- package/lib/snapshot.js +2 -1
- package/lib/snapshot.js.map +1 -1
- package/lib/stamp.js +1 -1
- package/lib/stamp.js.map +1 -1
- package/lib/sync.d.ts +12 -2
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +11 -10
- package/lib/sync.js.map +1 -1
- package/lib/vars.d.ts.map +1 -1
- package/lib/vars.js +2 -3
- package/lib/vars.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.9.0.tgz +0 -0
- package/rt-tools-agent-kit-0.8.3.tgz +0 -0
package/lib/commands.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines -- файл делится задачей RT-849 */
|
|
1
2
|
/**
|
|
2
3
|
* Команды и их вывод. Печать отделена от работы: команда возвращает строки и код возврата,
|
|
3
4
|
* а `process.exit` зовёт только точка входа — иначе ни одну из них нельзя было бы проверить
|
|
@@ -6,7 +7,8 @@
|
|
|
6
7
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { collectAssets } from './assets.js';
|
|
9
|
-
import { cascadeCuts
|
|
10
|
+
import { cascadeCuts } from './cascade.js';
|
|
11
|
+
import { isChosen, readCatalog } from './catalog.js';
|
|
10
12
|
import { debtLine, isUnfilled, pathOf as companionPathOf, unaddressedOf } from './companion.js';
|
|
11
13
|
import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readConfig, RT_KIT_DIR } from './config.js';
|
|
12
14
|
import { hooksSection, SETTINGS_PATH } from './hooks-map.js';
|
|
@@ -17,6 +19,7 @@ import { pendingOf, planSync, runSync } from './sync.js';
|
|
|
17
19
|
import { answersRequirement, readTraits, unknownTraits } from './traits.js';
|
|
18
20
|
import { placeholdersOf } from './vars.js';
|
|
19
21
|
import { readAxes, unansweredAxes } from './variants.js';
|
|
22
|
+
import { byText } from './order.js';
|
|
20
23
|
/** Что с файлом сделала раскладка. Прошедшее время здесь правда: `sync` уже записал. */
|
|
21
24
|
const DONE_WORD = {
|
|
22
25
|
create: 'положен',
|
|
@@ -83,20 +86,9 @@ const PROFILE_HELPER = 'rt_needs';
|
|
|
83
86
|
* есть.
|
|
84
87
|
*/
|
|
85
88
|
const PROFILE_DEFINE = /(rt_[a-z_]+)\s*\(\)\s*\{/g;
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
* Отдельной строкой в разборе состояния потому, что на месте вызова нехватка не видна вовсе:
|
|
90
|
-
* хук выходит с нулём, стоит в настройке, виден в списке — и читается как работающий. Тишина
|
|
91
|
-
* при этом означает разом «нечего проверять», «проверять нечем» и «всё в порядке».
|
|
92
|
-
*/
|
|
93
|
-
function profileLines(root, assetsDir, config) {
|
|
94
|
-
const wanted = new Set();
|
|
95
|
-
const defined = new Set();
|
|
96
|
-
for (const asset of collectAssets(config, assetsDir)) {
|
|
97
|
-
if (asset.kind !== 'hooks') {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
89
|
+
/** Что взятые хуки зовут у профиля и что определяют сами. */
|
|
90
|
+
function readHooks(assets, wanted, defined) {
|
|
91
|
+
for (const asset of assets.filter((one) => one.kind === 'hooks')) {
|
|
100
92
|
for (const found of asset.text.matchAll(PROFILE_CALL)) {
|
|
101
93
|
if (found[1] !== PROFILE_HELPER) {
|
|
102
94
|
wanted.add(found[1]);
|
|
@@ -109,23 +101,36 @@ function profileLines(root, assetsDir, config) {
|
|
|
109
101
|
defined.add(found[1]);
|
|
110
102
|
}
|
|
111
103
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
}
|
|
105
|
+
/** Функции, определённые самим профилем: разложенным умолчанием пакета и надстройкой дерева. */
|
|
106
|
+
function readProfiles(root, config, defined) {
|
|
115
107
|
// Профиль собирается из тех же файлов, что читает сам хук: сперва разложенное умолчание
|
|
116
108
|
// пакета, поверх — надстройка дерева. Судить по одной надстройке значило бы объявить мёртвым
|
|
117
109
|
// каждый гард дерева, которое умолчаний не переписывало.
|
|
118
110
|
const defaults = config.layout.defaults ?? DEFAULT_LAYOUT.defaults;
|
|
119
111
|
const sources = [join(root, defaults, PROFILE_FILE), join(root, RT_KIT_DIR, PROFILE_FILE)];
|
|
120
|
-
for (const path of sources) {
|
|
121
|
-
if (!existsSync(path)) {
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
112
|
+
for (const path of sources.filter((one) => existsSync(one))) {
|
|
124
113
|
for (const found of readFileSync(path, 'utf8').matchAll(PROFILE_DEFINE)) {
|
|
125
114
|
defined.add(found[1]);
|
|
126
115
|
}
|
|
127
116
|
}
|
|
128
|
-
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Функции профиля, которых ждут взятые хуки, и те из них, что дерево не определило.
|
|
120
|
+
*
|
|
121
|
+
* Отдельной строкой в разборе состояния потому, что на месте вызова нехватка не видна вовсе:
|
|
122
|
+
* хук выходит с нулём, стоит в настройке, виден в списке — и читается как работающий. Тишина
|
|
123
|
+
* при этом означает разом «нечего проверять», «проверять нечем» и «всё в порядке».
|
|
124
|
+
*/
|
|
125
|
+
function profileLines(root, assetsDir, config) {
|
|
126
|
+
const wanted = new Set();
|
|
127
|
+
const defined = new Set();
|
|
128
|
+
readHooks([...collectAssets(config, assetsDir)], wanted, defined);
|
|
129
|
+
if (!wanted.size) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
readProfiles(root, config, defined);
|
|
133
|
+
const absent = [...wanted].filter((name) => !defined.has(name)).sort(byText);
|
|
129
134
|
return [
|
|
130
135
|
`функции профиля, которых ждут взятые хуки: ${wanted.size}`,
|
|
131
136
|
...(absent.length
|
|
@@ -136,7 +141,14 @@ function profileLines(root, assetsDir, config) {
|
|
|
136
141
|
: [' все определены']),
|
|
137
142
|
];
|
|
138
143
|
}
|
|
139
|
-
|
|
144
|
+
/** Дырка в тексте пакета, как она написана в самом тексте. */
|
|
145
|
+
function holeOf(name) {
|
|
146
|
+
return `{{${name}}}`;
|
|
147
|
+
}
|
|
148
|
+
const holes = (result) => [...result.missing].map(([asset, names]) => {
|
|
149
|
+
const written = names.map(holeOf).join(', ');
|
|
150
|
+
return ` ${asset}: нет значений для ${written}`;
|
|
151
|
+
});
|
|
140
152
|
const unfilled = (result) => result.companions.filter(isUnfilled);
|
|
141
153
|
/**
|
|
142
154
|
* Строка про компаньон, которому нечего сказать.
|
|
@@ -201,13 +213,34 @@ function strangeTraits(config, assetsDir) {
|
|
|
201
213
|
const unboundLines = (result) => result.unbound.length
|
|
202
214
|
? [
|
|
203
215
|
`гарды разложены, но в \`${SETTINGS_PATH}\` их не зовёт никто: ${result.unbound.length}`,
|
|
204
|
-
...result.unbound.map((binding) =>
|
|
216
|
+
...result.unbound.map((binding) => {
|
|
217
|
+
const matcher = binding.matcher ? ' ' + binding.matcher : '';
|
|
218
|
+
return ` ${binding.path} — ${binding.event}${matcher}`;
|
|
219
|
+
}),
|
|
205
220
|
` вставь в \`${SETTINGS_PATH}\` раздел \`hooks\` — готовый кусок ниже:`,
|
|
206
221
|
...JSON.stringify({ hooks: hooksSection(result.unbound) }, null, 4)
|
|
207
222
|
.split('\n')
|
|
208
223
|
.map((line) => ` ${line}`),
|
|
209
224
|
]
|
|
210
225
|
: [];
|
|
226
|
+
/**
|
|
227
|
+
* Гарды, подписанные не на то, что объявляют.
|
|
228
|
+
*
|
|
229
|
+
* Называются обе стороны: объявленное гардом и стоящее в настройке. Одной стороной такую строку
|
|
230
|
+
* не починить — правится настройка, а верное значение лежит в гарде, и по разнице видно, что
|
|
231
|
+
* именно до него не доходит.
|
|
232
|
+
*/
|
|
233
|
+
const driftedLines = (result) => result.drifted.length
|
|
234
|
+
? [
|
|
235
|
+
`гарды подписаны не на то, что объявляют: ${result.drifted.length}`,
|
|
236
|
+
...result.drifted.flatMap((drift) => [
|
|
237
|
+
` ${drift.path} — ${drift.event}`,
|
|
238
|
+
` объявлено: ${drift.declared || '(без образца)'}`,
|
|
239
|
+
` в настройке: ${drift.bound || '(без образца)'}`,
|
|
240
|
+
]),
|
|
241
|
+
` поправь образец в \`${SETTINGS_PATH}\` по объявлению гарда: тело его разбирает то, что объявлено`,
|
|
242
|
+
]
|
|
243
|
+
: [];
|
|
211
244
|
/**
|
|
212
245
|
* Разорванные связи между ресурсами.
|
|
213
246
|
*
|
|
@@ -231,7 +264,10 @@ const brokenLines = (result) => result.broken.length
|
|
|
231
264
|
const idleLines = (result) => result.idle.length
|
|
232
265
|
? [
|
|
233
266
|
`строк отказа, которые ничего не снимают: ${result.idle.length}`,
|
|
234
|
-
...result.idle.map((one) =>
|
|
267
|
+
...result.idle.map((one) => {
|
|
268
|
+
const why = one.by ? 'снято отказом от ' + one.by : 'нет в пакете';
|
|
269
|
+
return ` ${one.id} — ${why}`;
|
|
270
|
+
}),
|
|
235
271
|
' это предупреждение, а не отказ: строки убирает дерево, и раскладка идёт дальше',
|
|
236
272
|
]
|
|
237
273
|
: [];
|
|
@@ -294,6 +330,7 @@ const describe = (result) => [
|
|
|
294
330
|
...holes(result),
|
|
295
331
|
...gapLines(result),
|
|
296
332
|
...unboundLines(result),
|
|
333
|
+
...driftedLines(result),
|
|
297
334
|
...warnings(result),
|
|
298
335
|
...pendingOf(result).map((entry) => ` ${entry.path} — ${STATE_WORD[entry.outcome]}`),
|
|
299
336
|
...unfilled(result).map(companionLine),
|
|
@@ -311,7 +348,7 @@ function placeholdersIn(config, assetsDir) {
|
|
|
311
348
|
}
|
|
312
349
|
}
|
|
313
350
|
}
|
|
314
|
-
return names.sort();
|
|
351
|
+
return names.sort(byText);
|
|
315
352
|
}
|
|
316
353
|
/**
|
|
317
354
|
* Что проект выбрал при заведении конфига. Пустой список — весь набор; выбор приходит сюда
|
|
@@ -323,7 +360,7 @@ export function init(root, only = [], variants = {}, assetsDir = '') {
|
|
|
323
360
|
if (existsSync(path)) {
|
|
324
361
|
return { code: 0, lines: [`${CONFIG_PATH} уже есть — оставлен как есть`] };
|
|
325
362
|
}
|
|
326
|
-
const config = { vars: {}, layout: DEFAULT_LAYOUT,
|
|
363
|
+
const config = { vars: {}, layout: DEFAULT_LAYOUT, skip: [], variants, only };
|
|
327
364
|
mkdirSync(join(root, OVERRIDES_DIR), { recursive: true });
|
|
328
365
|
mkdirSync(join(root, CONFIG_PATH, '..'), { recursive: true });
|
|
329
366
|
writeFileSync(path, `${JSON.stringify(config, null, 4)}\n`, 'utf8');
|
|
@@ -381,30 +418,34 @@ const staleRefusal = (stale) => ({
|
|
|
381
418
|
' собери пакет и повтори',
|
|
382
419
|
],
|
|
383
420
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Отказ, случившийся до первой записи на диск. Пусто — раскладке ничто не мешает.
|
|
423
|
+
*
|
|
424
|
+
* Ось без ответа отбивает раскладку целиком, а не пропускает свои ресурсы молча: правило
|
|
425
|
+
* поставки бывает в трёх видах, и дерево, не назвавшее свой, осталось бы вовсе без правила
|
|
426
|
+
* поставки — заметить это можно было бы только по тому, что гейт перестал его требовать.
|
|
427
|
+
*
|
|
428
|
+
* Незнакомое свойство — опечатка, и молчать о ней нельзя ни с одной стороны. У дерева она
|
|
429
|
+
* означает, что помеченного им ресурса оно не получит вовсе; у ресурса — что он не ляжет
|
|
430
|
+
* никуда и никогда, а причину в имени файла не разглядеть.
|
|
431
|
+
*/
|
|
432
|
+
function refusalBeforeSync(env, config) {
|
|
390
433
|
if (env.stale) {
|
|
391
434
|
return staleRefusal(env.stale);
|
|
392
435
|
}
|
|
393
|
-
|
|
394
|
-
// поставки бывает в трёх видах, и дерево, не назвавшее свой, осталось бы вовсе без правила
|
|
395
|
-
// поставки — заметить это можно было бы только по тому, что гейт перестал его требовать.
|
|
396
|
-
const unanswered = unansweredAxes(readAxes(assetsDir), config.variants);
|
|
436
|
+
const unanswered = unansweredAxes(readAxes(env.assetsDir), config.variants);
|
|
397
437
|
if (unanswered.length) {
|
|
398
438
|
return { code: 1, lines: ['раскладка не начата: не выбран вид', ...axisLines(unanswered)] };
|
|
399
439
|
}
|
|
400
|
-
|
|
401
|
-
// означает, что помеченного им ресурса оно не получит вовсе; у ресурса — что он не ляжет
|
|
402
|
-
// никуда и никогда, а причину в имени файла не разглядеть.
|
|
403
|
-
const strange = strangeTraits(config, assetsDir);
|
|
440
|
+
const strange = strangeTraits(config, env.assetsDir);
|
|
404
441
|
if (strange.length) {
|
|
405
442
|
return { code: 1, lines: ['раскладка не начата: свойство дерева не объявлено пакетом', ...strange] };
|
|
406
443
|
}
|
|
407
|
-
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
/** Ответ `sync --check`: расхождения считаются, на диск не пишется ничего. */
|
|
447
|
+
function syncCheck(config, root, version, assetsDir) {
|
|
448
|
+
{
|
|
408
449
|
const result = planSync(config, root, version, assetsDir);
|
|
409
450
|
const pending = pendingOf(result);
|
|
410
451
|
// Незаполненный компаньон — такое же расхождение, как отставший файл: правило разложено,
|
|
@@ -415,12 +456,30 @@ export function sync(env, check) {
|
|
|
415
456
|
// Разорванная связь в счёт расхождений не идёт: дерево вправе закрыть требование своим
|
|
416
457
|
// средством, и отказ отбивал бы законную раскладку. Но и сходство её не отменяет —
|
|
417
458
|
// предупреждение печатается и там, где расходиться больше нечему.
|
|
418
|
-
|
|
459
|
+
//
|
|
460
|
+
// Гард, подписанный не на то, что объявляет, идёт в счёт по той же причине и с большим
|
|
461
|
+
// основанием: неподключённый хотя бы не притворяется — этот выглядит работающим, и ветка
|
|
462
|
+
// его тела, ради которой всё писалось, не исполняется ни разу.
|
|
463
|
+
const count = result.missing.size + result.gaps.length + pending.length + empty.length + result.unbound.length + result.drifted.length;
|
|
419
464
|
if (!count) {
|
|
420
465
|
return { code: 0, lines: [`sync --check: разложенное сходится с пакетом v${version}`, ...warnings(result)] };
|
|
421
466
|
}
|
|
422
467
|
return { code: 1, lines: [`sync --check: расхождений ${count}`, ...describe(result)] };
|
|
423
468
|
}
|
|
469
|
+
}
|
|
470
|
+
export function sync(env, check) {
|
|
471
|
+
const { root, version, assetsDir } = env;
|
|
472
|
+
const config = readConfig(root);
|
|
473
|
+
if (!config) {
|
|
474
|
+
return { code: 1, lines: [NO_CONFIG] };
|
|
475
|
+
}
|
|
476
|
+
const refusal = refusalBeforeSync(env, config);
|
|
477
|
+
if (refusal) {
|
|
478
|
+
return refusal;
|
|
479
|
+
}
|
|
480
|
+
if (check) {
|
|
481
|
+
return syncCheck(config, root, version, assetsDir);
|
|
482
|
+
}
|
|
424
483
|
const result = runSync(config, root, version, assetsDir);
|
|
425
484
|
// Перечисляются только дырки: раскладка не начата, и назвать файл «положенным» значило бы
|
|
426
485
|
// сказать неправду — на диске его нет.
|
|
@@ -457,6 +516,7 @@ export function sync(env, check) {
|
|
|
457
516
|
? [`разложено файлов: ${result.written.length}`, ...result.written.map((path) => ` ${path}`)]
|
|
458
517
|
: ['всё уже разложено']),
|
|
459
518
|
...unboundLines(result),
|
|
519
|
+
...driftedLines(result),
|
|
460
520
|
...debtLines(config, root, assetsDir),
|
|
461
521
|
...warnings(result),
|
|
462
522
|
],
|
|
@@ -525,6 +585,49 @@ const countLines = (counted) => {
|
|
|
525
585
|
const width = counted.reduce((found, entry) => Math.max(found, entry.name.length), 0);
|
|
526
586
|
return counted.map((entry) => ` ${entry.name.padEnd(width)} ${entry.count}`);
|
|
527
587
|
};
|
|
588
|
+
/** Сводка строками: загруженное, незагруженное, отбивки гейта и отказы гардов. */
|
|
589
|
+
function statsLines(summary, swept, days, version, laidOut) {
|
|
590
|
+
return [
|
|
591
|
+
`наблюдения за ${days} дн., заходов ${summary.sessions}, событий ${summary.total}`,
|
|
592
|
+
...(summary.versions.length ? [`версии пакета в записях: ${summary.versions.join(', ')}`] : []),
|
|
593
|
+
'',
|
|
594
|
+
`правил загружено: ${summary.loads.reduce((found, entry) => found + entry.count, 0)}`,
|
|
595
|
+
...countLines(summary.loads),
|
|
596
|
+
...(summary.unused.length
|
|
597
|
+
? [
|
|
598
|
+
'',
|
|
599
|
+
`разложено и не загружено ни разу: ${summary.unused.length} из ${laidOut}`,
|
|
600
|
+
...summary.unused.slice(0, UNUSED_SHOWN).map((name) => ` ${name}`),
|
|
601
|
+
// Список говорится не весь, и об этом говорится вслух: молчаливый обрыв
|
|
602
|
+
// читается как «вот они все», и правило, не попавшее в первую дюжину,
|
|
603
|
+
// считалось бы работающим.
|
|
604
|
+
...(summary.unused.length > UNUSED_SHOWN
|
|
605
|
+
? [` … и ещё ${summary.unused.length - UNUSED_SHOWN} — целиком в \`--json\``]
|
|
606
|
+
: []),
|
|
607
|
+
]
|
|
608
|
+
: []),
|
|
609
|
+
...(summary.denials.length
|
|
610
|
+
? [
|
|
611
|
+
'',
|
|
612
|
+
`гейт отбивал: ${summary.denials.reduce((found, entry) => found + entry.count, 0)}`,
|
|
613
|
+
...countLines(summary.denials),
|
|
614
|
+
...(summary.kinds.length
|
|
615
|
+
? [' чаще всего на:', ...countLines(summary.kinds).map((line) => ` ${line}`)]
|
|
616
|
+
: []),
|
|
617
|
+
]
|
|
618
|
+
: []),
|
|
619
|
+
...(summary.guards.length
|
|
620
|
+
? [
|
|
621
|
+
'',
|
|
622
|
+
`гарды отказывали: ${summary.guards.reduce((found, entry) => found + entry.count, 0)}`,
|
|
623
|
+
...countLines(summary.guards),
|
|
624
|
+
]
|
|
625
|
+
: []),
|
|
626
|
+
...(swept.length ? ['', `снято по сроку хранения (${KEEP_DAYS} дн.): ${swept.length}`] : []),
|
|
627
|
+
'',
|
|
628
|
+
`пакет v${version}`,
|
|
629
|
+
];
|
|
630
|
+
}
|
|
528
631
|
/**
|
|
529
632
|
* Сводка наблюдений за отрезок дней.
|
|
530
633
|
*
|
|
@@ -576,46 +679,7 @@ export function stats(env, options) {
|
|
|
576
679
|
}
|
|
577
680
|
return {
|
|
578
681
|
code: 0,
|
|
579
|
-
lines:
|
|
580
|
-
`наблюдения за ${days} дн., заходов ${summary.sessions}, событий ${summary.total}`,
|
|
581
|
-
...(summary.versions.length ? [`версии пакета в записях: ${summary.versions.join(', ')}`] : []),
|
|
582
|
-
'',
|
|
583
|
-
`правил загружено: ${summary.loads.reduce((found, entry) => found + entry.count, 0)}`,
|
|
584
|
-
...countLines(summary.loads),
|
|
585
|
-
...(summary.unused.length
|
|
586
|
-
? [
|
|
587
|
-
'',
|
|
588
|
-
`разложено и не загружено ни разу: ${summary.unused.length} из ${laidOutSkills(config, assetsDir).length}`,
|
|
589
|
-
...summary.unused.slice(0, UNUSED_SHOWN).map((name) => ` ${name}`),
|
|
590
|
-
// Список говорится не весь, и об этом говорится вслух: молчаливый обрыв
|
|
591
|
-
// читается как «вот они все», и правило, не попавшее в первую дюжину,
|
|
592
|
-
// считалось бы работающим.
|
|
593
|
-
...(summary.unused.length > UNUSED_SHOWN
|
|
594
|
-
? [` … и ещё ${summary.unused.length - UNUSED_SHOWN} — целиком в \`--json\``]
|
|
595
|
-
: []),
|
|
596
|
-
]
|
|
597
|
-
: []),
|
|
598
|
-
...(summary.denials.length
|
|
599
|
-
? [
|
|
600
|
-
'',
|
|
601
|
-
`гейт отбивал: ${summary.denials.reduce((found, entry) => found + entry.count, 0)}`,
|
|
602
|
-
...countLines(summary.denials),
|
|
603
|
-
...(summary.kinds.length
|
|
604
|
-
? [' чаще всего на:', ...countLines(summary.kinds).map((line) => ` ${line}`)]
|
|
605
|
-
: []),
|
|
606
|
-
]
|
|
607
|
-
: []),
|
|
608
|
-
...(summary.guards.length
|
|
609
|
-
? [
|
|
610
|
-
'',
|
|
611
|
-
`гарды отказывали: ${summary.guards.reduce((found, entry) => found + entry.count, 0)}`,
|
|
612
|
-
...countLines(summary.guards),
|
|
613
|
-
]
|
|
614
|
-
: []),
|
|
615
|
-
...(result.swept.length ? ['', `снято по сроку хранения (${KEEP_DAYS} дн.): ${result.swept.length}`] : []),
|
|
616
|
-
'',
|
|
617
|
-
`пакет v${version}`,
|
|
618
|
-
],
|
|
682
|
+
lines: statsLines(summary, result.swept, days, version, laidOutSkills(config, assetsDir).length),
|
|
619
683
|
};
|
|
620
684
|
}
|
|
621
685
|
export function doctor(env) {
|
|
@@ -659,7 +723,10 @@ export function doctor(env) {
|
|
|
659
723
|
`не выбрано: ${catalog.length - taken - skipped - other - cut.size - needsTrait}, пропущено: ${skipped}, другой вид: ${other}, снято каскадом: ${cut.size}, нужно свойство: ${needsTrait}`,
|
|
660
724
|
...unchosen.map((entry) => ` не выбран: ${entry.id}`),
|
|
661
725
|
...needing.map((entry) => ` нужно свойство «${entry.needs}»: ${entry.id}`),
|
|
662
|
-
...cuts.map((one) =>
|
|
726
|
+
...cuts.map((one) => {
|
|
727
|
+
const root = one.parent === one.root ? '' : ', отвергнут ' + one.root;
|
|
728
|
+
return ` снят каскадом: ${one.id} — вслед за ${one.parent}${root}`;
|
|
729
|
+
}),
|
|
663
730
|
...profileLines(root, assetsDir, config),
|
|
664
731
|
`значений в конфиге: ${Object.keys(config.vars).length}`,
|
|
665
732
|
...chosen,
|