@lucasviola/specwiki 1.0.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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +226 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/generate.d.ts +5 -0
  8. package/dist/commands/generate.d.ts.map +1 -0
  9. package/dist/commands/generate.js +197 -0
  10. package/dist/commands/generate.js.map +1 -0
  11. package/dist/commands/init.d.ts +5 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +91 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/open.d.ts +8 -0
  16. package/dist/commands/open.d.ts.map +1 -0
  17. package/dist/commands/open.js +150 -0
  18. package/dist/commands/open.js.map +1 -0
  19. package/dist/config/loader.d.ts +20 -0
  20. package/dist/config/loader.d.ts.map +1 -0
  21. package/dist/config/loader.js +143 -0
  22. package/dist/config/loader.js.map +1 -0
  23. package/dist/config/patterns.d.ts +9 -0
  24. package/dist/config/patterns.d.ts.map +1 -0
  25. package/dist/config/patterns.js +159 -0
  26. package/dist/config/patterns.js.map +1 -0
  27. package/dist/core/Logger.d.ts +8 -0
  28. package/dist/core/Logger.d.ts.map +1 -0
  29. package/dist/core/Logger.js +33 -0
  30. package/dist/core/Logger.js.map +1 -0
  31. package/dist/core/paths.d.ts +8 -0
  32. package/dist/core/paths.d.ts.map +1 -0
  33. package/dist/core/paths.js +45 -0
  34. package/dist/core/paths.js.map +1 -0
  35. package/dist/discover/specs.d.ts +11 -0
  36. package/dist/discover/specs.d.ts.map +1 -0
  37. package/dist/discover/specs.js +157 -0
  38. package/dist/discover/specs.js.map +1 -0
  39. package/dist/output/html/assets/highlight.css +101 -0
  40. package/dist/output/html/assets/search.js +368 -0
  41. package/dist/output/html/assets/specwiki.css +905 -0
  42. package/dist/output/html/renderer.d.ts +21 -0
  43. package/dist/output/html/renderer.d.ts.map +1 -0
  44. package/dist/output/html/renderer.js +203 -0
  45. package/dist/output/html/renderer.js.map +1 -0
  46. package/dist/output/html/search-index.d.ts +20 -0
  47. package/dist/output/html/search-index.d.ts.map +1 -0
  48. package/dist/output/html/search-index.js +46 -0
  49. package/dist/output/html/search-index.js.map +1 -0
  50. package/dist/output/html/templates/article.mustache +85 -0
  51. package/dist/output/html/templates/index.mustache +80 -0
  52. package/dist/output/html/templates/layout.mustache +205 -0
  53. package/dist/output/llms.d.ts +4 -0
  54. package/dist/output/llms.d.ts.map +1 -0
  55. package/dist/output/llms.js +44 -0
  56. package/dist/output/llms.js.map +1 -0
  57. package/dist/output/readme-index.d.ts +17 -0
  58. package/dist/output/readme-index.d.ts.map +1 -0
  59. package/dist/output/readme-index.js +71 -0
  60. package/dist/output/readme-index.js.map +1 -0
  61. package/dist/output/wiki.d.ts +11 -0
  62. package/dist/output/wiki.d.ts.map +1 -0
  63. package/dist/output/wiki.js +400 -0
  64. package/dist/output/wiki.js.map +1 -0
  65. package/dist/parse/markdown.d.ts +6 -0
  66. package/dist/parse/markdown.d.ts.map +1 -0
  67. package/dist/parse/markdown.js +225 -0
  68. package/dist/parse/markdown.js.map +1 -0
  69. package/dist/types.d.ts +96 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +2 -0
  72. package/dist/types.js.map +1 -0
  73. package/package.json +67 -0
