@pseolint/core 0.3.1 → 0.4.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 +49 -1
- package/dist/ai/triage.d.ts.map +1 -1
- package/dist/ai/triage.js +8 -1
- package/dist/ai/triage.js.map +1 -1
- package/dist/auditor.d.ts.map +1 -1
- package/dist/auditor.js +495 -130
- package/dist/auditor.js.map +1 -1
- package/dist/backpressure.d.ts +68 -0
- package/dist/backpressure.d.ts.map +1 -0
- package/dist/backpressure.js +81 -0
- package/dist/backpressure.js.map +1 -0
- package/dist/cache.d.ts +73 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +258 -19
- package/dist/cache.js.map +1 -1
- package/dist/enrich-findings.d.ts.map +1 -1
- package/dist/enrich-findings.js +1 -14
- package/dist/enrich-findings.js.map +1 -1
- package/dist/fetch-observer.d.ts +97 -0
- package/dist/fetch-observer.d.ts.map +1 -0
- package/dist/fetch-observer.js +124 -0
- package/dist/fetch-observer.js.map +1 -0
- package/dist/formatters/console.d.ts +7 -9
- package/dist/formatters/console.d.ts.map +1 -1
- package/dist/formatters/console.js +218 -254
- package/dist/formatters/console.js.map +1 -1
- package/dist/formatters/html.d.ts +5 -1
- package/dist/formatters/html.d.ts.map +1 -1
- package/dist/formatters/html.js +352 -570
- package/dist/formatters/html.js.map +1 -1
- package/dist/formatters/index.d.ts +4 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +1 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/formatters/json.d.ts +11 -1
- package/dist/formatters/json.d.ts.map +1 -1
- package/dist/formatters/json.js +5 -1
- package/dist/formatters/json.js.map +1 -1
- package/dist/formatters/markdown.d.ts +7 -1
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +77 -70
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/index.d.ts +13 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/rule-references.d.ts.map +1 -1
- package/dist/rule-references.js +0 -6
- package/dist/rule-references.js.map +1 -1
- package/dist/rules/content/unique-value.d.ts.map +1 -1
- package/dist/rules/content/unique-value.js +1 -0
- package/dist/rules/content/unique-value.js.map +1 -1
- package/dist/rules/scope.d.ts.map +1 -1
- package/dist/rules/scope.js +6 -14
- package/dist/rules/scope.js.map +1 -1
- package/dist/rules/tech/robots-sitemap-presence.d.ts +9 -1
- package/dist/rules/tech/robots-sitemap-presence.d.ts.map +1 -1
- package/dist/rules/tech/robots-sitemap-presence.js +14 -5
- package/dist/rules/tech/robots-sitemap-presence.js.map +1 -1
- package/dist/safe-mode-preset.d.ts +27 -0
- package/dist/safe-mode-preset.d.ts.map +1 -0
- package/dist/safe-mode-preset.js +54 -0
- package/dist/safe-mode-preset.js.map +1 -0
- package/dist/site-classifier.d.ts +83 -0
- package/dist/site-classifier.d.ts.map +1 -0
- package/dist/site-classifier.js +205 -0
- package/dist/site-classifier.js.map +1 -0
- package/dist/ssrf-guard.d.ts +96 -0
- package/dist/ssrf-guard.d.ts.map +1 -0
- package/dist/ssrf-guard.js +268 -0
- package/dist/ssrf-guard.js.map +1 -0
- package/dist/types.d.ts +171 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch observation + origin-readiness aggregation.
|
|
3
|
+
*
|
|
4
|
+
* Every HTTP request during an audit emits a `FetchObservation` (url, status,
|
|
5
|
+
* wall-clock duration, whether the cache served or revalidated it). These
|
|
6
|
+
* observations feed two consumers:
|
|
7
|
+
*
|
|
8
|
+
* 1. `computeReadiness` — post-crawl aggregate (median / p95 / 5xx count /
|
|
9
|
+
* cache-assist ratio) surfaced as the `audit/origin-readiness` finding.
|
|
10
|
+
* 2. `BackpressureMonitor` (backpressure.ts) — in-flight watchdog that aborts
|
|
11
|
+
* the audit if the origin degrades under concurrent load.
|
|
12
|
+
*
|
|
13
|
+
* Both consumers treat pure cache hits (`fromCache` with no revalidation)
|
|
14
|
+
* as non-informative about the origin — latency stats should describe the
|
|
15
|
+
* SERVER, not our local SSD.
|
|
16
|
+
*/
|
|
17
|
+
/** v0.4: detect dev-server framework from response headers. Returns null when not a known dev server. */
|
|
18
|
+
export function detectDevServer(headers) {
|
|
19
|
+
const lower = {};
|
|
20
|
+
for (const [k, v] of Object.entries(headers))
|
|
21
|
+
lower[k.toLowerCase()] = v;
|
|
22
|
+
const xpb = lower["x-powered-by"] ?? "";
|
|
23
|
+
if (xpb.toLowerCase().includes("next"))
|
|
24
|
+
return "nextjs";
|
|
25
|
+
// x-nextjs-* headers (next/server runtime) and __nextjs_* cookies.
|
|
26
|
+
const cookie = lower["set-cookie"] ?? lower["cookie"] ?? "";
|
|
27
|
+
if (Object.keys(lower).some((k) => k.startsWith("x-nextjs-")) || /__nextjs_/i.test(cookie)) {
|
|
28
|
+
return "nextjs";
|
|
29
|
+
}
|
|
30
|
+
if (Object.keys(lower).some((k) => k.startsWith("x-vite-")))
|
|
31
|
+
return "vite";
|
|
32
|
+
if (Object.keys(lower).some((k) => k.startsWith("x-astro-")))
|
|
33
|
+
return "astro";
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function percentile(sortedAsc, p) {
|
|
37
|
+
if (sortedAsc.length === 0)
|
|
38
|
+
return 0;
|
|
39
|
+
if (sortedAsc.length === 1)
|
|
40
|
+
return sortedAsc[0];
|
|
41
|
+
// Nearest-rank method (simple, deterministic for small samples).
|
|
42
|
+
const rank = Math.ceil((p / 100) * sortedAsc.length) - 1;
|
|
43
|
+
return sortedAsc[Math.max(0, Math.min(sortedAsc.length - 1, rank))];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Aggregate a run's fetch observations into a readiness report. Returns `null`
|
|
47
|
+
* when there's no origin data to speak to (zero fetches, or all pure cache
|
|
48
|
+
* hits). Null is a signal to callers that the finding should be suppressed —
|
|
49
|
+
* it's not a result worth displaying.
|
|
50
|
+
*/
|
|
51
|
+
export function computeReadiness(observations, thresholds = {}) {
|
|
52
|
+
const framework = thresholds.detectedFramework ?? null;
|
|
53
|
+
// Dev servers (Next/Vite/Astro) routinely take seconds on cold compile.
|
|
54
|
+
// Spec §4.7: cap p95 at 10s and skip the first 5 fetches as warmup grace.
|
|
55
|
+
const isDevFramework = framework !== null;
|
|
56
|
+
const defaultNotReady = isDevFramework ? 10000 : 3000;
|
|
57
|
+
const defaultConcerning = isDevFramework ? 3000 : 800;
|
|
58
|
+
const t = {
|
|
59
|
+
notReadyP95Ms: thresholds.notReadyP95Ms ?? defaultNotReady,
|
|
60
|
+
concerningP95Ms: thresholds.concerningP95Ms ?? defaultConcerning,
|
|
61
|
+
notReadyErrorRatio: thresholds.notReadyErrorRatio ?? 0.1,
|
|
62
|
+
};
|
|
63
|
+
// Live fetches = anything that actually spoke to the origin (revalidation
|
|
64
|
+
// counts because the 304 round-trip is a real network call).
|
|
65
|
+
const live = observations.filter((o) => !o.fromCache || o.revalidated);
|
|
66
|
+
if (live.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
// Warmup grace: drop the first 5 live fetches from latency percentiles when
|
|
69
|
+
// a dev-server framework was detected. Keep them in the success/error ratios
|
|
70
|
+
// so genuine 5xx during warmup still surface.
|
|
71
|
+
const liveForLatency = isDevFramework && live.length > 5
|
|
72
|
+
? live.slice(5)
|
|
73
|
+
: live;
|
|
74
|
+
const durations = liveForLatency.map((o) => o.durationMs).sort((a, b) => a - b);
|
|
75
|
+
const medianMs = durations.length > 0 ? percentile(durations, 50) : 0;
|
|
76
|
+
const p95Ms = durations.length > 0 ? percentile(durations, 95) : 0;
|
|
77
|
+
const serverErrorCount = live.filter((o) => o.status >= 500 && o.status < 600).length;
|
|
78
|
+
const successCount = live.filter((o) => o.status >= 200 && o.status < 400).length;
|
|
79
|
+
const serverErrorRatio = serverErrorCount / live.length;
|
|
80
|
+
const successRatio = successCount / live.length;
|
|
81
|
+
const cacheAssistCount = observations.filter((o) => o.fromCache || o.revalidated).length;
|
|
82
|
+
const cacheAssistRatio = observations.length === 0 ? 0 : cacheAssistCount / observations.length;
|
|
83
|
+
let verdict;
|
|
84
|
+
if (p95Ms >= t.notReadyP95Ms || serverErrorRatio >= t.notReadyErrorRatio) {
|
|
85
|
+
verdict = "not-ready";
|
|
86
|
+
}
|
|
87
|
+
else if (p95Ms >= t.concerningP95Ms) {
|
|
88
|
+
verdict = "concerning";
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
verdict = "ready";
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
liveFetchCount: live.length,
|
|
95
|
+
medianMs,
|
|
96
|
+
p95Ms,
|
|
97
|
+
successRatio,
|
|
98
|
+
serverErrorCount,
|
|
99
|
+
serverErrorRatio,
|
|
100
|
+
cacheAssistRatio,
|
|
101
|
+
verdict,
|
|
102
|
+
...(framework ? { detectedFramework: framework } : {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A small collector so callers can hand the same object to the fetch pipeline
|
|
107
|
+
* (as an onObservation callback) and to the readiness/backpressure consumers.
|
|
108
|
+
*/
|
|
109
|
+
export class FetchObserver {
|
|
110
|
+
entries = [];
|
|
111
|
+
record(obs) {
|
|
112
|
+
this.entries.push(obs);
|
|
113
|
+
}
|
|
114
|
+
getAll() {
|
|
115
|
+
return this.entries;
|
|
116
|
+
}
|
|
117
|
+
snapshotLast(n) {
|
|
118
|
+
return this.entries.slice(Math.max(0, this.entries.length - n));
|
|
119
|
+
}
|
|
120
|
+
get size() {
|
|
121
|
+
return this.entries.length;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=fetch-observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-observer.js","sourceRoot":"","sources":["../src/fetch-observer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { AuditSummary } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* AEO-specific score label. AEO score is raw "damage" 0–100 — low is good.
|
|
4
|
-
* 0–20 AI-Ready pages structured for citation
|
|
5
|
-
* 21–40 Partial some citable, others vulnerable
|
|
6
|
-
* 41–60 Vulnerable most pages will be summarized away
|
|
7
|
-
* 61–80 Invisible pages offer nothing AI can't synthesize itself
|
|
8
|
-
* 81–100 Ghost actively blocked + no citable structure
|
|
9
|
-
*/
|
|
10
|
-
export declare function aeoScoreLabel(score: number): string;
|
|
11
2
|
export interface ConsoleFormatOptions {
|
|
12
3
|
noColor?: boolean;
|
|
4
|
+
/** When true, list every finding bucketed by severity instead of just top fixes. */
|
|
5
|
+
verbose?: boolean;
|
|
13
6
|
}
|
|
14
7
|
export declare function formatConsole(summary: AuditSummary, options?: ConsoleFormatOptions): string;
|
|
8
|
+
/**
|
|
9
|
+
* Legacy helper retained for back-compat with existing tests/imports.
|
|
10
|
+
* AEO score band → human label. Low score = AI-Ready, high = Ghost.
|
|
11
|
+
*/
|
|
12
|
+
export declare function aeoScoreLabel(score: number): string;
|
|
15
13
|
//# sourceMappingURL=console.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/formatters/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/formatters/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAOb,MAAM,aAAa,CAAC;AAgHrB,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAqID,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,CA8E3F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD"}
|