@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.
- package/README.md +3 -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 +1 -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 +33 -3
- 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/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 +57 -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/command.d.ts +20 -0
- package/dist/publish/command.d.ts.map +1 -1
- package/dist/publish/command.js +97 -4
- package/dist/publish/command.js.map +1 -1
- package/dist/publish/context.d.ts +8 -0
- package/dist/publish/context.d.ts.map +1 -1
- package/dist/publish/context.js.map +1 -1
- package/dist/publish/docs-apply.d.ts.map +1 -1
- package/dist/publish/docs-apply.js +13 -1
- package/dist/publish/docs-apply.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/manifest.d.ts.map +1 -1
- package/dist/publish/manifest.js +27 -2
- package/dist/publish/manifest.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/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/types.d.ts +22 -1
- package/dist/publish/types.d.ts.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 +122 -110
- 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,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHECK_HELP = exports.CHECK_VALUE_FLAGS = void 0;
|
|
4
|
+
exports.runCheck = runCheck;
|
|
5
|
+
exports.folderReport = folderReport;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const manifest_1 = require("../publish/manifest");
|
|
9
|
+
const names_1 = require("../publish/names");
|
|
10
|
+
const scan_1 = require("../publish/scan");
|
|
11
|
+
const exit_codes_1 = require("../exit-codes");
|
|
12
|
+
const external_1 = require("./external");
|
|
13
|
+
const markdown_links_1 = require("./markdown-links");
|
|
14
|
+
exports.CHECK_VALUE_FLAGS = ['--allow-redirect', '--ignore'];
|
|
15
|
+
exports.CHECK_HELP = `forumulate check links [folder] [flags]
|
|
16
|
+
forumulate check links --space <id|slug> [--org <id|slug>] [flags]
|
|
17
|
+
|
|
18
|
+
Find broken links before readers do (FMU-F05).
|
|
19
|
+
|
|
20
|
+
On a folder (the default, "."): reads it the way \`publish\` does — forumulate.yaml
|
|
21
|
+
and every space in it — and checks, without a token and without publishing:
|
|
22
|
+
internal links ./other.md, ../guide/setup.md, /slug, ./slug — the page must exist
|
|
23
|
+
anchors #section and page#section — the heading (or {#id}, or id="…") must exist,
|
|
24
|
+
with the product's own anchor rule (duplicates get -2, -3)
|
|
25
|
+
pictures a local image or file the page points at must be on disk
|
|
26
|
+
external links with --external: a 2xx passes; a redirect passes only to a host
|
|
27
|
+
--allow-redirect allows (default same-host); never followed
|
|
28
|
+
On a published space (--space): asks the API for the same report, which adds
|
|
29
|
+
reference-page operation anchors (#operationId and #get-v1-x) and uploads.
|
|
30
|
+
|
|
31
|
+
Flags:
|
|
32
|
+
--external also check outside links (slow; paced per host; cached a day)
|
|
33
|
+
--allow-redirect <list> hosts a redirect may land on: same-host, docs.x.com, *.x.com
|
|
34
|
+
(space mode: the space/org key links.check_allowed_redirect_hosts)
|
|
35
|
+
--ignore <pattern> skip links containing this text (repeatable)
|
|
36
|
+
--no-cache ask every outside link again
|
|
37
|
+
--json the report as JSON on stdout
|
|
38
|
+
|
|
39
|
+
Exit status: 0 nothing broken, 1 something broken, 2 usage, 3 auth, 4 not found.`;
|
|
40
|
+
const flagValues = (argv, name) => argv.flatMap((a, i) => a.startsWith(`${name}=`) ? [a.slice(name.length + 1)] : a === name ? [argv[i + 1] ?? ''] : []);
|
|
41
|
+
/** `forumulate check …` — only `links` today; the noun leaves room for the next check. */
|
|
42
|
+
async function runCheck(argv, rest, deps) {
|
|
43
|
+
const [what, folder] = rest;
|
|
44
|
+
if (what !== 'links' || argv.includes('--help')) {
|
|
45
|
+
(what === 'links' || argv.includes('--help') ? deps.out : deps.err)(exports.CHECK_HELP);
|
|
46
|
+
return what === 'links' || argv.includes('--help') ? exit_codes_1.EXIT.OK : exit_codes_1.EXIT.USAGE;
|
|
47
|
+
}
|
|
48
|
+
const json = argv.includes('--json');
|
|
49
|
+
const external = argv.includes('--external');
|
|
50
|
+
const allow = flagValues(argv, '--allow-redirect').flatMap((v) => v.split(',')).map((v) => v.trim()).filter(Boolean);
|
|
51
|
+
const ignore = flagValues(argv, '--ignore').filter(Boolean);
|
|
52
|
+
const space = flagValues(argv, '--space')[0];
|
|
53
|
+
let report;
|
|
54
|
+
try {
|
|
55
|
+
report = space
|
|
56
|
+
? await spaceReport(space, flagValues(argv, '--org')[0], external, deps)
|
|
57
|
+
: folderReport((0, node_path_1.resolve)(folder ?? '.'), deps);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
deps.err(`check links: ${e.message}`);
|
|
61
|
+
// A folder that cannot be read is the command line's fault (2); an API
|
|
62
|
+
// refusal maps as every command maps it (3 auth, 4 not found, …).
|
|
63
|
+
return space ? (0, exit_codes_1.exitCodeForError)(e) : exit_codes_1.EXIT.USAGE;
|
|
64
|
+
}
|
|
65
|
+
if (!space && external) {
|
|
66
|
+
const checker = new external_1.ExternalChecker({
|
|
67
|
+
allowRedirects: allow.length ? allow : ['same-host'],
|
|
68
|
+
cacheFile: argv.includes('--no-cache') ? null : (0, external_1.defaultCacheFile)(deps.env),
|
|
69
|
+
fetch: deps.fetch,
|
|
70
|
+
});
|
|
71
|
+
const pending = report.external;
|
|
72
|
+
const verdicts = await checker.checkAll(pending.keys());
|
|
73
|
+
report.checked.external = pending.size;
|
|
74
|
+
for (const [url, v] of verdicts) {
|
|
75
|
+
if (v.ok)
|
|
76
|
+
continue;
|
|
77
|
+
for (const at of pending.get(url) ?? [])
|
|
78
|
+
report.broken.push({
|
|
79
|
+
...at,
|
|
80
|
+
url,
|
|
81
|
+
reason: v.reason ?? 'external_unreachable',
|
|
82
|
+
status: v.status,
|
|
83
|
+
...(v.location ? { location: v.location } : {}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
report.broken = report.broken.filter((b) => !ignore.some((p) => b.url.includes(p)));
|
|
88
|
+
print(report, json, deps);
|
|
89
|
+
return report.broken.length ? exit_codes_1.EXIT.FAILED : exit_codes_1.EXIT.OK;
|
|
90
|
+
}
|
|
91
|
+
function print(report, json, deps) {
|
|
92
|
+
const { external: _x, ...plain } = report;
|
|
93
|
+
if (json) {
|
|
94
|
+
deps.out(JSON.stringify(plain, null, 2));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
for (const b of report.broken) {
|
|
98
|
+
const extra = b.anchor
|
|
99
|
+
? ` #${b.anchor} is not on that page`
|
|
100
|
+
: b.location
|
|
101
|
+
? ` → ${b.location} (${b.status})`
|
|
102
|
+
: b.status
|
|
103
|
+
? ` (${b.status})`
|
|
104
|
+
: '';
|
|
105
|
+
deps.out(`${b.where}${b.line ? `:${b.line}` : ''} ${b.reason.padEnd(20)} ${b.url}${extra}`);
|
|
106
|
+
}
|
|
107
|
+
const c = report.checked;
|
|
108
|
+
deps.out(`${report.broken.length ? `${report.broken.length} broken` : 'No broken links'} — ` +
|
|
109
|
+
`${c.links} link(s) on ${c.pages} page(s), ${c.anchors} anchor(s)` +
|
|
110
|
+
(c.external ? `, ${c.external} outside address(es)` : '') +
|
|
111
|
+
(c.unchecked ? `; ${c.unchecked} link(s) into other spaces not checked from a folder` : '') +
|
|
112
|
+
'.');
|
|
113
|
+
}
|
|
114
|
+
async function spaceReport(space, orgFlag, external, deps) {
|
|
115
|
+
const org = orgFlag ?? deps.env?.FORUMULATE_ORG ?? process.env.FORUMULATE_ORG;
|
|
116
|
+
if (!org)
|
|
117
|
+
throw Object.assign(new Error('which organization? Pass --org or set FORUMULATE_ORG.'), { exitCode: exit_codes_1.EXIT.USAGE });
|
|
118
|
+
if (!deps.request)
|
|
119
|
+
throw Object.assign(new Error('no API client'), { exitCode: exit_codes_1.EXIT.AUTH });
|
|
120
|
+
const r = await deps.request('GET', `/orgs/${encodeURIComponent(org)}/spaces/${encodeURIComponent(space)}/docs/broken-links`, {
|
|
121
|
+
query: { external, refresh: true },
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
mode: 'space',
|
|
125
|
+
checked: { ...r.checked, unchecked: 0 },
|
|
126
|
+
broken: r.broken.map(({ pageSlug, ...b }) => ({ where: pageSlug, ...b })),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Path inside a space folder → the page it is, case-insensitively. */
|
|
130
|
+
function pageIndex(root, scan) {
|
|
131
|
+
const bySlug = new Map();
|
|
132
|
+
const byFile = new Map();
|
|
133
|
+
for (const c of scan.collections)
|
|
134
|
+
for (const p of c.pages) {
|
|
135
|
+
if (p.stub)
|
|
136
|
+
continue;
|
|
137
|
+
bySlug.set(p.slug, p);
|
|
138
|
+
// `file` is relative to the publish root; links are relative to the page.
|
|
139
|
+
byFile.set((0, node_path_1.relative)(scan.absDir, (0, node_path_1.resolve)(root, p.file)).split(node_path_1.sep).join('/').toLowerCase(), p);
|
|
140
|
+
}
|
|
141
|
+
return { bySlug, byFile };
|
|
142
|
+
}
|
|
143
|
+
/** The folder half: every space the manifest names, read as `publish` reads it. */
|
|
144
|
+
function folderReport(root, _deps = {}) {
|
|
145
|
+
if (!(0, node_fs_1.existsSync)(root) || !(0, node_fs_1.statSync)(root).isDirectory())
|
|
146
|
+
throw new Error(`${root} is not a folder`);
|
|
147
|
+
const manifest = (0, manifest_1.loadManifest)((0, manifest_1.findManifest)(root));
|
|
148
|
+
const spaces = manifest.spaces.map((spec) => {
|
|
149
|
+
const scan = (0, scan_1.scanSpace)(root, spec, 'docs', []);
|
|
150
|
+
return { key: spec.space ?? spec.path, spec, scan, ...pageIndex(root, scan) };
|
|
151
|
+
});
|
|
152
|
+
const byKey = new Map(spaces.map((s) => [s.key ?? '', s]));
|
|
153
|
+
const anchorsMemo = new Map();
|
|
154
|
+
const anchorsOf = (p) => {
|
|
155
|
+
let a = anchorsMemo.get(p);
|
|
156
|
+
if (!a)
|
|
157
|
+
anchorsMemo.set(p, (a = (0, markdown_links_1.markdownAnchors)(p.source, p.title)));
|
|
158
|
+
return a;
|
|
159
|
+
};
|
|
160
|
+
const report = {
|
|
161
|
+
mode: 'folder',
|
|
162
|
+
checked: { pages: 0, links: 0, anchors: 0, external: 0, unchecked: 0 },
|
|
163
|
+
broken: [],
|
|
164
|
+
external: new Map(),
|
|
165
|
+
};
|
|
166
|
+
for (const s of spaces) {
|
|
167
|
+
for (const page of s.bySlug.values()) {
|
|
168
|
+
report.checked.pages += 1;
|
|
169
|
+
const fileAbs = (0, node_path_1.resolve)(root, page.file);
|
|
170
|
+
const inSpace = (0, node_path_1.relative)(s.scan.absDir, fileAbs).split(node_path_1.sep).join('/');
|
|
171
|
+
for (const link of (0, markdown_links_1.markdownLinks)(page.source)) {
|
|
172
|
+
const href = link.href.trim();
|
|
173
|
+
if (!href || /^(mailto|tel|javascript|data):/i.test(href))
|
|
174
|
+
continue;
|
|
175
|
+
report.checked.links += 1;
|
|
176
|
+
const where = { where: page.file, line: link.line };
|
|
177
|
+
if (/^https?:\/\//i.test(href)) {
|
|
178
|
+
const bare = href.replace(/#.*$/, '');
|
|
179
|
+
report.external.set(bare, [...(report.external.get(bare) ?? []), where]);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//'))
|
|
183
|
+
continue;
|
|
184
|
+
const hash = href.indexOf('#');
|
|
185
|
+
const path = (hash >= 0 ? href.slice(0, hash) : href).replace(/\?.*$/, '');
|
|
186
|
+
let anchor = null;
|
|
187
|
+
if (hash >= 0 && hash < href.length - 1) {
|
|
188
|
+
try {
|
|
189
|
+
anchor = decodeURIComponent(href.slice(hash + 1));
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
anchor = href.slice(hash + 1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
let target;
|
|
196
|
+
if (!path)
|
|
197
|
+
target = page;
|
|
198
|
+
else if (names_1.MD.test(path)) {
|
|
199
|
+
let rel;
|
|
200
|
+
try {
|
|
201
|
+
rel = node_path_1.posix.normalize(node_path_1.posix.join(node_path_1.posix.dirname(inSpace), decodeURI(path))).toLowerCase();
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
rel = path.toLowerCase();
|
|
205
|
+
}
|
|
206
|
+
target = s.byFile.get(rel);
|
|
207
|
+
if (!target) {
|
|
208
|
+
report.broken.push({ ...where, url: href, reason: 'internal_not_found' });
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (names_1.ASSET.test(path) || link.media) {
|
|
213
|
+
// A picture or file next to the page; a root path is the site's to serve.
|
|
214
|
+
if (path.startsWith('/'))
|
|
215
|
+
continue;
|
|
216
|
+
let abs;
|
|
217
|
+
try {
|
|
218
|
+
abs = (0, node_path_1.join)((0, node_path_1.dirname)(fileAbs), decodeURI(path));
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
abs = (0, node_path_1.join)((0, node_path_1.dirname)(fileAbs), path);
|
|
222
|
+
}
|
|
223
|
+
if (!(0, node_fs_1.existsSync)(abs))
|
|
224
|
+
report.broken.push({ ...where, url: href, reason: 'asset_missing' });
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const segments = path.split('/').filter((x) => x && x !== '.');
|
|
229
|
+
const last = (segments[segments.length - 1] ?? '').replace(/\.md$/i, '');
|
|
230
|
+
if (path.startsWith('/') && segments.length > 1) {
|
|
231
|
+
const other = byKey.get(segments[0]);
|
|
232
|
+
if (!other) {
|
|
233
|
+
// Another section of the site we cannot see from this folder.
|
|
234
|
+
report.checked.unchecked += 1;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
target = other.bySlug.get(last);
|
|
238
|
+
}
|
|
239
|
+
else
|
|
240
|
+
target = s.bySlug.get(last);
|
|
241
|
+
if (!target) {
|
|
242
|
+
report.broken.push({ ...where, url: href, reason: 'internal_not_found' });
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (anchor && target) {
|
|
247
|
+
report.checked.anchors += 1;
|
|
248
|
+
if (!anchorsOf(target).has(anchor))
|
|
249
|
+
report.broken.push({ ...where, url: href, reason: 'anchor_not_found', anchor });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return report;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/check/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** One outside link's verdict. */
|
|
2
|
+
export interface Verdict {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
status: number;
|
|
5
|
+
location?: string;
|
|
6
|
+
reason?: 'external_unreachable' | 'external_redirect';
|
|
7
|
+
/** When it was asked, ms since the epoch — for the cache. */
|
|
8
|
+
at: number;
|
|
9
|
+
}
|
|
10
|
+
export type Fetcher = (url: string, init: {
|
|
11
|
+
method: string;
|
|
12
|
+
redirect: 'manual';
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
headers: Record<string, string>;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
status: number;
|
|
17
|
+
headers: {
|
|
18
|
+
get(name: string): string | null;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
export interface ExternalOptions {
|
|
22
|
+
/** Hosts a redirect may land on and still pass: `same-host`, `docs.x.com`, `*.x.com`, `*`. */
|
|
23
|
+
allowRedirects: string[];
|
|
24
|
+
concurrency?: number;
|
|
25
|
+
/** Gap between two requests to one host, ms. */
|
|
26
|
+
perHostGapMs?: number;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
/** Where verdicts are kept between runs; null for none. */
|
|
29
|
+
cacheFile?: string | null;
|
|
30
|
+
okTtlMs?: number;
|
|
31
|
+
brokenTtlMs?: number;
|
|
32
|
+
fetch?: Fetcher;
|
|
33
|
+
sleep?: (ms: number) => Promise<void>;
|
|
34
|
+
now?: () => number;
|
|
35
|
+
}
|
|
36
|
+
/** Where the verdicts live: `$XDG_CACHE_HOME/forumulate/link-check.json`. */
|
|
37
|
+
export declare function defaultCacheFile(env?: NodeJS.ProcessEnv): string;
|
|
38
|
+
/**
|
|
39
|
+
* Whether a redirect from `from` to `location` is one the caller accepts —
|
|
40
|
+
* the same rule as the API's `redirectAllowed` (packages/shared docs-workflow),
|
|
41
|
+
* mirrored because this package ships alone.
|
|
42
|
+
*/
|
|
43
|
+
export declare function redirectAllowed(from: string, location: string, allow: readonly string[]): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Outside links, checked the way the API checks them (FMU-F05): no redirect is
|
|
46
|
+
* followed — a 3xx passes only to an allowed host, else it is
|
|
47
|
+
* `external_redirect` naming where it goes; `HEAD` first, `GET` when a host
|
|
48
|
+
* refuses `HEAD`; a few requests at once and a gap between two to the same
|
|
49
|
+
* host, so a docs folder with 300 links to one site is not a burst; and a
|
|
50
|
+
* verdict file, so a second run in a minute asks nothing (a day for a working
|
|
51
|
+
* link, an hour for a broken one).
|
|
52
|
+
*/
|
|
53
|
+
export declare class ExternalChecker {
|
|
54
|
+
private readonly last;
|
|
55
|
+
private cache;
|
|
56
|
+
private readonly o;
|
|
57
|
+
constructor(opts: ExternalOptions);
|
|
58
|
+
checkAll(urls: Iterable<string>): Promise<Map<string, Verdict>>;
|
|
59
|
+
private fresh;
|
|
60
|
+
check(url: string): Promise<Verdict>;
|
|
61
|
+
private pace;
|
|
62
|
+
private request;
|
|
63
|
+
private ask;
|
|
64
|
+
private save;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=external.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/check/external.ts"],"names":[],"mappings":"AAIA,kCAAkC;AAClC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,sBAAsB,GAAG,mBAAmB,CAAC;IACtD,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,KAC/F,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,CAAC,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG7E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAmBjG;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,QAAQ,CAAC,CAAC,CAGhB;gBAEU,IAAI,EAAE,eAAe;IAsB3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAWrE,OAAO,CAAC,KAAK;IAKP,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAQ5B,IAAI;YAaJ,OAAO;YAgBP,GAAG;IAyBjB,OAAO,CAAC,IAAI;CAWb"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExternalChecker = void 0;
|
|
4
|
+
exports.defaultCacheFile = defaultCacheFile;
|
|
5
|
+
exports.redirectAllowed = redirectAllowed;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_os_1 = require("node:os");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
/** Where the verdicts live: `$XDG_CACHE_HOME/forumulate/link-check.json`. */
|
|
10
|
+
function defaultCacheFile(env = process.env) {
|
|
11
|
+
const base = env.XDG_CACHE_HOME || (0, node_path_1.join)((0, node_os_1.homedir)(), '.cache');
|
|
12
|
+
return (0, node_path_1.join)(base, 'forumulate', 'link-check.json');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Whether a redirect from `from` to `location` is one the caller accepts —
|
|
16
|
+
* the same rule as the API's `redirectAllowed` (packages/shared docs-workflow),
|
|
17
|
+
* mirrored because this package ships alone.
|
|
18
|
+
*/
|
|
19
|
+
function redirectAllowed(from, location, allow) {
|
|
20
|
+
let a;
|
|
21
|
+
let b;
|
|
22
|
+
try {
|
|
23
|
+
a = new URL(from);
|
|
24
|
+
b = new URL(location, from);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const host = b.hostname.toLowerCase();
|
|
30
|
+
for (const raw of allow) {
|
|
31
|
+
const p = raw.trim().toLowerCase();
|
|
32
|
+
if (!p)
|
|
33
|
+
continue;
|
|
34
|
+
if (p === 'same-host' && host === a.hostname.toLowerCase())
|
|
35
|
+
return true;
|
|
36
|
+
if (p === '*')
|
|
37
|
+
return true;
|
|
38
|
+
if (p.startsWith('*.') && (host === p.slice(2) || host.endsWith(p.slice(1))))
|
|
39
|
+
return true;
|
|
40
|
+
if (host === p)
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Outside links, checked the way the API checks them (FMU-F05): no redirect is
|
|
47
|
+
* followed — a 3xx passes only to an allowed host, else it is
|
|
48
|
+
* `external_redirect` naming where it goes; `HEAD` first, `GET` when a host
|
|
49
|
+
* refuses `HEAD`; a few requests at once and a gap between two to the same
|
|
50
|
+
* host, so a docs folder with 300 links to one site is not a burst; and a
|
|
51
|
+
* verdict file, so a second run in a minute asks nothing (a day for a working
|
|
52
|
+
* link, an hour for a broken one).
|
|
53
|
+
*/
|
|
54
|
+
class ExternalChecker {
|
|
55
|
+
last = new Map();
|
|
56
|
+
cache = {};
|
|
57
|
+
o;
|
|
58
|
+
constructor(opts) {
|
|
59
|
+
this.o = {
|
|
60
|
+
concurrency: 4,
|
|
61
|
+
perHostGapMs: 300,
|
|
62
|
+
timeoutMs: 10_000,
|
|
63
|
+
okTtlMs: 86_400_000,
|
|
64
|
+
brokenTtlMs: 3_600_000,
|
|
65
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
66
|
+
now: () => Date.now(),
|
|
67
|
+
...opts,
|
|
68
|
+
fetch: opts.fetch ?? globalThis.fetch,
|
|
69
|
+
cacheFile: opts.cacheFile === undefined ? null : opts.cacheFile,
|
|
70
|
+
};
|
|
71
|
+
if (this.o.cacheFile && (0, node_fs_1.existsSync)(this.o.cacheFile)) {
|
|
72
|
+
try {
|
|
73
|
+
this.cache = JSON.parse((0, node_fs_1.readFileSync)(this.o.cacheFile, 'utf8'));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
this.cache = {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async checkAll(urls) {
|
|
81
|
+
const queue = [...new Set(urls)];
|
|
82
|
+
const out = new Map();
|
|
83
|
+
const worker = async () => {
|
|
84
|
+
for (let u = queue.shift(); u !== undefined; u = queue.shift())
|
|
85
|
+
out.set(u, await this.check(u));
|
|
86
|
+
};
|
|
87
|
+
await Promise.all(Array.from({ length: Math.min(this.o.concurrency, queue.length) }, worker));
|
|
88
|
+
this.save();
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
fresh(v) {
|
|
92
|
+
if (!v)
|
|
93
|
+
return false;
|
|
94
|
+
return this.o.now() - v.at < (v.ok ? this.o.okTtlMs : this.o.brokenTtlMs);
|
|
95
|
+
}
|
|
96
|
+
async check(url) {
|
|
97
|
+
const hit = this.cache[url];
|
|
98
|
+
if (this.fresh(hit))
|
|
99
|
+
return hit;
|
|
100
|
+
const v = await this.ask(url);
|
|
101
|
+
this.cache[url] = v;
|
|
102
|
+
return v;
|
|
103
|
+
}
|
|
104
|
+
async pace(url) {
|
|
105
|
+
let host = '';
|
|
106
|
+
try {
|
|
107
|
+
host = new URL(url).host;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const due = (this.last.get(host) ?? 0) + this.o.perHostGapMs;
|
|
113
|
+
this.last.set(host, Math.max(this.o.now(), due));
|
|
114
|
+
const wait = due - this.o.now();
|
|
115
|
+
if (wait > 0)
|
|
116
|
+
await this.o.sleep(wait);
|
|
117
|
+
}
|
|
118
|
+
async request(url, method) {
|
|
119
|
+
await this.pace(url);
|
|
120
|
+
const ctrl = new AbortController();
|
|
121
|
+
const timer = setTimeout(() => ctrl.abort(), this.o.timeoutMs);
|
|
122
|
+
try {
|
|
123
|
+
return await this.o.fetch(url, {
|
|
124
|
+
method,
|
|
125
|
+
redirect: 'manual',
|
|
126
|
+
signal: ctrl.signal,
|
|
127
|
+
headers: { 'user-agent': 'forumulate-cli link check', accept: 'text/html,*/*;q=0.8' },
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async ask(url) {
|
|
135
|
+
const at = this.o.now();
|
|
136
|
+
try {
|
|
137
|
+
let res = await this.request(url, 'HEAD');
|
|
138
|
+
if ([403, 405, 501].includes(res.status))
|
|
139
|
+
res = await this.request(url, 'GET');
|
|
140
|
+
const status = res.status;
|
|
141
|
+
if (status >= 200 && status < 300)
|
|
142
|
+
return { ok: true, status, at };
|
|
143
|
+
if (status >= 300 && status < 400) {
|
|
144
|
+
const raw = res.headers.get('location') ?? '';
|
|
145
|
+
let location = raw;
|
|
146
|
+
try {
|
|
147
|
+
location = new URL(raw, url).toString();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
/* kept as sent */
|
|
151
|
+
}
|
|
152
|
+
if (raw && redirectAllowed(url, location, this.o.allowRedirects))
|
|
153
|
+
return { ok: true, status, location, at };
|
|
154
|
+
return { ok: false, status, location, reason: 'external_redirect', at };
|
|
155
|
+
}
|
|
156
|
+
return { ok: false, status, reason: 'external_unreachable', at };
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return { ok: false, status: 0, reason: 'external_unreachable', at };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
save() {
|
|
163
|
+
if (!this.o.cacheFile)
|
|
164
|
+
return;
|
|
165
|
+
try {
|
|
166
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.o.cacheFile), { recursive: true });
|
|
167
|
+
// Only verdicts still worth keeping, so the file does not grow forever.
|
|
168
|
+
const keep = Object.fromEntries(Object.entries(this.cache).filter(([, v]) => this.fresh(v)));
|
|
169
|
+
(0, node_fs_1.writeFileSync)(this.o.cacheFile, JSON.stringify(keep));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
/* a cache that cannot be written is only a slower next run */
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.ExternalChecker = ExternalChecker;
|
|
177
|
+
//# sourceMappingURL=external.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/check/external.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links and anchors in a Markdown file, read the way the product reads them —
|
|
3
|
+
* for `forumulate check links` on a folder (FMU-F05), before anything is
|
|
4
|
+
* published.
|
|
5
|
+
*
|
|
6
|
+
* This package ships alone (it depends on the SDK and `yaml`, nothing else), so
|
|
7
|
+
* the heading rule is mirrored here rather than imported, the way `names.ts`
|
|
8
|
+
* mirrors the slug rule: `headingSlug` below is the docs-schema's
|
|
9
|
+
* `headingSlug` (packages/docs-schema/src/heading-slug.ts), and
|
|
10
|
+
* `markdown-links.test.ts` pins the same cases that file's tests do. A checker
|
|
11
|
+
* whose anchors differ from the renderer's would report working links broken.
|
|
12
|
+
*/
|
|
13
|
+
/** A heading's anchor id from its text — the renderer's rule. */
|
|
14
|
+
export declare function headingSlug(text: string): string;
|
|
15
|
+
/** The id the renderer used before the accent fix, still answered as an alias. */
|
|
16
|
+
export declare function legacyHeadingSlug(text: string): string | null;
|
|
17
|
+
/** A link as written, and the line it is on (1-based, in the file). */
|
|
18
|
+
export interface FoundLink {
|
|
19
|
+
href: string;
|
|
20
|
+
line: number;
|
|
21
|
+
/** An image or embedded file, rather than a link a reader follows. */
|
|
22
|
+
media: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Every `#fragment` the page will answer to: each heading's id (duplicates
|
|
26
|
+
* suffixed `-2`, `-3`, as the renderer claims them), its pre-fix alias, an
|
|
27
|
+
* explicit `{#id}` on a heading, and any `id="…"` / `name="…"` in HTML.
|
|
28
|
+
* `title` drops a leading `# Title` that repeats the page title, which the
|
|
29
|
+
* renderer does not render as a heading either.
|
|
30
|
+
*/
|
|
31
|
+
export declare function markdownAnchors(text: string, title?: string): Set<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Every link and picture in the file: `[t](href)`, ``, reference
|
|
34
|
+
* definitions `[id]: href`, and HTML `href=` / `src=` — not inside a code
|
|
35
|
+
* fence or an inline code span, where a URL is an example, not a link.
|
|
36
|
+
*/
|
|
37
|
+
export declare function markdownLinks(text: string): FoundLink[];
|
|
38
|
+
//# sourceMappingURL=markdown-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-links.d.ts","sourceRoot":"","sources":["../../src/check/markdown-links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS7D;AA4BD,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,KAAK,EAAE,OAAO,CAAC;CAChB;AAwBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAkCzE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAgBvD"}
|