@@ -0,0 +1,400 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { createRequire } from "node:module";
5
+ import { fileURLToPath } from "node:url";
6
+ import { log } from "../core/Logger.js";
7
+ import { CATEGORY_LABELS } from "../config/patterns.js";
8
+ import { renderMarkdown } from "../parse/markdown.js";
9
+ import { getHtmlRenderer, HtmlRenderer } from "./html/renderer.js";
10
+ import { buildSearchIndex, serializeSearchIndexForInlineScript, } from "./html/search-index.js";
11
+ import { categoryHasNonReadmePages, logIndexSummary, resolveReadmeIndexBindings, } from "./readme-index.js";
12
+ const require = createRequire(import.meta.url);
13
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
14
+ const PATH_ESCAPE_MESSAGE = "Path escapes output directory";
15
+ export class PathTraversalError extends Error {
16
+ constructor(relativePath) {
17
+ super(`${PATH_ESCAPE_MESSAGE}: ${relativePath}`);
18
+ this.name = "PathTraversalError";
19
+ }
20
+ }
21
+ export function assertPathConfined(outputDir, targetPath, relativePathForLog) {
22
+ const resolvedRoot = path.resolve(outputDir);
23
+ const resolvedTarget = path.resolve(targetPath);
24
+ const relative = path.relative(resolvedRoot, resolvedTarget);
25
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
26
+ log.error("output.error", {
27
+ relativePath: relativePathForLog,
28
+ message: PATH_ESCAPE_MESSAGE,
29
+ });
30
+ throw new PathTraversalError(relativePathForLog);
31
+ }
32
+ }
33
+ export function pageSlug(spec) {
34
+ const base = spec.file.relativePath
35
+ .replace(/\.(md|mdc|txt)$/, "")
36
+ .replace(/[/\\]/g, "-")
37
+ .replace(/^-+|-+$/g, "")
38
+ .toLowerCase();
39
+ return base || "untitled";
40
+ }
41
+ function slugHash(relativePath) {
42
+ return createHash("sha256").update(relativePath).digest("hex").slice(0, 8);
43
+ }
44
+ function assignUniqueSlugs(specs) {
45
+ const entries = specs.map((spec) => ({
46
+ spec,
47
+ base: pageSlug(spec),
48
+ }));
49
+ const byBase = new Map();
50
+ for (const entry of entries) {
51
+ const list = byBase.get(entry.base) ?? [];
52
+ list.push(entry);
53
+ byBase.set(entry.base, list);
54
+ }
55
+ const slugByPath = new Map();
56
+ for (const [, group] of byBase) {
57
+ if (group.length === 1) {
58
+ slugByPath.set(group[0].spec.file.relativePath, group[0].base);
59
+ continue;
60
+ }
61
+ const sorted = [...group].sort((a, b) => a.spec.file.relativePath.localeCompare(b.spec.file.relativePath));
62
+ for (let i = 0; i < sorted.length; i++) {
63
+ const { spec, base } = sorted[i];
64
+ if (i === 0) {
65
+ slugByPath.set(spec.file.relativePath, base);
66
+ continue;
67
+ }
68
+ const disambiguated = `${base}-${slugHash(spec.file.relativePath)}`;
69
+ log.info("output.slug-collision", {
70
+ originalSlug: base,
71
+ disambiguatedSlug: disambiguated,
72
+ sourcePath: spec.file.relativePath,
73
+ });
74
+ slugByPath.set(spec.file.relativePath, disambiguated);
75
+ }
76
+ }
77
+ return slugByPath;
78
+ }
79
+ function buildPageContent(spec) {
80
+ const lines = [
81
+ `# ${spec.title}`,
82
+ "",
83
+ `> Source: \`${spec.file.relativePath}\``,
84
+ "",
85
+ ];
86
+ if (spec.description) {
87
+ lines.push(spec.description, "");
88
+ }
89
+ if (spec.sections.length > 0) {
90
+ lines.push('<h2 id="specwiki-toc">Table of Contents</h2>', "");
91
+ for (const section of spec.sections) {
92
+ const indent = " ".repeat(Math.max(0, section.level - 2));
93
+ lines.push(`${indent}- [${section.title}](#${section.anchor})`);
94
+ }
95
+ lines.push("");
96
+ }
97
+ lines.push("---", "");
98
+ lines.push(spec.rawContent);
99
+ return lines.join("\n");
100
+ }
101
+ function buildIndex(pages, bindings, specs) {
102
+ const byCategory = new Map();
103
+ for (const page of pages) {
104
+ const list = byCategory.get(page.category) ?? [];
105
+ list.push(page);
106
+ byCategory.set(page.category, list);
107
+ }
108
+ const lines = ["# Spec Wiki", ""];
109
+ if (bindings.rootIntro) {
110
+ lines.push(bindings.rootIntro, "");
111
+ }
112
+ else {
113
+ lines.push("Structured documentation generated from AI specs, agent instructions,", "and spec-driven development files in this project.", "");
114
+ }
115
+ lines.push(`**${pages.length}** spec file${pages.length === 1 ? "" : "s"} indexed.`, "", "---", "");
116
+ const sortedCategories = [...byCategory.keys()]
117
+ .filter((category) => categoryHasNonReadmePages(specs, category))
118
+ .sort((a, b) => {
119
+ const labelA = CATEGORY_LABELS[a] ?? a;
120
+ const labelB = CATEGORY_LABELS[b] ?? b;
121
+ return labelA.localeCompare(labelB);
122
+ });
123
+ for (const category of sortedCategories) {
124
+ const categoryPages = byCategory.get(category);
125
+ const label = CATEGORY_LABELS[category] ?? category;
126
+ const categoryIntro = bindings.categoryIntros.get(category);
127
+ lines.push(`## ${label}`, "");
128
+ if (categoryIntro) {
129
+ lines.push(categoryIntro.content, "");
130
+ }
131
+ for (const page of categoryPages) {
132
+ lines.push(`- [${page.title}](${page.slug}.md) — \`${page.sourcePath}\``);
133
+ }
134
+ lines.push("");
135
+ }
136
+ return lines.join("\n");
137
+ }
138
+ export function buildWiki(specs) {
139
+ const slugByPath = assignUniqueSlugs(specs);
140
+ const bindings = resolveReadmeIndexBindings(specs);
141
+ const pages = specs.map((spec) => ({
142
+ slug: slugByPath.get(spec.file.relativePath) ?? pageSlug(spec),
143
+ title: spec.title,
144
+ category: spec.file.category,
145
+ content: buildPageContent(spec),
146
+ sourcePath: spec.file.relativePath,
147
+ description: spec.description,
148
+ sections: spec.sections,
149
+ }));
150
+ logIndexSummary(bindings.readmeIndexCount);
151
+ return {
152
+ pages,
153
+ indexContent: buildIndex(pages, bindings, specs),
154
+ indexMeta: {
155
+ rootIntro: bindings.rootIntro,
156
+ rootIntroSource: bindings.rootIntroSource,
157
+ categoryIntros: bindings.categoryIntros,
158
+ readmeIndexCount: bindings.readmeIndexCount,
159
+ },
160
+ };
161
+ }
162
+ export async function writeWiki(outputDir, wiki) {
163
+ try {
164
+ await fs.mkdir(outputDir, { recursive: true });
165
+ }
166
+ catch (err) {
167
+ log.error("output.error", {
168
+ relativePath: ".",
169
+ message: err instanceof Error ? err.message : String(err),
170
+ });
171
+ throw err;
172
+ }
173
+ const written = [];
174
+ const indexPath = path.join(outputDir, "index.md");
175
+ assertPathConfined(outputDir, indexPath, "index.md");
176
+ try {
177
+ await fs.writeFile(indexPath, wiki.indexContent, "utf-8");
178
+ }
179
+ catch (err) {
180
+ log.error("output.error", {
181
+ relativePath: "index.md",
182
+ message: err instanceof Error ? err.message : String(err),
183
+ });
184
+ throw err;
185
+ }
186
+ written.push(indexPath);
187
+ log.info("output.write", { relativePath: "index.md" });
188
+ for (const page of wiki.pages) {
189
+ const filePath = path.join(outputDir, `${page.slug}.md`);
190
+ const relativePath = `${page.slug}.md`;
191
+ assertPathConfined(outputDir, filePath, relativePath);
192
+ try {
193
+ await fs.writeFile(filePath, page.content, "utf-8");
194
+ }
195
+ catch (err) {
196
+ log.error("output.error", {
197
+ relativePath,
198
+ message: err instanceof Error ? err.message : String(err),
199
+ });
200
+ throw err;
201
+ }
202
+ written.push(filePath);
203
+ log.info("output.write", { relativePath: `${page.slug}.md` });
204
+ }
205
+ return written;
206
+ }
207
+ export async function writeHtmlWiki(outputDir, wiki, options = {}) {
208
+ const searchEnabled = !options.noSearch;
209
+ const htmlDir = path.join(outputDir, "html");
210
+ try {
211
+ await fs.mkdir(htmlDir, { recursive: true });
212
+ }
213
+ catch (err) {
214
+ log.error("output.error", {
215
+ relativePath: "html",
216
+ message: err instanceof Error ? err.message : String(err),
217
+ });
218
+ throw err;
219
+ }
220
+ let renderer;
221
+ try {
222
+ renderer = await getHtmlRenderer();
223
+ }
224
+ catch (err) {
225
+ log.error("output.error", {
226
+ relativePath: "html/templates",
227
+ message: err instanceof Error ? err.message : String(err),
228
+ });
229
+ throw err;
230
+ }
231
+ const written = [];
232
+ const assetsDir = path.join(htmlDir, "assets");
233
+ try {
234
+ await fs.mkdir(assetsDir, { recursive: true });
235
+ }
236
+ catch (err) {
237
+ log.error("output.error", {
238
+ relativePath: "html/assets",
239
+ message: err instanceof Error ? err.message : String(err),
240
+ });
241
+ throw err;
242
+ }
243
+ let bundledCss;
244
+ try {
245
+ bundledCss = await HtmlRenderer.bundleCss();
246
+ }
247
+ catch (err) {
248
+ log.error("output.error", {
249
+ relativePath: "html/assets/specwiki.css",
250
+ message: err instanceof Error ? err.message : String(err),
251
+ });
252
+ throw err;
253
+ }
254
+ const cssPath = path.join(assetsDir, HtmlRenderer.bundledCssFilename());
255
+ const cssRelativePath = "html/assets/specwiki.css";
256
+ assertPathConfined(outputDir, cssPath, cssRelativePath);
257
+ try {
258
+ await fs.writeFile(cssPath, bundledCss, "utf-8");
259
+ }
260
+ catch (err) {
261
+ log.error("output.error", {
262
+ relativePath: cssRelativePath,
263
+ message: err instanceof Error ? err.message : String(err),
264
+ });
265
+ throw err;
266
+ }
267
+ written.push(cssPath);
268
+ log.info("output.write", { relativePath: cssRelativePath });
269
+ let highlightCss;
270
+ try {
271
+ highlightCss = await HtmlRenderer.readHighlightCss();
272
+ }
273
+ catch (err) {
274
+ log.error("output.error", {
275
+ relativePath: "html/assets/highlight.css",
276
+ message: err instanceof Error ? err.message : String(err),
277
+ });
278
+ throw err;
279
+ }
280
+ const highlightCssPath = path.join(assetsDir, HtmlRenderer.highlightCssFilename());
281
+ const highlightCssRelativePath = "html/assets/highlight.css";
282
+ assertPathConfined(outputDir, highlightCssPath, highlightCssRelativePath);
283
+ try {
284
+ await fs.writeFile(highlightCssPath, highlightCss, "utf-8");
285
+ }
286
+ catch (err) {
287
+ log.error("output.error", {
288
+ relativePath: highlightCssRelativePath,
289
+ message: err instanceof Error ? err.message : String(err),
290
+ });
291
+ throw err;
292
+ }
293
+ written.push(highlightCssPath);
294
+ log.info("output.write", { relativePath: highlightCssRelativePath });
295
+ let searchIndexJson = "";
296
+ if (searchEnabled) {
297
+ try {
298
+ const searchIndex = buildSearchIndex(wiki.pages);
299
+ searchIndexJson = serializeSearchIndexForInlineScript(searchIndex);
300
+ const searchIndexPath = path.join(htmlDir, "search-index.json");
301
+ const searchIndexRelativePath = "html/search-index.json";
302
+ assertPathConfined(outputDir, searchIndexPath, searchIndexRelativePath);
303
+ await fs.writeFile(searchIndexPath, `${JSON.stringify(searchIndex, null, 2)}\n`, "utf-8");
304
+ written.push(searchIndexPath);
305
+ log.info("output.write", { relativePath: searchIndexRelativePath });
306
+ log.info("output.search-index", {
307
+ documentCount: searchIndex.documents.length,
308
+ });
309
+ const lunrSourcePath = require.resolve("lunr/lunr.min.js");
310
+ const lunrDestPath = path.join(assetsDir, "lunr.min.js");
311
+ const lunrRelativePath = "html/assets/lunr.min.js";
312
+ assertPathConfined(outputDir, lunrDestPath, lunrRelativePath);
313
+ await fs.copyFile(lunrSourcePath, lunrDestPath);
314
+ written.push(lunrDestPath);
315
+ log.info("output.write", { relativePath: lunrRelativePath });
316
+ const searchJsSourcePath = path.join(moduleDir, "html", "assets", "search.js");
317
+ const searchJsDestPath = path.join(assetsDir, "search.js");
318
+ const searchJsRelativePath = "html/assets/search.js";
319
+ assertPathConfined(outputDir, searchJsDestPath, searchJsRelativePath);
320
+ await fs.copyFile(searchJsSourcePath, searchJsDestPath);
321
+ written.push(searchJsDestPath);
322
+ log.info("output.write", { relativePath: searchJsRelativePath });
323
+ }
324
+ catch (err) {
325
+ log.error("output.error", {
326
+ relativePath: "html/search-index.json",
327
+ message: err instanceof Error ? err.message : String(err),
328
+ });
329
+ throw err;
330
+ }
331
+ }
332
+ const renderOptions = searchEnabled
333
+ ? { includeSearch: true, searchIndexJson }
334
+ : { includeSearch: false };
335
+ let indexHtml;
336
+ try {
337
+ log.info("output.render", { kind: "index", slug: "index" });
338
+ indexHtml = renderer.renderIndex(wiki.pages, wiki.indexMeta, renderOptions);
339
+ }
340
+ catch (err) {
341
+ log.error("output.error", {
342
+ relativePath: "html/index.html",
343
+ message: err instanceof Error ? err.message : String(err),
344
+ });
345
+ throw err;
346
+ }
347
+ const indexPath = path.join(htmlDir, "index.html");
348
+ assertPathConfined(outputDir, indexPath, "html/index.html");
349
+ try {
350
+ await fs.writeFile(indexPath, indexHtml, "utf-8");
351
+ }
352
+ catch (err) {
353
+ log.error("output.error", {
354
+ relativePath: "html/index.html",
355
+ message: err instanceof Error ? err.message : String(err),
356
+ });
357
+ throw err;
358
+ }
359
+ written.push(indexPath);
360
+ log.info("output.write", { relativePath: "html/index.html" });
361
+ for (const page of wiki.pages) {
362
+ let html;
363
+ try {
364
+ log.info("output.render", { kind: "article", slug: page.slug });
365
+ html = renderer.renderArticle(page, wiki.pages, renderMarkdown(page.content), renderOptions);
366
+ }
367
+ catch (err) {
368
+ log.error("output.error", {
369
+ relativePath: `html/${page.slug}.html`,
370
+ message: err instanceof Error ? err.message : String(err),
371
+ });
372
+ throw err;
373
+ }
374
+ const filePath = path.join(htmlDir, `${page.slug}.html`);
375
+ const relativePath = `html/${page.slug}.html`;
376
+ assertPathConfined(outputDir, filePath, relativePath);
377
+ try {
378
+ await fs.writeFile(filePath, html, "utf-8");
379
+ }
380
+ catch (err) {
381
+ log.error("output.error", {
382
+ relativePath,
383
+ message: err instanceof Error ? err.message : String(err),
384
+ });
385
+ throw err;
386
+ }
387
+ written.push(filePath);
388
+ log.info("output.write", { relativePath });
389
+ }
390
+ return written;
391
+ }
392
+ export function escapeHtml(text) {
393
+ return text
394
+ .replace(/&/g, "&amp;")
395
+ .replace(/</g, "&lt;")
396
+ .replace(/>/g, "&gt;")
397
+ .replace(/"/g, "&quot;")
398
+ .replace(/'/g, "&#39;");
399
+ }
400
+ //# sourceMappingURL=wiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki.js","sourceRoot":"","sources":["../../src/output/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EACL,gBAAgB,EAChB,mCAAmC,GACpC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,0BAA0B,GAE3B,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAE5D,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,YAAoB;QAC9B,KAAK,CAAC,GAAG,mBAAmB,KAAK,YAAY,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,UAAkB,EAClB,kBAA0B;IAE1B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAE7D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,kBAAkB;YAChC,OAAO,EAAE,mBAAmB;SAC7B,CAAC,CAAC;QACH,MAAM,IAAI,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAgB;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY;SAChC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,WAAW,EAAE,CAAC;IAEjB,OAAO,IAAI,IAAI,UAAU,CAAC;AAC5B,CAAC;AAED,SAAS,QAAQ,CAAC,YAAoB;IACpC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI;QACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;KACrB,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7C,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/D,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACtC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CACjE,CAAC;QAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACZ,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAC7C,SAAS;YACX,CAAC;YAED,MAAM,aAAa,GAAG,GAAG,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;gBAChC,YAAY,EAAE,IAAI;gBAClB,iBAAiB,EAAE,aAAa;gBAChC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;aACnC,CAAC,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAgB;IACxC,MAAM,KAAK,GAAa;QACtB,KAAK,IAAI,CAAC,KAAK,EAAE;QACjB,EAAE;QACF,eAAe,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI;QACzC,EAAE;KACH,CAAC;IAEF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,8CAA8C,EAAE,EAAE,CAAC,CAAC;QAC/D,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,KAAK,MAAM,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAClE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAE5B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CACjB,KAAiB,EACjB,QAA6B,EAC7B,KAAmB;IAEnB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,KAAK,GAAa,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAE5C,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACrC,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CACR,uEAAuE,EACvE,oDAAoD,EACpD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,MAAM,eAAe,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,EACxE,EAAE,EACF,KAAK,EACL,EAAE,CACH,CAAC;IAEF,MAAM,gBAAgB,GAAG,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;SAC5C,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;SAChE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEL,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;QAChD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;QACpD,MAAM,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE5D,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAE9B,IAAI,aAAa,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QAC5E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAmB;IAC3C,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC;IAEnD,MAAM,KAAK,GAAe,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC;QAC9D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;QAC5B,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC/B,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;QAClC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC,CAAC;IAEJ,eAAe,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE3C,OAAO;QACL,KAAK;QACL,YAAY,EAAE,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC;QAChD,SAAS,EAAE;YACT,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;SAC5C;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,SAAiB,EACjB,IAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,GAAG;YACjB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACnD,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,UAAU;YACxB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;IAEvD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC;QACvC,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;gBACxB,YAAY;gBACZ,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAC,CAAC;YACH,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,SAAiB,EACjB,IAAgB,EAChB,UAAgC,EAAE;IAElC,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,MAAM;YACpB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,QAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,gBAAgB;YAC9B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,aAAa;YAC3B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,0BAA0B;YACxC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACxE,MAAM,eAAe,GAAG,0BAA0B,CAAC;IACnD,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC;IAE5D,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,YAAY,CAAC,gBAAgB,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,2BAA2B;YACzC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAChC,SAAS,EACT,YAAY,CAAC,oBAAoB,EAAE,CACpC,CAAC;IACF,MAAM,wBAAwB,GAAG,2BAA2B,CAAC;IAC7D,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAC1E,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,wBAAwB;YACtC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,wBAAwB,EAAE,CAAC,CAAC;IAErE,IAAI,eAAe,GAAG,EAAE,CAAC;IACzB,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,eAAe,GAAG,mCAAmC,CAAC,WAAW,CAAC,CAAC;YAEnE,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;YAChE,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;YACzD,kBAAkB,CAAC,SAAS,EAAE,eAAe,EAAE,uBAAuB,CAAC,CAAC;YACxE,MAAM,EAAE,CAAC,SAAS,CAChB,eAAe,EACf,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAC3C,OAAO,CACR,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,uBAAuB,EAAE,CAAC,CAAC;YACpE,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE;gBAC9B,aAAa,EAAE,WAAW,CAAC,SAAS,CAAC,MAAM;aAC5C,CAAC,CAAC;YAEH,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACzD,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;YACnD,kBAAkB,CAAC,SAAS,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;YAC9D,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAE7D,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAClC,SAAS,EACT,MAAM,EACN,QAAQ,EACR,WAAW,CACZ,CAAC;YACF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAC3D,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;YACrD,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;YACtE,MAAM,EAAE,CAAC,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC/B,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;gBACxB,YAAY,EAAE,wBAAwB;gBACtC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAC,CAAC;YACH,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,aAAa;QACjC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE;QAC1C,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAE7B,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5D,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,iBAAiB;YAC/B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnD,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;YACxB,YAAY,EAAE,iBAAiB;YAC/B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAE9D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,GAAG,QAAQ,CAAC,aAAa,CAC3B,IAAI,EACJ,IAAI,CAAC,KAAK,EACV,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAC5B,aAAa,CACd,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;gBACxB,YAAY,EAAE,QAAQ,IAAI,CAAC,IAAI,OAAO;gBACtC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAC,CAAC;YACH,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,QAAQ,IAAI,CAAC,IAAI,OAAO,CAAC;QAC9C,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE;gBACxB,YAAY;gBACZ,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAC,CAAC;YACH,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ParsedSpec, SpecFile } from "../types.js";
2
+ export declare function slugify(text: string): string;
3
+ export declare function createAnchorAllocator(): (title: string) => string;
4
+ export declare function parseSpecFile(file: SpecFile): Promise<ParsedSpec>;
5
+ export declare function renderMarkdown(markdown: string): string;
6
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/parse/markdown.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AAErE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C;AAED,wBAAgB,qBAAqB,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAkBjE;AAwKD,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAsCvE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUvD"}
@@ -0,0 +1,225 @@
1
+ import fs from "node:fs/promises";
2
+ import matter from "gray-matter";
3
+ import hljs from "highlight.js/lib/core";
4
+ import bash from "highlight.js/lib/languages/bash";
5
+ import javascript from "highlight.js/lib/languages/javascript";
6
+ import json from "highlight.js/lib/languages/json";
7
+ import markdownLang from "highlight.js/lib/languages/markdown";
8
+ import typescript from "highlight.js/lib/languages/typescript";
9
+ import { marked } from "marked";
10
+ import { log } from "../core/Logger.js";
11
+ export function slugify(text) {
12
+ return text
13
+ .toLowerCase()
14
+ .replace(/[^\w\s-]/g, "")
15
+ .replace(/\s+/g, "-")
16
+ .replace(/-+/g, "-")
17
+ .trim();
18
+ }
19
+ export function createAnchorAllocator() {
20
+ const seen = new Map();
21
+ let emptyCounter = 0;
22
+ return (title) => {
23
+ const base = slugify(title);
24
+ if (!base || /^-*$/.test(base)) {
25
+ emptyCounter += 1;
26
+ return `section-${emptyCounter}`;
27
+ }
28
+ const count = seen.get(base) ?? 0;
29
+ seen.set(base, count + 1);
30
+ if (count === 0) {
31
+ return base;
32
+ }
33
+ return `${base}-${count + 1}`;
34
+ };
35
+ }
36
+ hljs.registerLanguage("bash", bash);
37
+ hljs.registerLanguage("javascript", javascript);
38
+ hljs.registerLanguage("json", json);
39
+ hljs.registerLanguage("markdown", markdownLang);
40
+ hljs.registerLanguage("typescript", typescript);
41
+ hljs.registerLanguage("js", javascript);
42
+ hljs.registerLanguage("ts", typescript);
43
+ hljs.registerLanguage("sh", bash);
44
+ hljs.registerLanguage("shell", bash);
45
+ function escapeHtml(text) {
46
+ return text
47
+ .replace(/&/g, "&amp;")
48
+ .replace(/</g, "&lt;")
49
+ .replace(/>/g, "&gt;")
50
+ .replace(/"/g, "&quot;")
51
+ .replace(/'/g, "&#39;");
52
+ }
53
+ function highlightCode(text, language) {
54
+ if (!hljs.getLanguage(language)) {
55
+ return null;
56
+ }
57
+ try {
58
+ return hljs.highlight(text, { language, ignoreIllegals: true }).value;
59
+ }
60
+ catch (err) {
61
+ log.error("render.error", {
62
+ message: err instanceof Error ? err.message : String(err),
63
+ });
64
+ return null;
65
+ }
66
+ }
67
+ let allocateHeadingAnchor = createAnchorAllocator();
68
+ marked.use({
69
+ gfm: true,
70
+ renderer: {
71
+ heading({ tokens, depth, text, raw }) {
72
+ const inner = this.parser.parseInline(tokens);
73
+ const tag = `h${depth}`;
74
+ if (depth >= 2 && depth <= 6) {
75
+ const rawTitle = raw
76
+ .trim()
77
+ .match(/^#+\s+(.+?)\s*#*\s*$/)?.[1]
78
+ ?.trim() ?? text;
79
+ const id = allocateHeadingAnchor(rawTitle);
80
+ const permalink = `<a class="heading-permalink" href="#${id}" aria-label="Link to this section">&#182;</a>`;
81
+ return `<${tag} id="${id}">${inner}${permalink}</${tag}>\n`;
82
+ }
83
+ return `<${tag}>${inner}</${tag}>\n`;
84
+ },
85
+ code({ text, lang, escaped }) {
86
+ const language = lang?.trim().split(/\s+/)[0]?.toLowerCase();
87
+ if (language) {
88
+ const highlighted = highlightCode(text, language);
89
+ if (highlighted !== null) {
90
+ return `<pre><code class="hljs language-${language}">${highlighted}</code></pre>\n`;
91
+ }
92
+ }
93
+ const body = escaped ? text : escapeHtml(text);
94
+ return `<pre><code>${body}</code></pre>\n`;
95
+ },
96
+ },
97
+ });
98
+ function extractSections(content) {
99
+ const lines = content.split("\n");
100
+ const sections = [];
101
+ let current = null;
102
+ const contentLines = [];
103
+ const allocateAnchor = createAnchorAllocator();
104
+ const flush = () => {
105
+ if (current) {
106
+ current.content = contentLines.join("\n").trim();
107
+ sections.push(current);
108
+ contentLines.length = 0;
109
+ }
110
+ };
111
+ for (const line of lines) {
112
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
113
+ if (headingMatch) {
114
+ flush();
115
+ const level = headingMatch[1].length;
116
+ const title = headingMatch[2].trim();
117
+ current = {
118
+ level,
119
+ title,
120
+ content: "",
121
+ anchor: allocateAnchor(title),
122
+ };
123
+ }
124
+ else if (current) {
125
+ contentLines.push(line);
126
+ }
127
+ }
128
+ flush();
129
+ return sections;
130
+ }
131
+ function extractDescription(content) {
132
+ const paragraphs = content
133
+ .split(/\n\n+/)
134
+ .map((p) => p.trim())
135
+ .filter((p) => p && !p.startsWith("#"));
136
+ return paragraphs[0]?.slice(0, 300) ?? "";
137
+ }
138
+ function stripFrontmatterBlock(raw) {
139
+ if (!raw.startsWith("---")) {
140
+ return raw;
141
+ }
142
+ const openingLineEnd = raw.indexOf("\n");
143
+ if (openingLineEnd === -1) {
144
+ return raw;
145
+ }
146
+ const closingIndex = raw.indexOf("\n---", openingLineEnd);
147
+ if (closingIndex === -1) {
148
+ return raw;
149
+ }
150
+ let contentStart = closingIndex + 4;
151
+ if (raw[contentStart] === "\r") {
152
+ contentStart += 1;
153
+ }
154
+ if (raw[contentStart] === "\n") {
155
+ contentStart += 1;
156
+ }
157
+ let content = raw.slice(contentStart);
158
+ if (content.startsWith("\r\n")) {
159
+ content = content.slice(2);
160
+ }
161
+ else if (content.startsWith("\n")) {
162
+ content = content.slice(1);
163
+ }
164
+ return content;
165
+ }
166
+ function parseFileContent(raw) {
167
+ try {
168
+ const { data, content } = matter(raw);
169
+ return { frontmatter: data, content, frontmatterFallback: false };
170
+ }
171
+ catch {
172
+ return {
173
+ frontmatter: {},
174
+ content: stripFrontmatterBlock(raw),
175
+ frontmatterFallback: true,
176
+ };
177
+ }
178
+ }
179
+ export async function parseSpecFile(file) {
180
+ try {
181
+ const raw = await fs.readFile(file.path, "utf-8");
182
+ const { frontmatter, content, frontmatterFallback } = parseFileContent(raw);
183
+ if (frontmatterFallback) {
184
+ log.info("parse.frontmatter-fallback", {
185
+ relativePath: file.relativePath,
186
+ });
187
+ }
188
+ const title = (typeof frontmatter.title === "string" && frontmatter.title) ||
189
+ file.title;
190
+ const sections = extractSections(content);
191
+ const parsed = {
192
+ file,
193
+ frontmatter,
194
+ title,
195
+ description: extractDescription(content),
196
+ sections,
197
+ rawContent: content,
198
+ };
199
+ log.info("parse.file", {
200
+ relativePath: file.relativePath,
201
+ sectionCount: sections.length,
202
+ });
203
+ return parsed;
204
+ }
205
+ catch (err) {
206
+ log.error("parse.error", {
207
+ path: file.relativePath,
208
+ message: err instanceof Error ? err.message : String(err),
209
+ });
210
+ throw err;
211
+ }
212
+ }
213
+ export function renderMarkdown(markdown) {
214
+ allocateHeadingAnchor = createAnchorAllocator();
215
+ try {
216
+ return marked.parse(markdown, { async: false });
217
+ }
218
+ catch (err) {
219
+ log.error("render.error", {
220
+ message: err instanceof Error ? err.message : String(err),
221
+ });
222
+ throw err;
223
+ }
224
+ }
225
+ //# sourceMappingURL=markdown.js.map