@cudoment/cudoc 0.2.0 → 0.4.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.
@@ -0,0 +1,465 @@
1
+ /**
2
+ * Whole-library reference checking.
3
+ *
4
+ * The build deliberately stops at the first unresolvable reference, because a
5
+ * site with a broken link should not ship. That makes it a poor way to find out
6
+ * what is wrong: an author fixes one link, rebuilds, and learns about the next.
7
+ * This walks every document instead and returns everything at once.
8
+ *
9
+ * It resolves through the same code the build uses, so a reference this reports
10
+ * as fine is one the build can resolve.
11
+ */
12
+ import { parseEmbedSpec } from "./resolve-embed.js";
13
+ import { collectSections } from "../sections.js";
14
+ import { resolveLocalTarget } from "./local-target.js";
15
+ /** A heading id that a slugger disambiguated, such as `overview-1`. */
16
+ const SUFFIXED = /-\d+$/;
17
+ const walkNodes = (node, visit) => {
18
+ visit(node);
19
+ node.children?.forEach((child) => walkNodes(child, visit));
20
+ };
21
+ /**
22
+ * Every anchor a document offers, and whether the author wrote it.
23
+ *
24
+ * Both halves matter. The ids answer whether a link resolves; the origin
25
+ * answers whether it will keep resolving, because a generated id depends on how
26
+ * many same-named headings precede it.
27
+ */
28
+ export function collectAnchors(tree) {
29
+ const anchors = [];
30
+ walkNodes(tree, (node) => {
31
+ if (node.type !== "heading")
32
+ return;
33
+ const id = node.data?.hProperties?.id;
34
+ if (typeof id === "string" && id)
35
+ anchors.push({ id, explicit: node.data?.cudoc?.explicitId === true });
36
+ });
37
+ return anchors;
38
+ }
39
+ /**
40
+ * Where a reference sits in the original Markdown.
41
+ *
42
+ * Collection strips positions from the stored tree on purpose: a persisted AST
43
+ * should not carry coordinates that go stale the moment the file is edited. The
44
+ * source snapshot is still here, so the first occurrence of the reference text
45
+ * gives an author somewhere real to jump to. A reference repeated in one
46
+ * document reports the first one; fixing it means fixing both anyway.
47
+ */
48
+ /**
49
+ * The forms a reference might take in the original Markdown.
50
+ *
51
+ * The tree holds what the host compiler produced, and hosts rewrite link
52
+ * destinations: Nextra drops the extension, VitePress rewrites it to `.html`
53
+ * and prefixes `./`. The author wrote `reference.md#limit`, so searching the
54
+ * source for the compiled form finds nothing. These are tried in turn.
55
+ */
56
+ const sourceForms = (reference) => {
57
+ const [pathname, anchor] = reference.split("#");
58
+ if (!pathname)
59
+ return [reference];
60
+ const bare = pathname.replace(/^\.\//, "").replace(/\.(?:mdx?|html?)$/i, "");
61
+ const suffix = anchor === undefined ? "" : `#${anchor}`;
62
+ return [
63
+ ...new Set([
64
+ reference,
65
+ `${bare}.md${suffix}`,
66
+ `${bare}.mdx${suffix}`,
67
+ `${bare}${suffix}`,
68
+ ]),
69
+ ];
70
+ };
71
+ const locate = (text, reference, skip = 0) => {
72
+ // A reference is often a prefix of a longer one on an earlier line, as
73
+ // `guide.md#limit` is of `guide.md#limits`. Prefer an occurrence that ends
74
+ // where a Markdown destination ends, and fall back to a plain search.
75
+ const ends = new Set([")", "]", '"', "'", " ", "\t", "\n", ""]);
76
+ let at = -1;
77
+ let remaining = skip;
78
+ for (let i = text.indexOf(reference); i >= 0; i = text.indexOf(reference, i + 1))
79
+ if (ends.has(text[i + reference.length] ?? "")) {
80
+ if (remaining-- > 0)
81
+ continue;
82
+ at = i;
83
+ break;
84
+ }
85
+ if (at < 0)
86
+ at = text.indexOf(reference);
87
+ if (at < 0)
88
+ return undefined;
89
+ const before = text.slice(0, at);
90
+ const line = before.split("\n").length;
91
+ const column = at - (before.lastIndexOf("\n") + 1) + 1;
92
+ const offset = at;
93
+ return {
94
+ start: { line, column, offset },
95
+ end: {
96
+ line,
97
+ column: column + reference.length,
98
+ offset: offset + reference.length,
99
+ },
100
+ };
101
+ };
102
+ /**
103
+ * Components in a tree that no portable renderer can turn into HTML.
104
+ *
105
+ * `documentToHast` renders an unknown `mdx*` or directive node only when the
106
+ * caller supplies an explicit HTML callback for it, and throws otherwise. A
107
+ * component sitting in a document is the author's business, because the host
108
+ * that owns it renders it. A component sitting in a section that an embed
109
+ * copies is different: the copy lands in another document, and standalone HTML
110
+ * export then has to render a name it was never given.
111
+ *
112
+ * `mdxjsEsm` and `yaml` are excluded because the renderer drops them rather
113
+ * than failing.
114
+ */
115
+ const unportableComponents = (tree) => {
116
+ const names = new Set();
117
+ walkNodes(tree, (node) => {
118
+ if (node.type === "mdxjsEsm" || node.type === "yaml")
119
+ return;
120
+ if (!node.type.startsWith("mdx") && !node.type.endsWith("Directive"))
121
+ return;
122
+ names.add(node.name ? `<${node.name}>` : node.type);
123
+ });
124
+ return [...names];
125
+ };
126
+ /** The visible text of a heading, used to spot an anchor that failed to parse. */
127
+ const headingText = (node) => {
128
+ let text = "";
129
+ walkNodes(node, (child) => {
130
+ if (child.type === "text" && typeof child.value === "string")
131
+ text += child.value;
132
+ });
133
+ return text;
134
+ };
135
+ /** The line each `cudoc-embed` fence opens on, in source order. */
136
+ const embedFenceLines = (text) => {
137
+ const lines = [];
138
+ text.split("\n").forEach((line, index) => {
139
+ if (/^\s*(?:`{3,}|~{3,})cudoc-embed\s*$/.test(line))
140
+ lines.push(index + 1);
141
+ });
142
+ return lines;
143
+ };
144
+ /**
145
+ * Whether a rule finds anything, asked the way the resolver asks it.
146
+ *
147
+ * Rules run in order and each sees the previous one's output, so a later rule
148
+ * can legitimately depend on an earlier substitution. Testing them against the
149
+ * untouched slice would report those as unmatched.
150
+ */
151
+ const matchedRules = (slice, rules) => {
152
+ const matched = [];
153
+ let value = slice;
154
+ rules.forEach((rule, index) => {
155
+ try {
156
+ if (rule.regex) {
157
+ const pattern = new RegExp(rule.find, rule.flags ?? "g");
158
+ matched[index] = pattern.test(value);
159
+ value = value.replace(new RegExp(rule.find, rule.flags ?? "g"), rule.replace);
160
+ }
161
+ else {
162
+ matched[index] = value.includes(rule.find);
163
+ value = value.split(rule.find).join(rule.replace);
164
+ }
165
+ }
166
+ catch {
167
+ // An unusable pattern is the resolver's error to raise, not a miss.
168
+ matched[index] = true;
169
+ }
170
+ });
171
+ return matched;
172
+ };
173
+ export function checkReferences(library, options = {}) {
174
+ const sourceRoot = options.sourceRoot ?? library.sourceRoot;
175
+ const ignore = new Set(options.ignore ?? []);
176
+ const issues = [];
177
+ let checkedReferences = 0;
178
+ const anchorsById = new Map(library.documents.map((doc) => [doc.id, collectAnchors(doc.tree)]));
179
+ const byId = new Map(library.documents.map((doc) => [doc.id, doc]));
180
+ const report = (doc,
181
+ /** A `position` given here wins; otherwise it is recovered from the source. */
182
+ issue,
183
+ /** Which occurrence of the reference to point at. A duplicate wants the later one. */
184
+ skip = 0) => {
185
+ if (ignore.has(issue.code))
186
+ return;
187
+ issues.push({
188
+ ...issue,
189
+ documentId: doc.id,
190
+ sourcePath: doc.sourcePath,
191
+ position: issue.position ??
192
+ sourceForms(issue.reference).reduce((found, form) => found ?? locate(doc.source?.text ?? "", form, skip), undefined),
193
+ });
194
+ };
195
+ /** Resolves `other.md#anchor` or a bare `#anchor` against the library. */
196
+ const checkDocumentLink = (doc, url) => {
197
+ const [pathname, anchor] = url.split("#");
198
+ let target = doc;
199
+ if (pathname) {
200
+ const id = resolveDocumentId(doc, pathname);
201
+ const found = id === undefined ? undefined : byId.get(id);
202
+ if (!found)
203
+ return false; // not a document link; the asset pass handles it
204
+ target = found;
205
+ }
206
+ if (!anchor)
207
+ return true;
208
+ const anchors = anchorsById.get(target.id) ?? [];
209
+ const match = anchors.find((entry) => entry.id === anchor);
210
+ if (!match) {
211
+ report(doc, {
212
+ code: "missing-anchor",
213
+ severity: "error",
214
+ message: `${target.id} has no anchor #${anchor}`,
215
+ reference: url,
216
+ available: anchors.map((entry) => entry.id),
217
+ });
218
+ return true;
219
+ }
220
+ if (!match.explicit && SUFFIXED.test(match.id))
221
+ report(doc, {
222
+ code: "unstable-anchor-link",
223
+ severity: "warning",
224
+ message: `#${anchor} in ${target.id} is generated from a repeated heading; inserting another one above it moves this link to a different section. Give the target heading an explicit anchor.`,
225
+ reference: url,
226
+ });
227
+ return true;
228
+ };
229
+ /**
230
+ * A link's document id, if it names a document in this library.
231
+ *
232
+ * Hosts do not agree on how a link looks once compiled. Docusaurus and
233
+ * Next.js leave `reference.md`, Nextra drops the extension, and VitePress
234
+ * rewrites it to the deployed `./reference.html`. All three mean the same
235
+ * document, so any of those endings resolves to the same id.
236
+ */
237
+ const resolveDocumentId = (doc, pathname) => {
238
+ if (/^(?:[a-z][\w+.-]*:|\/\/)/i.test(pathname))
239
+ return undefined;
240
+ const decoded = decodeURIComponent(pathname);
241
+ const joined = decoded.startsWith("/")
242
+ ? decoded.slice(1)
243
+ : posixJoin(dirname(doc.sourcePath), decoded);
244
+ return normalize(joined).replace(/\.(?:mdx?|html?)$/i, "");
245
+ };
246
+ for (const doc of library.documents) {
247
+ // Anchors the document declares, before anything references them.
248
+ const seen = new Set();
249
+ const counts = new Map();
250
+ walkNodes(doc.tree, (node) => {
251
+ if (node.type !== "heading")
252
+ return;
253
+ const id = node.data?.hProperties?.id;
254
+ if (typeof id === "string" && id) {
255
+ const before = counts.get(id) ?? 0;
256
+ if (before)
257
+ report(doc, {
258
+ code: "duplicate-anchor",
259
+ severity: "error",
260
+ message: `duplicate heading anchor #${id}`,
261
+ reference: `#${id}`,
262
+ }, before);
263
+ counts.set(id, before + 1);
264
+ seen.add(id);
265
+ }
266
+ // An anchor marker with no id never becomes an anchor. It stays in the
267
+ // heading text instead, and silently joins the generated slug.
268
+ const text = headingText(node);
269
+ const empty = text.match(/\((#)\)|\{(#)\}/);
270
+ if (empty)
271
+ report(doc, {
272
+ code: "empty-anchor",
273
+ severity: "error",
274
+ message: `empty anchor marker left in the heading text: "${text.trim()}"`,
275
+ reference: empty[0],
276
+ });
277
+ });
278
+ walkNodes(doc.tree, (node) => {
279
+ const url = node.type === "link" || node.type === "image" ? node.url : undefined;
280
+ if (typeof url !== "string" || !url)
281
+ return;
282
+ // A heading permalink is machinery the host inserted, not something an
283
+ // author wrote. VitePress and Eleventy add one per heading, and counting
284
+ // them would report every generated anchor as an unstable link.
285
+ if (node.data?.cudoc?.kind === "permalink")
286
+ return;
287
+ checkedReferences++;
288
+ if (/^(?:[a-z][\w+.-]*:|\/\/)/i.test(url))
289
+ return; // external
290
+ if (node.type === "link" && checkDocumentLink(doc, url))
291
+ return;
292
+ if (url.startsWith("#"))
293
+ return; // handled above as a same-document anchor
294
+ if (!sourceRoot)
295
+ return;
296
+ const target = resolveLocalTarget(url, doc.sourcePath, {
297
+ ...options,
298
+ sourceRoot,
299
+ });
300
+ if (target.kind === "missing")
301
+ report(doc, {
302
+ code: node.type === "image" ? "missing-asset" : "missing-document",
303
+ severity: "error",
304
+ message: node.type === "image"
305
+ ? `no file for image ${url}; check sourceRoot and assetDirs`
306
+ : `no document or file for ${url}`,
307
+ reference: url,
308
+ });
309
+ });
310
+ // Fence lines are read from the source because collection strips positions,
311
+ // and they are what turns a YAML error's own coordinate into a file one.
312
+ const fences = embedFenceLines(doc.source?.text ?? "");
313
+ let blockNumber = 0;
314
+ walkNodes(doc.tree, (node) => {
315
+ if (node.type !== "code" || node.lang !== "cudoc-embed")
316
+ return;
317
+ const fence = fences[blockNumber++];
318
+ let spec;
319
+ try {
320
+ spec = parseEmbedSpec(node.value ?? "");
321
+ }
322
+ catch (error) {
323
+ // The YAML parser counts from the start of the block; an author counts
324
+ // from the start of the file. Add the fence so both agree.
325
+ const at = error.linePos?.[0];
326
+ const line = fence === undefined ? undefined : fence + (at?.line ?? 0);
327
+ report(doc, {
328
+ code: "invalid-embed-spec",
329
+ severity: "error",
330
+ // The parser's own "at line 3, column 24" is dropped: it counts from
331
+ // the block, and the position below already says the same thing in
332
+ // the file's own coordinates.
333
+ message: error.message
334
+ .replace(/^cudoc: /, "")
335
+ .split("\n")[0]
336
+ .replace(/\s*at line \d+, column \d+:?\s*$/, "")
337
+ .trim(),
338
+ reference: `embed block ${blockNumber}`,
339
+ position: line === undefined
340
+ ? undefined
341
+ : {
342
+ start: { line, column: at?.col ?? 1 },
343
+ end: { line, column: at?.col ?? 1 },
344
+ },
345
+ });
346
+ return;
347
+ }
348
+ for (const reference of spec.sources ?? []) {
349
+ checkedReferences++;
350
+ const [pathname, anchor] = String(reference).split("#");
351
+ const id = resolveDocumentId(doc, pathname);
352
+ const target = id === undefined ? undefined : byId.get(id);
353
+ if (!target) {
354
+ report(doc, {
355
+ code: "missing-embed-source",
356
+ severity: "error",
357
+ message: `embed source ${reference} does not name a collected document`,
358
+ reference: String(reference),
359
+ });
360
+ continue;
361
+ }
362
+ const anchors = anchorsById.get(target.id) ?? [];
363
+ if (anchor && !anchors.some((entry) => entry.id === anchor)) {
364
+ report(doc, {
365
+ code: "missing-embed-anchor",
366
+ severity: "error",
367
+ message: `${target.id} has no section #${anchor} to embed`,
368
+ reference: String(reference),
369
+ available: anchors.map((entry) => entry.id),
370
+ });
371
+ continue;
372
+ }
373
+ // The same selection the resolver will apply, so what is inspected is
374
+ // what would actually be copied. `select.anchors` can name a section
375
+ // that does not exist, which `collectSections` rejects; the build hits
376
+ // the same error, so it is reported rather than swallowed.
377
+ const selection = anchor ? { anchors: [anchor] } : spec.select;
378
+ let copied;
379
+ try {
380
+ copied = selection
381
+ ? collectSections(target.tree, selection)
382
+ : [{ anchorId: undefined, tree: target.tree }];
383
+ }
384
+ catch (error) {
385
+ report(doc, {
386
+ code: "missing-embed-anchor",
387
+ severity: "error",
388
+ message: `${target.id}: ${error.message.replace(/^cudoc: /, "")}`,
389
+ reference: String(reference),
390
+ available: anchors.map((entry) => entry.id),
391
+ });
392
+ continue;
393
+ }
394
+ // Replacement runs on the original Markdown, not the tree, so the
395
+ // slices are recomputed the way `transformedSection` cuts them. A rule
396
+ // that matches no slice changed nothing, and the embed silently shows
397
+ // the source's own wording in a place written to expect otherwise.
398
+ const rules = spec.replace ?? [];
399
+ if (rules.length && target.source) {
400
+ const matched = rules.map(() => false);
401
+ for (const section of copied) {
402
+ const range = section.anchorId
403
+ ? target.source.sections[section.anchorId]
404
+ : undefined;
405
+ const slice = range
406
+ ? target.source.text.slice(range.start, spec.select?.includeChildren === false
407
+ ? (range.ownEnd ?? range.end)
408
+ : range.end)
409
+ : target.source.text;
410
+ matchedRules(slice, rules).forEach((hit, index) => {
411
+ if (hit)
412
+ matched[index] = true;
413
+ });
414
+ }
415
+ rules.forEach((rule, index) => {
416
+ if (matched[index])
417
+ return;
418
+ report(doc, {
419
+ code: "unmatched-embed-replacement",
420
+ severity: "warning",
421
+ message: `replacement ${index + 1} found no "${rule.find}" in what this embed copies from ${target.id}, so nothing was changed. The source may have been reworded${rule.regex ? "" : "; literal rules are case-sensitive"}.`,
422
+ reference: rule.find,
423
+ });
424
+ });
425
+ }
426
+ // A summary table carries heading text and nothing else, so whatever
427
+ // else the body holds never travels. Replacement above still applies,
428
+ // because it reaches the title and summary columns.
429
+ if (typeof spec.render === "object" && spec.render?.type === "table")
430
+ continue;
431
+ const names = [
432
+ ...new Set(copied.flatMap((section) => unportableComponents(section.tree))),
433
+ ];
434
+ if (names.length)
435
+ report(doc, {
436
+ code: "unportable-embed-component",
437
+ severity: "warning",
438
+ message: `this embed copies ${names.join(", ")} out of ${target.id}. A host that owns those components renders them, but standalone HTML export has no renderer for them and fails. Keep embedded sections to Markdown, or pass a renderer for each name.`,
439
+ reference: String(reference),
440
+ });
441
+ }
442
+ });
443
+ }
444
+ return { issues, documentCount: library.documents.length, checkedReferences };
445
+ }
446
+ // Posix path helpers, kept local so this module stays usable without importing
447
+ // the whole node path surface into a browser-safe boundary by accident.
448
+ const dirname = (value) => {
449
+ const at = value.lastIndexOf("/");
450
+ return at <= 0 ? "." : value.slice(0, at);
451
+ };
452
+ const posixJoin = (base, value) => base === "." ? value : `${base}/${value}`;
453
+ const normalize = (value) => {
454
+ const out = [];
455
+ for (const part of value.split("/")) {
456
+ if (!part || part === ".")
457
+ continue;
458
+ if (part === "..")
459
+ out.pop();
460
+ else
461
+ out.push(part);
462
+ }
463
+ return out.join("/");
464
+ };
465
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/node/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAyB,MAAM,mBAAmB,CAAA;AA6C7E,uEAAuE;AACvE,MAAM,QAAQ,GAAG,OAAO,CAAA;AAIxB,MAAM,SAAS,GAAG,CAAC,IAAkB,EAAE,KAAmC,EAAE,EAAE;IAC5E,KAAK,CAAC,IAAI,CAAC,CAAA;IACX,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAC5D,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,SAAS,CAAC,IAA+B,EAAE,CAAC,IAAI,EAAE,EAAE;QAClD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAM;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,CAAA;QACrC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE;YAC9B,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,EAAE,CAAC,CAAA;IACzE,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,CAAC,SAAiB,EAAY,EAAE;IAClD,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC/C,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;IAC5E,MAAM,MAAM,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAA;IACvD,OAAO;QACL,GAAG,IAAI,GAAG,CAAC;YACT,SAAS;YACT,GAAG,IAAI,MAAM,MAAM,EAAE;YACrB,GAAG,IAAI,OAAO,MAAM,EAAE;YACtB,GAAG,IAAI,GAAG,MAAM,EAAE;SACnB,CAAC;KACH,CAAA;AACH,CAAC,CAAA;AAED,MAAM,MAAM,GAAG,CACb,IAAY,EACZ,SAAiB,EACjB,IAAI,GAAG,CAAC,EACc,EAAE;IACxB,uEAAuE;IACvE,2EAA2E;IAC3E,sEAAsE;IACtE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;IAC/D,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;IACX,IAAI,SAAS,GAAG,IAAI,CAAA;IACpB,KACE,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAC/B,CAAC,IAAI,CAAC,EACN,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC/C,IAAI,SAAS,EAAE,GAAG,CAAC;gBAAE,SAAQ;YAC7B,EAAE,GAAG,CAAC,CAAA;YACN,MAAK;QACP,CAAC;IACH,IAAI,EAAE,GAAG,CAAC;QAAE,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACxC,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAA;IACtC,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;IACtD,MAAM,MAAM,GAAG,EAAE,CAAA;IACjB,OAAO;QACL,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;QAC/B,GAAG,EAAE;YACH,IAAI;YACJ,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM;YACjC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM;SAClC;KACF,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,oBAAoB,GAAG,CAAC,IAAU,EAAY,EAAE;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAC/B,SAAS,CAAC,IAA+B,EAAE,CAAC,IAAI,EAAE,EAAE;QAClD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAM;QAC5D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAM;QAC5E,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,KAAK,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,kFAAkF;AAClF,MAAM,WAAW,GAAG,CAAC,IAAkB,EAAU,EAAE;IACjD,IAAI,IAAI,GAAG,EAAE,CAAA;IACb,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;QACxB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;YAC1D,IAAI,IAAI,KAAK,CAAC,KAAK,CAAA;IACvB,CAAC,CAAC,CAAA;IACF,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED,mEAAmE;AACnE,MAAM,eAAe,GAAG,CAAC,IAAY,EAAY,EAAE;IACjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACvC,IAAI,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAKD;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,KAAoB,EAAa,EAAE;IACtE,MAAM,OAAO,GAAc,EAAE,CAAA;IAC7B,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,CAAA;gBACxD,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACpC,KAAK,GAAG,KAAK,CAAC,OAAO,CACnB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,EACxC,IAAI,CAAC,OAAO,CACb,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC1C,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACnD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;YACpE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;QACvB,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,UAAwB,EAAE;IAE1B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAA;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;IAC5C,MAAM,MAAM,GAAqB,EAAE,CAAA;IACnC,IAAI,iBAAiB,GAAG,CAAC,CAAA;IAEzB,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CACnE,CAAA;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAEnE,MAAM,MAAM,GAAG,CACb,GAAmB;IACnB,+EAA+E;IAC/E,KAAwD;IACxD,sFAAsF;IACtF,IAAI,GAAG,CAAC,EACR,EAAE;QACF,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAM;QAClC,MAAM,CAAC,IAAI,CAAC;YACV,GAAG,KAAK;YACR,UAAU,EAAE,GAAG,CAAC,EAAE;YAClB,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EACN,KAAK,CAAC,QAAQ;gBACd,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CACjC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,EACpE,SAAS,CACV;SACJ,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,0EAA0E;IAC1E,MAAM,iBAAiB,GAAG,CAAC,GAAmB,EAAE,GAAW,EAAE,EAAE;QAC7D,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,MAAM,GAAG,GAAG,CAAA;QAChB,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;YAC3C,MAAM,KAAK,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACzD,IAAI,CAAC,KAAK;gBAAE,OAAO,KAAK,CAAA,CAAC,iDAAiD;YAC1E,MAAM,GAAG,KAAK,CAAA;QAChB,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,CAAA;QAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,GAAG,EAAE;gBACV,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,GAAG,MAAM,CAAC,EAAE,mBAAmB,MAAM,EAAE;gBAChD,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;aAC5C,CAAC,CAAA;YACF,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,EAAE;gBACV,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,IAAI,MAAM,OAAO,MAAM,CAAC,EAAE,2JAA2J;gBAC9L,SAAS,EAAE,GAAG;aACf,CAAC,CAAA;QACJ,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED;;;;;;;OAOG;IACH,MAAM,iBAAiB,GAAG,CAAC,GAAmB,EAAE,QAAgB,EAAE,EAAE;QAClE,IAAI,2BAA2B,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAA;QAChE,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACpC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;QAC/C,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;IAC5D,CAAC,CAAA;IAED,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACpC,kEAAkE;QAClE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;QACxC,SAAS,CAAC,GAAG,CAAC,IAA+B,EAAE,CAAC,IAAI,EAAE,EAAE;YACtD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,OAAM;YACnC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,CAAA;YACrC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;gBAClC,IAAI,MAAM;oBACR,MAAM,CACJ,GAAG,EACH;wBACE,IAAI,EAAE,kBAAkB;wBACxB,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,6BAA6B,EAAE,EAAE;wBAC1C,SAAS,EAAE,IAAI,EAAE,EAAE;qBACpB,EACD,MAAM,CACP,CAAA;gBACH,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,CAAA;gBAC1B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACd,CAAC;YACD,uEAAuE;YACvE,+DAA+D;YAC/D,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;YAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAC3C,IAAI,KAAK;gBACP,MAAM,CAAC,GAAG,EAAE;oBACV,IAAI,EAAE,cAAc;oBACpB,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,kDAAkD,IAAI,CAAC,IAAI,EAAE,GAAG;oBACzE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;iBACpB,CAAC,CAAA;QACN,CAAC,CAAC,CAAA;QAEF,SAAS,CAAC,GAAG,CAAC,IAA+B,EAAE,CAAC,IAAI,EAAE,EAAE;YACtD,MAAM,GAAG,GACP,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;YACtE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG;gBAAE,OAAM;YAC3C,uEAAuE;YACvE,yEAAyE;YACzE,gEAAgE;YAChE,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,WAAW;gBAAE,OAAM;YAClD,iBAAiB,EAAE,CAAA;YACnB,IAAI,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAM,CAAC,WAAW;YAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC;gBAAE,OAAM;YAC/D,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAM,CAAC,0CAA0C;YAC1E,IAAI,CAAC,UAAU;gBAAE,OAAM;YACvB,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,UAAU,EAAE;gBACrD,GAAG,OAAO;gBACV,UAAU;aACX,CAAC,CAAA;YACF,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;gBAC3B,MAAM,CAAC,GAAG,EAAE;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,kBAAkB;oBAClE,QAAQ,EAAE,OAAO;oBACjB,OAAO,EACL,IAAI,CAAC,IAAI,KAAK,OAAO;wBACnB,CAAC,CAAC,qBAAqB,GAAG,kCAAkC;wBAC5D,CAAC,CAAC,2BAA2B,GAAG,EAAE;oBACtC,SAAS,EAAE,GAAG;iBACf,CAAC,CAAA;QACN,CAAC,CAAC,CAAA;QAEF,4EAA4E;QAC5E,yEAAyE;QACzE,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,CAAA;QACtD,IAAI,WAAW,GAAG,CAAC,CAAA;QACnB,SAAS,CAAC,GAAG,CAAC,IAA+B,EAAE,CAAC,IAAI,EAAE,EAAE;YACtD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa;gBAAE,OAAM;YAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;YACnC,IAAI,IAAuC,CAAA;YAC3C,IAAI,CAAC;gBACH,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,uEAAuE;gBACvE,2DAA2D;gBAC3D,MAAM,EAAE,GAAI,KAAiB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC1C,MAAM,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC,CAAA;gBACtE,MAAM,CAAC,GAAG,EAAE;oBACV,IAAI,EAAE,oBAAoB;oBAC1B,QAAQ,EAAE,OAAO;oBACjB,qEAAqE;oBACrE,mEAAmE;oBACnE,8BAA8B;oBAC9B,OAAO,EAAG,KAAe,CAAC,OAAO;yBAC9B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;yBACvB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;yBACd,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC;yBAC/C,IAAI,EAAE;oBACT,SAAS,EAAE,eAAe,WAAW,EAAE;oBACvC,QAAQ,EACN,IAAI,KAAK,SAAS;wBAChB,CAAC,CAAC,SAAS;wBACX,CAAC,CAAC;4BACE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE;4BACrC,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE;yBACpC;iBACR,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YACD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBAC3C,iBAAiB,EAAE,CAAA;gBACnB,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBACvD,MAAM,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;gBAC3C,MAAM,MAAM,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,GAAG,EAAE;wBACV,IAAI,EAAE,sBAAsB;wBAC5B,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,gBAAgB,SAAS,qCAAqC;wBACvE,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;qBAC7B,CAAC,CAAA;oBACF,SAAQ;gBACV,CAAC;gBACD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;gBAChD,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;oBAC5D,MAAM,CAAC,GAAG,EAAE;wBACV,IAAI,EAAE,sBAAsB;wBAC5B,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,GAAG,MAAM,CAAC,EAAE,oBAAoB,MAAM,WAAW;wBAC1D,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;wBAC5B,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;qBAC5C,CAAC,CAAA;oBACF,SAAQ;gBACV,CAAC;gBAED,sEAAsE;gBACtE,qEAAqE;gBACrE,uEAAuE;gBACvE,2DAA2D;gBAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;gBAC9D,IAAI,MAA2C,CAAA;gBAC/C,IAAI,CAAC;oBACH,MAAM,GAAG,SAAS;wBAChB,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;wBACzC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;gBAClD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,GAAG,EAAE;wBACV,IAAI,EAAE,sBAAsB;wBAC5B,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,GAAG,MAAM,CAAC,EAAE,KAAM,KAAe,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE;wBAC5E,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;wBAC5B,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;qBAC5C,CAAC,CAAA;oBACF,SAAQ;gBACV,CAAC;gBAED,kEAAkE;gBAClE,uEAAuE;gBACvE,sEAAsE;gBACtE,mEAAmE;gBACnE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;gBAChC,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;oBACtC,KAAK,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC;wBAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ;4BAC5B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;4BAC1C,CAAC,CAAC,SAAS,CAAA;wBACb,MAAM,KAAK,GAAG,KAAK;4BACjB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CACtB,KAAK,CAAC,KAAK,EACX,IAAI,CAAC,MAAM,EAAE,eAAe,KAAK,KAAK;gCACpC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC;gCAC7B,CAAC,CAAC,KAAK,CAAC,GAAG,CACd;4BACH,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAA;wBACtB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;4BAChD,IAAI,GAAG;gCAAE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;wBAChC,CAAC,CAAC,CAAA;oBACJ,CAAC;oBACD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBAC5B,IAAI,OAAO,CAAC,KAAK,CAAC;4BAAE,OAAM;wBAC1B,MAAM,CAAC,GAAG,EAAE;4BACV,IAAI,EAAE,6BAA6B;4BACnC,QAAQ,EAAE,SAAS;4BACnB,OAAO,EAAE,eAAe,KAAK,GAAG,CAAC,cAAc,IAAI,CAAC,IAAI,oCAAoC,MAAM,CAAC,EAAE,8DAA8D,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oCAAoC,GAAG;4BAC5N,SAAS,EAAE,IAAI,CAAC,IAAI;yBACrB,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAA;gBACJ,CAAC;gBAED,qEAAqE;gBACrE,sEAAsE;gBACtE,oDAAoD;gBACpD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,OAAO;oBAClE,SAAQ;gBAEV,MAAM,KAAK,GAAG;oBACZ,GAAG,IAAI,GAAG,CACR,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAChE;iBACF,CAAA;gBACD,IAAI,KAAK,CAAC,MAAM;oBACd,MAAM,CAAC,GAAG,EAAE;wBACV,IAAI,EAAE,4BAA4B;wBAClC,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,qBAAqB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,EAAE,wLAAwL;wBAC1P,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;qBAC7B,CAAC,CAAA;YACN,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAA;AAC/E,CAAC;AAED,+EAA+E;AAC/E,wEAAwE;AACxE,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE;IAChC,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACjC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAC3C,CAAC,CAAA;AACD,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAChD,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAA;AAC3C,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE;IAClC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG;YAAE,SAAQ;QACnC,IAAI,IAAI,KAAK,IAAI;YAAE,GAAG,CAAC,GAAG,EAAE,CAAA;;YACvB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtB,CAAC,CAAA"}
package/dist/node/cli.js CHANGED
@@ -5,24 +5,45 @@ import { pathToFileURL } from "node:url";
5
5
  import { buildDocuments, buildDocumentsAsync } from "./library.js";
6
6
  import { prepareEmbeds } from "./prepare-embeds.js";
7
7
  import { generateDataset } from "./dataset.js";
8
+ import { checkReferences } from "./check.js";
9
+ import { formatCheckResult } from "./report.js";
10
+ const USAGE = "Usage: cudoc <collect|check|dataset> --config config.mjs [--format text|json] [--strict]";
8
11
  try {
9
- const [command, ...args] = process.argv.slice(2);
10
- if (!["collect", "dataset"].includes(command) ||
11
- args.length !== 2 ||
12
- args[0] !== "--config")
13
- throw new Error("Usage: cudoc <collect|dataset> --config config.mjs");
14
- const config = args[1].endsWith(".json")
15
- ? JSON.parse(fs.readFileSync(args[1], "utf8"))
16
- : (await import(pathToFileURL(path.resolve(args[1])).href)).default;
17
- if (command === "dataset")
18
- console.log(JSON.stringify(generateDataset(config)));
19
- else {
12
+ const argv = process.argv.slice(2);
13
+ const [command] = argv;
14
+ const flags = new Set(argv.filter((value) => value.startsWith("--")));
15
+ const configIndex = argv.indexOf("--config");
16
+ if (!["collect", "check", "dataset"].includes(command) ||
17
+ configIndex === -1 ||
18
+ !argv[configIndex + 1])
19
+ throw new Error(USAGE);
20
+ const configPath = argv[configIndex + 1];
21
+ const config = configPath.endsWith(".json")
22
+ ? JSON.parse(fs.readFileSync(configPath, "utf8"))
23
+ : (await import(pathToFileURL(path.resolve(configPath)).href)).default;
24
+ const collect = async () => {
20
25
  const library = config.compiler
21
26
  ? await buildDocumentsAsync({
22
27
  ...config,
23
28
  compiler: async (...parameters) => config.compiler(...parameters),
24
29
  })
25
30
  : buildDocuments(config);
31
+ return library;
32
+ };
33
+ if (command === "dataset")
34
+ console.log(JSON.stringify(generateDataset(config)));
35
+ else if (command === "check") {
36
+ // Checking reads the same collection the build does, so it sees exactly
37
+ // what the site will render. It writes nothing.
38
+ const result = checkReferences(await collect(), config.check ?? {});
39
+ const json = argv[argv.indexOf("--format") + 1] === "json";
40
+ console.log(json ? JSON.stringify(result, null, 2) : formatCheckResult(result));
41
+ const blocking = result.issues.filter((issue) => issue.severity === "error" || flags.has("--strict"));
42
+ if (blocking.length)
43
+ process.exitCode = 1;
44
+ }
45
+ else {
46
+ const library = await collect();
26
47
  await prepareEmbeds(library, config.outDir);
27
48
  console.log(JSON.stringify({
28
49
  documentCount: library.documents.length,
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/node/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,IAAI,CAAC;IACH,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAChD,IACE,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QACzC,IAAI,CAAC,MAAM,KAAK,CAAC;QACjB,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU;QAEtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;IACrE,IAAI,OAAO,KAAK,SAAS;QACvB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;SACjD,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ;YAC7B,CAAC,CAAC,MAAM,mBAAmB,CAAC;gBACxB,GAAG,MAAM;gBACT,QAAQ,EAAE,KAAK,EACb,GAAG,UAA2D,EAC9D,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC;aACpC,CAAC;YACJ,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QAC1B,MAAM,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3C,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC;YACb,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;YACvC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,kBAAkB;SAC5C,CAAC,CACH,CAAA;IACH,CAAC;AACH,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACpB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/node/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Where a local link or image in a document actually points on disk.
3
+ *
4
+ * The HTML exporter copies what this finds; the reference checker reports what
5
+ * it does not. Both go through here so a document cannot pass one and fail the
6
+ * other: a link the checker calls resolvable is a link the exporter can copy.
7
+ */
8
+ export type LocalTargetRoots = {
9
+ /** The document source root. A document-relative path resolves here first. */
10
+ sourceRoot: string;
11
+ /** Extra roots holding host assets, such as `public` or `static`. */
12
+ assetDirs?: string[];
13
+ /**
14
+ * Strips a deployment base path from a root-relative URL, matching the host's
15
+ * routing. Defaults to identity for a site served from the domain root.
16
+ */
17
+ withoutBase?: (pathname: string) => string;
18
+ };
19
+ export type LocalTarget = {
20
+ kind: "external";
21
+ url: string;
22
+ } | {
23
+ kind: "resolved";
24
+ relative: string;
25
+ source: string;
26
+ suffix: string;
27
+ } | {
28
+ kind: "missing";
29
+ relative: string;
30
+ suffix: string;
31
+ };
32
+ /**
33
+ * Resolves one URL against the document that carries it.
34
+ *
35
+ * A fragment, scheme or protocol-relative URL is external and left alone. A
36
+ * document-relative path resolves against `sourceRoot`; a root-relative one is
37
+ * also tried against each asset directory, after the deployment base is
38
+ * removed. A path that escapes every root is reported as missing rather than
39
+ * resolved, so the caller names the link and its document instead of a
40
+ * traversal that means nothing to an author.
41
+ */
42
+ export declare function resolveLocalTarget(url: string, documentSourcePath: string, roots: LocalTargetRoots): LocalTarget;
43
+ //# sourceMappingURL=local-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-target.d.ts","sourceRoot":"","sources":["../../src/node/local-target.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;CAC3C,CAAA;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAWzD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,gBAAgB,GACtB,WAAW,CAwBb"}