@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,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rootRedirect = rootRedirect;
|
|
4
|
+
exports.metaDescription = metaDescription;
|
|
5
|
+
exports.favicon = favicon;
|
|
6
|
+
exports.llmsProblems = llmsProblems;
|
|
7
|
+
exports.llmsTxt = llmsTxt;
|
|
8
|
+
const boilerplate_1 = require("./boilerplate");
|
|
9
|
+
const probe_1 = require("./probe");
|
|
10
|
+
/** A redirect needs a Location and nothing else; more than this is a page nobody sees. */
|
|
11
|
+
const REDIRECT_BODY_LIMIT = 1024;
|
|
12
|
+
/**
|
|
13
|
+
* `root-redirect`: a space's root either renders or moves permanently, cheaply.
|
|
14
|
+
*
|
|
15
|
+
* `GET /docs` answered `307` with a 66 KB HTML body and a canonical naming
|
|
16
|
+
* `/docs` (FMU-B06). A 307 says "this is temporary, keep asking me", so a
|
|
17
|
+
* crawler keeps the root as the indexed URL and never settles on the page it
|
|
18
|
+
* lands on; the body is bytes every visitor downloads and throws away. A 301
|
|
19
|
+
* or 308 with an empty body, or the front page rendered in place, are both fine.
|
|
20
|
+
*/
|
|
21
|
+
async function rootRedirect(get, roots) {
|
|
22
|
+
const title = 'Space roots render, or redirect permanently with no body';
|
|
23
|
+
const matters = 'A temporary (302/307) redirect keeps the root as the indexed address and the destination as a guest; a heavy redirect body is downloaded by every visitor and shown to none.';
|
|
24
|
+
const bad = [];
|
|
25
|
+
const good = [];
|
|
26
|
+
for (const root of [...new Set(roots)]) {
|
|
27
|
+
const p = await get(root, { accept: 'text/html' });
|
|
28
|
+
if (!p.ok) {
|
|
29
|
+
bad.push(`${root} → ${p.error}`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (p.status === 200) {
|
|
33
|
+
good.push(`${root} renders`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (p.status < 300 || p.status >= 400) {
|
|
37
|
+
bad.push(`${root} → ${p.status}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const to = p.headers.get('location') ?? '';
|
|
41
|
+
const bytes = Buffer.byteLength(p.text);
|
|
42
|
+
const problems = [];
|
|
43
|
+
if (p.status !== 301 && p.status !== 308)
|
|
44
|
+
problems.push(`${p.status} is temporary`);
|
|
45
|
+
if (bytes > REDIRECT_BODY_LIMIT)
|
|
46
|
+
problems.push(`${bytes}-byte body`);
|
|
47
|
+
if (to) {
|
|
48
|
+
const target = new URL(to, root).toString();
|
|
49
|
+
const landed = await get(target, { accept: 'text/html' });
|
|
50
|
+
const canonical = landed.ok ? (0, probe_1.canonicalOf)(landed.text, target) : undefined;
|
|
51
|
+
if (canonical && !(0, probe_1.sameUrl)(canonical, target))
|
|
52
|
+
problems.push(`its target ${target} names canonical ${canonical}`);
|
|
53
|
+
}
|
|
54
|
+
if (problems.length)
|
|
55
|
+
bad.push(`${root} → ${p.status} ${to}: ${problems.join(', ')}`);
|
|
56
|
+
else
|
|
57
|
+
good.push(`${root} → ${p.status} ${to}`);
|
|
58
|
+
}
|
|
59
|
+
if (bad.length === 0 && good.length === 0)
|
|
60
|
+
return (0, probe_1.check)('root-redirect', title, 'skip', 'No space root to check.', '');
|
|
61
|
+
return (0, probe_1.check)('root-redirect', title, bad.length ? 'fail' : 'pass', bad.length ? (0, probe_1.some)(bad) : (0, probe_1.some)(good), bad.length ? matters : '');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `meta-description`: every page says what it is, in words somebody wrote.
|
|
65
|
+
*
|
|
66
|
+
* Checked across the crawl sample, not just the page given, because the
|
|
67
|
+
* preset fallbacks live on the pages nobody opens while testing — a space
|
|
68
|
+
* index, a blog home, a generated reference landing (FMU-B08, FMU-B09).
|
|
69
|
+
*/
|
|
70
|
+
function metaDescription(pages) {
|
|
71
|
+
const title = 'Pages carry a real meta and og:description';
|
|
72
|
+
const html = pages.filter((p) => p.ok && p.status === 200);
|
|
73
|
+
if (html.length === 0)
|
|
74
|
+
return (0, probe_1.check)('meta-description', title, 'skip', 'No page answered 200 to read.', '');
|
|
75
|
+
const bad = [];
|
|
76
|
+
for (const p of html) {
|
|
77
|
+
const meta = (0, boilerplate_1.descriptionProblem)((0, probe_1.metaOf)(p.text, 'description'));
|
|
78
|
+
const og = (0, boilerplate_1.descriptionProblem)((0, probe_1.metaOf)(p.text, 'og:description'));
|
|
79
|
+
if (meta)
|
|
80
|
+
bad.push(`${p.url}: description ${meta}`);
|
|
81
|
+
else if (og)
|
|
82
|
+
bad.push(`${p.url}: og:description ${og}`);
|
|
83
|
+
}
|
|
84
|
+
return (0, probe_1.check)('meta-description', title, bad.length ? 'fail' : 'pass', bad.length
|
|
85
|
+
? `${bad.length} of ${html.length}: ${(0, probe_1.some)(bad)}`
|
|
86
|
+
: `${html.length} page(s) described.`, bad.length
|
|
87
|
+
? 'The description is what a search result and every link preview show. A missing one is chosen by the crawler; a template sentence tells the reader nothing about this page.'
|
|
88
|
+
: '');
|
|
89
|
+
}
|
|
90
|
+
const IMAGE_TYPE = /^image\/|^application\/octet-stream/i;
|
|
91
|
+
/**
|
|
92
|
+
* `favicon`: the icons the page and the host advertise are images.
|
|
93
|
+
*
|
|
94
|
+
* `GET /docs/favicon.ico` on a customer's domain answered a 404 with a 104 KB
|
|
95
|
+
* HTML page (FMU-B17). Browsers, feed readers and link unfurlers ask for these
|
|
96
|
+
* addresses on their own; a 100 KB error page per ask is load, and a missing
|
|
97
|
+
* icon is a blank tab. An asset-shaped path that is not there should be a tiny
|
|
98
|
+
* 404, not the site's whole not-found page.
|
|
99
|
+
*/
|
|
100
|
+
async function favicon(get, pageUrl, html, roots) {
|
|
101
|
+
const title = 'The favicon resolves to an image';
|
|
102
|
+
const origin = new URL(pageUrl).origin;
|
|
103
|
+
const declared = (0, probe_1.tags)(html, 'link')
|
|
104
|
+
.filter((t) => /\bicon\b/i.test((0, probe_1.attr)(t, 'rel') ?? ''))
|
|
105
|
+
.map((t) => (0, probe_1.attr)(t, 'href'))
|
|
106
|
+
.filter((h) => !!h)
|
|
107
|
+
.map((h) => new URL(h, pageUrl).toString());
|
|
108
|
+
const required = [...new Set([`${origin}/favicon.ico`, ...declared])];
|
|
109
|
+
// Under a mount the host's own favicon is the customer's, but the mount's
|
|
110
|
+
// asset paths are ours: they must at least not answer with a page.
|
|
111
|
+
const mounted = [...new Set(roots.map((r) => `${r.replace(/\/+$/, '')}/favicon.ico`))].filter((u) => !required.includes(u));
|
|
112
|
+
const bad = [];
|
|
113
|
+
for (const url of required) {
|
|
114
|
+
const p = await get(url, { accept: 'image/*' });
|
|
115
|
+
const type = p.ok ? (p.headers.get('content-type') ?? '') : '';
|
|
116
|
+
if (!p.ok || p.status !== 200 || !IMAGE_TYPE.test(type))
|
|
117
|
+
bad.push(`${url} → ${(0, probe_1.describeProbe)(p)}${type ? ` ${type}` : ''}${p.ok ? ` (${Buffer.byteLength(p.text)} bytes)` : ''}`);
|
|
118
|
+
}
|
|
119
|
+
for (const url of mounted) {
|
|
120
|
+
const p = await get(url, { accept: 'image/*' });
|
|
121
|
+
if (!p.ok)
|
|
122
|
+
continue;
|
|
123
|
+
const type = p.headers.get('content-type') ?? '';
|
|
124
|
+
const bytes = Buffer.byteLength(p.text);
|
|
125
|
+
if (p.status === 200 && IMAGE_TYPE.test(type))
|
|
126
|
+
continue;
|
|
127
|
+
if (/html/i.test(type) && bytes > 2048)
|
|
128
|
+
bad.push(`${url} → ${p.status} with a ${bytes}-byte HTML page`);
|
|
129
|
+
}
|
|
130
|
+
return (0, probe_1.check)('favicon', title, bad.length ? 'fail' : 'pass', bad.length ? (0, probe_1.some)(bad) : `${required.length + mounted.length} icon address(es) answered.`, bad.length
|
|
131
|
+
? 'Browsers and link previews fetch the icon on their own; an HTML error page in its place is a blank tab and wasted bytes on every request.'
|
|
132
|
+
: '');
|
|
133
|
+
}
|
|
134
|
+
/** `Title (some-slug)`: the generator disambiguated two titles with a slug a reader never sees. */
|
|
135
|
+
const SLUG_LEAK = /\[[^\]]*\(([a-z0-9]+(?:-[a-z0-9]+)+)\)\]/;
|
|
136
|
+
/** One llms.txt judged against the llmstxt.org shape. */
|
|
137
|
+
function llmsProblems(text, url, platformHost) {
|
|
138
|
+
const lines = text.split(/\r?\n/);
|
|
139
|
+
const first = lines.find((l) => l.trim());
|
|
140
|
+
const out = [];
|
|
141
|
+
if (!first || !/^#\s+\S/.test(first))
|
|
142
|
+
out.push('does not open with an H1 title');
|
|
143
|
+
const quote = lines.find((l) => /^>\s*/.test(l));
|
|
144
|
+
const summary = quote?.replace(/^>\s*/, '').trim();
|
|
145
|
+
if (summary && (0, boilerplate_1.isPresetDescription)(summary))
|
|
146
|
+
out.push(`summary is preset text "${summary}"`);
|
|
147
|
+
const links = [...text.matchAll(/^\s*[-*]\s*\[([^\]]*)\]\(([^)\s]+)\)(.*)$/gm)];
|
|
148
|
+
if (links.length === 0)
|
|
149
|
+
out.push('lists no links');
|
|
150
|
+
// Links elsewhere are fine (a GitHub repo, a status page). Links back to
|
|
151
|
+
// the PLATFORM from a file on the customer's own domain are the leak: the
|
|
152
|
+
// agent is sent to a host the customer did not publish on (FMU-B15/B16).
|
|
153
|
+
const host = (0, probe_1.hostOf)(url);
|
|
154
|
+
const platform = platformHost?.toLowerCase();
|
|
155
|
+
const foreign = platform && host !== platform
|
|
156
|
+
? links
|
|
157
|
+
.map((m) => m[2])
|
|
158
|
+
.filter((href) => /^https?:/i.test(href) && (0, probe_1.hostOf)(href)?.replace(/^www\./, '') === platform)
|
|
159
|
+
: [];
|
|
160
|
+
if (foreign.length)
|
|
161
|
+
out.push(`links to the platform host: ${(0, probe_1.some)(foreign, 3)}`);
|
|
162
|
+
const leaks = links.filter((m) => SLUG_LEAK.test(`[${m[1]}]`)).map((m) => m[1]);
|
|
163
|
+
if (leaks.length)
|
|
164
|
+
out.push(`titles carry slugs: ${(0, probe_1.some)(leaks, 3)}`);
|
|
165
|
+
const sections = lines.filter((l) => /^##\s+\S/.test(l)).length;
|
|
166
|
+
const described = links.filter((m) => /^\s*:\s*\S/.test(m[3] ?? '')).length;
|
|
167
|
+
if (links.length > 0 && sections === 0 && described === 0)
|
|
168
|
+
out.push(`${links.length} links in one flat list with no sections and no descriptions`);
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* `llms-txt`: the file an AI agent reads first exists and is shaped for one.
|
|
173
|
+
*
|
|
174
|
+
* The space's llms.txt opened with "Published documentation for API Docs.",
|
|
175
|
+
* listed pages in recent-edit order with no descriptions, and titled a clash
|
|
176
|
+
* "Authentication (ref-authentication)" (FMU-B16). An agent picks pages from
|
|
177
|
+
* this list by their descriptions; without them it fetches at random.
|
|
178
|
+
*/
|
|
179
|
+
async function llmsTxt(get, origin, roots, platformHost) {
|
|
180
|
+
const title = 'llms.txt is present and follows llmstxt.org';
|
|
181
|
+
const candidates = [
|
|
182
|
+
...new Set([`${origin}/llms.txt`, ...roots.map((r) => `${r.replace(/\/+$/, '')}/llms.txt`)]),
|
|
183
|
+
];
|
|
184
|
+
const found = [];
|
|
185
|
+
const bad = [];
|
|
186
|
+
for (const url of candidates) {
|
|
187
|
+
const p = await get(url, { accept: 'text/plain,text/markdown' });
|
|
188
|
+
if (!p.ok || p.status !== 200)
|
|
189
|
+
continue;
|
|
190
|
+
found.push(url);
|
|
191
|
+
for (const problem of llmsProblems(p.text, url, platformHost))
|
|
192
|
+
bad.push(`${url} ${problem}`);
|
|
193
|
+
}
|
|
194
|
+
if (found.length === 0)
|
|
195
|
+
return (0, probe_1.check)('llms-txt', title, 'fail', `None of ${candidates.join(', ')} answered 200.`, 'Agents look for /llms.txt before anything else; without it they crawl HTML and guess.');
|
|
196
|
+
return (0, probe_1.check)('llms-txt', title, bad.length ? 'fail' : 'pass', bad.length ? (0, probe_1.some)(bad) : `${found.join(', ')}: well formed.`, bad.length
|
|
197
|
+
? 'An agent chooses which pages to read from this list; links with no descriptions, or back to the platform’s host, send it to the wrong ones.'
|
|
198
|
+
: '');
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=page-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-checks.js","sourceRoot":"","sources":["../../src/doctor/page-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Check } from '../conformance';
|
|
2
|
+
/**
|
|
3
|
+
* The HTTP and HTML plumbing every doctor check shares.
|
|
4
|
+
*
|
|
5
|
+
* One `get`, so every request the doctor makes has the same three properties:
|
|
6
|
+
* it never follows a redirect on its own (a redirect is usually the thing being
|
|
7
|
+
* checked, and `fetch`'s default hides both the status and the body), it gives
|
|
8
|
+
* up after a fixed time (one hung address must not hang a CI job), and it never
|
|
9
|
+
* throws (a refused connection is a finding, not a crash).
|
|
10
|
+
*/
|
|
11
|
+
export type Probe = {
|
|
12
|
+
ok: true;
|
|
13
|
+
url: string;
|
|
14
|
+
status: number;
|
|
15
|
+
headers: Headers;
|
|
16
|
+
text: string;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
url: string;
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
export interface ProbeOptions {
|
|
23
|
+
fetchImpl: typeof fetch;
|
|
24
|
+
/** Per-request budget. A crawl of a hundred pages must not wait on one forever. */
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEFAULT_TIMEOUT_MS = 15000;
|
|
28
|
+
export declare function makeGet(opts: ProbeOptions): (url: string, headers?: Record<string, string>) => Promise<Probe>;
|
|
29
|
+
export type Get = ReturnType<typeof makeGet>;
|
|
30
|
+
/**
|
|
31
|
+
* Run `fn` over `items`, at most `limit` at a time, in input order.
|
|
32
|
+
*
|
|
33
|
+
* Bounded on purpose: the doctor runs from a laptop or a CI runner against a
|
|
34
|
+
* customer's production site, and a hundred simultaneous requests is how a
|
|
35
|
+
* health check becomes the incident it was meant to catch.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pool<T, R>(items: T[], limit: number, fn: (t: T) => Promise<R>): Promise<R[]>;
|
|
38
|
+
/** Follow redirects by hand (bounded), keeping every hop — the hops are what a check judges. */
|
|
39
|
+
export declare function follow(get: Get, url: string, headers?: Record<string, string>, maxHops?: number): Promise<{
|
|
40
|
+
hops: Probe[];
|
|
41
|
+
final: Probe;
|
|
42
|
+
}>;
|
|
43
|
+
/** One attribute's value from one tag's source, quoted or not. */
|
|
44
|
+
declare function attrOf(tag: string, name: string): string | undefined;
|
|
45
|
+
export declare function tags(html: string, name: string): string[];
|
|
46
|
+
export declare const attr: typeof attrOf;
|
|
47
|
+
/** `<link rel="canonical">`, absolute against `base`, or undefined. */
|
|
48
|
+
export declare function canonicalOf(html: string, base: string): string | undefined;
|
|
49
|
+
/** `<meta name|property=X content=…>`. */
|
|
50
|
+
export declare function metaOf(html: string, key: string): string | undefined;
|
|
51
|
+
/** The document's declared language (`<html lang>`), lower-cased, or undefined. */
|
|
52
|
+
export declare function htmlLang(html: string): string | undefined;
|
|
53
|
+
export declare function decodeEntities(s: string): string;
|
|
54
|
+
export declare function hostOf(href: string): string | undefined;
|
|
55
|
+
/** Compare two URLs the way a search engine would: no fragment, no trailing slash. */
|
|
56
|
+
export declare function sameUrl(a: string | undefined, b: string | undefined): boolean;
|
|
57
|
+
export declare function describeProbe(p: Probe): string;
|
|
58
|
+
/** A check under construction: the shape `runDoctor` reports. */
|
|
59
|
+
export declare function check(id: string, title: string, state: Check['state'], detail: string, matters?: string): Check;
|
|
60
|
+
/** At most `n` items of a list in a detail line, and how many were left out. */
|
|
61
|
+
export declare function some(list: string[], n?: number): string;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/doctor/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.attr = exports.DEFAULT_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.makeGet = makeGet;
|
|
5
|
+
exports.pool = pool;
|
|
6
|
+
exports.follow = follow;
|
|
7
|
+
exports.tags = tags;
|
|
8
|
+
exports.canonicalOf = canonicalOf;
|
|
9
|
+
exports.metaOf = metaOf;
|
|
10
|
+
exports.htmlLang = htmlLang;
|
|
11
|
+
exports.decodeEntities = decodeEntities;
|
|
12
|
+
exports.hostOf = hostOf;
|
|
13
|
+
exports.sameUrl = sameUrl;
|
|
14
|
+
exports.describeProbe = describeProbe;
|
|
15
|
+
exports.check = check;
|
|
16
|
+
exports.some = some;
|
|
17
|
+
exports.DEFAULT_TIMEOUT_MS = 15_000;
|
|
18
|
+
function makeGet(opts) {
|
|
19
|
+
const timeoutMs = opts.timeoutMs ?? exports.DEFAULT_TIMEOUT_MS;
|
|
20
|
+
return async (url, headers = {}) => {
|
|
21
|
+
const ctl = typeof AbortController === 'function' ? new AbortController() : undefined;
|
|
22
|
+
const timer = ctl ? setTimeout(() => ctl.abort(), timeoutMs) : undefined;
|
|
23
|
+
try {
|
|
24
|
+
const res = await opts.fetchImpl(url, {
|
|
25
|
+
headers,
|
|
26
|
+
redirect: 'manual',
|
|
27
|
+
...(ctl ? { signal: ctl.signal } : {}),
|
|
28
|
+
});
|
|
29
|
+
const text = await res.text();
|
|
30
|
+
return { ok: true, url, status: res.status, headers: res.headers, text };
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
const aborted = e instanceof Error && e.name === 'AbortError';
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
url,
|
|
37
|
+
error: aborted
|
|
38
|
+
? `no answer within ${timeoutMs} ms`
|
|
39
|
+
: e instanceof Error
|
|
40
|
+
? e.message
|
|
41
|
+
: String(e),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (timer)
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Run `fn` over `items`, at most `limit` at a time, in input order.
|
|
52
|
+
*
|
|
53
|
+
* Bounded on purpose: the doctor runs from a laptop or a CI runner against a
|
|
54
|
+
* customer's production site, and a hundred simultaneous requests is how a
|
|
55
|
+
* health check becomes the incident it was meant to catch.
|
|
56
|
+
*/
|
|
57
|
+
async function pool(items, limit, fn) {
|
|
58
|
+
const out = new Array(items.length);
|
|
59
|
+
let next = 0;
|
|
60
|
+
const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => {
|
|
61
|
+
while (next < items.length) {
|
|
62
|
+
const i = next++;
|
|
63
|
+
out[i] = await fn(items[i]);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
await Promise.all(workers);
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
/** Follow redirects by hand (bounded), keeping every hop — the hops are what a check judges. */
|
|
70
|
+
async function follow(get, url, headers = {}, maxHops = 5) {
|
|
71
|
+
const hops = [];
|
|
72
|
+
let at = url;
|
|
73
|
+
for (let i = 0; i <= maxHops; i++) {
|
|
74
|
+
const p = await get(at, headers);
|
|
75
|
+
hops.push(p);
|
|
76
|
+
if (!p.ok || p.status < 300 || p.status >= 400)
|
|
77
|
+
return { hops, final: p };
|
|
78
|
+
const loc = p.headers.get('location');
|
|
79
|
+
if (!loc)
|
|
80
|
+
return { hops, final: p };
|
|
81
|
+
at = new URL(loc, at).toString();
|
|
82
|
+
}
|
|
83
|
+
return { hops, final: hops[hops.length - 1] };
|
|
84
|
+
}
|
|
85
|
+
/** One attribute's value from one tag's source, quoted or not. */
|
|
86
|
+
function attrOf(tag, name) {
|
|
87
|
+
const m = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, 'i').exec(tag);
|
|
88
|
+
return m ? (m[1] ?? m[2] ?? m[3]) : undefined;
|
|
89
|
+
}
|
|
90
|
+
function tags(html, name) {
|
|
91
|
+
return html.match(new RegExp(`<${name}\\b[^>]*>`, 'gi')) ?? [];
|
|
92
|
+
}
|
|
93
|
+
exports.attr = attrOf;
|
|
94
|
+
/** `<link rel="canonical">`, absolute against `base`, or undefined. */
|
|
95
|
+
function canonicalOf(html, base) {
|
|
96
|
+
for (const t of tags(html, 'link')) {
|
|
97
|
+
const rel = (attrOf(t, 'rel') ?? '').toLowerCase().split(/\s+/);
|
|
98
|
+
if (!rel.includes('canonical'))
|
|
99
|
+
continue;
|
|
100
|
+
const href = attrOf(t, 'href');
|
|
101
|
+
if (!href)
|
|
102
|
+
continue;
|
|
103
|
+
try {
|
|
104
|
+
return new URL(decodeEntities(href), base).toString();
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return href;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
/** `<meta name|property=X content=…>`. */
|
|
113
|
+
function metaOf(html, key) {
|
|
114
|
+
for (const t of tags(html, 'meta')) {
|
|
115
|
+
const k = (attrOf(t, 'name') ?? attrOf(t, 'property') ?? '').toLowerCase();
|
|
116
|
+
if (k === key.toLowerCase())
|
|
117
|
+
return decodeEntities(attrOf(t, 'content') ?? '');
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
/** The document's declared language (`<html lang>`), lower-cased, or undefined. */
|
|
122
|
+
function htmlLang(html) {
|
|
123
|
+
const t = tags(html, 'html')[0];
|
|
124
|
+
return t ? attrOf(t, 'lang')?.toLowerCase() : undefined;
|
|
125
|
+
}
|
|
126
|
+
function decodeEntities(s) {
|
|
127
|
+
return s
|
|
128
|
+
.replace(/&/g, '&')
|
|
129
|
+
.replace(/"/g, '"')
|
|
130
|
+
.replace(/'|'/g, "'")
|
|
131
|
+
.replace(/</g, '<')
|
|
132
|
+
.replace(/>/g, '>');
|
|
133
|
+
}
|
|
134
|
+
function hostOf(href) {
|
|
135
|
+
try {
|
|
136
|
+
return new URL(href).host.toLowerCase();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Compare two URLs the way a search engine would: no fragment, no trailing slash. */
|
|
143
|
+
function sameUrl(a, b) {
|
|
144
|
+
if (!a || !b)
|
|
145
|
+
return false;
|
|
146
|
+
const norm = (u) => {
|
|
147
|
+
try {
|
|
148
|
+
const x = new URL(u);
|
|
149
|
+
x.hash = '';
|
|
150
|
+
return `${x.protocol}//${x.host.toLowerCase()}${x.pathname.replace(/\/+$/, '') || '/'}${x.search}`;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return u;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return norm(a) === norm(b);
|
|
157
|
+
}
|
|
158
|
+
function describeProbe(p) {
|
|
159
|
+
return p.ok ? `${p.status}` : p.error;
|
|
160
|
+
}
|
|
161
|
+
/** A check under construction: the shape `runDoctor` reports. */
|
|
162
|
+
function check(id, title, state, detail, matters = '') {
|
|
163
|
+
return { id, title, state, detail, matters };
|
|
164
|
+
}
|
|
165
|
+
/** At most `n` items of a list in a detail line, and how many were left out. */
|
|
166
|
+
function some(list, n = 5) {
|
|
167
|
+
const shown = list.slice(0, n).join('; ');
|
|
168
|
+
return list.length > n ? `${shown}; …and ${list.length - n} more` : shown;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/doctor/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Check } from '../conformance';
|
|
2
|
+
import type { Http } from '../api/http';
|
|
3
|
+
export declare const READER_AUTH_ATLAS_CHECK = "reader-auth-atlas";
|
|
4
|
+
/**
|
|
5
|
+
* `reader-auth-atlas` (FMU-X01): does this organization sign its readers in
|
|
6
|
+
* the recommended way?
|
|
7
|
+
*
|
|
8
|
+
* The rule is the API's, not the CLI's: `GET /orgs/{org}/reader-auth`
|
|
9
|
+
* answers `recommendations`, computed from the org's own configuration (an
|
|
10
|
+
* Atlas issuer on an `external_jwt` provider, a linked Atlas instance), and
|
|
11
|
+
* the manifest plan answers the same list. This prints what it says.
|
|
12
|
+
*
|
|
13
|
+
* Advice, like `robots-params`: a recommendation is a SKIP with the steps,
|
|
14
|
+
* never a FAIL, because signing readers in from your own app is a supported
|
|
15
|
+
* setup and a scheduled doctor must not fail CI over it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readerAuthAtlasCheck(http: Http, org: string): Promise<Check>;
|
|
18
|
+
//# sourceMappingURL=reader-auth-atlas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-auth-atlas.d.ts","sourceRoot":"","sources":["../../src/doctor/reader-auth-atlas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAe3D;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAsClF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.READER_AUTH_ATLAS_CHECK = void 0;
|
|
4
|
+
exports.readerAuthAtlasCheck = readerAuthAtlasCheck;
|
|
5
|
+
const probe_1 = require("./probe");
|
|
6
|
+
exports.READER_AUTH_ATLAS_CHECK = 'reader-auth-atlas';
|
|
7
|
+
const TITLE = 'Readers sign in with the Atlas reader provider when the organization uses Atlas';
|
|
8
|
+
const MATTERS = 'A hand-rolled external_jwt route is code you maintain that the Atlas provider does for you: Forumulate verifies Atlas’s own reader token and keys members on (iss, sub).';
|
|
9
|
+
/**
|
|
10
|
+
* `reader-auth-atlas` (FMU-X01): does this organization sign its readers in
|
|
11
|
+
* the recommended way?
|
|
12
|
+
*
|
|
13
|
+
* The rule is the API's, not the CLI's: `GET /orgs/{org}/reader-auth`
|
|
14
|
+
* answers `recommendations`, computed from the org's own configuration (an
|
|
15
|
+
* Atlas issuer on an `external_jwt` provider, a linked Atlas instance), and
|
|
16
|
+
* the manifest plan answers the same list. This prints what it says.
|
|
17
|
+
*
|
|
18
|
+
* Advice, like `robots-params`: a recommendation is a SKIP with the steps,
|
|
19
|
+
* never a FAIL, because signing readers in from your own app is a supported
|
|
20
|
+
* setup and a scheduled doctor must not fail CI over it.
|
|
21
|
+
*/
|
|
22
|
+
async function readerAuthAtlasCheck(http, org) {
|
|
23
|
+
let body;
|
|
24
|
+
try {
|
|
25
|
+
body = (await http.send({ method: 'GET', path: `/orgs/${encodeURIComponent(org)}/reader-auth` }))
|
|
26
|
+
.data;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
const err = e;
|
|
30
|
+
return (0, probe_1.check)(exports.READER_AUTH_ATLAS_CHECK, TITLE, 'skip', `Could not read the organization's reader sign-in (${err.status ?? 'error'}): it needs a token with org.manage_settings.`, '');
|
|
31
|
+
}
|
|
32
|
+
const rec = (body.recommendations ?? []).find((r) => r.code === 'reader_auth.use_atlas');
|
|
33
|
+
if (!rec) {
|
|
34
|
+
const methods = (body.providers ?? []).map((p) => p.method);
|
|
35
|
+
return (0, probe_1.check)(exports.READER_AUTH_ATLAS_CHECK, TITLE, 'pass', methods.includes('atlas_byo')
|
|
36
|
+
? 'atlas_byo is configured.'
|
|
37
|
+
: `Nothing to recommend${methods.length ? ` (${methods.join(', ')})` : ''}.`, '');
|
|
38
|
+
}
|
|
39
|
+
const lines = [
|
|
40
|
+
`Recommended — ${rec.message}`,
|
|
41
|
+
rec.relink
|
|
42
|
+
? ` 1. Relink the members: POST /orgs/${org}/members/relink ${JSON.stringify({ from: rec.relink.from, to: rec.relink.to })} (dry run first; ?apply=true writes)`
|
|
43
|
+
: null,
|
|
44
|
+
` ${rec.relink ? '2' : '1'}. Switch: PATCH /orgs/${org}/reader-auth ${JSON.stringify(rec.patch)}`,
|
|
45
|
+
` or in a manifest (version 2): ${JSON.stringify(rec.manifest)}`,
|
|
46
|
+
rec.docs ? ` See ${rec.docs}` : null,
|
|
47
|
+
].filter((l) => l !== null);
|
|
48
|
+
return (0, probe_1.check)(exports.READER_AUTH_ATLAS_CHECK, TITLE, 'skip', lines.join('\n'), MATTERS);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=reader-auth-atlas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-auth-atlas.js","sourceRoot":"","sources":["../../src/doctor/reader-auth-atlas.ts"],"names":[],"mappings":";;;AAgCA,oDAsCC;AApED,mCAAgC;AAEnB,QAAA,uBAAuB,GAAG,mBAAmB,CAAC;AAE3D,MAAM,KAAK,GAAG,iFAAiF,CAAC;AAChG,MAAM,OAAO,GACX,0KAA0K,CAAC;AAW7K;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,oBAAoB,CAAC,IAAU,EAAE,GAAW;IAChE,IAAI,IAA8E,CAAC;IACnF,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,kBAAkB,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;aAC9F,IAAmB,CAAC;IACzB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAA0C,CAAC;QACvD,OAAO,IAAA,aAAK,EACV,+BAAuB,EACvB,KAAK,EACL,MAAM,EACN,qDAAqD,GAAG,CAAC,MAAM,IAAI,OAAO,+CAA+C,EACzH,EAAE,CACH,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,CAAC,CAAC;IACzF,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC5D,OAAO,IAAA,aAAK,EACV,+BAAuB,EACvB,KAAK,EACL,MAAM,EACN,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC3B,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,uBAAuB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAC9E,EAAE,CACH,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG;QACZ,iBAAiB,GAAG,CAAC,OAAO,EAAE;QAC9B,GAAG,CAAC,MAAM;YACR,CAAC,CAAC,uCAAuC,GAAG,mBAAmB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,sCAAsC;YACjK,CAAC,CAAC,IAAI;QACR,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,yBAAyB,GAAG,gBAAgB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAClG,sCAAsC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;QACpE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;KACtC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACzC,OAAO,IAAA,aAAK,EAAC,+BAAuB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Check } from '../conformance';
|
|
2
|
+
/**
|
|
3
|
+
* `robots-params`: the `Disallow` lines that keep crawlers off the re-cut
|
|
4
|
+
* addresses of a mounted space's lists (SEO spec §1, item 5).
|
|
5
|
+
*
|
|
6
|
+
* A reader page offers its sort, order, scope, period, filter, solved and
|
|
7
|
+
* range choices as GET forms, which crawlers do not submit — but an address
|
|
8
|
+
* already in a crawler's queue, a backlink, or another site's
|
|
9
|
+
* `?sort=name&order=desc` link still reaches it. Those copies say `noindex`
|
|
10
|
+
* (or name the uncut page as canonical), and are still a crawl each. The host's
|
|
11
|
+
* robots.txt is the one place that can say "do not even fetch these", and for
|
|
12
|
+
* a space mounted on somebody else's domain (`acme.com/community`) it is THEIR
|
|
13
|
+
* file — we cannot write it. So the doctor prints the lines to paste.
|
|
14
|
+
*
|
|
15
|
+
* Advisory, never a failure: a site that wants those copies crawled is making
|
|
16
|
+
* a choice. Passes once every line for this host is already in its robots.txt.
|
|
17
|
+
* Judged by HTTP alone, so it is the same for any host the doctor is pointed at.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The query keys a reader list is re-cut by — the web reader's
|
|
21
|
+
* `VARIANT_PARAMS` (apps/web/src/lib/reader-variant-robots.ts). Never `page`
|
|
22
|
+
* (a page of a list is a document of its own) and never a search.
|
|
23
|
+
*/
|
|
24
|
+
export declare const VARIANT_PARAMS: readonly ["sort", "order", "scope", "period", "window", "filter", "solved", "range", "replies", "release"];
|
|
25
|
+
/**
|
|
26
|
+
* The lines for one mount. `*` and the `?` are robots wildcards (RFC 9309 §2.2.3
|
|
27
|
+
* — Google, Bing and every major crawler read them): `/community*?*sort=` is any
|
|
28
|
+
* address under the mount whose query names `sort`, first or not.
|
|
29
|
+
*/
|
|
30
|
+
export declare function disallowLines(mountPath: string): string[];
|
|
31
|
+
/** The roots grouped by the origin whose robots.txt would carry them. */
|
|
32
|
+
export declare function linesByOrigin(roots: readonly string[]): Map<string, string[]>;
|
|
33
|
+
export declare function robotsParams(roots: readonly string[],
|
|
34
|
+
/** The origin whose robots.txt was read. */
|
|
35
|
+
origin: string, robots: string | undefined): Check;
|
|
36
|
+
//# sourceMappingURL=robots-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots-params.d.ts","sourceRoot":"","sources":["../../src/doctor/robots-params.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,4GAWjB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAIzD;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAc7E;AAUD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,MAAM,EAAE;AACxB,4CAA4C;AAC5C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,KAAK,CAuBP"}
|