@pseolint/core 0.3.2 → 0.4.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.
Files changed (80) hide show
  1. package/README.md +49 -1
  2. package/dist/ai/triage.d.ts.map +1 -1
  3. package/dist/ai/triage.js +8 -1
  4. package/dist/ai/triage.js.map +1 -1
  5. package/dist/auditor.d.ts.map +1 -1
  6. package/dist/auditor.js +566 -136
  7. package/dist/auditor.js.map +1 -1
  8. package/dist/backpressure.d.ts +68 -0
  9. package/dist/backpressure.d.ts.map +1 -0
  10. package/dist/backpressure.js +81 -0
  11. package/dist/backpressure.js.map +1 -0
  12. package/dist/cache.d.ts +73 -0
  13. package/dist/cache.d.ts.map +1 -1
  14. package/dist/cache.js +258 -19
  15. package/dist/cache.js.map +1 -1
  16. package/dist/enrich-findings.d.ts.map +1 -1
  17. package/dist/enrich-findings.js +1 -14
  18. package/dist/enrich-findings.js.map +1 -1
  19. package/dist/fetch-observer.d.ts +97 -0
  20. package/dist/fetch-observer.d.ts.map +1 -0
  21. package/dist/fetch-observer.js +124 -0
  22. package/dist/fetch-observer.js.map +1 -0
  23. package/dist/formatters/console.d.ts +7 -9
  24. package/dist/formatters/console.d.ts.map +1 -1
  25. package/dist/formatters/console.js +218 -254
  26. package/dist/formatters/console.js.map +1 -1
  27. package/dist/formatters/html.d.ts +5 -1
  28. package/dist/formatters/html.d.ts.map +1 -1
  29. package/dist/formatters/html.js +352 -570
  30. package/dist/formatters/html.js.map +1 -1
  31. package/dist/formatters/index.d.ts +4 -1
  32. package/dist/formatters/index.d.ts.map +1 -1
  33. package/dist/formatters/index.js +1 -1
  34. package/dist/formatters/index.js.map +1 -1
  35. package/dist/formatters/json.d.ts +11 -1
  36. package/dist/formatters/json.d.ts.map +1 -1
  37. package/dist/formatters/json.js +5 -1
  38. package/dist/formatters/json.js.map +1 -1
  39. package/dist/formatters/markdown.d.ts +7 -1
  40. package/dist/formatters/markdown.d.ts.map +1 -1
  41. package/dist/formatters/markdown.js +77 -70
  42. package/dist/formatters/markdown.js.map +1 -1
  43. package/dist/index.d.ts +13 -8
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +6 -7
  46. package/dist/index.js.map +1 -1
  47. package/dist/page-filter.d.ts +50 -0
  48. package/dist/page-filter.d.ts.map +1 -0
  49. package/dist/page-filter.js +86 -0
  50. package/dist/page-filter.js.map +1 -0
  51. package/dist/rule-references.d.ts.map +1 -1
  52. package/dist/rule-references.js +0 -6
  53. package/dist/rule-references.js.map +1 -1
  54. package/dist/rules/content/unique-value.d.ts.map +1 -1
  55. package/dist/rules/content/unique-value.js +1 -0
  56. package/dist/rules/content/unique-value.js.map +1 -1
  57. package/dist/rules/scope.d.ts.map +1 -1
  58. package/dist/rules/scope.js +6 -14
  59. package/dist/rules/scope.js.map +1 -1
  60. package/dist/rules/tech/robots-sitemap-presence.d.ts +9 -1
  61. package/dist/rules/tech/robots-sitemap-presence.d.ts.map +1 -1
  62. package/dist/rules/tech/robots-sitemap-presence.js +14 -5
  63. package/dist/rules/tech/robots-sitemap-presence.js.map +1 -1
  64. package/dist/safe-mode-preset.d.ts +27 -0
  65. package/dist/safe-mode-preset.d.ts.map +1 -0
  66. package/dist/safe-mode-preset.js +54 -0
  67. package/dist/safe-mode-preset.js.map +1 -0
  68. package/dist/site-classifier.d.ts +83 -0
  69. package/dist/site-classifier.d.ts.map +1 -0
  70. package/dist/site-classifier.js +205 -0
  71. package/dist/site-classifier.js.map +1 -0
  72. package/dist/ssrf-guard.d.ts +96 -0
  73. package/dist/ssrf-guard.d.ts.map +1 -0
  74. package/dist/ssrf-guard.js +268 -0
  75. package/dist/ssrf-guard.js.map +1 -0
  76. package/dist/types.d.ts +202 -19
  77. package/dist/types.d.ts.map +1 -1
  78. package/dist/types.js +2 -1
  79. package/dist/types.js.map +1 -1
  80. 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,EAAE,YAAY,EAAmD,MAAM,aAAa,CAAC;AA4BjG;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAuED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAwP3F"}
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"}