@pseolint/core 0.4.3 → 0.5.4
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 +264 -169
- package/dist/ai/manifest/diff.d.ts +78 -0
- package/dist/ai/manifest/diff.d.ts.map +1 -0
- package/dist/ai/manifest/diff.js +139 -0
- package/dist/ai/manifest/diff.js.map +1 -0
- package/dist/ai/manifest/index.d.ts +18 -0
- package/dist/ai/manifest/index.d.ts.map +1 -0
- package/dist/ai/manifest/index.js +15 -0
- package/dist/ai/manifest/index.js.map +1 -0
- package/dist/ai/manifest/validate-manifest.d.ts +37 -0
- package/dist/ai/manifest/validate-manifest.d.ts.map +1 -0
- package/dist/ai/manifest/validate-manifest.js +67 -0
- package/dist/ai/manifest/validate-manifest.js.map +1 -0
- package/dist/ai/manifest/validators/domain-patches.d.ts +15 -0
- package/dist/ai/manifest/validators/domain-patches.d.ts.map +1 -0
- package/dist/ai/manifest/validators/domain-patches.js +110 -0
- package/dist/ai/manifest/validators/domain-patches.js.map +1 -0
- package/dist/ai/manifest/validators/index.d.ts +5 -0
- package/dist/ai/manifest/validators/index.d.ts.map +1 -0
- package/dist/ai/manifest/validators/index.js +4 -0
- package/dist/ai/manifest/validators/index.js.map +1 -0
- package/dist/ai/manifest/validators/page-changes.d.ts +36 -0
- package/dist/ai/manifest/validators/page-changes.d.ts.map +1 -0
- package/dist/ai/manifest/validators/page-changes.js +221 -0
- package/dist/ai/manifest/validators/page-changes.js.map +1 -0
- package/dist/ai/manifest/validators/types.d.ts +17 -0
- package/dist/ai/manifest/validators/types.d.ts.map +1 -0
- package/dist/ai/manifest/validators/types.js +5 -0
- package/dist/ai/manifest/validators/types.js.map +1 -0
- package/dist/ai/orchestrate.d.ts +74 -0
- package/dist/ai/orchestrate.d.ts.map +1 -0
- package/dist/ai/orchestrate.js +54 -0
- package/dist/ai/orchestrate.js.map +1 -0
- package/dist/ai/orchestrator/budget.d.ts +57 -0
- package/dist/ai/orchestrator/budget.d.ts.map +1 -0
- package/dist/ai/orchestrator/budget.js +114 -0
- package/dist/ai/orchestrator/budget.js.map +1 -0
- package/dist/ai/orchestrator/finish-tool.d.ts +568 -0
- package/dist/ai/orchestrator/finish-tool.d.ts.map +1 -0
- package/dist/ai/orchestrator/finish-tool.js +114 -0
- package/dist/ai/orchestrator/finish-tool.js.map +1 -0
- package/dist/ai/orchestrator/index.d.ts +25 -0
- package/dist/ai/orchestrator/index.d.ts.map +1 -0
- package/dist/ai/orchestrator/index.js +21 -0
- package/dist/ai/orchestrator/index.js.map +1 -0
- package/dist/ai/orchestrator/log.d.ts +24 -0
- package/dist/ai/orchestrator/log.d.ts.map +1 -0
- package/dist/ai/orchestrator/log.js +48 -0
- package/dist/ai/orchestrator/log.js.map +1 -0
- package/dist/ai/orchestrator/page-cache.d.ts +64 -0
- package/dist/ai/orchestrator/page-cache.d.ts.map +1 -0
- package/dist/ai/orchestrator/page-cache.js +127 -0
- package/dist/ai/orchestrator/page-cache.js.map +1 -0
- package/dist/ai/orchestrator/prompt.d.ts +16 -0
- package/dist/ai/orchestrator/prompt.d.ts.map +1 -0
- package/dist/ai/orchestrator/prompt.js +52 -0
- package/dist/ai/orchestrator/prompt.js.map +1 -0
- package/dist/ai/orchestrator/runner.d.ts +65 -0
- package/dist/ai/orchestrator/runner.d.ts.map +1 -0
- package/dist/ai/orchestrator/runner.js +223 -0
- package/dist/ai/orchestrator/runner.js.map +1 -0
- package/dist/ai/orchestrator/session.d.ts +44 -0
- package/dist/ai/orchestrator/session.d.ts.map +1 -0
- package/dist/ai/orchestrator/session.js +64 -0
- package/dist/ai/orchestrator/session.js.map +1 -0
- package/dist/ai/orchestrator/types.d.ts +99 -0
- package/dist/ai/orchestrator/types.d.ts.map +1 -0
- package/dist/ai/orchestrator/types.js +8 -0
- package/dist/ai/orchestrator/types.js.map +1 -0
- package/dist/ai/probes/cache.d.ts +12 -0
- package/dist/ai/probes/cache.d.ts.map +1 -0
- package/dist/ai/probes/cache.js +46 -0
- package/dist/ai/probes/cache.js.map +1 -0
- package/dist/ai/tools/ask-ai-engine.d.ts +77 -0
- package/dist/ai/tools/ask-ai-engine.d.ts.map +1 -0
- package/dist/ai/tools/ask-ai-engine.js +253 -0
- package/dist/ai/tools/ask-ai-engine.js.map +1 -0
- package/dist/ai/tools/check-domain-crawler-access.d.ts +71 -0
- package/dist/ai/tools/check-domain-crawler-access.d.ts.map +1 -0
- package/dist/ai/tools/check-domain-crawler-access.js +76 -0
- package/dist/ai/tools/check-domain-crawler-access.js.map +1 -0
- package/dist/ai/tools/check-domain-llms-txt.d.ts +70 -0
- package/dist/ai/tools/check-domain-llms-txt.d.ts.map +1 -0
- package/dist/ai/tools/check-domain-llms-txt.js +75 -0
- package/dist/ai/tools/check-domain-llms-txt.js.map +1 -0
- package/dist/ai/tools/check-indexability.d.ts +58 -0
- package/dist/ai/tools/check-indexability.d.ts.map +1 -0
- package/dist/ai/tools/check-indexability.js +64 -0
- package/dist/ai/tools/check-indexability.js.map +1 -0
- package/dist/ai/tools/check-robots.d.ts +68 -0
- package/dist/ai/tools/check-robots.d.ts.map +1 -0
- package/dist/ai/tools/check-robots.js +90 -0
- package/dist/ai/tools/check-robots.js.map +1 -0
- package/dist/ai/tools/check-rule-answer-first.d.ts +54 -0
- package/dist/ai/tools/check-rule-answer-first.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-answer-first.js +50 -0
- package/dist/ai/tools/check-rule-answer-first.js.map +1 -0
- package/dist/ai/tools/check-rule-canonical-consistency.d.ts +66 -0
- package/dist/ai/tools/check-rule-canonical-consistency.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-canonical-consistency.js +51 -0
- package/dist/ai/tools/check-rule-canonical-consistency.js.map +1 -0
- package/dist/ai/tools/check-rule-citable-facts.d.ts +58 -0
- package/dist/ai/tools/check-rule-citable-facts.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-citable-facts.js +41 -0
- package/dist/ai/tools/check-rule-citable-facts.js.map +1 -0
- package/dist/ai/tools/check-rule-content-modularity.d.ts +58 -0
- package/dist/ai/tools/check-rule-content-modularity.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-content-modularity.js +45 -0
- package/dist/ai/tools/check-rule-content-modularity.js.map +1 -0
- package/dist/ai/tools/check-rule-faq-coverage.d.ts +54 -0
- package/dist/ai/tools/check-rule-faq-coverage.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-faq-coverage.js +39 -0
- package/dist/ai/tools/check-rule-faq-coverage.js.map +1 -0
- package/dist/ai/tools/check-rule-freshness-signals.d.ts +54 -0
- package/dist/ai/tools/check-rule-freshness-signals.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-freshness-signals.js +45 -0
- package/dist/ai/tools/check-rule-freshness-signals.js.map +1 -0
- package/dist/ai/tools/check-rule-json-ld-valid.d.ts +54 -0
- package/dist/ai/tools/check-rule-json-ld-valid.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-json-ld-valid.js +44 -0
- package/dist/ai/tools/check-rule-json-ld-valid.js.map +1 -0
- package/dist/ai/tools/check-rule-missing-author.d.ts +54 -0
- package/dist/ai/tools/check-rule-missing-author.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-missing-author.js +45 -0
- package/dist/ai/tools/check-rule-missing-author.js.map +1 -0
- package/dist/ai/tools/check-rule-near-duplicate.d.ts +82 -0
- package/dist/ai/tools/check-rule-near-duplicate.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-near-duplicate.js +63 -0
- package/dist/ai/tools/check-rule-near-duplicate.js.map +1 -0
- package/dist/ai/tools/check-rule-required-fields.d.ts +50 -0
- package/dist/ai/tools/check-rule-required-fields.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-required-fields.js +38 -0
- package/dist/ai/tools/check-rule-required-fields.js.map +1 -0
- package/dist/ai/tools/check-rule-schema-consistency.d.ts +54 -0
- package/dist/ai/tools/check-rule-schema-consistency.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-schema-consistency.js +44 -0
- package/dist/ai/tools/check-rule-schema-consistency.js.map +1 -0
- package/dist/ai/tools/check-rule-summary-bait.d.ts +54 -0
- package/dist/ai/tools/check-rule-summary-bait.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-summary-bait.js +39 -0
- package/dist/ai/tools/check-rule-summary-bait.js.map +1 -0
- package/dist/ai/tools/check-rule-thin-content.d.ts +66 -0
- package/dist/ai/tools/check-rule-thin-content.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-thin-content.js +58 -0
- package/dist/ai/tools/check-rule-thin-content.js.map +1 -0
- package/dist/ai/tools/detect-templates.d.ts +60 -0
- package/dist/ai/tools/detect-templates.d.ts.map +1 -0
- package/dist/ai/tools/detect-templates.js +43 -0
- package/dist/ai/tools/detect-templates.js.map +1 -0
- package/dist/ai/tools/fetch-page.d.ts +70 -0
- package/dist/ai/tools/fetch-page.d.ts.map +1 -0
- package/dist/ai/tools/fetch-page.js +93 -0
- package/dist/ai/tools/fetch-page.js.map +1 -0
- package/dist/ai/tools/fetch-sitemap.d.ts +60 -0
- package/dist/ai/tools/fetch-sitemap.d.ts.map +1 -0
- package/dist/ai/tools/fetch-sitemap.js +116 -0
- package/dist/ai/tools/fetch-sitemap.js.map +1 -0
- package/dist/ai/tools/index.d.ts +1555 -0
- package/dist/ai/tools/index.d.ts.map +1 -0
- package/dist/ai/tools/index.js +119 -0
- package/dist/ai/tools/index.js.map +1 -0
- package/dist/ai/tools/parse-page.d.ts +94 -0
- package/dist/ai/tools/parse-page.d.ts.map +1 -0
- package/dist/ai/tools/parse-page.js +108 -0
- package/dist/ai/tools/parse-page.js.map +1 -0
- package/dist/ai/tools/query-serp.d.ts +113 -0
- package/dist/ai/tools/query-serp.d.ts.map +1 -0
- package/dist/ai/tools/query-serp.js +131 -0
- package/dist/ai/tools/query-serp.js.map +1 -0
- package/dist/ai/tools/sample-template.d.ts +67 -0
- package/dist/ai/tools/sample-template.d.ts.map +1 -0
- package/dist/ai/tools/sample-template.js +75 -0
- package/dist/ai/tools/sample-template.js.map +1 -0
- package/dist/ai/tools/types.d.ts +73 -0
- package/dist/ai/tools/types.d.ts.map +1 -0
- package/dist/ai/tools/types.js +64 -0
- package/dist/ai/tools/types.js.map +1 -0
- package/dist/ai/tools/validate-jsonld.d.ts +62 -0
- package/dist/ai/tools/validate-jsonld.d.ts.map +1 -0
- package/dist/ai/tools/validate-jsonld.js +84 -0
- package/dist/ai/tools/validate-jsonld.js.map +1 -0
- package/dist/auditor.d.ts +4 -0
- package/dist/auditor.d.ts.map +1 -1
- package/dist/auditor.js +629 -64
- package/dist/auditor.js.map +1 -1
- package/dist/backpressure.d.ts.map +1 -1
- package/dist/backpressure.js +10 -3
- package/dist/backpressure.js.map +1 -1
- package/dist/enrich-findings.d.ts.map +1 -1
- package/dist/enrich-findings.js +15 -1
- package/dist/enrich-findings.js.map +1 -1
- package/dist/formatters/console.d.ts.map +1 -1
- package/dist/formatters/console.js +13 -0
- package/dist/formatters/console.js.map +1 -1
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +20 -2
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/rule-references.d.ts.map +1 -1
- package/dist/rule-references.js +5 -0
- package/dist/rule-references.js.map +1 -1
- package/dist/rules/content/heading-structure.d.ts +21 -0
- package/dist/rules/content/heading-structure.d.ts.map +1 -0
- package/dist/rules/content/heading-structure.js +56 -0
- package/dist/rules/content/heading-structure.js.map +1 -0
- package/dist/rules/content/image-alt-text.d.ts +18 -0
- package/dist/rules/content/image-alt-text.d.ts.map +1 -0
- package/dist/rules/content/image-alt-text.js +77 -0
- package/dist/rules/content/image-alt-text.js.map +1 -0
- package/dist/rules/content/title-uniqueness.d.ts +18 -0
- package/dist/rules/content/title-uniqueness.d.ts.map +1 -0
- package/dist/rules/content/title-uniqueness.js +70 -0
- package/dist/rules/content/title-uniqueness.js.map +1 -0
- package/dist/rules/links/host-section-divergence.d.ts +3 -0
- package/dist/rules/links/host-section-divergence.d.ts.map +1 -0
- package/dist/rules/links/host-section-divergence.js +158 -0
- package/dist/rules/links/host-section-divergence.js.map +1 -0
- package/dist/rules/links/link-depth.d.ts +12 -1
- package/dist/rules/links/link-depth.d.ts.map +1 -1
- package/dist/rules/links/link-depth.js +25 -12
- package/dist/rules/links/link-depth.js.map +1 -1
- package/dist/rules/scope.d.ts.map +1 -1
- package/dist/rules/scope.js +5 -0
- package/dist/rules/scope.js.map +1 -1
- package/dist/rules/spam/doorway-pattern.d.ts.map +1 -1
- package/dist/rules/spam/doorway-pattern.js +27 -4
- package/dist/rules/spam/doorway-pattern.js.map +1 -1
- package/dist/rules/spam/publication-velocity.d.ts +1 -1
- package/dist/rules/spam/publication-velocity.d.ts.map +1 -1
- package/dist/rules/spam/publication-velocity.js +9 -4
- package/dist/rules/spam/publication-velocity.js.map +1 -1
- package/dist/rules/spam/template-coverage.js +1 -1
- package/dist/rules/spam/template-coverage.js.map +1 -1
- package/dist/rules/spam/template-diversity.js +1 -1
- package/dist/rules/spam/template-diversity.js.map +1 -1
- package/dist/rules/tech/hreflang-consistency.d.ts.map +1 -1
- package/dist/rules/tech/hreflang-consistency.js +33 -4
- package/dist/rules/tech/hreflang-consistency.js.map +1 -1
- package/dist/rules/tech/og-completeness.d.ts +11 -0
- package/dist/rules/tech/og-completeness.d.ts.map +1 -1
- package/dist/rules/tech/og-completeness.js +22 -23
- package/dist/rules/tech/og-completeness.js.map +1 -1
- package/dist/ruleset-version.d.ts +8 -0
- package/dist/ruleset-version.d.ts.map +1 -0
- package/dist/ruleset-version.js +8 -0
- package/dist/ruleset-version.js.map +1 -0
- package/dist/scrape-strategy.d.ts +42 -0
- package/dist/scrape-strategy.d.ts.map +1 -0
- package/dist/scrape-strategy.js +101 -0
- package/dist/scrape-strategy.js.map +1 -0
- package/dist/site-classifier.d.ts.map +1 -1
- package/dist/site-classifier.js +1 -0
- package/dist/site-classifier.js.map +1 -1
- package/dist/state.d.ts +36 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +3 -1
- package/dist/state.js.map +1 -1
- package/dist/stratified-sample.d.ts +9 -1
- package/dist/stratified-sample.d.ts.map +1 -1
- package/dist/stratified-sample.js +23 -6
- package/dist/stratified-sample.js.map +1 -1
- package/dist/types.d.ts +135 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/url-normalize.d.ts.map +1 -1
- package/dist/url-normalize.js +13 -1
- package/dist/url-normalize.js.map +1 -1
- package/package.json +90 -90
package/README.md
CHANGED
|
@@ -1,170 +1,265 @@
|
|
|
1
|
-
# @pseolint/core
|
|
2
|
-
|
|
3
|
-
> Programmatic SEO audit engine for SpamBrain-risk detection across large template-generated sites.
|
|
4
|
-
|
|
5
|
-
The core engine behind [pseolint](https://www.npmjs.com/package/pseolint). Use this package to embed pSEO auditing into your own tools, CI pipelines, or SaaS products.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @pseolint/core
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
import { auditSource } from "@pseolint/core";
|
|
17
|
-
|
|
18
|
-
const summary = await auditSource("./out");
|
|
19
|
-
console.log(`Score: ${summary.score}/100`);
|
|
20
|
-
console.log(`Findings: ${summary.findings.length}`);
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
`auditSource` accepts a local directory, a single HTML file, a page URL, or a sitemap URL.
|
|
24
|
-
|
|
25
|
-
## What It Checks
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- **Spam / SpamBrain risk** (8) — near-duplicate (SimHash), entity-swap doorways, thin content, boilerplate ratio, template diversity, template coverage, publication velocity, doorway pattern
|
|
30
|
-
- **Technical SEO** (
|
|
31
|
-
- **AEO / AI Overview citability** (
|
|
32
|
-
- **Content** (
|
|
33
|
-
- **Internal linking** (
|
|
34
|
-
- **Structured data** (3) — JSON-LD validity, required fields, cross-page schema consistency
|
|
35
|
-
- **Cannibalization** (
|
|
36
|
-
- **Data binding** (2) — verify rendered pages expose values from a source dataset (missing or identical-across-pages bindings)
|
|
37
|
-
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
###
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
# @pseolint/core
|
|
2
|
+
|
|
3
|
+
> Programmatic SEO audit engine for SpamBrain-risk detection across large template-generated sites.
|
|
4
|
+
|
|
5
|
+
The core engine behind [pseolint](https://www.npmjs.com/package/pseolint). Use this package to embed pSEO auditing into your own tools, CI pipelines, or SaaS products.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @pseolint/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { auditSource } from "@pseolint/core";
|
|
17
|
+
|
|
18
|
+
const summary = await auditSource("./out");
|
|
19
|
+
console.log(`Score: ${summary.score}/100`);
|
|
20
|
+
console.log(`Findings: ${summary.findings.length}`);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`auditSource` accepts a local directory, a single HTML file, a page URL, or a sitemap URL.
|
|
24
|
+
|
|
25
|
+
## What It Checks
|
|
26
|
+
|
|
27
|
+
45 rules grouped into 4 scoring super-categories (v0.4): **Integrity** (spam + content + cannibal, weight 0.50), **Discoverability** (links + tech, 0.20), **Citation** (aeo + schema, 0.25), **Data** (0.05). Source-tree namespaces remain `spam/*`, `aeo/*`, etc. for stable rule IDs.
|
|
28
|
+
|
|
29
|
+
- **Spam / SpamBrain risk** (8) — near-duplicate (SimHash), entity-swap doorways, thin content, boilerplate ratio, template diversity, template coverage, publication velocity, doorway pattern (cluster-collapsed since v0.5.2)
|
|
30
|
+
- **Technical SEO** (9) — canonical consistency, canonical/noindex and robots/noindex conflicts, sitemap completeness, robots compliance, redirect chains, soft 404s, hreflang reciprocity, robots-sitemap presence, **og-completeness** (v0.5.2)
|
|
31
|
+
- **AEO / AI Overview citability** (8, v0.3.0–v0.3.1) — `llms.txt` presence, AI-crawler access in robots.txt, freshness signals, FAQ coverage, answer-first opener, citable-fact density, content modularity, **summary-bait** (pages optimized for summarization over retention)
|
|
32
|
+
- **Content** (7) — unique value, meta uniqueness, author attribution, E-E-A-T signals, plus **title-uniqueness**, **heading-structure**, **image-alt-text** (all v0.5.2)
|
|
33
|
+
- **Internal linking** (6) — orphan pages, dead ends, cluster connectivity, link depth, unreachable-from-root (sample-aware), **host-section-divergence** (v0.5.1, site-reputation-abuse detector)
|
|
34
|
+
- **Structured data** (3) — JSON-LD validity, required fields, cross-page schema consistency
|
|
35
|
+
- **Cannibalization** (1) — URL pattern conflicts (`title-overlap` and `keyword-collision` were dropped in v0.4 due to high false-positive rates)
|
|
36
|
+
- **Data binding** (2) — verify rendered pages expose values from a source dataset (missing or identical-across-pages bindings)
|
|
37
|
+
|
|
38
|
+
## What's new in v0.5.2 — credibility layer
|
|
39
|
+
|
|
40
|
+
- **4 new content-quality rules** addressing the v0.5.1 blind-spot audit's tier-1 gaps: `content/title-uniqueness` (raw, not entity-masked — catalog templates with per-record entity values still pass), `content/heading-structure` (H1 presence, single-H1, hierarchy), `content/image-alt-text` (skips `role="presentation"` / `aria-hidden="true"` / explicit `alt=""`), `tech/og-completeness` (the README-promised rule that finally ships).
|
|
41
|
+
- **`AuditOptions.authorityScore`** (0-100) — bring-your-own-DA. ≥80 shifts the verdict ladder one tier lenient (established brand can absorb shapes a newer site can't). ≤30 shifts one tier stricter (newer/lower-authority operator). Raw `risk` number unchanged so CI gates stay stable. The engine itself remains authority-blind by design — no Moz/Ahrefs/Semrush dependency.
|
|
42
|
+
- **`AuditOptions.sampleSeed`** — deterministic `mulberry32` PRNG plumbed through the stratified sampler. Repeated audits with the same seed pick the same pages and produce reproducible verdicts.
|
|
43
|
+
- **`spam/doorway-pattern` cluster collapse** — emits in the same `pageUrl` + `relatedUrls[0]` shape as `spam/near-duplicate` and is registered in `CLUSTERABLE_RULES`. C(N,2) per-pair findings on entity-swap-heavy catalogs collapse into one cluster finding per template-tied group.
|
|
44
|
+
- **Per-bucket info-severity cap** — a flood of info findings can't fill a category bucket on its own (capped at 50 separately from the 100 cap on warning+ findings).
|
|
45
|
+
- **`summary.appliedSeverityDemotions: string[]`** — engine emits the list of rule IDs whose severity was overridden by the active scoring profile so consumers (formatters, CI) can show *which* rules got demoted and *why*. Pass `--strict` to disable demotions entirely.
|
|
46
|
+
- **Sample-aware rules** — `links/unreachable-from-root` skips on partial-sample audits (it can't distinguish real graph isolation from sample-shape).
|
|
47
|
+
- **Markdown formatter** collapses informational findings under `<details>` so PR comments don't drown actionable items in 100+ info bullets.
|
|
48
|
+
|
|
49
|
+
The full per-round iteration story (9 calibration rounds against a curated reputable-pSEO corpus) and the trade-offs we accepted are at [`docs/superpowers/specs/2026-05-03-calibration-against-reputable-pseo.md`](../../docs/superpowers/specs/2026-05-03-calibration-against-reputable-pseo.md). The honest blind-spot audit (what we still don't detect, including the domain-authority gap that motivated `--authority-score`) is at [`docs/superpowers/specs/2026-05-03-pseolint-blind-spots.md`](../../docs/superpowers/specs/2026-05-03-pseolint-blind-spots.md). The dated user-facing methodology summary is at [pseolint.dev/methodology](https://pseolint.dev/methodology).
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### `auditSource(source, options?)`
|
|
54
|
+
|
|
55
|
+
Returns an `AuditSummary` with composite score, category scores, enriched findings, and optional cache / state / AI-triage metadata.
|
|
56
|
+
|
|
57
|
+
Selected options (see `AuditOptions` in `types.ts` for the full surface):
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
await auditSource("https://example.com/sitemap.xml", {
|
|
61
|
+
concurrency: 5,
|
|
62
|
+
timeout: 30_000,
|
|
63
|
+
sampleSize: 200,
|
|
64
|
+
samplingStrategy: "stratified", // or "random"
|
|
65
|
+
ignore: ["**/api/**"],
|
|
66
|
+
maxFetchBytes: 52_428_800, // 50 MB hard cap per run
|
|
67
|
+
cache: { dir: ".pseolint/cache", ttlMs: 7 * 24 * 60 * 60 * 1000 },
|
|
68
|
+
state: {
|
|
69
|
+
path: ".pseolint/state.json",
|
|
70
|
+
mode: "monitoring", // v0.5+: pre-fetch decision matrix; "fresh" forces full re-audit.
|
|
71
|
+
// Omit to auto-monitor when prior state exists.
|
|
72
|
+
ageFloorDays: 7, // v0.5+: forces refetch on URLs older than N days
|
|
73
|
+
exitOnRegression: true,
|
|
74
|
+
since: true, // v0.5+ alias for mode: "monitoring" (back-compat)
|
|
75
|
+
},
|
|
76
|
+
pageGroups: {
|
|
77
|
+
blog: { match: "**/blog/**", rules: ["content/*", "spam/*"] },
|
|
78
|
+
products: { match: "**/p/**", overrides: { "spam/thin-content": { thinContentMinWords: 200 } } },
|
|
79
|
+
},
|
|
80
|
+
dataSource: { records: [{ url: "/p/*", data: { price: "$19", stock: 12 } }] },
|
|
81
|
+
entityPatterns: [{ placeholder: "[CITY]", pattern: "\\b(NYC|LA|SF)\\b", flags: "gi" }],
|
|
82
|
+
ai: { enabled: true, provider: "anthropic", model: "claude-haiku-4-5-20251001", maxCostUsd: 0.1 },
|
|
83
|
+
telemetry: { enabled: true, path: ".pseolint/telemetry.jsonl" },
|
|
84
|
+
// Safety (v0.3.2–v0.3.3)
|
|
85
|
+
safeMode: "saas", // "saas" | "cli" — flips guardSsrf + caps
|
|
86
|
+
guardSsrf: true, // DNS-validated SSRF check on every URL
|
|
87
|
+
respectRobotsTxt: true, // skip sitemap URLs Disallow'd by target robots.txt
|
|
88
|
+
followRedirects: true,
|
|
89
|
+
maxCrawlDiscovered: 2000, // hard ceiling on link-discovery fan-out
|
|
90
|
+
signal: controller.signal, // AbortSignal — ctrl-C / quota-exhausted cancels cleanly
|
|
91
|
+
rules: {
|
|
92
|
+
nearDuplicateThreshold: 0.85,
|
|
93
|
+
thinContentMinWords: 300,
|
|
94
|
+
titleOverlapThreshold: 0.8,
|
|
95
|
+
// ...
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Safety primitives (SSRF, abort, crawl-ceiling)
|
|
101
|
+
|
|
102
|
+
`@pseolint/core` ships a few primitives for hosts that run audits against
|
|
103
|
+
user-submitted URLs. All are opt-in; local CLI use doesn't change.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import {
|
|
107
|
+
safeFetch, // SSRF-safe fetch for non-audit use cases
|
|
108
|
+
validateTargetHost, // throws SSRFError on private-range / DNS-rebinding targets
|
|
109
|
+
isPrivateOrReservedHost,
|
|
110
|
+
SSRFError,
|
|
111
|
+
DnsResolutionError,
|
|
112
|
+
} from "@pseolint/core";
|
|
113
|
+
|
|
114
|
+
// Validate a user-submitted URL before enqueuing:
|
|
115
|
+
await validateTargetHost(new URL(userUrl).hostname);
|
|
116
|
+
|
|
117
|
+
// Fetch with SSRF guard baked in:
|
|
118
|
+
const res = await safeFetch(userUrl, { timeoutMs: 10_000, followRedirects: false });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The full audit picks up the same guard via `auditSource(url, { safeMode: "saas" })`
|
|
122
|
+
or via the individual `guardSsrf` / `respectRobotsTxt` / `followRedirects` flags.
|
|
123
|
+
|
|
124
|
+
### Render-mode analytics blocking
|
|
125
|
+
|
|
126
|
+
Rendered audits (`options.render = {...}`) block known analytics endpoints
|
|
127
|
+
by default so the audit doesn't inject fake sessions into the site owner's
|
|
128
|
+
GA / Plausible / PostHog / Mixpanel / Hotjar / Sentry dashboards.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
await auditSource(url, {
|
|
132
|
+
render: {
|
|
133
|
+
analyticsMode: "block", // default — blocks ~40 analytics hosts
|
|
134
|
+
// "allow-first-party" — block third-party only
|
|
135
|
+
// "allow" — don't intercept anything
|
|
136
|
+
extraBlockedHosts: ["my-internal-metrics.corp"],
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Formatters
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { formatConsole, formatJson, formatMarkdown, formatHtml } from "@pseolint/core";
|
|
145
|
+
|
|
146
|
+
const out = formatConsole(summary);
|
|
147
|
+
const json = formatJson(summary);
|
|
148
|
+
const md = formatMarkdown(summary);
|
|
149
|
+
const html = formatHtml(summary);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### AI triage
|
|
153
|
+
|
|
154
|
+
When `ai.enabled` is set, findings are clustered into root-causes by an LLM. Providers are loaded lazily from optional peer deps — install only the one you need:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm install @ai-sdk/anthropic # or @ai-sdk/openai, @ai-sdk/google, @ai-sdk/mistral,
|
|
158
|
+
# @ai-sdk/groq, @ai-sdk/xai, @ai-sdk/cohere,
|
|
159
|
+
# ollama-ai-provider-v2
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { triageFindings, createLanguageModel, estimateCostUsd } from "@pseolint/core";
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Cost and daily-budget caps are enforced pre-flight; results are cached on disk by default.
|
|
167
|
+
|
|
168
|
+
### AI orchestrator (v0.5)
|
|
169
|
+
|
|
170
|
+
Net-new in v0.5. `orchestrate()` drives an LLM through 25 deterministic tools (sitemap fetch, template clustering, per-page rule checks, AEO probes against live answer engines, SerpAPI) and produces a **fix manifest** of concrete patches — not just a list of findings.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { orchestrate } from "@pseolint/core";
|
|
174
|
+
|
|
175
|
+
const { session, manifest, validation, diff } = await orchestrate({
|
|
176
|
+
domain: "https://example.com",
|
|
177
|
+
userId: "demo",
|
|
178
|
+
budget: { maxSessionUsd: 3 }, // optional; default $5
|
|
179
|
+
onEvent: (e) => console.log(e), // optional; SSE-friendly callback
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (session.reason === "completed") {
|
|
183
|
+
console.log(`Verdict: ${manifest!.verdict}`);
|
|
184
|
+
console.log(`${validation!.validPatches}/${validation!.totalPatches} patches valid`);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**What you get:**
|
|
189
|
+
- `manifest` — `FixManifest` with verdict, category grades, page/template/domain patches (replace_h1, rewrite_meta, add_jsonld, add_faq_block, rewrite_intro, add_internal_link, remove_thin_block, robots_txt, sitemap_xml, canonical_strategy)
|
|
190
|
+
- `validation` — patch-by-patch `ManifestValidationReport`. Failed patches are dropped from the manifest before it returns; `failures` carries the location + reason.
|
|
191
|
+
- `diff` — `ManifestDiff` of structured `PatchDiff` objects (5 kinds — text_replace, html_insert, html_remove, file_replace, guidance) suitable for direct UI rendering.
|
|
192
|
+
|
|
193
|
+
**Architecture**: rules become tools the LLM calls. The LLM picks order. Budget caps (LLM tokens + external probe USD, pre-flight + reactive) bound spend. Watchdog injects a convergence reminder every N tool calls. AsyncLocalStorage-backed page cache means HTML never travels in conversation history — token cost stays bounded as audits scale.
|
|
194
|
+
|
|
195
|
+
**Lower-level exports** for callers who want individual pieces:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import {
|
|
199
|
+
runOrchestrator, // direct runner — bring your own LanguageModel
|
|
200
|
+
orchestratorTools, // the 25-tool registry
|
|
201
|
+
defineTool, // helper to add custom tools
|
|
202
|
+
validateManifest, // walk a manifest, return per-failure report
|
|
203
|
+
diffManifest, // produce a structured-diff projection
|
|
204
|
+
manifestSchema, // Zod schema for FixManifest
|
|
205
|
+
buildSystemPrompt, // canonical orchestrator system prompt
|
|
206
|
+
DEFAULT_BUDGET, // BudgetCaps defaults
|
|
207
|
+
} from "@pseolint/core";
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
External probes (`query_serp`, `ask_ai_engine`) read API keys from the call's `apiKey` arg or `SERPAPI_API_KEY` / `ANTHROPIC_API_KEY` / `PERPLEXITY_API_KEY` / `GOOGLE_GENERATIVE_AI_API_KEY` env vars.
|
|
211
|
+
|
|
212
|
+
### Change-driven monitoring (v0.5)
|
|
213
|
+
|
|
214
|
+
When prior state exists, `auditSource` defaults to **monitoring mode**: the decision matrix decides which URLs to fetch BEFORE the network round-trip. URLs without change signals are skipped entirely; their findings are carried forward from prior state with `carriedForward: true` and `lastVerifiedAt` markers.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { planScrapeStrategy, CORE_RULESET_VERSION, DEFAULT_AGE_FLOOR_DAYS } from "@pseolint/core";
|
|
218
|
+
|
|
219
|
+
// The decision matrix is also exposed as a pure function for callers that
|
|
220
|
+
// want to plan their own fetches:
|
|
221
|
+
const plan = planScrapeStrategy({
|
|
222
|
+
candidateUrls,
|
|
223
|
+
priorState,
|
|
224
|
+
sitemapLastmodByUrl, // Map<url, ISO-string>
|
|
225
|
+
currentRulesetVersion: CORE_RULESET_VERSION,
|
|
226
|
+
ageFloorDays: DEFAULT_AGE_FLOOR_DAYS,
|
|
227
|
+
now: new Date(),
|
|
228
|
+
// Optional Pro-only inputs:
|
|
229
|
+
// gscDeltasByUrl, gscThresholds
|
|
230
|
+
});
|
|
231
|
+
// plan.refetch: Map<url, RefetchReason>
|
|
232
|
+
// plan.skip: Map<url, "unchanged">
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Reasons** (first match wins): `new` → `age` → `ruleset` → `recheck` (warning/error/critical only — info findings carry forward) → `lastmod` → `gsc` → `no-signal` → else `unchanged`.
|
|
236
|
+
|
|
237
|
+
`AuditSummary.scrapePlan` reports `{ fetched, intended, carriedForward, reasonCounts, rulesetVersion, lastFullAuditAt }` — populated only on monitoring runs.
|
|
238
|
+
|
|
239
|
+
**Bump `CORE_RULESET_VERSION`** when shipping a new rule or materially changing rule logic so monitoring runs re-evaluate previously-skipped URLs against the new ruleset.
|
|
240
|
+
|
|
241
|
+
**Regression gating.** `state.exitOnRegression: true` flags a run where a new rule ID fires on any previously clean URL (`summary.hasRegression`). Carried-forward findings are excluded from the regression baseline so a regression on a skipped URL isn't masked by stale findings.
|
|
242
|
+
|
|
243
|
+
### State schema v2
|
|
244
|
+
|
|
245
|
+
`UrlStateEntry` v2 stores full finding records (not just IDs) so future runs can carry them forward. Persists `lastModified`, `etag`, `sitemapLastmodAtAudit`, `rulesetVersion` per URL. `RunState` adds `lastFullAuditAt` and `rulesetVersion`. Existing v1 state files (v0.4) are discarded on read with a warning, triggering one baseline re-audit.
|
|
246
|
+
|
|
247
|
+
### Caching
|
|
248
|
+
|
|
249
|
+
Setting `cache` enables an ETag/Last-Modified-aware disk cache for HTTP fetches. `summary.cacheStats` reports `{ hits, total, bytesSavedEstimate }`.
|
|
250
|
+
|
|
251
|
+
### Page groups
|
|
252
|
+
|
|
253
|
+
Classify pages by glob and apply different rule subsets or threshold overrides per group. Results are surfaced in `summary.groupScores` / `summary.groupPageCounts`.
|
|
254
|
+
|
|
255
|
+
### Rendering
|
|
256
|
+
|
|
257
|
+
For client-rendered pages, install `playwright-core` and pass `render: { browserWsEndpoint }` to connect to an existing browser endpoint.
|
|
258
|
+
|
|
259
|
+
## Peer dependencies
|
|
260
|
+
|
|
261
|
+
All AI providers and `playwright-core` are optional peers — you only install the ones you actually use.
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
170
265
|
MIT
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { FixManifest } from "../orchestrator/finish-tool.js";
|
|
2
|
+
type PageChange = FixManifest["pages"][number]["changes"][number];
|
|
3
|
+
type DomainPatch = FixManifest["domainLevel"][number];
|
|
4
|
+
/**
|
|
5
|
+
* Structured patch diff. Each manifest change maps to one of these,
|
|
6
|
+
* which downstream consumers (web UI, CLI, GitHub-PR generator) render
|
|
7
|
+
* differently:
|
|
8
|
+
* - text_replace → before/after blocks side-by-side, copy-paste UI
|
|
9
|
+
* - html_insert → HTML snippet to paste into the named region
|
|
10
|
+
* - html_remove → CSS selector to delete
|
|
11
|
+
* - file_replace → full-file diff (robots.txt / sitemap.xml)
|
|
12
|
+
* - guidance → freeform prose (canonical_strategy)
|
|
13
|
+
*
|
|
14
|
+
* Kept deliberately narrow — not a unified-diff library. The manifest
|
|
15
|
+
* patches are paste-into-CMS-friendly outputs, not source-level diffs.
|
|
16
|
+
*/
|
|
17
|
+
export type PatchDiff = {
|
|
18
|
+
kind: "text_replace";
|
|
19
|
+
field: "h1" | "title" | "meta_description" | "intro";
|
|
20
|
+
before: string;
|
|
21
|
+
after: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "html_insert";
|
|
25
|
+
target: "head" | "body" | "after_h1";
|
|
26
|
+
description: string;
|
|
27
|
+
html: string;
|
|
28
|
+
reason: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "html_remove";
|
|
31
|
+
selector: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "file_replace";
|
|
35
|
+
path: string;
|
|
36
|
+
before: string | null;
|
|
37
|
+
after: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "guidance";
|
|
41
|
+
topic: "canonical_strategy";
|
|
42
|
+
text: string;
|
|
43
|
+
reason: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Convert a single page-change into a `PatchDiff`. Pure transformation —
|
|
47
|
+
* does not validate (use `validatePageChange` for that). The two should
|
|
48
|
+
* be called in sequence at manifest finalization: validate first, drop
|
|
49
|
+
* failures, then diff the survivors.
|
|
50
|
+
*/
|
|
51
|
+
export declare function diffPageChange(c: PageChange): PatchDiff;
|
|
52
|
+
/** Convert a single domain patch into a `PatchDiff`. */
|
|
53
|
+
export declare function diffDomainPatch(p: DomainPatch): PatchDiff;
|
|
54
|
+
export interface ManifestDiff {
|
|
55
|
+
pages: Array<{
|
|
56
|
+
url: string;
|
|
57
|
+
diffs: PatchDiff[];
|
|
58
|
+
}>;
|
|
59
|
+
templates: Array<{
|
|
60
|
+
templateId: string;
|
|
61
|
+
affectedUrlCount: number;
|
|
62
|
+
recommendation: string;
|
|
63
|
+
examples: Array<{
|
|
64
|
+
url: string;
|
|
65
|
+
diffs: PatchDiff[];
|
|
66
|
+
}>;
|
|
67
|
+
}>;
|
|
68
|
+
domainLevel: PatchDiff[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Walk every patch in a manifest and produce a structured-diff projection.
|
|
72
|
+
* Mirrors the manifest's tree structure so the UI can render side-by-side
|
|
73
|
+
* (page list / template clusters / domain-level), each populated with
|
|
74
|
+
* already-typed patch diffs.
|
|
75
|
+
*/
|
|
76
|
+
export declare function diffManifest(manifest: FixManifest): ManifestDiff;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/ai/manifest/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAElE,KAAK,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAClE,KAAK,WAAW,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GACjB;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,IAAI,GAAG,OAAO,GAAG,kBAAkB,GAAG,OAAO,CAAC;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAgBN;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAmEvD;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,CA0BzD;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,EAAE,CAAA;KAAE,CAAC,CAAC;IAClD,SAAS,EAAE,KAAK,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,SAAS,EAAE,CAAA;SAAE,CAAC,CAAC;KACtD,CAAC,CAAC;IACH,WAAW,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,CAiBhE"}
|