@pseolint/core 0.1.0 → 0.2.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 (116) hide show
  1. package/dist/ai/adapters/index.d.ts +53 -0
  2. package/dist/ai/adapters/index.d.ts.map +1 -0
  3. package/dist/ai/adapters/index.js +158 -0
  4. package/dist/ai/adapters/index.js.map +1 -0
  5. package/dist/ai/cache.d.ts +11 -0
  6. package/dist/ai/cache.d.ts.map +1 -0
  7. package/dist/ai/cache.js +40 -0
  8. package/dist/ai/cache.js.map +1 -0
  9. package/dist/ai/cost.d.ts +3 -0
  10. package/dist/ai/cost.d.ts.map +1 -0
  11. package/dist/ai/cost.js +22 -0
  12. package/dist/ai/cost.js.map +1 -0
  13. package/dist/ai/feedback-prompt.d.ts +22 -0
  14. package/dist/ai/feedback-prompt.d.ts.map +1 -0
  15. package/dist/ai/feedback-prompt.js +39 -0
  16. package/dist/ai/feedback-prompt.js.map +1 -0
  17. package/dist/ai/prompt.d.ts +10 -0
  18. package/dist/ai/prompt.d.ts.map +1 -0
  19. package/dist/ai/prompt.js +51 -0
  20. package/dist/ai/prompt.js.map +1 -0
  21. package/dist/ai/triage.d.ts +28 -0
  22. package/dist/ai/triage.d.ts.map +1 -0
  23. package/dist/ai/triage.js +136 -0
  24. package/dist/ai/triage.js.map +1 -0
  25. package/dist/ai/types.d.ts +27 -0
  26. package/dist/ai/types.d.ts.map +1 -0
  27. package/dist/ai/types.js +2 -0
  28. package/dist/ai/types.js.map +1 -0
  29. package/dist/auditor.d.ts.map +1 -1
  30. package/dist/auditor.js +399 -83
  31. package/dist/auditor.js.map +1 -1
  32. package/dist/cache.d.ts +44 -0
  33. package/dist/cache.d.ts.map +1 -0
  34. package/dist/cache.js +182 -0
  35. package/dist/cache.js.map +1 -0
  36. package/dist/data-source-loader.d.ts +14 -0
  37. package/dist/data-source-loader.d.ts.map +1 -0
  38. package/dist/data-source-loader.js +76 -0
  39. package/dist/data-source-loader.js.map +1 -0
  40. package/dist/enrich-findings.d.ts.map +1 -1
  41. package/dist/enrich-findings.js +4 -0
  42. package/dist/enrich-findings.js.map +1 -1
  43. package/dist/formatters/console.d.ts.map +1 -1
  44. package/dist/formatters/console.js +30 -0
  45. package/dist/formatters/console.js.map +1 -1
  46. package/dist/formatters/html.d.ts.map +1 -1
  47. package/dist/formatters/html.js +92 -70
  48. package/dist/formatters/html.js.map +1 -1
  49. package/dist/formatters/markdown.d.ts.map +1 -1
  50. package/dist/formatters/markdown.js +29 -0
  51. package/dist/formatters/markdown.js.map +1 -1
  52. package/dist/index.d.ts +20 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +11 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/rule-references.d.ts.map +1 -1
  57. package/dist/rule-references.js +3 -0
  58. package/dist/rule-references.js.map +1 -1
  59. package/dist/rules/data/data-binding.d.ts +4 -0
  60. package/dist/rules/data/data-binding.d.ts.map +1 -0
  61. package/dist/rules/data/data-binding.js +107 -0
  62. package/dist/rules/data/data-binding.js.map +1 -0
  63. package/dist/rules/tech/robots-sitemap-presence.d.ts +2 -1
  64. package/dist/rules/tech/robots-sitemap-presence.d.ts.map +1 -1
  65. package/dist/rules/tech/robots-sitemap-presence.js +101 -0
  66. package/dist/rules/tech/robots-sitemap-presence.js.map +1 -1
  67. package/dist/rules/tech/sitemap-completeness.d.ts.map +1 -1
  68. package/dist/rules/tech/sitemap-completeness.js +15 -0
  69. package/dist/rules/tech/sitemap-completeness.js.map +1 -1
  70. package/dist/state.d.ts +35 -0
  71. package/dist/state.d.ts.map +1 -0
  72. package/dist/state.js +64 -0
  73. package/dist/state.js.map +1 -0
  74. package/dist/stratified-sample.d.ts +3 -0
  75. package/dist/stratified-sample.d.ts.map +1 -0
  76. package/dist/stratified-sample.js +88 -0
  77. package/dist/stratified-sample.js.map +1 -0
  78. package/dist/telemetry/aggregator.d.ts +47 -0
  79. package/dist/telemetry/aggregator.d.ts.map +1 -0
  80. package/dist/telemetry/aggregator.js +77 -0
  81. package/dist/telemetry/aggregator.js.map +1 -0
  82. package/dist/telemetry/index.d.ts +5 -0
  83. package/dist/telemetry/index.d.ts.map +1 -0
  84. package/dist/telemetry/index.js +5 -0
  85. package/dist/telemetry/index.js.map +1 -0
  86. package/dist/telemetry/reader.d.ts +12 -0
  87. package/dist/telemetry/reader.d.ts.map +1 -0
  88. package/dist/telemetry/reader.js +35 -0
  89. package/dist/telemetry/reader.js.map +1 -0
  90. package/dist/telemetry/types.d.ts +126 -0
  91. package/dist/telemetry/types.d.ts.map +1 -0
  92. package/dist/telemetry/types.js +75 -0
  93. package/dist/telemetry/types.js.map +1 -0
  94. package/dist/telemetry/writer.d.ts +12 -0
  95. package/dist/telemetry/writer.d.ts.map +1 -0
  96. package/dist/telemetry/writer.js +38 -0
  97. package/dist/telemetry/writer.js.map +1 -0
  98. package/dist/types.d.ts +96 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/package.json +26 -6
  101. package/dist/algorithms/entity-mask.test.d.ts +0 -2
  102. package/dist/algorithms/entity-mask.test.d.ts.map +0 -1
  103. package/dist/algorithms/entity-mask.test.js +0 -23
  104. package/dist/algorithms/entity-mask.test.js.map +0 -1
  105. package/dist/algorithms/simhash.test.d.ts +0 -2
  106. package/dist/algorithms/simhash.test.d.ts.map +0 -1
  107. package/dist/algorithms/simhash.test.js +0 -23
  108. package/dist/algorithms/simhash.test.js.map +0 -1
  109. package/dist/auditor.test.d.ts +0 -2
  110. package/dist/auditor.test.d.ts.map +0 -1
  111. package/dist/auditor.test.js +0 -134
  112. package/dist/auditor.test.js.map +0 -1
  113. package/dist/parser.test.d.ts +0 -2
  114. package/dist/parser.test.d.ts.map +0 -1
  115. package/dist/parser.test.js +0 -37
  116. package/dist/parser.test.js.map +0 -1
