@forumulate/cli 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/api/args.d.ts.map +1 -1
- package/dist/api/args.js +1 -0
- package/dist/api/args.js.map +1 -1
- package/dist/api/command.d.ts +1 -1
- package/dist/api/command.d.ts.map +1 -1
- package/dist/api/command.js +86 -11
- package/dist/api/command.js.map +1 -1
- package/dist/api/http.d.ts +8 -0
- package/dist/api/http.d.ts.map +1 -1
- package/dist/api/http.js +3 -2
- package/dist/api/http.js.map +1 -1
- package/dist/api/live-spec.d.ts +48 -0
- package/dist/api/live-spec.d.ts.map +1 -0
- package/dist/api/live-spec.js +72 -0
- package/dist/api/live-spec.js.map +1 -0
- package/dist/api/operations.json +1 -1
- package/dist/api/request.d.ts.map +1 -1
- package/dist/api/request.js +25 -1
- package/dist/api/request.js.map +1 -1
- package/dist/api/send.d.ts +10 -1
- package/dist/api/send.d.ts.map +1 -1
- package/dist/api/send.js +14 -3
- package/dist/api/send.js.map +1 -1
- package/dist/api/slim.d.ts +14 -0
- package/dist/api/slim.d.ts.map +1 -0
- package/dist/api/slim.js +174 -0
- package/dist/api/slim.js.map +1 -0
- package/dist/cache-command.d.ts +12 -0
- package/dist/cache-command.d.ts.map +1 -0
- package/dist/cache-command.js +93 -0
- package/dist/cache-command.js.map +1 -0
- package/dist/check/command.d.ts +48 -0
- package/dist/check/command.d.ts.map +1 -0
- package/dist/check/command.js +256 -0
- package/dist/check/command.js.map +1 -0
- package/dist/check/external.d.ts +66 -0
- package/dist/check/external.d.ts.map +1 -0
- package/dist/check/external.js +177 -0
- package/dist/check/external.js.map +1 -0
- package/dist/check/markdown-links.d.ts +38 -0
- package/dist/check/markdown-links.d.ts.map +1 -0
- package/dist/check/markdown-links.js +166 -0
- package/dist/check/markdown-links.js.map +1 -0
- package/dist/cli.js +42 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +17 -0
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +6 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/groups-api.d.ts.map +1 -1
- package/dist/commands/groups-api.js +20 -2
- package/dist/commands/groups-api.js.map +1 -1
- package/dist/commands/groups-docs.d.ts.map +1 -1
- package/dist/commands/groups-docs.js +8 -2
- package/dist/commands/groups-docs.js.map +1 -1
- package/dist/commands/groups-jobs.d.ts +10 -0
- package/dist/commands/groups-jobs.d.ts.map +1 -0
- package/dist/commands/groups-jobs.js +89 -0
- package/dist/commands/groups-jobs.js.map +1 -0
- package/dist/commands/groups-org.d.ts.map +1 -1
- package/dist/commands/groups-org.js +59 -2
- package/dist/commands/groups-org.js.map +1 -1
- package/dist/commands/groups-site.d.ts.map +1 -1
- package/dist/commands/groups-site.js +47 -10
- package/dist/commands/groups-site.js.map +1 -1
- package/dist/commands/groups-spaces.d.ts.map +1 -1
- package/dist/commands/groups-spaces.js +11 -5
- package/dist/commands/groups-spaces.js.map +1 -1
- package/dist/commands/groups-types.d.ts.map +1 -1
- package/dist/commands/groups-types.js +74 -0
- package/dist/commands/groups-types.js.map +1 -1
- package/dist/commands/groups.d.ts.map +1 -1
- package/dist/commands/groups.js +2 -0
- package/dist/commands/groups.js.map +1 -1
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +18 -3
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/job-wait.d.ts +64 -0
- package/dist/commands/job-wait.d.ts.map +1 -0
- package/dist/commands/job-wait.js +91 -0
- package/dist/commands/job-wait.js.map +1 -0
- package/dist/commands/resource.d.ts +19 -0
- package/dist/commands/resource.d.ts.map +1 -1
- package/dist/commands/resource.js +65 -3
- package/dist/commands/resource.js.map +1 -1
- package/dist/content/library.d.ts +8 -0
- package/dist/content/library.d.ts.map +1 -1
- package/dist/content/library.js +10 -2
- package/dist/content/library.js.map +1 -1
- package/dist/credentials.d.ts +43 -4
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +67 -4
- package/dist/credentials.js.map +1 -1
- package/dist/dev/command.d.ts +41 -0
- package/dist/dev/command.d.ts.map +1 -0
- package/dist/dev/command.js +158 -0
- package/dist/dev/command.js.map +1 -0
- package/dist/doctor/address-checks.d.ts +56 -0
- package/dist/doctor/address-checks.d.ts.map +1 -0
- package/dist/doctor/address-checks.js +183 -0
- package/dist/doctor/address-checks.js.map +1 -0
- package/dist/doctor/boilerplate.d.ts +33 -0
- package/dist/doctor/boilerplate.d.ts.map +1 -0
- package/dist/doctor/boilerplate.js +78 -0
- package/dist/doctor/boilerplate.js.map +1 -0
- package/dist/doctor/command.d.ts +18 -0
- package/dist/doctor/command.d.ts.map +1 -0
- package/dist/doctor/command.js +234 -0
- package/dist/doctor/command.js.map +1 -0
- package/dist/doctor/crawl.d.ts +49 -0
- package/dist/doctor/crawl.d.ts.map +1 -0
- package/dist/doctor/crawl.js +210 -0
- package/dist/doctor/crawl.js.map +1 -0
- package/dist/doctor/extended.d.ts +52 -0
- package/dist/doctor/extended.d.ts.map +1 -0
- package/dist/doctor/extended.js +150 -0
- package/dist/doctor/extended.js.map +1 -0
- package/dist/doctor/page-checks.d.ts +42 -0
- package/dist/doctor/page-checks.d.ts.map +1 -0
- package/dist/doctor/page-checks.js +200 -0
- package/dist/doctor/page-checks.js.map +1 -0
- package/dist/doctor/probe.d.ts +63 -0
- package/dist/doctor/probe.d.ts.map +1 -0
- package/dist/doctor/probe.js +170 -0
- package/dist/doctor/probe.js.map +1 -0
- package/dist/doctor/reader-auth-atlas.d.ts +18 -0
- package/dist/doctor/reader-auth-atlas.d.ts.map +1 -0
- package/dist/doctor/reader-auth-atlas.js +50 -0
- package/dist/doctor/reader-auth-atlas.js.map +1 -0
- package/dist/doctor/robots-params.d.ts +36 -0
- package/dist/doctor/robots-params.d.ts.map +1 -0
- package/dist/doctor/robots-params.js +97 -0
- package/dist/doctor/robots-params.js.map +1 -0
- package/dist/doctor/site.d.ts +42 -0
- package/dist/doctor/site.d.ts.map +1 -0
- package/dist/doctor/site.js +82 -0
- package/dist/doctor/site.js.map +1 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +94 -3
- package/dist/doctor.js.map +1 -1
- package/dist/exit-codes.d.ts +40 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +66 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/help-commands.d.ts +13 -0
- package/dist/help-commands.d.ts.map +1 -0
- package/dist/help-commands.js +215 -0
- package/dist/help-commands.js.map +1 -0
- package/dist/help.d.ts +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +74 -11
- package/dist/help.js.map +1 -1
- package/dist/io.d.ts +33 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +81 -0
- package/dist/io.js.map +1 -0
- package/dist/preview-comment.d.ts +20 -0
- package/dist/preview-comment.d.ts.map +1 -0
- package/dist/preview-comment.js +88 -0
- package/dist/preview-comment.js.map +1 -0
- package/dist/publish/about.d.ts +23 -0
- package/dist/publish/about.d.ts.map +1 -0
- package/dist/publish/about.js +66 -0
- package/dist/publish/about.js.map +1 -0
- package/dist/publish/api.d.ts +30 -12
- package/dist/publish/api.d.ts.map +1 -1
- package/dist/publish/api.js +62 -10
- package/dist/publish/api.js.map +1 -1
- package/dist/publish/client.d.ts +19 -0
- package/dist/publish/client.d.ts.map +1 -0
- package/dist/publish/client.js +3 -0
- package/dist/publish/client.js.map +1 -0
- package/dist/publish/command.d.ts +51 -2
- package/dist/publish/command.d.ts.map +1 -1
- package/dist/publish/command.js +157 -17
- package/dist/publish/command.js.map +1 -1
- package/dist/publish/context.d.ts +12 -1
- package/dist/publish/context.d.ts.map +1 -1
- package/dist/publish/context.js.map +1 -1
- package/dist/publish/diff.d.ts +21 -0
- package/dist/publish/diff.d.ts.map +1 -0
- package/dist/publish/diff.js +96 -0
- package/dist/publish/diff.js.map +1 -0
- package/dist/publish/docs-apply.d.ts +8 -1
- package/dist/publish/docs-apply.d.ts.map +1 -1
- package/dist/publish/docs-apply.js +49 -10
- package/dist/publish/docs-apply.js.map +1 -1
- package/dist/publish/docs-plan.d.ts +16 -3
- package/dist/publish/docs-plan.d.ts.map +1 -1
- package/dist/publish/docs-plan.js +57 -6
- package/dist/publish/docs-plan.js.map +1 -1
- package/dist/publish/extras.d.ts +10 -0
- package/dist/publish/extras.d.ts.map +1 -1
- package/dist/publish/extras.js +54 -2
- package/dist/publish/extras.js.map +1 -1
- package/dist/publish/frontmatter.d.ts +1 -1
- package/dist/publish/frontmatter.d.ts.map +1 -1
- package/dist/publish/frontmatter.js +1 -0
- package/dist/publish/frontmatter.js.map +1 -1
- package/dist/publish/index.d.ts +12 -0
- package/dist/publish/index.d.ts.map +1 -0
- package/dist/publish/index.js +17 -0
- package/dist/publish/index.js.map +1 -0
- package/dist/publish/manifest.d.ts.map +1 -1
- package/dist/publish/manifest.js +48 -3
- package/dist/publish/manifest.js.map +1 -1
- package/dist/publish/names.d.ts +28 -0
- package/dist/publish/names.d.ts.map +1 -0
- package/dist/publish/names.js +80 -0
- package/dist/publish/names.js.map +1 -0
- package/dist/publish/posts.d.ts.map +1 -1
- package/dist/publish/posts.js +46 -13
- package/dist/publish/posts.js.map +1 -1
- package/dist/publish/preview.d.ts +56 -0
- package/dist/publish/preview.d.ts.map +1 -0
- package/dist/publish/preview.js +133 -0
- package/dist/publish/preview.js.map +1 -0
- package/dist/publish/redirects.d.ts +13 -0
- package/dist/publish/redirects.d.ts.map +1 -0
- package/dist/publish/redirects.js +154 -0
- package/dist/publish/redirects.js.map +1 -0
- package/dist/publish/reserved.d.ts +49 -0
- package/dist/publish/reserved.d.ts.map +1 -0
- package/dist/publish/reserved.js +118 -0
- package/dist/publish/reserved.js.map +1 -0
- package/dist/publish/rewrite.d.ts +12 -0
- package/dist/publish/rewrite.d.ts.map +1 -1
- package/dist/publish/rewrite.js +22 -0
- package/dist/publish/rewrite.js.map +1 -1
- package/dist/publish/scan-docs.d.ts +10 -0
- package/dist/publish/scan-docs.d.ts.map +1 -0
- package/dist/publish/scan-docs.js +160 -0
- package/dist/publish/scan-docs.js.map +1 -0
- package/dist/publish/scan.d.ts +21 -17
- package/dist/publish/scan.d.ts.map +1 -1
- package/dist/publish/scan.js +99 -232
- package/dist/publish/scan.js.map +1 -1
- package/dist/publish/slots.d.ts +39 -0
- package/dist/publish/slots.d.ts.map +1 -0
- package/dist/publish/slots.js +64 -0
- package/dist/publish/slots.js.map +1 -0
- package/dist/publish/studio-edits.d.ts +25 -0
- package/dist/publish/studio-edits.d.ts.map +1 -0
- package/dist/publish/studio-edits.js +53 -0
- package/dist/publish/studio-edits.js.map +1 -0
- package/dist/publish/tags.d.ts +9 -0
- package/dist/publish/tags.d.ts.map +1 -0
- package/dist/publish/tags.js +194 -0
- package/dist/publish/tags.js.map +1 -0
- package/dist/publish/types.d.ts +84 -1
- package/dist/publish/types.d.ts.map +1 -1
- package/dist/publish/types.js +0 -10
- package/dist/publish/types.js.map +1 -1
- package/dist/publish-command.d.ts +55 -0
- package/dist/publish-command.d.ts.map +1 -0
- package/dist/publish-command.js +197 -0
- package/dist/publish-command.js.map +1 -0
- package/dist/run.d.ts +3 -10
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +171 -112
- package/dist/run.js.map +1 -1
- package/dist/translations/target.d.ts.map +1 -1
- package/dist/translations/target.js +2 -2
- package/dist/translations/target.js.map +1 -1
- package/dist/validate.d.ts +39 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +166 -0
- package/dist/validate.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/whoami.d.ts +41 -0
- package/dist/whoami.d.ts.map +1 -0
- package/dist/whoami.js +161 -0
- package/dist/whoami.js.map +1 -0
- package/package.json +2 -2
- package/dist/publish-dir.d.ts +0 -54
- package/dist/publish-dir.d.ts.map +0 -1
- package/dist/publish-dir.js +0 -119
- package/dist/publish-dir.js.map +0 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sitemapsFromRobots = sitemapsFromRobots;
|
|
4
|
+
exports.readSitemaps = readSitemaps;
|
|
5
|
+
exports.sample = sample;
|
|
6
|
+
exports.sitemapCrawl = sitemapCrawl;
|
|
7
|
+
exports.groupOf = groupOf;
|
|
8
|
+
exports.cacheClass = cacheClass;
|
|
9
|
+
exports.cacheConsistency = cacheConsistency;
|
|
10
|
+
const probe_1 = require("./probe");
|
|
11
|
+
/**
|
|
12
|
+
* `sitemap-crawl`: every address the site hands a crawler, fetched.
|
|
13
|
+
*
|
|
14
|
+
* The sitemap is the list a search engine trusts most, so an entry that
|
|
15
|
+
* redirects, 404s, names a different canonical or sits on another host is a
|
|
16
|
+
* page the site is asking to have indexed and then refusing to be. A space root
|
|
17
|
+
* that 307s to its first page was listed as itself (FMU-B06); a platform copy
|
|
18
|
+
* that 308s elsewhere named itself canonical (FMU-B01). Each is invisible from
|
|
19
|
+
* the page a person happens to open, and obvious once every listed URL is asked.
|
|
20
|
+
*
|
|
21
|
+
* The pages fetched here are also the SAMPLE the other site-wide checks judge
|
|
22
|
+
* (cache headers, descriptions), so the crawl is done once.
|
|
23
|
+
*/
|
|
24
|
+
/** How many sitemap documents (index included) one run will read. */
|
|
25
|
+
const SITEMAP_DOC_LIMIT = 25;
|
|
26
|
+
/** `Sitemap:` lines of robots.txt, else the conventional address. */
|
|
27
|
+
function sitemapsFromRobots(robots, origin) {
|
|
28
|
+
const named = [...robots.matchAll(/^\s*sitemap:\s*(\S+)/gim)].map((m) => m[1]);
|
|
29
|
+
return named.length ? named : [`${origin}/sitemap.xml`];
|
|
30
|
+
}
|
|
31
|
+
function locs(xml) {
|
|
32
|
+
return [...xml.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/gi)].map((m) => m[1].replace(/&/g, '&'));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Read sitemaps (following a sitemap index), returning the URL lists per
|
|
36
|
+
* document, so the sample can draw from every space rather than exhaust the
|
|
37
|
+
* first one: a site with a 900-page reference and a 4-page legal space would
|
|
38
|
+
* otherwise never have its legal pages looked at.
|
|
39
|
+
*/
|
|
40
|
+
async function readSitemaps(get, start) {
|
|
41
|
+
const lists = [];
|
|
42
|
+
const problems = [];
|
|
43
|
+
const queue = [...new Set(start)];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
while (queue.length && seen.size < SITEMAP_DOC_LIMIT) {
|
|
46
|
+
const url = queue.shift();
|
|
47
|
+
if (seen.has(url))
|
|
48
|
+
continue;
|
|
49
|
+
seen.add(url);
|
|
50
|
+
const p = await get(url, { accept: 'application/xml,text/xml' });
|
|
51
|
+
if (!p.ok || p.status !== 200) {
|
|
52
|
+
problems.push(`${url} → ${(0, probe_1.describeProbe)(p)}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (/<sitemapindex\b/i.test(p.text))
|
|
56
|
+
queue.push(...locs(p.text));
|
|
57
|
+
else
|
|
58
|
+
lists.push({ sitemap: url, urls: locs(p.text) });
|
|
59
|
+
}
|
|
60
|
+
return { lists, problems };
|
|
61
|
+
}
|
|
62
|
+
/** Up to `limit` URLs, round-robin across the sitemaps so every space is represented. */
|
|
63
|
+
function sample(lists, limit) {
|
|
64
|
+
const out = [];
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
for (let i = 0; out.length < limit; i++) {
|
|
67
|
+
let any = false;
|
|
68
|
+
for (const l of lists) {
|
|
69
|
+
const u = l.urls[i];
|
|
70
|
+
if (u === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
any = true;
|
|
73
|
+
if (!seen.has(u)) {
|
|
74
|
+
seen.add(u);
|
|
75
|
+
out.push(u);
|
|
76
|
+
if (out.length >= limit)
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!any)
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
async function sitemapCrawl(get, origin, robots, limit) {
|
|
86
|
+
const title = 'Every URL in the sitemap answers 200, as itself';
|
|
87
|
+
const matters = 'A sitemap entry that redirects, 404s or names another canonical is a page the site asks to have indexed and then disowns; search engines lower their trust in the whole list.';
|
|
88
|
+
const { lists, problems } = await readSitemaps(get, sitemapsFromRobots(robots ?? '', origin));
|
|
89
|
+
if (lists.length === 0) {
|
|
90
|
+
return {
|
|
91
|
+
check: (0, probe_1.check)('sitemap-crawl', title, 'fail', problems.length ? `No sitemap could be read: ${(0, probe_1.some)(problems)}` : 'No sitemap found.', matters),
|
|
92
|
+
pages: [],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const total = lists.reduce((n, l) => n + l.urls.length, 0);
|
|
96
|
+
const urls = sample(lists, limit);
|
|
97
|
+
const siteHost = (0, probe_1.hostOf)(origin);
|
|
98
|
+
const pages = await (0, probe_1.pool)(urls, 6, (u) => get(u, { accept: 'text/html' }));
|
|
99
|
+
const bad = [...problems];
|
|
100
|
+
for (const p of pages) {
|
|
101
|
+
if ((0, probe_1.hostOf)(p.url) !== siteHost) {
|
|
102
|
+
bad.push(`${p.url} is on another host than the sitemap (${siteHost})`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (!p.ok) {
|
|
106
|
+
bad.push(`${p.url} → ${p.error}`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (p.status >= 300 && p.status < 400) {
|
|
110
|
+
bad.push(`${p.url} → ${p.status} ${p.headers.get('location') ?? ''} (list the final URL)`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (p.status !== 200) {
|
|
114
|
+
bad.push(`${p.url} → ${p.status}`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const canonical = (0, probe_1.canonicalOf)(p.text, p.url);
|
|
118
|
+
if (canonical && !(0, probe_1.sameUrl)(canonical, p.url))
|
|
119
|
+
bad.push(`${p.url} names canonical ${canonical}`);
|
|
120
|
+
}
|
|
121
|
+
const scope = `${pages.length} of ${total} listed URL${total === 1 ? '' : 's'} across ${lists.length} sitemap${lists.length === 1 ? '' : 's'}`;
|
|
122
|
+
return {
|
|
123
|
+
check: (0, probe_1.check)('sitemap-crawl', title, bad.length ? 'fail' : 'pass', bad.length
|
|
124
|
+
? `${bad.length} problem(s) in ${scope}: ${(0, probe_1.some)(bad)}`
|
|
125
|
+
: `${scope}: all 200, all self-canonical.`, bad.length ? matters : ''),
|
|
126
|
+
pages,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The space a URL belongs to, for grouping: its first path segment, which is
|
|
131
|
+
* where a path-mounted space lives (`/docs`, `/legal`). Pages at the root of a
|
|
132
|
+
* site domain group together under `/`. Coarse, and that is fine — a
|
|
133
|
+
* difference between two spaces is a choice; a difference inside one is what
|
|
134
|
+
* this is looking for.
|
|
135
|
+
*/
|
|
136
|
+
function groupOf(url) {
|
|
137
|
+
try {
|
|
138
|
+
const u = new URL(url);
|
|
139
|
+
const segs = u.pathname.split('/').filter(Boolean);
|
|
140
|
+
// A short reader prefix followed by owner and space (`/p/<org>/<space>/…`)
|
|
141
|
+
// is how a multi-tenant host addresses a space; grouping by `/p` alone
|
|
142
|
+
// would compare every tenant's pages with every other's.
|
|
143
|
+
if (segs.length >= 4 && segs[0].length <= 3)
|
|
144
|
+
return `${u.host}/${segs.slice(0, 3).join('/')}`;
|
|
145
|
+
return `${u.host}/${segs[0] ?? ''}`;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return url;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** `public` (a shared cache may keep it) or `private` (it may not), by the directives that decide it. */
|
|
152
|
+
function cacheClass(cc) {
|
|
153
|
+
if (!cc)
|
|
154
|
+
return 'none';
|
|
155
|
+
if (/\b(?:private|no-store)\b/i.test(cc))
|
|
156
|
+
return 'private';
|
|
157
|
+
if (/\b(?:public|s-maxage)\b/i.test(cc))
|
|
158
|
+
return 'public';
|
|
159
|
+
return 'private';
|
|
160
|
+
}
|
|
161
|
+
/** Headers a site may send to say a page is uncached on purpose (and why). */
|
|
162
|
+
const REASON_HEADERS = ['x-forumulate-cache-reason', 'x-cache-reason', 'x-cache-control-reason'];
|
|
163
|
+
/**
|
|
164
|
+
* `cache-consistency`: the pages of one space are cached the same way.
|
|
165
|
+
*
|
|
166
|
+
* Every legal document answered `private, no-cache, no-store` to a reader with
|
|
167
|
+
* no cookie at all, while the legal index beside it was `public, s-maxage=10`
|
|
168
|
+
* (FMU-B05). Neither header is wrong on its own; the difference is the defect —
|
|
169
|
+
* either something is gated and should say so, or a code path forgot the
|
|
170
|
+
* public header and every reader pays a full origin render. A page that sends
|
|
171
|
+
* a reason header is taken at its word.
|
|
172
|
+
*/
|
|
173
|
+
function cacheConsistency(pages) {
|
|
174
|
+
const title = 'Pages within a space are cached alike';
|
|
175
|
+
const groups = new Map();
|
|
176
|
+
for (const p of pages) {
|
|
177
|
+
if (!p.ok || p.status !== 200)
|
|
178
|
+
continue;
|
|
179
|
+
if (REASON_HEADERS.some((h) => p.headers.get(h)))
|
|
180
|
+
continue;
|
|
181
|
+
const cc = p.headers.get('cache-control');
|
|
182
|
+
const g = groupOf(p.url);
|
|
183
|
+
groups.set(g, [
|
|
184
|
+
...(groups.get(g) ?? []),
|
|
185
|
+
{ url: p.url, cls: cacheClass(cc), cc: cc ?? '(none)' },
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
if (groups.size === 0)
|
|
189
|
+
return (0, probe_1.check)('cache-consistency', title, 'skip', 'No sampled pages answered 200.', '');
|
|
190
|
+
const odd = [];
|
|
191
|
+
for (const [g, rows] of groups) {
|
|
192
|
+
const classes = [...new Set(rows.map((r) => r.cls))];
|
|
193
|
+
if (classes.length < 2)
|
|
194
|
+
continue;
|
|
195
|
+
// Every side of the split is named. Which side is the bug is the site's
|
|
196
|
+
// call (usually the private one — a published page has no reason to be),
|
|
197
|
+
// and a "majority" of three legal documents is evidence of nothing.
|
|
198
|
+
const sides = classes.map((cls) => {
|
|
199
|
+
const of = rows.filter((r) => r.cls === cls);
|
|
200
|
+
return `${of.length} ${cls} ("${of[0].cc}": ${(0, probe_1.some)(of.map((r) => r.url), 3)})`;
|
|
201
|
+
});
|
|
202
|
+
odd.push(`${g}: ${sides.join(' vs ')}`);
|
|
203
|
+
}
|
|
204
|
+
return (0, probe_1.check)('cache-consistency', title, odd.length ? 'fail' : 'pass', odd.length
|
|
205
|
+
? (0, probe_1.some)(odd)
|
|
206
|
+
: `${[...groups.values()].reduce((n, r) => n + r.length, 0)} pages in ${groups.size} space(s), one rule each.`, odd.length
|
|
207
|
+
? 'A published page answering no-store to an anonymous reader is rendered at the origin for every visit. If it is gated on purpose, say so with an x-forumulate-cache-reason header.'
|
|
208
|
+
: '');
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=crawl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crawl.js","sourceRoot":"","sources":["../../src/doctor/crawl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Check } from '../conformance';
|
|
2
|
+
import { type AddressSet } from './address-checks';
|
|
3
|
+
import { type Get } from './probe';
|
|
4
|
+
/**
|
|
5
|
+
* The site-wide checks added for FMU-D14, run after the single-page ones.
|
|
6
|
+
*
|
|
7
|
+
* Every one of them is a defect an integrator found on a live site after
|
|
8
|
+
* readers did, and every one is judged from the outside, by HTTP alone, so the
|
|
9
|
+
* report is the same whether it is pointed at our platform, a staging mount or
|
|
10
|
+
* any other docs host. The only thing a check knows about "the platform" is
|
|
11
|
+
* its hostname, which is an input (`--platform-host`, else the host of
|
|
12
|
+
* FORUMULATE_URL, else forumulate.com) — never a constant in the logic.
|
|
13
|
+
*/
|
|
14
|
+
/** Every check id this file can produce, in report order. `--only` validates against it. */
|
|
15
|
+
export declare const EXTENDED_CHECK_IDS: readonly ["canonical-both-addresses", "sitemap-crawl", "cache-consistency", "root-redirect", "meta-description", "llms-txt", "favicon", "consent-tag", "white-label-leaks", "robots-params"];
|
|
16
|
+
export declare const DEFAULT_PLATFORM_HOST = "forumulate.com";
|
|
17
|
+
/** URLs one run fetches from the sitemaps; enough to see a pattern, few enough for CI. */
|
|
18
|
+
export declare const DEFAULT_SAMPLE_LIMIT = 60;
|
|
19
|
+
export interface ExtendedInput {
|
|
20
|
+
/** The page judged: where the asked-for address landed after redirects. */
|
|
21
|
+
url: string;
|
|
22
|
+
/** The address as given, before any redirect. */
|
|
23
|
+
requestedUrl?: string;
|
|
24
|
+
/** The page's HTML as an anonymous reader got it ('' when it did not answer). */
|
|
25
|
+
html: string;
|
|
26
|
+
/** robots.txt, when it answered 200. */
|
|
27
|
+
robots?: string;
|
|
28
|
+
get: Get;
|
|
29
|
+
platformHost: string;
|
|
30
|
+
/** The platform's API base (`https://forumulate.com/api`), for the primary-address lookup. */
|
|
31
|
+
platformApi?: string;
|
|
32
|
+
/** Other addresses of this page/space, from `--also` or `--site`. */
|
|
33
|
+
addressSets?: AddressSet[];
|
|
34
|
+
/** Space roots to check, from `--site`; derived from the URL otherwise. */
|
|
35
|
+
roots?: string[];
|
|
36
|
+
limit?: number;
|
|
37
|
+
/** Run only these ids (undefined: all). */
|
|
38
|
+
only?: ReadonlySet<string>;
|
|
39
|
+
}
|
|
40
|
+
/** The roots a URL's space is served at: the mount (first segment), or the platform copy's space root. */
|
|
41
|
+
export declare function deriveRoots(url: string, platformHost: string): string[];
|
|
42
|
+
/**
|
|
43
|
+
* The customer's address of a platform-hosted page, asked of the platform.
|
|
44
|
+
*
|
|
45
|
+
* Only for a URL on the platform host in the platform-copy shape: the public
|
|
46
|
+
* primary-domain lookup answers where the space's readers are sent, which is
|
|
47
|
+
* what both copies must name as canonical. Any failure is "unknown", and the
|
|
48
|
+
* check then compares the copies it has with each other.
|
|
49
|
+
*/
|
|
50
|
+
export declare function discoverPrimary(get: Get, url: string, platformHost: string, platformApi: string | undefined): Promise<AddressSet | undefined>;
|
|
51
|
+
export declare function extendedChecks(input: ExtendedInput): Promise<Check[]>;
|
|
52
|
+
//# sourceMappingURL=extended.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extended.d.ts","sourceRoot":"","sources":["../../src/doctor/extended.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAiB,KAAK,GAAG,EAAc,MAAM,SAAS,CAAC;AAG9D;;;;;;;;;GASG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,8LAWrB,CAAC;AAEX,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AACtD,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,EAAE,MAAM,CAAC;IACrB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC5B;AASD,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CASvE;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAuBjC;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAiF3E"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_SAMPLE_LIMIT = exports.DEFAULT_PLATFORM_HOST = exports.EXTENDED_CHECK_IDS = void 0;
|
|
4
|
+
exports.deriveRoots = deriveRoots;
|
|
5
|
+
exports.discoverPrimary = discoverPrimary;
|
|
6
|
+
exports.extendedChecks = extendedChecks;
|
|
7
|
+
const address_checks_1 = require("./address-checks");
|
|
8
|
+
const crawl_1 = require("./crawl");
|
|
9
|
+
const page_checks_1 = require("./page-checks");
|
|
10
|
+
const probe_1 = require("./probe");
|
|
11
|
+
const robots_params_1 = require("./robots-params");
|
|
12
|
+
/**
|
|
13
|
+
* The site-wide checks added for FMU-D14, run after the single-page ones.
|
|
14
|
+
*
|
|
15
|
+
* Every one of them is a defect an integrator found on a live site after
|
|
16
|
+
* readers did, and every one is judged from the outside, by HTTP alone, so the
|
|
17
|
+
* report is the same whether it is pointed at our platform, a staging mount or
|
|
18
|
+
* any other docs host. The only thing a check knows about "the platform" is
|
|
19
|
+
* its hostname, which is an input (`--platform-host`, else the host of
|
|
20
|
+
* FORUMULATE_URL, else forumulate.com) — never a constant in the logic.
|
|
21
|
+
*/
|
|
22
|
+
/** Every check id this file can produce, in report order. `--only` validates against it. */
|
|
23
|
+
exports.EXTENDED_CHECK_IDS = [
|
|
24
|
+
'canonical-both-addresses',
|
|
25
|
+
'sitemap-crawl',
|
|
26
|
+
'cache-consistency',
|
|
27
|
+
'root-redirect',
|
|
28
|
+
'meta-description',
|
|
29
|
+
'llms-txt',
|
|
30
|
+
'favicon',
|
|
31
|
+
'consent-tag',
|
|
32
|
+
'white-label-leaks',
|
|
33
|
+
'robots-params',
|
|
34
|
+
];
|
|
35
|
+
exports.DEFAULT_PLATFORM_HOST = 'forumulate.com';
|
|
36
|
+
/** URLs one run fetches from the sitemaps; enough to see a pattern, few enough for CI. */
|
|
37
|
+
exports.DEFAULT_SAMPLE_LIMIT = 60;
|
|
38
|
+
/**
|
|
39
|
+
* A platform copy's address shape: `/<prefix>/<org>/<space>/<rest>`, where the
|
|
40
|
+
* prefix is a short reader route (`/p/`, `/b/`, `/f/` …). Read only on the
|
|
41
|
+
* platform host, where that shape is the platform's own convention.
|
|
42
|
+
*/
|
|
43
|
+
const PLATFORM_COPY = /^\/([a-z]{1,3})\/([^/]+)\/([^/]+)(\/.*)?$/;
|
|
44
|
+
/** The roots a URL's space is served at: the mount (first segment), or the platform copy's space root. */
|
|
45
|
+
function deriveRoots(url, platformHost) {
|
|
46
|
+
const u = new URL(url);
|
|
47
|
+
const path = u.pathname.replace(/\/+$/, '');
|
|
48
|
+
if ((0, probe_1.hostOf)(url) === platformHost.toLowerCase()) {
|
|
49
|
+
const m = PLATFORM_COPY.exec(path);
|
|
50
|
+
if (m)
|
|
51
|
+
return [`${u.origin}/${m[1]}/${m[2]}/${m[3]}`];
|
|
52
|
+
}
|
|
53
|
+
const first = path.split('/').filter(Boolean)[0];
|
|
54
|
+
return [first ? `${u.origin}/${first}` : u.origin];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The customer's address of a platform-hosted page, asked of the platform.
|
|
58
|
+
*
|
|
59
|
+
* Only for a URL on the platform host in the platform-copy shape: the public
|
|
60
|
+
* primary-domain lookup answers where the space's readers are sent, which is
|
|
61
|
+
* what both copies must name as canonical. Any failure is "unknown", and the
|
|
62
|
+
* check then compares the copies it has with each other.
|
|
63
|
+
*/
|
|
64
|
+
async function discoverPrimary(get, url, platformHost, platformApi) {
|
|
65
|
+
if (!platformApi || (0, probe_1.hostOf)(url) !== platformHost.toLowerCase())
|
|
66
|
+
return undefined;
|
|
67
|
+
const u = new URL(url);
|
|
68
|
+
const m = PLATFORM_COPY.exec(u.pathname.replace(/\/+$/, ''));
|
|
69
|
+
if (!m)
|
|
70
|
+
return undefined;
|
|
71
|
+
const q = new URLSearchParams({
|
|
72
|
+
org: decodeURIComponent(m[2]),
|
|
73
|
+
space: decodeURIComponent(m[3]),
|
|
74
|
+
});
|
|
75
|
+
const p = await get(`${platformApi.replace(/\/+$/, '')}/public/domains/primary?${q}`, {
|
|
76
|
+
accept: 'application/json',
|
|
77
|
+
});
|
|
78
|
+
if (!p.ok || p.status !== 200)
|
|
79
|
+
return undefined;
|
|
80
|
+
try {
|
|
81
|
+
const primary = JSON.parse(p.text).primary;
|
|
82
|
+
if (!primary?.origin)
|
|
83
|
+
return undefined;
|
|
84
|
+
const expected = `${primary.origin.replace(/\/+$/, '')}${primary.basePath ?? ''}${m[4] ?? ''}`;
|
|
85
|
+
return { expected, urls: [url, expected] };
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function extendedChecks(input) {
|
|
92
|
+
const want = (id) => !input.only || input.only.has(id);
|
|
93
|
+
const { get, url, html, platformHost } = input;
|
|
94
|
+
const origin = new URL(url).origin;
|
|
95
|
+
const roots = input.roots?.length
|
|
96
|
+
? input.roots
|
|
97
|
+
: deriveRoots(input.requestedUrl ?? url, platformHost);
|
|
98
|
+
const out = [];
|
|
99
|
+
if (want('canonical-both-addresses')) {
|
|
100
|
+
const sets = [...(input.addressSets ?? [])];
|
|
101
|
+
if (sets.length === 0) {
|
|
102
|
+
// The address the user GAVE: a platform copy that redirects (the legal
|
|
103
|
+
// page went `/p/<org>/legal/<doc>` → `/legal/<org>/<doc>`) has lost the
|
|
104
|
+
// org/space shape by the time it lands.
|
|
105
|
+
const asked = input.requestedUrl ?? url;
|
|
106
|
+
const found = await discoverPrimary(get, asked, platformHost, input.platformApi);
|
|
107
|
+
if (found)
|
|
108
|
+
sets.push(found);
|
|
109
|
+
}
|
|
110
|
+
out.push(await (0, address_checks_1.canonicalBothAddresses)(get, sets));
|
|
111
|
+
}
|
|
112
|
+
// One crawl feeds three checks; it is skipped only when none of them is asked for.
|
|
113
|
+
let pages = [];
|
|
114
|
+
if (want('sitemap-crawl') || want('cache-consistency') || want('meta-description')) {
|
|
115
|
+
const crawl = await (0, crawl_1.sitemapCrawl)(get, origin, input.robots, input.limit ?? exports.DEFAULT_SAMPLE_LIMIT);
|
|
116
|
+
pages = crawl.pages;
|
|
117
|
+
if (want('sitemap-crawl'))
|
|
118
|
+
out.push(crawl.check);
|
|
119
|
+
}
|
|
120
|
+
if (want('cache-consistency'))
|
|
121
|
+
out.push((0, crawl_1.cacheConsistency)(pages));
|
|
122
|
+
if (want('root-redirect'))
|
|
123
|
+
out.push(await (0, page_checks_1.rootRedirect)(get, roots));
|
|
124
|
+
if (want('meta-description')) {
|
|
125
|
+
// The page that was asked about is judged too, even when no sitemap lists it.
|
|
126
|
+
const self = html
|
|
127
|
+
? [{ ok: true, url, status: 200, headers: new Headers(), text: html }]
|
|
128
|
+
: [];
|
|
129
|
+
const listed = pages.some((p) => p.url === url);
|
|
130
|
+
out.push((0, page_checks_1.metaDescription)(listed ? pages : [...self, ...pages]));
|
|
131
|
+
}
|
|
132
|
+
if (want('llms-txt'))
|
|
133
|
+
out.push(await (0, page_checks_1.llmsTxt)(get, origin, roots.filter((r) => r !== origin), platformHost));
|
|
134
|
+
if (want('favicon')) {
|
|
135
|
+
out.push(html
|
|
136
|
+
? await (0, page_checks_1.favicon)(get, url, html, roots.filter((r) => r !== origin))
|
|
137
|
+
: (0, probe_1.check)('favicon', 'The favicon resolves to an image', 'skip', 'No HTML to read.', ''));
|
|
138
|
+
}
|
|
139
|
+
if (want('consent-tag'))
|
|
140
|
+
out.push(html
|
|
141
|
+
? (0, address_checks_1.consentTag)(html)
|
|
142
|
+
: (0, probe_1.check)('consent-tag', 'The consent banner is told the page language', 'skip', 'No HTML to read.', ''));
|
|
143
|
+
if (want('white-label-leaks'))
|
|
144
|
+
out.push(await (0, address_checks_1.whiteLabelLeaks)(get, url, html, platformHost));
|
|
145
|
+
// SEO §1: the Disallow lines for each mount's sorted / filtered copies.
|
|
146
|
+
if (want('robots-params'))
|
|
147
|
+
out.push((0, robots_params_1.robotsParams)(roots, origin, input.robots));
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=extended.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extended.js","sourceRoot":"","sources":["../../src/doctor/extended.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Check } from '../conformance';
|
|
2
|
+
import { type Get, type Probe } from './probe';
|
|
3
|
+
/**
|
|
4
|
+
* `root-redirect`: a space's root either renders or moves permanently, cheaply.
|
|
5
|
+
*
|
|
6
|
+
* `GET /docs` answered `307` with a 66 KB HTML body and a canonical naming
|
|
7
|
+
* `/docs` (FMU-B06). A 307 says "this is temporary, keep asking me", so a
|
|
8
|
+
* crawler keeps the root as the indexed URL and never settles on the page it
|
|
9
|
+
* lands on; the body is bytes every visitor downloads and throws away. A 301
|
|
10
|
+
* or 308 with an empty body, or the front page rendered in place, are both fine.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rootRedirect(get: Get, roots: string[]): Promise<Check>;
|
|
13
|
+
/**
|
|
14
|
+
* `meta-description`: every page says what it is, in words somebody wrote.
|
|
15
|
+
*
|
|
16
|
+
* Checked across the crawl sample, not just the page given, because the
|
|
17
|
+
* preset fallbacks live on the pages nobody opens while testing — a space
|
|
18
|
+
* index, a blog home, a generated reference landing (FMU-B08, FMU-B09).
|
|
19
|
+
*/
|
|
20
|
+
export declare function metaDescription(pages: Probe[]): Check;
|
|
21
|
+
/**
|
|
22
|
+
* `favicon`: the icons the page and the host advertise are images.
|
|
23
|
+
*
|
|
24
|
+
* `GET /docs/favicon.ico` on a customer's domain answered a 404 with a 104 KB
|
|
25
|
+
* HTML page (FMU-B17). Browsers, feed readers and link unfurlers ask for these
|
|
26
|
+
* addresses on their own; a 100 KB error page per ask is load, and a missing
|
|
27
|
+
* icon is a blank tab. An asset-shaped path that is not there should be a tiny
|
|
28
|
+
* 404, not the site's whole not-found page.
|
|
29
|
+
*/
|
|
30
|
+
export declare function favicon(get: Get, pageUrl: string, html: string, roots: string[]): Promise<Check>;
|
|
31
|
+
/** One llms.txt judged against the llmstxt.org shape. */
|
|
32
|
+
export declare function llmsProblems(text: string, url: string, platformHost?: string): string[];
|
|
33
|
+
/**
|
|
34
|
+
* `llms-txt`: the file an AI agent reads first exists and is shaped for one.
|
|
35
|
+
*
|
|
36
|
+
* The space's llms.txt opened with "Published documentation for API Docs.",
|
|
37
|
+
* listed pages in recent-edit order with no descriptions, and titled a clash
|
|
38
|
+
* "Authentication (ref-authentication)" (FMU-B16). An agent picks pages from
|
|
39
|
+
* this list by their descriptions; without them it fetches at random.
|
|
40
|
+
*/
|
|
41
|
+
export declare function llmsTxt(get: Get, origin: string, roots: string[], platformHost?: string): Promise<Check>;
|
|
42
|
+
//# sourceMappingURL=page-checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-checks.d.ts","sourceRoot":"","sources":["../../src/doctor/page-checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAUL,KAAK,GAAG,EACR,KAAK,KAAK,EACX,MAAM,SAAS,CAAC;AAKjB;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CA4C5E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAuBrD;AAID;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,KAAK,CAAC,CAyChB;AAKD,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BvF;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,KAAK,CAAC,CA8BhB"}
|