@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
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.credentialsPath = credentialsPath;
4
4
  exports.readCredentials = readCredentials;
5
+ exports.resolveTokenSource = resolveTokenSource;
5
6
  exports.resolveToken = resolveToken;
7
+ exports.takeTokenFlag = takeTokenFlag;
6
8
  exports.storeToken = storeToken;
7
9
  exports.forgetToken = forgetToken;
8
10
  exports.normalizeBase = normalizeBase;
@@ -27,11 +29,72 @@ function readCredentials(env = process.env) {
27
29
  return { tokens: {} };
28
30
  }
29
31
  }
30
- /** The token for a base URL: the environment first, then what `login` stored. */
31
- function resolveToken(baseUrl, env = process.env) {
32
+ /**
33
+ * The token for a base URL, and where it was found: `--token` beats
34
+ * `FORUMULATE_TOKEN` beats `credentials.json`. An integrator with one PAT in
35
+ * the shell, another in the file and a third in CI could not tell which one a
36
+ * 401 was about; this is the precedence, said once.
37
+ */
38
+ function resolveTokenSource(baseUrl, env = process.env, flagToken) {
39
+ if (flagToken?.trim())
40
+ return { token: flagToken.trim(), source: 'flag' };
32
41
  if (env.FORUMULATE_TOKEN)
33
- return env.FORUMULATE_TOKEN;
34
- return readCredentials(env).tokens[normalizeBase(baseUrl)];
42
+ return { token: env.FORUMULATE_TOKEN, source: 'env' };
43
+ const stored = readCredentials(env).tokens[normalizeBase(baseUrl)];
44
+ if (stored)
45
+ return { token: stored, source: 'credentials', path: credentialsPath(env) };
46
+ return { source: 'none' };
47
+ }
48
+ /**
49
+ * The token for a base URL, by the same precedence as `resolveTokenSource`:
50
+ * `--token`, then the environment, then what `login` stored.
51
+ *
52
+ * This used to skip the flag. `cli.ts` read `--token` for the SDK client only,
53
+ * so `whoami --token x` used x while `items list --token x`, `api …`, `doctor
54
+ * --site` and `validate` (each building its own transport through here) sent
55
+ * `FORUMULATE_TOKEN` or the stored token instead: the documented precedence
56
+ * held for two commands (FMU-DOC01). One resolver, every caller.
57
+ */
58
+ function resolveToken(baseUrl, env = process.env, flagToken) {
59
+ return resolveTokenSource(baseUrl, env, flagToken).token;
60
+ }
61
+ /**
62
+ * Take the global `--token <pat>` / `--token=<pat>` out of an argument list.
63
+ *
64
+ * The older commands in `run.ts` parse against a fixed list of value flags,
65
+ * so the flag and its value are removed before they see the arguments. The
66
+ * `api` and resource commands get the arguments as given: there an operation
67
+ * that declares a `token` parameter (a registry's, a GitHub connection's)
68
+ * owns the flag, and `api/request.ts` keeps it out of the query string
69
+ * everywhere else. Nothing after `--` is touched; the last `--token` wins, as
70
+ * with every other repeated flag.
71
+ */
72
+ function takeTokenFlag(argv) {
73
+ const rest = [];
74
+ let token;
75
+ for (let i = 0; i < argv.length; i++) {
76
+ const a = argv[i];
77
+ if (a === '--') {
78
+ rest.push(...argv.slice(i));
79
+ break;
80
+ }
81
+ if (a.startsWith('--token='))
82
+ token = a.slice('--token='.length);
83
+ else if (a === '--token') {
84
+ const next = argv[i + 1];
85
+ // `--token --json` is a missing value, not a token called "--json".
86
+ if (next !== undefined && !next.startsWith('-')) {
87
+ token = next;
88
+ i += 1;
89
+ }
90
+ else
91
+ token = '';
92
+ }
93
+ else
94
+ rest.push(a);
95
+ }
96
+ const trimmed = token?.trim();
97
+ return { argv: rest, token: trimmed ? trimmed : undefined };
35
98
  }
36
99
  function storeToken(baseUrl, token, env = process.env) {
37
100
  const path = credentialsPath(env);
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { Check } from '../conformance';
2
+ import { type Get } from './probe';
3
+ /**
4
+ * Checks about a page that lives at more than one address.
5
+ *
6
+ * A hosted docs platform serves every published page twice: on its own host
7
+ * (`platform.example/p/<org>/<space>/<page>`) and on the customer's domain.
8
+ * Search engines must be told, by BOTH copies, that the customer's address is
9
+ * the real one. The platform copy of a LEGAL page redirected to a third
10
+ * address that named itself canonical (FMU-B01), so the customer's legal pages
11
+ * competed with a copy on our host. Nothing on the customer's domain showed it.
12
+ */
13
+ /** One page at its several addresses: the primary (canonical) one first when known. */
14
+ export interface AddressSet {
15
+ /** What the canonical of every copy should be, when it is known. */
16
+ expected?: string;
17
+ urls: string[];
18
+ }
19
+ export declare function canonicalBothAddresses(get: Get, sets: AddressSet[]): Promise<Check>;
20
+ /**
21
+ * Strip what a browser does not show and a reader cannot click: scripts,
22
+ * styles, and `<link>` tags (preloads, stylesheets). A white-label site may
23
+ * well load its JavaScript from the platform's CDN — that is infrastructure,
24
+ * not branding — so only the markup a person sees, and the links they follow,
25
+ * are judged.
26
+ */
27
+ export declare function visibleMarkup(html: string): string;
28
+ /** Platform URLs with a path: a bare-origin link is a "powered by" badge, which is a setting, not a leak. */
29
+ export declare function platformMentions(text: string, platformHost: string): string[];
30
+ /**
31
+ * `white-label-leaks`: a page on the customer's domain does not send its
32
+ * readers, or its agents, back to the platform's host.
33
+ *
34
+ * The Markdown twin of a reference page on a white-label domain ended with a
35
+ * "For AI agents" block pointing at the platform's API host for llms-full.txt
36
+ * and MCP, while the same files existed on the customer's domain (FMU-B15).
37
+ * The HTML looked clean; the agent-facing copy was the leak. Both are read.
38
+ */
39
+ export declare function whiteLabelLeaks(get: Get, pageUrl: string, html: string, platformHost: string): Promise<Check>;
40
+ export declare function findCmps(html: string): {
41
+ name: string;
42
+ at: number[];
43
+ }[];
44
+ export declare function languageHints(html: string, at: number[]): string[];
45
+ /**
46
+ * `consent-tag`: the cookie banner speaks the page's language.
47
+ *
48
+ * The CMP tag carried no culture, so the banner followed the BROWSER's
49
+ * language rather than the page's (FMU-B17, S05): a German reader on the
50
+ * English page got a German banner over English text, and on a translated
51
+ * page the banner and the page disagree for everyone whose browser is set to
52
+ * anything else. Consent shown in a language the page is not in is a consent
53
+ * a regulator can question. No CMP on the page is a skip, not a pass.
54
+ */
55
+ export declare function consentTag(html: string): Check;
56
+ //# sourceMappingURL=address-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address-checks.d.ts","sourceRoot":"","sources":["../../src/doctor/address-checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAQL,KAAK,GAAG,EACT,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;GASG;AAEH,uFAAuF;AACvF,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CA4CzF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ7E;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,CAAC,CA+BhB;AA4CD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,CAWvE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQlE;AAID;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAuC9C"}
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canonicalBothAddresses = canonicalBothAddresses;
4
+ exports.visibleMarkup = visibleMarkup;
5
+ exports.platformMentions = platformMentions;
6
+ exports.whiteLabelLeaks = whiteLabelLeaks;
7
+ exports.findCmps = findCmps;
8
+ exports.languageHints = languageHints;
9
+ exports.consentTag = consentTag;
10
+ const probe_1 = require("./probe");
11
+ async function canonicalBothAddresses(get, sets) {
12
+ const title = 'Every address of a page names the same canonical';
13
+ const usable = sets.filter((s) => s.urls.length >= 2 || s.expected);
14
+ if (usable.length === 0)
15
+ return (0, probe_1.check)('canonical-both-addresses', title, 'skip', 'Only one address of this page is known. Pass --also <the other address>, or --site <slug> to have them looked up.', '');
16
+ const bad = [];
17
+ const good = [];
18
+ for (const set of usable) {
19
+ const before = bad.length;
20
+ const seen = [];
21
+ for (const url of [...new Set(set.urls)]) {
22
+ const { hops, final } = await (0, probe_1.follow)(get, url, { accept: 'text/html' });
23
+ const landed = hops.length > 1 && final.ok ? final.url : url;
24
+ if (!final.ok || final.status !== 200) {
25
+ bad.push(`${url} → ${(0, probe_1.describeProbe)(final)}`);
26
+ continue;
27
+ }
28
+ seen.push({ url, final: landed, canonical: (0, probe_1.canonicalOf)(final.text, landed) });
29
+ }
30
+ const expected = set.expected ?? seen.find((s) => s.canonical)?.canonical;
31
+ for (const s of seen) {
32
+ const via = s.final !== s.url ? ` (via ${s.final})` : '';
33
+ if (!s.canonical)
34
+ bad.push(`${s.url}${via} declares no canonical`);
35
+ else if (expected && !(0, probe_1.sameUrl)(s.canonical, expected))
36
+ bad.push(`${s.url}${via} names ${s.canonical}, expected ${expected}`);
37
+ }
38
+ if (seen.length && bad.length === before)
39
+ good.push(`${seen.map((s) => s.url).join(' and ')} → ${expected}`);
40
+ }
41
+ return (0, probe_1.check)('canonical-both-addresses', title, bad.length ? 'fail' : 'pass', bad.length ? (0, probe_1.some)(bad) : (0, probe_1.some)(good), bad.length
42
+ ? 'Two copies with different canonicals compete in search, and the platform copy can outrank the customer’s own domain.'
43
+ : '');
44
+ }
45
+ /**
46
+ * Strip what a browser does not show and a reader cannot click: scripts,
47
+ * styles, and `<link>` tags (preloads, stylesheets). A white-label site may
48
+ * well load its JavaScript from the platform's CDN — that is infrastructure,
49
+ * not branding — so only the markup a person sees, and the links they follow,
50
+ * are judged.
51
+ */
52
+ function visibleMarkup(html) {
53
+ return html
54
+ .replace(/<script\b[\s\S]*?<\/script>/gi, '')
55
+ .replace(/<style\b[\s\S]*?<\/style>/gi, '')
56
+ .replace(/<link\b[^>]*>/gi, '')
57
+ .replace(/<meta\b[^>]*>/gi, '');
58
+ }
59
+ /** Platform URLs with a path: a bare-origin link is a "powered by" badge, which is a setting, not a leak. */
60
+ function platformMentions(text, platformHost) {
61
+ const host = platformHost.replace(/[.]/g, '\\.');
62
+ const re = new RegExp(`https?://(?:www\\.)?${host}(/[^\\s"'<>)\\]\`]+)`, 'gi');
63
+ return [
64
+ ...new Set([...text.matchAll(re)].map((m) => m[0]).filter((u) => !/^https?:\/\/[^/]+\/?$/.test(u))),
65
+ ];
66
+ }
67
+ /**
68
+ * `white-label-leaks`: a page on the customer's domain does not send its
69
+ * readers, or its agents, back to the platform's host.
70
+ *
71
+ * The Markdown twin of a reference page on a white-label domain ended with a
72
+ * "For AI agents" block pointing at the platform's API host for llms-full.txt
73
+ * and MCP, while the same files existed on the customer's domain (FMU-B15).
74
+ * The HTML looked clean; the agent-facing copy was the leak. Both are read.
75
+ */
76
+ async function whiteLabelLeaks(get, pageUrl, html, platformHost) {
77
+ const title = 'No platform URLs on a custom-domain page';
78
+ const host = (0, probe_1.hostOf)(pageUrl);
79
+ if (!host || host === platformHost.toLowerCase() || host === `www.${platformHost.toLowerCase()}`)
80
+ return (0, probe_1.check)('white-label-leaks', title, 'skip', `${pageUrl} is on the platform host itself.`, '');
81
+ const leaks = platformMentions(visibleMarkup(html), platformHost).map((u) => `HTML: ${u}`);
82
+ const u = new URL(pageUrl);
83
+ const mdUrl = `${u.origin}${u.pathname.replace(/\/+$/, '')}.md`;
84
+ const md = await get(mdUrl, { accept: 'text/markdown,text/plain' });
85
+ const isMd = md.ok && md.status === 200 && !/text\/html/i.test(md.headers.get('content-type') ?? '');
86
+ if (isMd)
87
+ for (const m of platformMentions(md.text, platformHost))
88
+ leaks.push(`${mdUrl}: ${m}`);
89
+ return (0, probe_1.check)('white-label-leaks', title, leaks.length ? 'fail' : 'pass', leaks.length
90
+ ? (0, probe_1.some)(leaks)
91
+ : `No ${platformHost} links in the page${isMd ? ' or its Markdown twin' : ''}.`, leaks.length
92
+ ? `A white-label site that links readers or agents to ${platformHost} exposes the platform behind it and splits its traffic; serve the same resources under the customer's domain.`
93
+ : '');
94
+ }
95
+ /**
96
+ * Consent managers, and where each one is told which language to speak.
97
+ *
98
+ * Matched on the vendor's loader host or global, so a CMP is recognised
99
+ * whether it is a `<script>` in the HTML, a preload of a script a framework
100
+ * injects later, or a config object in a serialized payload. The language
101
+ * hints are each vendor's documented attribute or query parameter; a vendor
102
+ * not listed here is still found by the generic hints below.
103
+ */
104
+ const CMPS = [
105
+ { name: 'Cookie Munch', match: /cookiemunch\.(?:net|com)/i },
106
+ { name: 'Cookiebot', match: /consent\.cookiebot\.(?:com|eu)|uc\.js/i },
107
+ { name: 'OneTrust', match: /otSDKStub|cdn\.cookielaw\.org|onetrust\.com/i },
108
+ { name: 'Osano', match: /cmp\.osano\.com/i },
109
+ { name: 'Termly', match: /app\.termly\.io/i },
110
+ { name: 'Usercentrics', match: /usercentrics\.eu/i },
111
+ { name: 'Didomi', match: /sdk\.privacy-center\.org|didomi/i },
112
+ { name: 'iubenda', match: /cdn\.iubenda\.com/i },
113
+ { name: 'Complianz', match: /complianz/i },
114
+ { name: 'TCF CMP', match: /__tcfapi/ },
115
+ ];
116
+ /**
117
+ * Where a CMP's language is stated, across vendors: `data-culture` (Cookie
118
+ * Munch, Cookiebot), `data-language` (OneTrust's `data-document-language`,
119
+ * Usercentrics), `data-lang`, a `culture`/`language` key in a serialized
120
+ * config, or a `?culture=`/`?language=`/`?lang=`/`?hl=` loader parameter.
121
+ */
122
+ const LANGUAGE_HINTS = [
123
+ /data-(?:document-)?(?:culture|language|lang)\s*=\s*\\?["']([A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{2,8})?)/g,
124
+ /\\?["'](?:culture|language|lang|uiLanguage)\\?["']\s*:\s*\\?["']([A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{2,8})?)/g,
125
+ /[?&](?:culture|language|lang|hl)=([A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{2,8})?)/g,
126
+ ];
127
+ /**
128
+ * How far around a vendor mention a language hint still belongs to it. Small
129
+ * on purpose: a framework's serialized payload also carries the PAGE's
130
+ * language a few kilobytes away, and reading that as the banner's would pass
131
+ * exactly the page this check exists to fail.
132
+ */
133
+ const HINT_WINDOW = 600;
134
+ function findCmps(html) {
135
+ const out = [];
136
+ for (const cmp of CMPS) {
137
+ const re = new RegExp(cmp.match.source, cmp.match.flags.includes('g') ? cmp.match.flags : `${cmp.match.flags}g`);
138
+ const at = [...html.matchAll(re)].map((m) => m.index ?? 0);
139
+ if (at.length)
140
+ out.push({ name: cmp.name, at });
141
+ }
142
+ return out;
143
+ }
144
+ function languageHints(html, at) {
145
+ const found = new Set();
146
+ for (const i of at) {
147
+ const win = html.slice(Math.max(0, i - HINT_WINDOW), i + HINT_WINDOW);
148
+ for (const re of LANGUAGE_HINTS)
149
+ for (const m of win.matchAll(new RegExp(re.source, re.flags)))
150
+ found.add(m[1].toLowerCase());
151
+ }
152
+ return [...found];
153
+ }
154
+ const primary = (tag) => tag.toLowerCase().split(/[-_]/)[0];
155
+ /**
156
+ * `consent-tag`: the cookie banner speaks the page's language.
157
+ *
158
+ * The CMP tag carried no culture, so the banner followed the BROWSER's
159
+ * language rather than the page's (FMU-B17, S05): a German reader on the
160
+ * English page got a German banner over English text, and on a translated
161
+ * page the banner and the page disagree for everyone whose browser is set to
162
+ * anything else. Consent shown in a language the page is not in is a consent
163
+ * a regulator can question. No CMP on the page is a skip, not a pass.
164
+ */
165
+ function consentTag(html) {
166
+ const title = 'The consent banner is told the page language';
167
+ const cmps = findCmps(html);
168
+ if (cmps.length === 0)
169
+ return (0, probe_1.check)('consent-tag', title, 'skip', 'No consent manager found on this page.', '');
170
+ const lang = /<html\b[^>]*\blang\s*=\s*["']?([A-Za-z-]+)/i.exec(html)?.[1]?.toLowerCase();
171
+ const names = cmps.map((c) => c.name).join(', ');
172
+ const matters = 'A banner that follows the browser rather than the page asks for consent in a language the page is not written in.';
173
+ if (!lang)
174
+ return (0, probe_1.check)('consent-tag', title, 'fail', `${names} found, but the page declares no <html lang>.`, matters);
175
+ const hints = languageHints(html, cmps.flatMap((c) => c.at));
176
+ if (hints.length === 0)
177
+ return (0, probe_1.check)('consent-tag', title, 'fail', `${names} is given no language (no data-culture / data-language / language parameter); the banner will follow the browser, while the page is "${lang}".`, matters);
178
+ const wrong = hints.filter((h) => primary(h) !== primary(lang));
179
+ return (0, probe_1.check)('consent-tag', title, wrong.length ? 'fail' : 'pass', wrong.length
180
+ ? `${names} is told "${wrong.join(', ')}" on a "${lang}" page.`
181
+ : `${names} is told "${hints.join(', ')}", matching the page ("${lang}").`, wrong.length ? matters : '');
182
+ }
183
+ //# sourceMappingURL=address-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address-checks.js","sourceRoot":"","sources":["../../src/doctor/address-checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Descriptions that were written by a template, not by the site's owner.
3
+ *
4
+ * A space with no description of its own fell back to the preset's blurb —
5
+ * "Guides and reference, in a nested tree.", "Community discussions in
6
+ * Community." — and that sentence became the meta description, the og:description
7
+ * a link preview shows, and the summary line of llms.txt (FMU-B08). Nothing was
8
+ * broken in the HTTP sense, so no check noticed; a reader sharing a link noticed.
9
+ *
10
+ * One list, in one place, so `meta-description` and `llms-txt` judge the same
11
+ * strings. Patterns rather than exact strings: the template interpolates the
12
+ * space or site name, and the point is to recognise the template whatever was
13
+ * poured into it. They are generic generator phrasings — any docs platform
14
+ * that fills an empty description from a type preset produces sentences of this
15
+ * shape — not a list of one vendor's copy.
16
+ */
17
+ export declare const PRESET_DESCRIPTION_PATTERNS: readonly RegExp[];
18
+ export declare function isPresetDescription(text: string): boolean;
19
+ /**
20
+ * True when a description is headings run together rather than prose.
21
+ *
22
+ * A generated reference landing described itself as its tag names joined with
23
+ * spaces — "Referalli API Analytics and operations Billing Authentication
24
+ * Programs and partners Partner portal Payouts…" (FMU-B09). It has words, so
25
+ * "missing" never fires; it has no sentence in it, which is what this tests:
26
+ * eight words or more, no sentence punctuation at all, and most of the
27
+ * non-trivial words capitalised. A run of CamelCase joins (`PayoutsPartners`)
28
+ * counts too — that is the same list with the spaces lost.
29
+ */
30
+ export declare function isHeadingSoup(text: string): boolean;
31
+ /** Why a description is not one, or undefined when it reads like one somebody wrote. */
32
+ export declare function descriptionProblem(text: string | undefined): string | undefined;
33
+ //# sourceMappingURL=boilerplate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boilerplate.d.ts","sourceRoot":"","sources":["../../src/doctor/boilerplate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAUxD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGzD;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAM/E"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRESET_DESCRIPTION_PATTERNS = void 0;
4
+ exports.isPresetDescription = isPresetDescription;
5
+ exports.isHeadingSoup = isHeadingSoup;
6
+ exports.descriptionProblem = descriptionProblem;
7
+ /**
8
+ * Descriptions that were written by a template, not by the site's owner.
9
+ *
10
+ * A space with no description of its own fell back to the preset's blurb —
11
+ * "Guides and reference, in a nested tree.", "Community discussions in
12
+ * Community." — and that sentence became the meta description, the og:description
13
+ * a link preview shows, and the summary line of llms.txt (FMU-B08). Nothing was
14
+ * broken in the HTTP sense, so no check noticed; a reader sharing a link noticed.
15
+ *
16
+ * One list, in one place, so `meta-description` and `llms-txt` judge the same
17
+ * strings. Patterns rather than exact strings: the template interpolates the
18
+ * space or site name, and the point is to recognise the template whatever was
19
+ * poured into it. They are generic generator phrasings — any docs platform
20
+ * that fills an empty description from a type preset produces sentences of this
21
+ * shape — not a list of one vendor's copy.
22
+ */
23
+ exports.PRESET_DESCRIPTION_PATTERNS = [
24
+ /^guides and reference, in a nested tree\.?$/i,
25
+ /^community discussions in .+\.?$/i,
26
+ /^the latest posts from .+\.?$/i,
27
+ /^published documentation for .+\.?$/i,
28
+ /^documentation for .+\.?$/i,
29
+ /^welcome to .+ (?:docs|documentation)\.?$/i,
30
+ // A not-found page's title used as a description: the address in the
31
+ // sitemap or llms.txt is dead, and the preview says so to everybody.
32
+ /this page isn[’']t here/i,
33
+ ];
34
+ function isPresetDescription(text) {
35
+ const t = text.trim();
36
+ return exports.PRESET_DESCRIPTION_PATTERNS.some((re) => re.test(t));
37
+ }
38
+ /** Small words a real sentence has and a list of headings also has. */
39
+ const STOP = new Set(['and', 'or', 'the', 'of', 'a', 'an', 'to', 'in', 'for', 'with', 'on', '&']);
40
+ /**
41
+ * True when a description is headings run together rather than prose.
42
+ *
43
+ * A generated reference landing described itself as its tag names joined with
44
+ * spaces — "Referalli API Analytics and operations Billing Authentication
45
+ * Programs and partners Partner portal Payouts…" (FMU-B09). It has words, so
46
+ * "missing" never fires; it has no sentence in it, which is what this tests:
47
+ * eight words or more, no sentence punctuation at all, and most of the
48
+ * non-trivial words capitalised. A run of CamelCase joins (`PayoutsPartners`)
49
+ * counts too — that is the same list with the spaces lost.
50
+ */
51
+ function isHeadingSoup(text) {
52
+ const t = text.replace(/(?:…|\.\.\.)\s*$/, '').trim();
53
+ if ((t.match(/[a-z][A-Z][a-z]/g) ?? []).length >= 2 && !/[.,;:!?]/.test(t))
54
+ return true;
55
+ if (/[.,;:!?—–()]/.test(t))
56
+ return false;
57
+ const words = t.split(/\s+/).filter(Boolean);
58
+ if (words.length < 8)
59
+ return false;
60
+ const content = words.filter((w) => !STOP.has(w.toLowerCase()));
61
+ if (content.length === 0)
62
+ return false;
63
+ const capped = content.filter((w) => /^[A-Z0-9]/.test(w)).length;
64
+ return capped / content.length >= 0.5;
65
+ }
66
+ /** Why a description is not one, or undefined when it reads like one somebody wrote. */
67
+ function descriptionProblem(text) {
68
+ if (text === undefined)
69
+ return 'missing';
70
+ if (!text.trim())
71
+ return 'empty';
72
+ if (isPresetDescription(text))
73
+ return `preset text "${text.trim()}"`;
74
+ if (isHeadingSoup(text))
75
+ return `headings run together "${text.trim().slice(0, 80)}…"`;
76
+ return undefined;
77
+ }
78
+ //# sourceMappingURL=boilerplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boilerplate.js","sourceRoot":"","sources":["../../src/doctor/boilerplate.ts"],"names":[],"mappings":";;;AA4BA,kDAGC;AAgBD,sCAUC;AAGD,gDAMC;AAlED;;;;;;;;;;;;;;;GAeG;AACU,QAAA,2BAA2B,GAAsB;IAC5D,8CAA8C;IAC9C,mCAAmC;IACnC,gCAAgC;IAChC,sCAAsC;IACtC,4BAA4B;IAC5B,4CAA4C;IAC5C,qEAAqE;IACrE,qEAAqE;IACrE,0BAA0B;CAC3B,CAAC;AAEF,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,OAAO,mCAA2B,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,uEAAuE;AACvE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAElG;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACxF,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjE,OAAO,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;AACxC,CAAC;AAED,wFAAwF;AACxF,SAAgB,kBAAkB,CAAC,IAAwB;IACzD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,OAAO,CAAC;IACjC,IAAI,mBAAmB,CAAC,IAAI,CAAC;QAAE,OAAO,gBAAgB,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;IACrE,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC;IACvF,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Http } from '../api/http';
2
+ import { type DoctorReport } from '../doctor';
3
+ export declare const DOCTOR_CHECK_IDS: readonly string[];
4
+ export declare const DOCTOR_HELP = "forumulate doctor \u2014 check a live site from the outside\n\nUsage:\n forumulate doctor <readerUrl> [flags]\n forumulate doctor --site <slug|id> [--org <slug|id>] [flags]\n\nPlain HTTP, no token needed for a URL: the same report against our platform, a\nstaging mount or any other docs host. --site asks the API for every address of\nevery space on a site (needs a token), and checks each copy.\n\nFlags:\n --site <slug|id> every member space, at its custom domain AND its\n platform copy (token: FORUMULATE_TOKEN or `login`)\n --org <slug|id> the org --site is in (default: FORUMULATE_ORG / config)\n --also <url> another address of the same page, e.g. the platform copy\n of a custom-domain page (repeatable)\n --only a,b run just these checks (ids below)\n --json the full report as JSON on stdout (nothing else there)\n --api-base <origin> where /api/health lives, when not on the page's host\n --platform-host <h> the hosting platform's hostname, for white-label and\n canonical checks (default: FORUMULATE_URL's host, else\n forumulate.com)\n --limit <n> sitemap URLs to sample (default 60)\n --timeout <ms> per-request timeout (default 15000)\n\nChecks (id \u2014 what fails it):\n health /api/health not 2xx JSON (also tried under the mount)\n public-read a logged-out reader is sent to sign-in\n canonical the page's canonical names another host\n html-untouched a proxy rewrites the markup (email obfuscation)\n cache-headers no Cache-Control, or no-store, on the page\n robots / sitemap robots.txt missing, disallows the path, no Sitemap:\n crawler-access a crawler gets a different status than a reader\n feeds an advertised feed does not answer 200\n images a picture on the page does not load\n canonical-both-addresses a page's copies (custom domain, platform) name\n different canonicals, or not the primary address\n sitemap-crawl a sitemap URL redirects, 404s, is on another host or\n names another canonical\n cache-consistency pages of one space cached differently (public vs\n private/no-store) with no x-forumulate-cache-reason\n root-redirect a space root answers 302/307, or a redirect with a\n body over 1 KB, or lands on a page canonical elsewhere\n meta-description missing description/og:description, template text\n (\"Guides and reference, in a nested tree.\"), or\n headings run together\n llms-txt /llms.txt missing, no H1, preset summary, links on\n another host, slugs in titles, no sections/descriptions\n favicon /favicon.ico or <link rel=icon> not an image; an\n asset path answering a large HTML page\n consent-tag a consent manager (any CMP: Cookie Munch, Cookiebot,\n OneTrust, Osano, Termly, Usercentrics, Didomi,\n iubenda, Complianz, TCF) not told the page language\n white-label-leaks a custom-domain page or its .md twin links to the\n platform host\n robots-params (advice) prints the Disallow lines that keep crawlers\n off each mount's ?sort= / ?period= / ?range= copies\n reader-auth-atlas (advice, needs --org and a token) the organization\n uses Atlas but signs readers in another way (an\n external_jwt route): prints the relink and the switch\n to the Atlas reader provider (readerAuth: atlas)\n\nExamples:\n forumulate doctor https://docs.acme.com/docs\n forumulate doctor https://acme.com/legal/privacy --also https://forumulate.com/p/acme/legal/privacy\n forumulate doctor --site main --org acme --json > doctor.json\n forumulate doctor https://acme.com/docs --only sitemap-crawl,root-redirect\n\nIn CI: packages/cli/examples/github-action-doctor.yml runs this on a schedule and\non every push, uploads the JSON report and fails the job on any FAIL.\n\nExit status:\n 0 ok 1 failed (verification mismatch, doctor FAIL)\n 2 usage 3 auth (no token, 401, token not allowed)\n 4 not found 5 conflict (409/412, edited in the studio since your last publish)\n 124 --wait timed out (the job keeps running; with --wait, a failed job is 1)\n doctor: 1 when any check FAILs; a SKIP is not a failure.";
5
+ export interface DoctorCommandDeps {
6
+ out: (line: string) => void;
7
+ err: (line: string) => void;
8
+ env?: NodeJS.ProcessEnv;
9
+ fetchImpl?: typeof fetch;
10
+ http?: Http;
11
+ userAgent?: string;
12
+ /** The global `--token` (FMU-DOC01); beats FORUMULATE_TOKEN and the stored token. */
13
+ flagToken?: string;
14
+ }
15
+ /** `forumulate doctor …` → an exit code. */
16
+ export declare function runDoctorCommand(argv: string[], deps: DoctorCommandDeps): Promise<number>;
17
+ export declare function printReport(report: DoctorReport, out: (l: string) => void): void;
18
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/doctor/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAKnC,OAAO,EAA6B,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAOzE,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAI7C,CAAC;AAEF,eAAO,MAAM,WAAW,gtJAwEmC,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,4CAA4C;AAC5C,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4H/F;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAUhF"}