@@ -1,3 +1,4 @@
1
- import type { RuleResult } from "../../types.js";
1
+ import type { ParsedPage, RuleResult } from "../../types.js";
2
2
  export declare function robotsSitemapPresenceRule(source: string): Promise<RuleResult[]>;
3
+ export declare function robotsComplianceRule(pages: ParsedPage[], sitemapUrls: Set<string>, robotsTxtContent: string): RuleResult[];
3
4
  //# sourceMappingURL=robots-sitemap-presence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"robots-sitemap-presence.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/robots-sitemap-presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAcjD,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAqDrF"}
1
+ {"version":3,"file":"robots-sitemap-presence.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/robots-sitemap-presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAc7D,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAqDrF;AA8ED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,gBAAgB,EAAE,MAAM,GACvB,UAAU,EAAE,CA+Bd"}
@@ -58,4 +58,105 @@ export async function robotsSitemapPresenceRule(source) {
58
58
  }
59
59
  return findings;
60
60
  }
61
+ /**
62
+ * Parses Disallow directives from the `User-agent: *` block of a robots.txt string.
63
+ * Returns an array of raw pattern strings (e.g. "/secret", "/templates/", "/*.json").
64
+ */
65
+ function parseDisallowPatterns(robotsTxt) {
66
+ const lines = robotsTxt.split(/\r?\n/);
67
+ const patterns = [];
68
+ let inWildcardBlock = false;
69
+ for (const rawLine of lines) {
70
+ const line = rawLine.trim();
71
+ // Skip comments and blank lines
72
+ if (!line || line.startsWith("#"))
73
+ continue;
74
+ // Detect User-agent directive
75
+ if (/^user-agent\s*:/i.test(line)) {
76
+ const value = line.replace(/^user-agent\s*:\s*/i, "").trim();
77
+ inWildcardBlock = value === "*";
78
+ continue;
79
+ }
80
+ if (!inWildcardBlock)
81
+ continue;
82
+ if (/^disallow\s*:/i.test(line)) {
83
+ const value = line.replace(/^disallow\s*:\s*/i, "").trim();
84
+ // Empty Disallow means "allow everything" — skip
85
+ if (value) {
86
+ patterns.push(value);
87
+ }
88
+ }
89
+ }
90
+ return patterns;
91
+ }
92
+ /**
93
+ * Returns true if `urlPath` (e.g. "/blog/post") is blocked by a single Disallow pattern.
94
+ *
95
+ * Rules:
96
+ * - Trailing slash: "/foo/" blocks any path starting with "/foo/"
97
+ * - Wildcard `*`: iterative segment matching (no dynamic RegExp — avoids ReDoS)
98
+ * - Exact match otherwise
99
+ */
100
+ function isBlockedByPattern(urlPath, pattern) {
101
+ if (!pattern)
102
+ return false;
103
+ if (pattern.includes("*")) {
104
+ // Iterative wildcard match: split pattern on `*` and verify each literal
105
+ // segment appears in order within urlPath.
106
+ const parts = pattern.split("*");
107
+ let pos = 0;
108
+ for (let i = 0; i < parts.length; i += 1) {
109
+ const part = parts[i];
110
+ if (i === 0) {
111
+ // First segment must match at the start (robots patterns are prefix-anchored)
112
+ if (!urlPath.startsWith(part))
113
+ return false;
114
+ pos = part.length;
115
+ }
116
+ else {
117
+ const idx = urlPath.indexOf(part, pos);
118
+ if (idx === -1)
119
+ return false;
120
+ pos = idx + part.length;
121
+ }
122
+ }
123
+ return true;
124
+ }
125
+ // Trailing slash: prefix match
126
+ if (pattern.endsWith("/")) {
127
+ return urlPath.startsWith(pattern);
128
+ }
129
+ // Exact match
130
+ return urlPath === pattern;
131
+ }
132
+ export function robotsComplianceRule(pages, sitemapUrls, robotsTxtContent) {
133
+ const disallowPatterns = parseDisallowPatterns(robotsTxtContent);
134
+ if (disallowPatterns.length === 0)
135
+ return [];
136
+ const findings = [];
137
+ for (const page of pages) {
138
+ if (!sitemapUrls.has(page.url))
139
+ continue;
140
+ let parsedPath;
141
+ try {
142
+ parsedPath = new URL(page.url).pathname;
143
+ }
144
+ catch {
145
+ continue;
146
+ }
147
+ for (const pattern of disallowPatterns) {
148
+ if (isBlockedByPattern(parsedPath, pattern)) {
149
+ findings.push({
150
+ ruleId: "tech/robots-compliance",
151
+ severity: "error",
152
+ message: `${page.url} is in the sitemap but blocked by robots.txt (Disallow: ${pattern}).`,
153
+ pageUrl: page.url,
154
+ fix: "Remove the Disallow directive or remove this URL from your sitemap.",
155
+ });
156
+ break; // one finding per page is enough
157
+ }
158
+ }
159
+ }
160
+ return findings;
161
+ }
61
162
  //# sourceMappingURL=robots-sitemap-presence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"robots-sitemap-presence.js","sourceRoot":"","sources":["../../../src/rules/tech/robots-sitemap-presence.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,MAAc;IAC5D,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;IAChC,MAAM,SAAS,GAAG,GAAG,MAAM,aAAa,CAAC;IACzC,MAAM,UAAU,GAAG,GAAG,MAAM,cAAc,CAAC;IAC3C,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,8BAA8B;YACtC,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,mBAAmB,SAAS,GAAG;YACxC,GAAG,EAAE,+BAA+B,SAAS,4DAA4D;SAC1G,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,8BAA8B;YACtC,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,GAAG,SAAS,wCAAwC;YAC7D,GAAG,EAAE,8BAA8B,SAAS,oBAAoB,UAAU,EAAE;SAC7E,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,8BAA8B;YACtC,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,mBAAmB,UAAU,GAAG;YACzC,GAAG,EAAE,4BAA4B,UAAU,wBAAwB,SAAS,GAAG;SAChF,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,8BAA8B;YACtC,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,GAAG,UAAU,kDAAkD;YACxE,GAAG,EAAE,UAAU,UAAU,6EAA6E;SACvG,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"robots-sitemap-presence.js","sourceRoot":"","sources":["../../../src/rules/tech/robots-sitemap-presence.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sitemap-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,UAAU,EAAE,CA0Cd"}
