@operstack/audit 0.17.0 → 0.17.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/package.json +1 -1
- package/src/collect.mjs +21 -6
- package/src/test-overall.mjs +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@operstack/audit",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "SEO, AEO and GEO audit generator: collect public signals from a site into audit.json, let an analyst or an agent fill the narrative, render the OperStack report as HTML and PDF.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/collect.mjs
CHANGED
|
@@ -207,7 +207,7 @@ async function readSitemap(startUrl, { maxFiles = 6, budgetMs = 45000, maxUrls =
|
|
|
207
207
|
|
|
208
208
|
export const __readSitemap = readSitemap;
|
|
209
209
|
|
|
210
|
-
export async function collect(startUrl, { pages = 20, log = () => {}, backlinks = null, lang = 'en', rendered = false, renderedPages = 3, preparedBy = '', requestTimeout = 15000, sitemapBudgetMs = 45000, sitemapMaxFiles = 6 } = {}) {
|
|
210
|
+
export async function collect(startUrl, { pages = 20, log = () => {}, backlinks = null, lang = 'en', rendered = false, renderedPages = 3, preparedBy = '', visibility: givenVisibility = null, requestTimeout = 15000, sitemapBudgetMs = 45000, sitemapMaxFiles = 6 } = {}) {
|
|
211
211
|
REQUEST_TIMEOUT = requestTimeout;
|
|
212
212
|
const opts = { backlinks };
|
|
213
213
|
const origin = new URL(startUrl).origin;
|
|
@@ -483,11 +483,26 @@ export async function collect(startUrl, { pages = 20, log = () => {}, backlinks
|
|
|
483
483
|
* тут больше страниц или времени, и число снова разойдётся со страницей. Глубина аудита живёт в
|
|
484
484
|
* его собственных шести областях и в списке задач, а не в этом числе.
|
|
485
485
|
*/
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
486
|
+
/*
|
|
487
|
+
* Если балл уже измерен, второй раз не меряем.
|
|
488
|
+
*
|
|
489
|
+
* Живая сверка 14.09.2026 на whitewill.ru дала 47 и 44 в двух прогонах подряд, а на habr.com
|
|
490
|
+
* один прогон вернул отказ. Код один и тот же, разная только удача сети: бюджет у проверки
|
|
491
|
+
* 8,5 секунды, и на медленном сайте карта сайта то успевает прочитаться, то нет, а движок
|
|
492
|
+
* честно не штрафует за непрочитанное. Два измерения одного сайта дают два числа, и покупателю
|
|
493
|
+
* всё равно, что оба честные: он видит 44 на странице и 47 в письме.
|
|
494
|
+
*
|
|
495
|
+
* Поэтому в воронке балл измеряется РОВНО ОДИН РАЗ, на странице, и приезжает сюда готовым
|
|
496
|
+
* через опцию `visibility`. Тогда страница, письмо и отчёт несут одно число по построению,
|
|
497
|
+
* а не по совпадению. Меряем сами только когда аудит запускают отдельно и мерить больше некому.
|
|
498
|
+
*/
|
|
499
|
+
let visibility = givenVisibility;
|
|
500
|
+
if (visibility) log('visibility score: taken from the check the visitor already ran');
|
|
501
|
+
else { log('visibility score'); visibility = await checkVisibility(startUrl, { ...VISIBILITY_DEFAULTS, lang }); }
|
|
502
|
+
const measuredAt = visibility && visibility.checkedAt ? visibility.checkedAt : null;
|
|
503
|
+
const overall = visibility && visibility.ok
|
|
504
|
+
? { score: visibility.score, grade: visibility.grade, areas: visibility.areas, source: givenVisibility ? 'visibility:reused' : 'visibility', measuredAt, ms: visibility.ms }
|
|
505
|
+
: { score: null, grade: 'not measured', areas: [], source: givenVisibility ? 'visibility:reused' : 'visibility', error: (visibility && visibility.error) || 'no visibility result' };
|
|
491
506
|
// Шесть областей отчёта остаются, но это другое измерение, а не разбивка общего балла.
|
|
492
507
|
const reportScore = computeOverall(checks);
|
|
493
508
|
|
package/src/test-overall.mjs
CHANGED
|
@@ -107,6 +107,46 @@ ok('параметры нельзя поменять на ходу', Object.isFr
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/*
|
|
111
|
+
* Балл, уже измеренный на странице, приезжает в отчёт готовым и не перемеряется.
|
|
112
|
+
*
|
|
113
|
+
* Живая сверка 14.09.2026: whitewill.ru дал 47 и 44 в двух прогонах подряд, habr.com в одном
|
|
114
|
+
* прогоне вернул отказ. Тот же код, разная удача сети: бюджет 8,5 секунды, и карта сайта то
|
|
115
|
+
* успевает прочитаться, то нет. Покупателю всё равно, что оба числа честные, он видит два разных.
|
|
116
|
+
* Поэтому в воронке измерение происходит один раз.
|
|
117
|
+
*/
|
|
118
|
+
{
|
|
119
|
+
const server = createServer((req, res) => {
|
|
120
|
+
res.setHeader('content-type', 'text/html');
|
|
121
|
+
res.end('<!doctype html><html><head><title>Local</title></head><body><h1>Local</h1><p>Text.</p></body></html>');
|
|
122
|
+
});
|
|
123
|
+
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
|
124
|
+
const base = `http://127.0.0.1:${server.address().port}/`;
|
|
125
|
+
|
|
126
|
+
const fromPage = { ok: true, score: 63, grade: 'B', checkedAt: '2026-09-14T20:00:00.000Z', ms: 4200, areas: [
|
|
127
|
+
{ id: 'access', label: 'Access for AI crawlers', score: 25, max: 25 },
|
|
128
|
+
{ id: 'index', label: 'Agent index', score: 10, max: 15 },
|
|
129
|
+
{ id: 'entity', label: 'Entity and structure', score: 12, max: 20 },
|
|
130
|
+
{ id: 'content', label: 'Answer-first content', score: 11, max: 25 },
|
|
131
|
+
{ id: 'trust', label: 'Freshness and sources', score: 5, max: 15 }] };
|
|
132
|
+
|
|
133
|
+
const audit = await collect(base, { pages: 2, log: () => {}, visibility: fromPage });
|
|
134
|
+
server.close();
|
|
135
|
+
|
|
136
|
+
is('готовый балл берётся как есть', audit.overall.score, 63);
|
|
137
|
+
is('и оценка буквой тоже', audit.overall.grade, 'B');
|
|
138
|
+
is('видно, что балл переиспользован, а не измерен заново', audit.overall.source, 'visibility:reused');
|
|
139
|
+
is('сохранено, когда его измерили', audit.overall.measuredAt, '2026-09-14T20:00:00.000Z');
|
|
140
|
+
is('пять областей приехали целиком', audit.overall.areas.length, 5);
|
|
141
|
+
is('и складываются в заголовок', audit.overall.areas.reduce((a, x) => a + x.score, 0), 63);
|
|
142
|
+
is('отчёт сходится сам с собой', verifyScores(audit), []);
|
|
143
|
+
// Локальный адрес движок бы мерить отказался, а балл всё равно есть: значит второго измерения не было.
|
|
144
|
+
ok('второго измерения не случилось', audit.overall.score === 63 && !audit.overall.error);
|
|
145
|
+
|
|
146
|
+
audit.meta.lang = 'ru';
|
|
147
|
+
is('и отчёт печатает именно его', Number((toHtml(audit, null).match(/overall-num[^>]*>(\d+)<span>/) || [])[1]), 63);
|
|
148
|
+
}
|
|
149
|
+
|
|
110
150
|
// ---- движок в пакете это та же программа, что стоит на сайтах
|
|
111
151
|
{
|
|
112
152
|
const here = readFileSync(new URL('./visibility.mjs', import.meta.url), 'utf8');
|