@forumulate/cli 0.9.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.
Files changed (224) hide show
  1. package/README.md +3 -3
  2. package/dist/api/args.d.ts.map +1 -1
  3. package/dist/api/args.js +1 -0
  4. package/dist/api/args.js.map +1 -1
  5. package/dist/api/command.d.ts +1 -1
  6. package/dist/api/command.d.ts.map +1 -1
  7. package/dist/api/command.js +86 -11
  8. package/dist/api/command.js.map +1 -1
  9. package/dist/api/http.d.ts +8 -0
  10. package/dist/api/http.d.ts.map +1 -1
  11. package/dist/api/http.js +3 -2
  12. package/dist/api/http.js.map +1 -1
  13. package/dist/api/live-spec.d.ts +48 -0
  14. package/dist/api/live-spec.d.ts.map +1 -0
  15. package/dist/api/live-spec.js +72 -0
  16. package/dist/api/live-spec.js.map +1 -0
  17. package/dist/api/operations.json +1 -1
  18. package/dist/api/request.d.ts.map +1 -1
  19. package/dist/api/request.js +25 -1
  20. package/dist/api/request.js.map +1 -1
  21. package/dist/api/send.d.ts +10 -1
  22. package/dist/api/send.d.ts.map +1 -1
  23. package/dist/api/send.js +14 -3
  24. package/dist/api/send.js.map +1 -1
  25. package/dist/api/slim.d.ts +14 -0
  26. package/dist/api/slim.d.ts.map +1 -0
  27. package/dist/api/slim.js +174 -0
  28. package/dist/api/slim.js.map +1 -0
  29. package/dist/cache-command.d.ts +12 -0
  30. package/dist/cache-command.d.ts.map +1 -0
  31. package/dist/cache-command.js +93 -0
  32. package/dist/cache-command.js.map +1 -0
  33. package/dist/check/command.d.ts +48 -0
  34. package/dist/check/command.d.ts.map +1 -0
  35. package/dist/check/command.js +256 -0
  36. package/dist/check/command.js.map +1 -0
  37. package/dist/check/external.d.ts +66 -0
  38. package/dist/check/external.d.ts.map +1 -0
  39. package/dist/check/external.js +177 -0
  40. package/dist/check/external.js.map +1 -0
  41. package/dist/check/markdown-links.d.ts +38 -0
  42. package/dist/check/markdown-links.d.ts.map +1 -0
  43. package/dist/check/markdown-links.js +166 -0
  44. package/dist/check/markdown-links.js.map +1 -0
  45. package/dist/cli.js +42 -7
  46. package/dist/cli.js.map +1 -1
  47. package/dist/commands/completion.d.ts.map +1 -1
  48. package/dist/commands/completion.js +1 -0
  49. package/dist/commands/completion.js.map +1 -1
  50. package/dist/commands/context.d.ts.map +1 -1
  51. package/dist/commands/context.js +6 -2
  52. package/dist/commands/context.js.map +1 -1
  53. package/dist/commands/groups-api.d.ts.map +1 -1
  54. package/dist/commands/groups-api.js +20 -2
  55. package/dist/commands/groups-api.js.map +1 -1
  56. package/dist/commands/groups-docs.d.ts.map +1 -1
  57. package/dist/commands/groups-docs.js +8 -2
  58. package/dist/commands/groups-docs.js.map +1 -1
  59. package/dist/commands/groups-jobs.d.ts +10 -0
  60. package/dist/commands/groups-jobs.d.ts.map +1 -0
  61. package/dist/commands/groups-jobs.js +89 -0
  62. package/dist/commands/groups-jobs.js.map +1 -0
  63. package/dist/commands/groups-org.d.ts.map +1 -1
  64. package/dist/commands/groups-org.js +59 -2
  65. package/dist/commands/groups-org.js.map +1 -1
  66. package/dist/commands/groups-site.d.ts.map +1 -1
  67. package/dist/commands/groups-site.js +33 -3
  68. package/dist/commands/groups-site.js.map +1 -1
  69. package/dist/commands/groups-spaces.d.ts.map +1 -1
  70. package/dist/commands/groups-spaces.js +11 -5
  71. package/dist/commands/groups-spaces.js.map +1 -1
  72. package/dist/commands/groups-types.d.ts.map +1 -1
  73. package/dist/commands/groups-types.js +74 -0
  74. package/dist/commands/groups-types.js.map +1 -1
  75. package/dist/commands/groups.d.ts.map +1 -1
  76. package/dist/commands/groups.js +2 -0
  77. package/dist/commands/groups.js.map +1 -1
  78. package/dist/commands/index.d.ts +9 -0
  79. package/dist/commands/index.d.ts.map +1 -1
  80. package/dist/commands/index.js +18 -3
  81. package/dist/commands/index.js.map +1 -1
  82. package/dist/commands/job-wait.d.ts +64 -0
  83. package/dist/commands/job-wait.d.ts.map +1 -0
  84. package/dist/commands/job-wait.js +91 -0
  85. package/dist/commands/job-wait.js.map +1 -0
  86. package/dist/commands/resource.d.ts +19 -0
  87. package/dist/commands/resource.d.ts.map +1 -1
  88. package/dist/commands/resource.js +65 -3
  89. package/dist/commands/resource.js.map +1 -1
  90. package/dist/content/library.d.ts +8 -0
  91. package/dist/content/library.d.ts.map +1 -1
  92. package/dist/content/library.js +10 -2
  93. package/dist/content/library.js.map +1 -1
  94. package/dist/credentials.d.ts +43 -4
  95. package/dist/credentials.d.ts.map +1 -1
  96. package/dist/credentials.js +67 -4
  97. package/dist/credentials.js.map +1 -1
  98. package/dist/doctor/address-checks.d.ts +56 -0
  99. package/dist/doctor/address-checks.d.ts.map +1 -0
  100. package/dist/doctor/address-checks.js +183 -0
  101. package/dist/doctor/address-checks.js.map +1 -0
  102. package/dist/doctor/boilerplate.d.ts +33 -0
  103. package/dist/doctor/boilerplate.d.ts.map +1 -0
  104. package/dist/doctor/boilerplate.js +78 -0
  105. package/dist/doctor/boilerplate.js.map +1 -0
  106. package/dist/doctor/command.d.ts +18 -0
  107. package/dist/doctor/command.d.ts.map +1 -0
  108. package/dist/doctor/command.js +234 -0
  109. package/dist/doctor/command.js.map +1 -0
  110. package/dist/doctor/crawl.d.ts +49 -0
  111. package/dist/doctor/crawl.d.ts.map +1 -0
  112. package/dist/doctor/crawl.js +210 -0
  113. package/dist/doctor/crawl.js.map +1 -0
  114. package/dist/doctor/extended.d.ts +52 -0
  115. package/dist/doctor/extended.d.ts.map +1 -0
  116. package/dist/doctor/extended.js +150 -0
  117. package/dist/doctor/extended.js.map +1 -0
  118. package/dist/doctor/page-checks.d.ts +42 -0
  119. package/dist/doctor/page-checks.d.ts.map +1 -0
  120. package/dist/doctor/page-checks.js +200 -0
  121. package/dist/doctor/page-checks.js.map +1 -0
  122. package/dist/doctor/probe.d.ts +63 -0
  123. package/dist/doctor/probe.d.ts.map +1 -0
  124. package/dist/doctor/probe.js +170 -0
  125. package/dist/doctor/probe.js.map +1 -0
  126. package/dist/doctor/reader-auth-atlas.d.ts +18 -0
  127. package/dist/doctor/reader-auth-atlas.d.ts.map +1 -0
  128. package/dist/doctor/reader-auth-atlas.js +50 -0
  129. package/dist/doctor/reader-auth-atlas.js.map +1 -0
  130. package/dist/doctor/robots-params.d.ts +36 -0
  131. package/dist/doctor/robots-params.d.ts.map +1 -0
  132. package/dist/doctor/robots-params.js +97 -0
  133. package/dist/doctor/robots-params.js.map +1 -0
  134. package/dist/doctor/site.d.ts +42 -0
  135. package/dist/doctor/site.d.ts.map +1 -0
  136. package/dist/doctor/site.js +82 -0
  137. package/dist/doctor/site.js.map +1 -0
  138. package/dist/doctor.d.ts +21 -0
  139. package/dist/doctor.d.ts.map +1 -1
  140. package/dist/doctor.js +94 -3
  141. package/dist/doctor.js.map +1 -1
  142. package/dist/exit-codes.d.ts +40 -0
  143. package/dist/exit-codes.d.ts.map +1 -0
  144. package/dist/exit-codes.js +66 -0
  145. package/dist/exit-codes.js.map +1 -0
  146. package/dist/help-commands.d.ts +13 -0
  147. package/dist/help-commands.d.ts.map +1 -0
  148. package/dist/help-commands.js +215 -0
  149. package/dist/help-commands.js.map +1 -0
  150. package/dist/help.d.ts +1 -1
  151. package/dist/help.d.ts.map +1 -1
  152. package/dist/help.js +57 -11
  153. package/dist/help.js.map +1 -1
  154. package/dist/io.d.ts +33 -0
  155. package/dist/io.d.ts.map +1 -0
  156. package/dist/io.js +81 -0
  157. package/dist/io.js.map +1 -0
  158. package/dist/preview-comment.d.ts +20 -0
  159. package/dist/preview-comment.d.ts.map +1 -0
  160. package/dist/preview-comment.js +88 -0
  161. package/dist/preview-comment.js.map +1 -0
  162. package/dist/publish/about.d.ts +23 -0
  163. package/dist/publish/about.d.ts.map +1 -0
  164. package/dist/publish/about.js +66 -0
  165. package/dist/publish/about.js.map +1 -0
  166. package/dist/publish/command.d.ts +20 -0
  167. package/dist/publish/command.d.ts.map +1 -1
  168. package/dist/publish/command.js +97 -4
  169. package/dist/publish/command.js.map +1 -1
  170. package/dist/publish/context.d.ts +8 -0
  171. package/dist/publish/context.d.ts.map +1 -1
  172. package/dist/publish/context.js.map +1 -1
  173. package/dist/publish/docs-apply.d.ts.map +1 -1
  174. package/dist/publish/docs-apply.js +13 -1
  175. package/dist/publish/docs-apply.js.map +1 -1
  176. package/dist/publish/extras.d.ts +10 -0
  177. package/dist/publish/extras.d.ts.map +1 -1
  178. package/dist/publish/extras.js +54 -2
  179. package/dist/publish/extras.js.map +1 -1
  180. package/dist/publish/manifest.d.ts.map +1 -1
  181. package/dist/publish/manifest.js +27 -2
  182. package/dist/publish/manifest.js.map +1 -1
  183. package/dist/publish/preview.d.ts +56 -0
  184. package/dist/publish/preview.d.ts.map +1 -0
  185. package/dist/publish/preview.js +133 -0
  186. package/dist/publish/preview.js.map +1 -0
  187. package/dist/publish/redirects.d.ts +13 -0
  188. package/dist/publish/redirects.d.ts.map +1 -0
  189. package/dist/publish/redirects.js +154 -0
  190. package/dist/publish/redirects.js.map +1 -0
  191. package/dist/publish/studio-edits.d.ts +25 -0
  192. package/dist/publish/studio-edits.d.ts.map +1 -0
  193. package/dist/publish/studio-edits.js +53 -0
  194. package/dist/publish/studio-edits.js.map +1 -0
  195. package/dist/publish/types.d.ts +22 -1
  196. package/dist/publish/types.d.ts.map +1 -1
  197. package/dist/publish-command.d.ts +55 -0
  198. package/dist/publish-command.d.ts.map +1 -0
  199. package/dist/publish-command.js +197 -0
  200. package/dist/publish-command.js.map +1 -0
  201. package/dist/run.d.ts +3 -10
  202. package/dist/run.d.ts.map +1 -1
  203. package/dist/run.js +122 -110
  204. package/dist/run.js.map +1 -1
  205. package/dist/translations/target.d.ts.map +1 -1
  206. package/dist/translations/target.js +2 -2
  207. package/dist/translations/target.js.map +1 -1
  208. package/dist/validate.d.ts +39 -0
  209. package/dist/validate.d.ts.map +1 -0
  210. package/dist/validate.js +166 -0
  211. package/dist/validate.js.map +1 -0
  212. package/dist/version.d.ts +8 -0
  213. package/dist/version.d.ts.map +1 -0
  214. package/dist/version.js +11 -0
  215. package/dist/version.js.map +1 -0
  216. package/dist/whoami.d.ts +41 -0
  217. package/dist/whoami.d.ts.map +1 -0
  218. package/dist/whoami.js +161 -0
  219. package/dist/whoami.js.map +1 -0
  220. package/package.json +2 -2
  221. package/dist/publish-dir.d.ts +0 -54
  222. package/dist/publish-dir.d.ts.map +0 -1
  223. package/dist/publish-dir.js +0 -119
  224. package/dist/publish-dir.js.map +0 -1
@@ -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"}
@@ -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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"}