1
+ {"version":3,"file":"sitemap-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,UAAU,EAAE,CA0Dd"}
@@ -35,6 +35,21 @@ export function sitemapCompletenessRule(pages, sitemapUrls) {
35
35
  });
36
36
  }
37
37
  }
38
+ for (const page of pages) {
39
+ if (!sitemapUrls.has(page.url))
40
+ continue;
41
+ const hasNoindex = /noindex/i.test(page.robotsMeta) ||
42
+ (page.httpMeta?.xRobotsTag != null && /noindex/i.test(page.httpMeta.xRobotsTag));
43
+ if (hasNoindex) {
44
+ findings.push({
45
+ ruleId: "tech/sitemap-completeness",
46
+ severity: "error",
47
+ message: `${page.url} is in the sitemap but has a noindex directive.`,
48
+ pageUrl: page.url,
49
+ fix: "Remove the noindex directive or remove this URL from your sitemap.",
50
+ });
51
+ }
52
+ }
38
53
  return findings;
39
54
  }
40
55
  //# sourceMappingURL=sitemap-completeness.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sitemap-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CACrC,KAAmB,EACnB,WAAwB;IAExB,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,GAAG,kBAAkB,CAAC,MAAM,oCAAoC;YACzE,GAAG,EAAE,sEAAsE;YAC3E,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;SACzD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAE3D,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,UAAU,GAAG;gBAC5E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,sEAAsE;aAC5E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;YAClF,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG;gBAC1E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,WAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBACrC,GAAG,EAAE,4CAA4C,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"sitemap-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CACrC,KAAmB,EACnB,WAAwB;IAExB,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,GAAG,kBAAkB,CAAC,MAAM,oCAAoC;YACzE,GAAG,EAAE,sEAAsE;YAC3E,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;SACzD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAE3D,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,UAAU,GAAG;gBAC5E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,sEAAsE;aAC5E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;YAClF,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG;gBAC1E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,WAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBACrC,GAAG,EAAE,4CAA4C,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,MAAM,UAAU,GACd,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QACnF,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,iDAAiD;gBACrE,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,oEAAoE;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,35 @@
1
+ export declare const STATE_SCHEMA_VERSION = 1;
2
+ export type RenderMode = "static" | "rendered";
3
+ export interface UrlStateEntry {
4
+ contentHash: string;
5
+ fetchedAt: string;
6
+ status: number;
7
+ findingIds: string[];
8
+ }
9
+ export interface RunState {
10
+ version: number;
11
+ lastRun: string;
12
+ source: string;
13
+ renderMode: RenderMode;
14
+ urls: Record<string, UrlStateEntry>;
15
+ summary: {
16
+ score: number;
17
+ totalFindings: number;
18
+ byCategory: Record<string, number>;
19
+ };
20
+ }
21
+ /**
22
+ * Normalize HTML so content hashing is stable across irrelevant diffs.
23
+ *
24
+ * Strips: HTML comments, script bodies, style bodies, whitespace runs.
25
+ *
26
+ * Known limitations (use as "likely-unchanged" signal, not proof):
27
+ * - Attribute order changes are NOT normalized (regex parser, not DOM)
28
+ * - Inline event handlers and `data-*` attributes with dynamic values
29
+ * (nonces, CSRF tokens) will produce different hashes
30
+ */
31
+ export declare function normalizeHtmlForHash(html: string): string;
32
+ export declare function computeContentHash(html: string): string;
33
+ export declare function readState(path: string): Promise<RunState | null>;
34
+ export declare function writeState(path: string, state: RunState): Promise<void>;
35
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA8BtE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7E"}
package/dist/state.js ADDED
@@ -0,0 +1,64 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ export const STATE_SCHEMA_VERSION = 1;
5
+ /**
6
+ * Normalize HTML so content hashing is stable across irrelevant diffs.
7
+ *
8
+ * Strips: HTML comments, script bodies, style bodies, whitespace runs.
9
+ *
10
+ * Known limitations (use as "likely-unchanged" signal, not proof):
11
+ * - Attribute order changes are NOT normalized (regex parser, not DOM)
12
+ * - Inline event handlers and `data-*` attributes with dynamic values
13
+ * (nonces, CSRF tokens) will produce different hashes
14
+ */
15
+ export function normalizeHtmlForHash(html) {
16
+ return html
17
+ .replace(/<!--[\s\S]*?-->/g, "")
18
+ .replace(/<script[\s\S]*?<\/script>/gi, "<script></script>")
19
+ .replace(/<style[\s\S]*?<\/style>/gi, "<style></style>")
20
+ .replace(/\s+/g, " ")
21
+ .trim();
22
+ }
23
+ export function computeContentHash(html) {
24
+ const norm = normalizeHtmlForHash(html);
25
+ return "sha256:" + createHash("sha256").update(norm).digest("hex");
26
+ }
27
+ export async function readState(path) {
28
+ let raw;
29
+ try {
30
+ raw = await readFile(path, "utf8");
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(raw);
38
+ }
39
+ catch (e) {
40
+ throw new Error(`state file at ${path} is not valid JSON: ${e.message}`);
41
+ }
42
+ if (!parsed || typeof parsed !== "object") {
43
+ throw new Error(`state file at ${path} is not an object`);
44
+ }
45
+ const state = parsed;
46
+ if (state.version !== STATE_SCHEMA_VERSION) {
47
+ throw new Error(`unsupported state version ${state.version} at ${path}, expected ${STATE_SCHEMA_VERSION}`);
48
+ }
49
+ if (typeof state.lastRun !== "string" ||
50
+ typeof state.source !== "string" ||
51
+ typeof state.renderMode !== "string" ||
52
+ !state.urls || typeof state.urls !== "object" ||
53
+ !state.summary || typeof state.summary !== "object") {
54
+ throw new Error(`state file at ${path} has malformed shape`);
55
+ }
56
+ return state;
57
+ }
58
+ export async function writeState(path, state) {
59
+ await mkdir(dirname(path), { recursive: true });
60
+ const tmp = `${path}.tmp`;
61
+ await writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
62
+ await rename(tmp, path);
63
+ }
64
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAwBtC;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI;SACR,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,OAAO,CAAC,6BAA6B,EAAE,mBAAmB,CAAC;SAC3D,OAAO,CAAC,2BAA2B,EAAE,iBAAiB,CAAC;SACvD,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,uBAAwB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,mBAAmB,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,KAAK,GAAG,MAAkB,CAAC;IACjC,IAAI,KAAK,CAAC,OAAO,KAAK,oBAAoB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,CAAC,OAAO,OAAO,IAAI,cAAc,oBAAoB,EAAE,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QACpC,CAAC,KAAK,CAAC,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC7C,CAAC,KAAK,CAAC,OAAO,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,sBAAsB,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,KAAe;IAC5D,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC;IAC1B,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function inferUrlTemplate(url: string): string;
2
+ export declare function stratifiedSample(urls: string[], n: number): string[];
3
+ //# sourceMappingURL=stratified-sample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stratified-sample.d.ts","sourceRoot":"","sources":["../src/stratified-sample.ts"],"names":[],"mappings":"AAeA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWpD;AAaD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAwCpE"}
@@ -0,0 +1,88 @@
1
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
+ const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
3
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
4
+ const ALL_DIGITS_RE = /^\d+$/;
5
+ function generalizeSegment(seg) {
6
+ if (!seg)
7
+ return seg;
8
+ if (ALL_DIGITS_RE.test(seg))
9
+ return ":num";
10
+ if (UUID_RE.test(seg) || ULID_RE.test(seg))
11
+ return ":id";
12
+ if (DATE_RE.test(seg))
13
+ return ":date";
14
+ const hyphenCount = (seg.match(/-/g) ?? []).length;
15
+ if (hyphenCount >= 2)
16
+ return ":slug";
17
+ return seg;
18
+ }
19
+ export function inferUrlTemplate(url) {
20
+ let pathname;
21
+ try {
22
+ pathname = new URL(url).pathname;
23
+ }
24
+ catch {
25
+ pathname = url;
26
+ }
27
+ if (pathname === "" || pathname === "/")
28
+ return "/";
29
+ const trimmed = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
30
+ const segments = trimmed.split("/").slice(1).map(generalizeSegment);
31
+ return "/" + segments.join("/");
32
+ }
33
+ function fisherYates(items, n) {
34
+ const arr = [...items];
35
+ const out = [];
36
+ for (let i = 0; i < n && arr.length > 0; i += 1) {
37
+ const idx = Math.floor(Math.random() * arr.length);
38
+ out.push(arr[idx]);
39
+ arr.splice(idx, 1);
40
+ }
41
+ return out;
42
+ }
43
+ export function stratifiedSample(urls, n) {
44
+ if (n <= 0 || n >= urls.length)
45
+ return [...urls];
46
+ const clusters = new Map();
47
+ for (const u of urls) {
48
+ const t = inferUrlTemplate(u);
49
+ let arr = clusters.get(t);
50
+ if (!arr) {
51
+ arr = [];
52
+ clusters.set(t, arr);
53
+ }
54
+ arr.push(u);
55
+ }
56
+ if (clusters.size <= 1)
57
+ return fisherYates(urls, n);
58
+ const entries = [...clusters.values()];
59
+ const sqrtSizes = entries.map(e => Math.sqrt(e.length));
60
+ const sqrtSum = sqrtSizes.reduce((a, b) => a + b, 0);
61
+ const allocations = sqrtSizes.map(s => Math.floor((s / sqrtSum) * n));
62
+ if (n >= entries.length) {
63
+ for (let i = 0; i < allocations.length; i += 1) {
64
+ if (allocations[i] === 0)
65
+ allocations[i] = 1;
66
+ }
67
+ }
68
+ for (let i = 0; i < allocations.length; i += 1) {
69
+ allocations[i] = Math.min(allocations[i], entries[i].length);
70
+ }
71
+ let total = allocations.reduce((a, b) => a + b, 0);
72
+ while (total < n) {
73
+ const candidates = allocations
74
+ .map((a, i) => ({ idx: i, slack: entries[i].length - a }))
75
+ .filter(x => x.slack > 0);
76
+ if (candidates.length === 0)
77
+ break;
78
+ const chosen = candidates[Math.floor(Math.random() * candidates.length)];
79
+ allocations[chosen.idx] += 1;
80
+ total += 1;
81
+ }
82
+ const result = [];
83
+ for (let i = 0; i < entries.length; i += 1) {
84
+ result.push(...fisherYates(entries[i], allocations[i]));
85
+ }
86
+ return result;
87
+ }
88
+ //# sourceMappingURL=stratified-sample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stratified-sample.js","sourceRoot":"","sources":["../src/stratified-sample.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAClF,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAC3C,MAAM,OAAO,GAAG,qBAAqB,CAAC;AACtC,MAAM,aAAa,GAAG,OAAO,CAAC;AAE9B,SAAS,iBAAiB,CAAC,GAAW;IACpC,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACnD,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACrC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,GAAG,GAAG,CAAC;IACjB,CAAC;IACD,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACpE,OAAO,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,WAAW,CAAI,KAAU,EAAE,CAAS;IAC3C,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACvB,MAAM,GAAG,GAAQ,EAAE,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACnB,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAc,EAAE,CAAS;IACxD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG,EAAE,CAAC;YAAC,GAAG,GAAG,EAAE,CAAC;YAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAAC,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAEpD,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAErD,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,IAAI,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;gBAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,OAAO,KAAK,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,WAAW;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;aACzD,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC5B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QACnC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACzE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { TelemetryRecord } from "./types.js";
2
+ export interface TelemetryStats {
3
+ totalAudits: number;
4
+ totalFeedback: number;
5
+ avgDurationMs: number | null;
6
+ avgScore: number | null;
7
+ avgFindings: number | null;
8
+ avgPages: number | null;
9
+ /** Audits where ai.enabled was true (success + failure). */
10
+ triageAttempts: number;
11
+ /** Audits where triage returned a result (excludes skipped/failed). */
12
+ triageUsed: number;
13
+ /** Breakdown of skip reasons from failed attempts. */
14
+ triageSkipReasons: Record<string, number>;
15
+ triageCacheHits: number;
16
+ totalTokenInput: number;
17
+ totalTokenOutput: number;
18
+ totalEstimatedCostUsd: number;
19
+ feedbackBreakdown: {
20
+ helpful: number;
21
+ unhelpful: number;
22
+ skipped: number;
23
+ };
24
+ firstRun: string | null;
25
+ lastRun: string | null;
26
+ }
27
+ /**
28
+ * Pure summary function over a set of telemetry records. Splits audits and
29
+ * feedback, averages the audit-level metrics, and sums triage aggregates over
30
+ * just the audits that included triage.
31
+ */
32
+ export declare function aggregateTelemetry(records: TelemetryRecord[]): TelemetryStats;
33
+ /**
34
+ * Sum estimated USD cost for successful triages that fell on the given UTC date
35
+ * (defaults to today). Used to enforce a daily budget before a new triage call.
36
+ * Returns 0 when the telemetry file is missing or empty.
37
+ *
38
+ * **Cache hits are excluded** — they did not incur a real API call, so they must
39
+ * not count toward today's spend. Without this filter, re-running the same audit
40
+ * multiple times per day would over-report spend and falsely trip a budget cap.
41
+ *
42
+ * The "UTC day" window is a calendar day in UTC (YYYY-MM-DD from the timestamp).
43
+ * Users in non-UTC timezones see "today" roll over at their local time offset
44
+ * from UTC midnight — documented in the README.
45
+ */
46
+ export declare function todayTriageSpendUsd(telemetryPath: string, now?: Date): Promise<number>;
47
+ //# sourceMappingURL=aggregator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregator.d.ts","sourceRoot":"","sources":["../../src/telemetry/aggregator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,eAAe,EAChB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,GACzB,cAAc,CA0DhB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,IAAiB,GACrB,OAAO,CAAC,MAAM,CAAC,CAajB"}
@@ -0,0 +1,77 @@
1
+ import { readTelemetryJsonl } from "./reader.js";
2
+ /**
3
+ * Pure summary function over a set of telemetry records. Splits audits and
4
+ * feedback, averages the audit-level metrics, and sums triage aggregates over
5
+ * just the audits that included triage.
6
+ */
7
+ export function aggregateTelemetry(records) {
8
+ const audits = records.filter((r) => r.type === "audit");
9
+ const feedback = records.filter((r) => r.type === "feedback");
10
+ const n = audits.length;
11
+ const sum = (k) => audits.reduce((s, a) => s + k(a), 0);
12
+ const triageAttempts = audits.filter((a) => a.triage);
13
+ const triageSuccesses = triageAttempts.filter((a) => a.triage.success);
14
+ const triageSkipReasons = {};
15
+ for (const a of triageAttempts) {
16
+ if (!a.triage.success && a.triage.skipReason) {
17
+ // Canonicalize by the first ":"-separated kind for cleaner grouping.
18
+ const key = a.triage.skipReason.split(":")[0].trim() || "unknown";
19
+ triageSkipReasons[key] = (triageSkipReasons[key] ?? 0) + 1;
20
+ }
21
+ }
22
+ return {
23
+ totalAudits: n,
24
+ totalFeedback: feedback.length,
25
+ avgDurationMs: n ? sum((a) => a.durationMs) / n : null,
26
+ avgScore: n ? sum((a) => a.score) / n : null,
27
+ avgFindings: n ? sum((a) => a.findingCount) / n : null,
28
+ avgPages: n ? sum((a) => a.pageCount) / n : null,
29
+ triageAttempts: triageAttempts.length,
30
+ triageUsed: triageSuccesses.length,
31
+ triageSkipReasons,
32
+ triageCacheHits: triageSuccesses.filter((a) => a.triage.cacheHit).length,
33
+ totalTokenInput: triageSuccesses.reduce((s, a) => s + a.triage.tokenUsage.input, 0),
34
+ totalTokenOutput: triageSuccesses.reduce((s, a) => s + a.triage.tokenUsage.output, 0),
35
+ totalEstimatedCostUsd: triageSuccesses.reduce((s, a) => s + (a.triage.estimatedCostUsd ?? 0), 0),
36
+ feedbackBreakdown: {
37
+ helpful: feedback.filter((f) => f.rating === "helpful").length,
38
+ unhelpful: feedback.filter((f) => f.rating === "unhelpful").length,
39
+ skipped: feedback.filter((f) => f.rating === "skipped").length,
40
+ },
41
+ firstRun: audits.reduce((m, a) => (!m || a.timestamp < m ? a.timestamp : m), null),
42
+ lastRun: audits.reduce((m, a) => (!m || a.timestamp > m ? a.timestamp : m), null),
43
+ };
44
+ }
45
+ /**
46
+ * Sum estimated USD cost for successful triages that fell on the given UTC date
47
+ * (defaults to today). Used to enforce a daily budget before a new triage call.
48
+ * Returns 0 when the telemetry file is missing or empty.
49
+ *
50
+ * **Cache hits are excluded** — they did not incur a real API call, so they must
51
+ * not count toward today's spend. Without this filter, re-running the same audit
52
+ * multiple times per day would over-report spend and falsely trip a budget cap.
53
+ *
54
+ * The "UTC day" window is a calendar day in UTC (YYYY-MM-DD from the timestamp).
55
+ * Users in non-UTC timezones see "today" roll over at their local time offset
56
+ * from UTC midnight — documented in the README.
57
+ */
58
+ export async function todayTriageSpendUsd(telemetryPath, now = new Date()) {
59
+ const records = await readTelemetryJsonl(telemetryPath);
60
+ const utcDay = now.toISOString().slice(0, 10); // YYYY-MM-DD
61
+ let total = 0;
62
+ for (const r of records) {
63
+ if (r.type !== "audit")
64
+ continue;
65
+ if (!r.triage?.success)
66
+ continue;
67
+ if (r.triage.cacheHit)
68
+ continue; // cache hits are not real API spend
69
+ if (r.triage.estimatedCostUsd === undefined)
70
+ continue;
71
+ if (!r.timestamp.startsWith(utcDay))
72
+ continue;
73
+ total += r.triage.estimatedCostUsd;
74
+ }
75
+ return total;
76
+ }
77
+ //# sourceMappingURL=aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../../src/telemetry/aggregator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from "./types.js";
2
+ export * from "./writer.js";
3
+ export * from "./reader.js";
4
+ export * from "./aggregator.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from "./types.js";
2
+ export * from "./writer.js";
3
+ export * from "./reader.js";
4
+ export * from "./aggregator.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type TelemetryRecord } from "./types.js";
2
+ /**
3
+ * Read a telemetry JSONL file and return all records that parse successfully
4
+ * against the current schema.
5
+ *
6
+ * - Missing file -> empty array.
7
+ * - Malformed JSON lines -> skipped silently.
8
+ * - Schema-violating lines (e.g. from a future schema version or corruption)
9
+ * -> skipped silently. This keeps the reader forward-compatible.
10
+ */
11
+ export declare function readTelemetryJsonl(path: string): Promise<TelemetryRecord[]>;
12
+ //# sourceMappingURL=reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/telemetry/reader.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzE;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,EAAE,CAAC,CAoB5B"}
@@ -0,0 +1,35 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { telemetryRecordSchema } from "./types.js";
3
+ /**
4
+ * Read a telemetry JSONL file and return all records that parse successfully
5
+ * against the current schema.
6
+ *
7
+ * - Missing file -> empty array.
8
+ * - Malformed JSON lines -> skipped silently.
9
+ * - Schema-violating lines (e.g. from a future schema version or corruption)
10
+ * -> skipped silently. This keeps the reader forward-compatible.
11
+ */
12
+ export async function readTelemetryJsonl(path) {
13
+ let raw;
14
+ try {
15
+ raw = await readFile(path, "utf8");
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ const records = [];
21
+ for (const line of raw.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed)
24
+ continue;
25
+ try {
26
+ const json = JSON.parse(trimmed);
27
+ records.push(telemetryRecordSchema.parse(json));
28
+ }
29
+ catch {
30
+ // skip malformed / schema-violating lines
31
+ }
32
+ }
33
+ return records;
34
+ }
35
+ //# sourceMappingURL=reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/telemetry/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAwB,MAAM,YAAY,CAAC;AAEzE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAY;IAEZ,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}