@qualcomm-ui/mdx-vite 2.14.2 → 2.15.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 (64) hide show
  1. package/dist/ai-knowledge/env.d.ts +27 -0
  2. package/dist/ai-knowledge/env.d.ts.map +1 -0
  3. package/dist/{open-web-ui-knowledge/generate-knowledge.d.ts → ai-knowledge/generator/command.d.ts} +3 -3
  4. package/dist/ai-knowledge/generator/command.d.ts.map +1 -0
  5. package/dist/ai-knowledge/generator/config.d.ts +4 -0
  6. package/dist/ai-knowledge/generator/config.d.ts.map +1 -0
  7. package/dist/ai-knowledge/generator/demo-plugin.d.ts +8 -0
  8. package/dist/ai-knowledge/generator/demo-plugin.d.ts.map +1 -0
  9. package/dist/ai-knowledge/generator/doc-props-plugin.d.ts +16 -0
  10. package/dist/ai-knowledge/generator/doc-props-plugin.d.ts.map +1 -0
  11. package/dist/ai-knowledge/generator/generator.types.d.ts +51 -0
  12. package/dist/ai-knowledge/generator/generator.types.d.ts.map +1 -0
  13. package/dist/ai-knowledge/generator/index.d.ts +2 -0
  14. package/dist/ai-knowledge/generator/index.d.ts.map +1 -0
  15. package/dist/ai-knowledge/generator/knowledge-generator.d.ts +35 -0
  16. package/dist/ai-knowledge/generator/knowledge-generator.d.ts.map +1 -0
  17. package/dist/ai-knowledge/generator/npm-install-tabs-plugin.d.ts +3 -0
  18. package/dist/ai-knowledge/generator/npm-install-tabs-plugin.d.ts.map +1 -0
  19. package/dist/ai-knowledge/generator/qds-theme-plugin.d.ts +7 -0
  20. package/dist/ai-knowledge/generator/qds-theme-plugin.d.ts.map +1 -0
  21. package/dist/ai-knowledge/generator/section-extractor.d.ts +47 -0
  22. package/dist/ai-knowledge/generator/section-extractor.d.ts.map +1 -0
  23. package/dist/ai-knowledge/generator/section.types.d.ts +83 -0
  24. package/dist/ai-knowledge/generator/section.types.d.ts.map +1 -0
  25. package/dist/ai-knowledge/generator/utils.d.ts +11 -0
  26. package/dist/ai-knowledge/generator/utils.d.ts.map +1 -0
  27. package/dist/ai-knowledge/open-web-ui/api.d.ts.map +1 -0
  28. package/dist/ai-knowledge/open-web-ui/common.d.ts +67 -0
  29. package/dist/ai-knowledge/open-web-ui/common.d.ts.map +1 -0
  30. package/dist/ai-knowledge/open-web-ui/download-knowledge.d.ts.map +1 -0
  31. package/dist/{open-web-ui-knowledge → ai-knowledge/open-web-ui}/knowledge-cleaner.d.ts +1 -1
  32. package/dist/ai-knowledge/open-web-ui/knowledge-cleaner.d.ts.map +1 -0
  33. package/dist/ai-knowledge/open-web-ui/upload-knowledge.d.ts.map +1 -0
  34. package/dist/ai-knowledge/types.d.ts +57 -0
  35. package/dist/ai-knowledge/types.d.ts.map +1 -0
  36. package/dist/cli.js +1879 -1459
  37. package/dist/cli.js.map +4 -4
  38. package/dist/docs-plugin/docs-plugin.d.ts.map +1 -1
  39. package/dist/docs-plugin/internal/config-schema.d.ts +30 -2
  40. package/dist/docs-plugin/internal/config-schema.d.ts.map +1 -1
  41. package/dist/docs-plugin/internal/services/markdown/remark-remove-jsx.d.ts.map +1 -1
  42. package/dist/docs-plugin/mdx-plugins.d.ts.map +1 -1
  43. package/dist/docs-plugin/remark/remark-extract-meta.d.ts +18 -0
  44. package/dist/docs-plugin/remark/remark-extract-meta.d.ts.map +1 -0
  45. package/dist/docs-plugin/types.d.ts +51 -6
  46. package/dist/docs-plugin/types.d.ts.map +1 -1
  47. package/dist/index.js +1126 -709
  48. package/dist/index.js.map +4 -4
  49. package/dist/tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/dist/open-web-ui-knowledge/api.d.ts.map +0 -1
  52. package/dist/open-web-ui-knowledge/common.d.ts +0 -33
  53. package/dist/open-web-ui-knowledge/common.d.ts.map +0 -1
  54. package/dist/open-web-ui-knowledge/download-knowledge.d.ts.map +0 -1
  55. package/dist/open-web-ui-knowledge/generate-knowledge.d.ts.map +0 -1
  56. package/dist/open-web-ui-knowledge/knowledge-cleaner.d.ts.map +0 -1
  57. package/dist/open-web-ui-knowledge/load-config-from-env.d.ts +0 -32
  58. package/dist/open-web-ui-knowledge/load-config-from-env.d.ts.map +0 -1
  59. package/dist/open-web-ui-knowledge/types.d.ts +0 -74
  60. package/dist/open-web-ui-knowledge/types.d.ts.map +0 -1
  61. package/dist/open-web-ui-knowledge/upload-knowledge.d.ts.map +0 -1
  62. /package/dist/{open-web-ui-knowledge → ai-knowledge/open-web-ui}/api.d.ts +0 -0
  63. /package/dist/{open-web-ui-knowledge → ai-knowledge/open-web-ui}/download-knowledge.d.ts +0 -0
  64. /package/dist/{open-web-ui-knowledge → ai-knowledge/open-web-ui}/upload-knowledge.d.ts +0 -0
package/dist/cli.js CHANGED
@@ -1815,14 +1815,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
1815
1815
  * @return {Command} `this` command for chaining
1816
1816
  * @private
1817
1817
  */
1818
- _optionEx(config2, flags, description, fn, defaultValue) {
1818
+ _optionEx(config3, flags, description, fn, defaultValue) {
1819
1819
  if (typeof flags === "object" && flags instanceof Option2) {
1820
1820
  throw new Error(
1821
1821
  "To add an Option object use addOption() instead of option() or requiredOption()"
1822
1822
  );
1823
1823
  }
1824
1824
  const option = this.createOption(flags, description);
1825
- option.makeOptionMandatory(!!config2.mandatory);
1825
+ option.makeOptionMandatory(!!config3.mandatory);
1826
1826
  if (typeof fn === "function") {
1827
1827
  option.default(defaultValue).argParser(fn);
1828
1828
  } else if (fn instanceof RegExp) {
@@ -2797,9 +2797,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2797
2797
  this._outputConfiguration.writeErr("\n");
2798
2798
  this.outputHelp({ error: true });
2799
2799
  }
2800
- const config2 = errorOptions || {};
2801
- const exitCode = config2.exitCode || 1;
2802
- const code = config2.code || "commander.error";
2800
+ const config3 = errorOptions || {};
2801
+ const exitCode = config3.exitCode || 1;
2802
+ const code = config3.code || "commander.error";
2803
2803
  this._exit(exitCode, code, message);
2804
2804
  }
2805
2805
  /**
@@ -3501,11 +3501,10 @@ var {
3501
3501
  Help
3502
3502
  } = import_index.default;
3503
3503
 
3504
- // src/docs-plugin/generate-page-map.ts
3505
- import { glob } from "glob";
3506
- import { writeFile } from "node:fs/promises";
3507
- import { resolve } from "node:path";
3508
- import { cwd } from "node:process";
3504
+ // src/ai-knowledge/env.ts
3505
+ import { config } from "dotenv";
3506
+ import { existsSync } from "node:fs";
3507
+ import { join as join2, resolve } from "node:path";
3509
3508
 
3510
3509
  // src/docs-plugin/internal/config-loader.ts
3511
3510
  import { cosmiconfigSync } from "cosmiconfig";
@@ -3555,15 +3554,27 @@ var knowledgeExtraFileSchema = implement().with({
3555
3554
  processAsMdx: z2.boolean().optional(),
3556
3555
  title: z2.string().optional()
3557
3556
  });
3557
+ var frontmatterConfigSchema = z2.object({
3558
+ exclude: z2.array(z2.string()).optional(),
3559
+ extraFields: z2.record(z2.string(), z2.union([z2.string(), z2.array(z2.string())])).optional(),
3560
+ include: z2.array(z2.string()).optional()
3561
+ }).optional();
3562
+ var sectionsExportsSchema = implement().with({
3563
+ depths: z2.array(z2.number()).optional(),
3564
+ minContentLength: z2.number().optional(),
3565
+ outputPath: z2.string().optional()
3566
+ });
3558
3567
  var knowledgeExportsSchema = implement().with({
3559
3568
  enabled: z2.boolean().optional(),
3560
3569
  exclude: z2.array(z2.string()).optional(),
3561
3570
  extraFiles: z2.array(knowledgeExtraFileSchema).optional(),
3571
+ frontmatter: frontmatterConfigSchema,
3562
3572
  generateBulkZip: z2.boolean().optional(),
3563
3573
  generateManifest: z2.boolean().optional(),
3564
3574
  manifestPath: z2.string().optional(),
3565
3575
  metadata: z2.record(z2.string(), z2.string()).optional(),
3566
3576
  pageTitlePrefix: z2.string().optional(),
3577
+ sections: sectionsExportsSchema.optional(),
3567
3578
  staticPath: z2.string().optional()
3568
3579
  });
3569
3580
  var knowledgeIntegrationSchema = implement().with(
@@ -3573,7 +3584,7 @@ var knowledgeIntegrationSchema = implement().with(
3573
3584
  exclude: z2.array(z2.string()).optional(),
3574
3585
  exports: knowledgeExportsSchema.optional(),
3575
3586
  extraFiles: z2.array(knowledgeExtraFileSchema).optional(),
3576
- frontmatterFields: z2.union([z2.array(z2.string()), z2.any()]).optional(),
3587
+ frontmatter: frontmatterConfigSchema,
3577
3588
  metadata: z2.record(z2.string(), z2.string()).optional(),
3578
3589
  name: z2.string().optional(),
3579
3590
  outputMode: z2.union([z2.literal("per-page"), z2.literal("aggregated")]).optional(),
@@ -3588,7 +3599,7 @@ var knowledgeEnvironmentSchema = implement().with({
3588
3599
  exclude: z2.array(z2.string()).optional(),
3589
3600
  exports: knowledgeExportsSchema.optional(),
3590
3601
  extraFiles: z2.array(knowledgeExtraFileSchema).optional(),
3591
- frontmatterFields: z2.array(z2.string()).optional(),
3602
+ frontmatter: frontmatterConfigSchema,
3592
3603
  id: z2.string(),
3593
3604
  metadata: z2.record(z2.string(), z2.string()).optional(),
3594
3605
  name: z2.string().optional(),
@@ -3682,8 +3693,8 @@ var ConfigLoader = class {
3682
3693
  }
3683
3694
  return result;
3684
3695
  }
3685
- resolveConfigFromCosmiconfig(config2) {
3686
- const parsed = configSchema.safeParse(config2.config);
3696
+ resolveConfigFromCosmiconfig(config3) {
3697
+ const parsed = configSchema.safeParse(config3.config);
3687
3698
  if (!parsed.success) {
3688
3699
  console.dir(parsed.error.issues, { depth: 10 });
3689
3700
  throw new Error("Failed to parse config file.");
@@ -3692,13 +3703,13 @@ var ConfigLoader = class {
3692
3703
  return {
3693
3704
  ...conf,
3694
3705
  appDirectory: conf.appDirectory || "app",
3695
- filePath: config2.filepath,
3706
+ filePath: config3.filepath,
3696
3707
  pageDirectory: conf.pageDirectory ? removeTrailingSlash(conf.pageDirectory) : "routes"
3697
3708
  };
3698
3709
  }
3699
3710
  loadConfig() {
3700
- const config2 = this.getCosmiconfig();
3701
- return this.resolveConfigFromCosmiconfig(config2);
3711
+ const config3 = this.getCosmiconfig();
3712
+ return this.resolveConfigFromCosmiconfig(config3);
3702
3713
  }
3703
3714
  };
3704
3715
 
@@ -4438,6 +4449,7 @@ var remarkRemoveJsx = () => {
4438
4449
  return (tree, _file, done) => {
4439
4450
  remove2(tree, "mdxjsEsm");
4440
4451
  remove2(tree, "mdxJsxFlowElement");
4452
+ remove2(tree, "mdxJsxTextElement");
4441
4453
  done();
4442
4454
  };
4443
4455
  };
@@ -5027,11 +5039,11 @@ function transformRouteMetaArray(meta, routeMetaNav) {
5027
5039
 
5028
5040
  // src/docs-plugin/internal/search-indexer.ts
5029
5041
  var SearchIndexer = class {
5030
- constructor(config2, logWarnings = true, addons = {}) {
5031
- this.config = config2;
5042
+ constructor(config3, logWarnings = true, addons = {}) {
5043
+ this.config = config3;
5032
5044
  this.logWarnings = logWarnings;
5033
5045
  this.allowedHeadings = new Set(
5034
- Array.from(config2?.headings || ["h2", "h3", "h4"])
5046
+ Array.from(config3?.headings || ["h2", "h3", "h4"])
5035
5047
  );
5036
5048
  this.metaJson = transformRouteMetaArray(
5037
5049
  this.config.navConfig ?? [],
@@ -5264,741 +5276,1032 @@ var SearchIndexer = class {
5264
5276
  }
5265
5277
  };
5266
5278
 
5267
- // src/docs-plugin/generate-page-map.ts
5268
- function addGeneratePageMapCommand() {
5269
- program.command("generate-page-map").description(
5270
- "Invokes the docs-plugin once to build the site data and writes it to json"
5271
- ).option(
5272
- "-c, --config-file <configFile>",
5273
- "Path to the qui-docs.config.ts config file"
5274
- ).option(
5275
- "-r, --routes-dir <routesDir>",
5276
- "Path to the routes directory",
5277
- "src/routes"
5278
- ).option(
5279
- "-o, --output <output>",
5280
- "Output path for the site data json",
5281
- "site-data.json"
5282
- ).action(async (options) => {
5283
- try {
5284
- const configLoader = new ConfigLoader({ configFile: options.configFile });
5285
- const resolvedConfig = configLoader.loadConfig();
5286
- const routesDir = fixPath(
5287
- resolve(resolvedConfig.appDirectory, resolvedConfig.pageDirectory)
5288
- );
5289
- const indexer = new SearchIndexer({
5290
- ...resolvedConfig,
5291
- srcDir: fixPath(resolve(cwd(), resolvedConfig.appDirectory)),
5292
- typeDocProps: {}
5293
- });
5294
- const files = glob.sync(
5295
- [`${routesDir}/**/*.mdx`, `${routesDir}/**/*.tsx`],
5296
- {
5297
- absolute: true,
5298
- cwd: cwd()
5299
- }
5300
- );
5301
- indexer.buildIndex(files, true);
5302
- await writeFile(
5303
- resolve(cwd(), options.output),
5304
- JSON.stringify(indexer.pageMap, null, 2),
5305
- "utf-8"
5306
- );
5307
- } catch (error) {
5308
- console.error(
5309
- "Generate Site Data Error:",
5310
- error instanceof Error ? error.message : String(error)
5311
- );
5312
- process.exit(1);
5313
- }
5314
- });
5279
+ // src/ai-knowledge/env.ts
5280
+ function loadEnv() {
5281
+ const options = program.optsWithGlobals();
5282
+ if (options.env) {
5283
+ config({ path: options.env, quiet: true });
5284
+ } else {
5285
+ config({ quiet: true });
5286
+ }
5315
5287
  }
5316
-
5317
- // src/open-web-ui-knowledge/download-knowledge.ts
5318
- import dotenv from "dotenv";
5319
- import { mkdir, writeFile as writeFile2 } from "node:fs/promises";
5320
- import { resolve as resolve2 } from "node:path";
5321
-
5322
- // src/open-web-ui-knowledge/api.ts
5323
- function isErrorResponse(response) {
5324
- return typeof response === "object" && response !== null && "detail" in response;
5288
+ function getConfigFromEnv() {
5289
+ const openWebUiUrl = process.env.WEB_UI_URL || process.env.OPEN_WEB_UI_URL;
5290
+ const openWebUiKey = process.env.WEB_UI_KEY || process.env.OPEN_WEB_UI_API_KEY;
5291
+ const knowledgeId = process.env.KNOWLEDGE_ID || process.env.OPEN_WEB_UI_KNOWLEDGE_ID;
5292
+ if (!openWebUiUrl || !openWebUiKey || !knowledgeId) {
5293
+ throw new Error("WEB_UI_URL, WEB_UI_KEY, and KNOWLEDGE_ID must be set");
5294
+ }
5295
+ return {
5296
+ knowledgeId,
5297
+ webUiKey: openWebUiKey,
5298
+ webUiUrl: openWebUiUrl
5299
+ };
5325
5300
  }
5326
- var FilesApi = class {
5327
- config;
5328
- constructor(config2) {
5329
- this.config = config2;
5301
+ function parseCliMetadata(cliMetadata) {
5302
+ if (!cliMetadata?.length) {
5303
+ return void 0;
5330
5304
  }
5331
- get headers() {
5332
- return {
5333
- Authorization: `Bearer ${this.config.apiKey}`
5334
- };
5305
+ return Object.fromEntries(cliMetadata.map((entry) => entry.split("=")));
5306
+ }
5307
+ function loadKnowledgeConfigFromEnv(options) {
5308
+ const configLoader = new ConfigLoader({});
5309
+ const resolvedConfig = configLoader.loadConfig();
5310
+ const fileConfig = resolvedConfig.knowledge?.global;
5311
+ const exclude = (options.exclude?.length ? options.exclude : void 0) ?? fileConfig?.exclude ?? (process.env.FILE_EXCLUDE_PATTERN ?? "").split(",").filter(Boolean);
5312
+ const outputPath = options.outputPath ?? fileConfig?.outputPath ?? process.env.KNOWLEDGE_OUTPUT_PATH;
5313
+ if (!outputPath) {
5314
+ throw new Error("Missing required outputPath");
5335
5315
  }
5336
- get jsonHeaders() {
5337
- return {
5338
- ...this.headers,
5339
- "Content-Type": "application/json"
5340
- };
5316
+ const routeDir = join2(
5317
+ resolvedConfig.appDirectory,
5318
+ resolvedConfig.pageDirectory
5319
+ );
5320
+ if (!existsSync(resolve(routeDir))) {
5321
+ throw new Error(`Route directory ${routeDir} does not exist`);
5341
5322
  }
5342
- async handleResponse(response) {
5343
- const data = await response.json();
5344
- if (isErrorResponse(data)) {
5345
- throw new Error(data.detail);
5346
- }
5347
- return data;
5323
+ const cliMetadata = parseCliMetadata(options.metadata);
5324
+ const mergedMetadata = fileConfig?.metadata || cliMetadata ? { ...fileConfig?.metadata, ...cliMetadata } : void 0;
5325
+ return {
5326
+ ...fileConfig,
5327
+ ...options,
5328
+ baseUrl: options.baseUrl ?? fileConfig?.baseUrl ?? process.env.DOCS_SITE_BASE_URL,
5329
+ docPropsPath: resolvedConfig.typeDocProps,
5330
+ exclude,
5331
+ extraFiles: fileConfig?.extraFiles,
5332
+ manifestOutputPath: outputPath,
5333
+ metadata: mergedMetadata,
5334
+ outputPath,
5335
+ pageTitlePrefix: options.pageTitlePrefix ?? fileConfig?.pageTitlePrefix ?? process.env.PAGE_TITLE_PREFIX,
5336
+ routeDir
5337
+ };
5338
+ }
5339
+ function mergeEnvironmentConfig(global, environment) {
5340
+ return {
5341
+ ...global,
5342
+ ...environment,
5343
+ extraFiles: environment.extraFiles ?? global?.extraFiles,
5344
+ metadata: global?.metadata || environment.metadata ? { ...global?.metadata, ...environment.metadata } : void 0
5345
+ };
5346
+ }
5347
+ function loadEnvironmentConfigs(options = {}) {
5348
+ const configLoader = new ConfigLoader({});
5349
+ const resolvedConfig = configLoader.loadConfig();
5350
+ const knowledgeConfig = resolvedConfig.knowledge;
5351
+ const globalConfig = knowledgeConfig?.global;
5352
+ const environments = knowledgeConfig?.environments;
5353
+ const routeDir = join2(
5354
+ resolvedConfig.appDirectory,
5355
+ resolvedConfig.pageDirectory
5356
+ );
5357
+ if (!existsSync(resolve(routeDir))) {
5358
+ throw new Error(`Route directory ${routeDir} does not exist`);
5348
5359
  }
5349
- async upload(file, filename, options) {
5350
- const formData = new FormData();
5351
- let blob;
5352
- if (file instanceof Blob) {
5353
- blob = file;
5354
- } else if (file instanceof ArrayBuffer) {
5355
- blob = new Blob([file]);
5356
- } else {
5357
- const copy = new Uint8Array(file).buffer;
5358
- blob = new Blob([copy]);
5359
- }
5360
- formData.append("file", blob, filename);
5361
- if (options?.metadata) {
5362
- formData.append("metadata", JSON.stringify(options.metadata));
5360
+ if (!environments || environments.length === 0 || options.cliOptions?.outputPath) {
5361
+ const legacyConfig = loadKnowledgeConfigFromEnv(
5362
+ options.cliOptions ?? { outputMode: "per-page" }
5363
+ );
5364
+ return [legacyConfig];
5365
+ }
5366
+ let filteredEnvironments = environments;
5367
+ if (options.environments?.length) {
5368
+ const filterSet = new Set(options.environments);
5369
+ filteredEnvironments = environments.filter((env) => filterSet.has(env.id));
5370
+ }
5371
+ if (filteredEnvironments.length === 0) {
5372
+ throw new Error(
5373
+ `No matching environments found. Available: ${environments.map((e) => e.id).join(", ")}`
5374
+ );
5375
+ }
5376
+ return filteredEnvironments.map((envConfig) => {
5377
+ const merged = mergeEnvironmentConfig(globalConfig, envConfig);
5378
+ const cliOpts = options.cliOptions;
5379
+ const cliMetadata = parseCliMetadata(cliOpts?.metadata);
5380
+ const mergedMetadata = merged.metadata || cliMetadata ? { ...merged.metadata, ...cliMetadata } : void 0;
5381
+ return {
5382
+ ...merged,
5383
+ ...cliOpts,
5384
+ baseUrl: cliOpts?.baseUrl ?? merged.baseUrl ?? process.env.DOCS_SITE_BASE_URL,
5385
+ docPropsPath: resolvedConfig.typeDocProps,
5386
+ environmentName: envConfig.id,
5387
+ exclude: (cliOpts?.exclude?.length ? cliOpts.exclude : void 0) ?? merged.exclude ?? (process.env.FILE_EXCLUDE_PATTERN ?? "").split(",").filter(Boolean),
5388
+ extraFiles: merged.extraFiles,
5389
+ manifestOutputPath: merged.outputPath,
5390
+ metadata: mergedMetadata,
5391
+ outputMode: cliOpts?.outputMode ?? merged.outputMode ?? "per-page",
5392
+ outputPath: merged.outputPath,
5393
+ pageTitlePrefix: cliOpts?.pageTitlePrefix ?? merged.pageTitlePrefix ?? process.env.PAGE_TITLE_PREFIX,
5394
+ routeDir
5395
+ };
5396
+ });
5397
+ }
5398
+
5399
+ // src/ai-knowledge/generator/knowledge-generator.ts
5400
+ import AdmZip from "adm-zip";
5401
+ import chalk3 from "chalk";
5402
+ import { minimatch } from "minimatch";
5403
+ import { mkdir, readdir, readFile as readFile4, rm, stat, writeFile } from "node:fs/promises";
5404
+ import { basename as basename2, dirname as dirname3, join as join6, relative as relative2, resolve as resolve4 } from "node:path";
5405
+ import remarkFrontmatter2 from "remark-frontmatter";
5406
+ import remarkMdx3 from "remark-mdx";
5407
+ import remarkParse4 from "remark-parse";
5408
+ import remarkParseFrontmatter2 from "remark-parse-frontmatter";
5409
+ import remarkStringify4 from "remark-stringify";
5410
+ import { unified as unified5 } from "unified";
5411
+ import { visit as visit11 } from "unist-util-visit";
5412
+ import { kebabCase as kebabCase3 } from "@qualcomm-ui/utils/change-case";
5413
+
5414
+ // src/docs-plugin/remark/remark-extract-meta.ts
5415
+ import { SKIP, visit as visit5 } from "unist-util-visit";
5416
+ function parseValue(value) {
5417
+ const trimmed = value.trim();
5418
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
5419
+ const inner = trimmed.slice(1, -1);
5420
+ return inner.split(",").map((item) => item.trim()).filter(Boolean);
5421
+ }
5422
+ return trimmed;
5423
+ }
5424
+ function parseMetaContent(content) {
5425
+ const result = {};
5426
+ const lines = content.split("\n");
5427
+ for (const line of lines) {
5428
+ const trimmed = line.trim();
5429
+ if (!trimmed || trimmed === ":::") {
5430
+ continue;
5363
5431
  }
5364
- const params = new URLSearchParams();
5365
- if (options?.process !== void 0) {
5366
- params.set("process", String(options.process));
5432
+ const colonIndex = trimmed.indexOf(":");
5433
+ if (colonIndex === -1) {
5434
+ continue;
5367
5435
  }
5368
- if (options?.processInBackground !== void 0) {
5369
- params.set("process_in_background", String(options.processInBackground));
5436
+ const key = trimmed.slice(0, colonIndex).trim();
5437
+ const value = trimmed.slice(colonIndex + 1).trim();
5438
+ if (key && value) {
5439
+ result[key] = parseValue(value);
5370
5440
  }
5371
- const url = `${this.config.baseUrl}/api/v1/files/${params.toString() ? `?${params}` : ""}`;
5372
- const response = await fetch(url, {
5373
- body: formData,
5374
- headers: this.headers,
5375
- method: "POST"
5376
- });
5377
- return this.handleResponse(response);
5378
5441
  }
5379
- async list(includeContent = true) {
5380
- const params = new URLSearchParams({ content: String(includeContent) });
5381
- const response = await fetch(
5382
- `${this.config.baseUrl}/api/v1/files/?${params}`,
5383
- {
5384
- headers: this.headers
5442
+ return result;
5443
+ }
5444
+ var remarkExtractMeta = (metadata = {}) => {
5445
+ return (tree) => {
5446
+ const nodesToRemove = [];
5447
+ visit5(tree, "paragraph", (node, index, parent) => {
5448
+ if (!parent || index === void 0) {
5449
+ return;
5450
+ }
5451
+ const firstChild = node.children[0];
5452
+ if (firstChild?.type !== "text") {
5453
+ return;
5454
+ }
5455
+ const text = firstChild.value;
5456
+ const openMatch = text.match(/^:::\s*meta\s*/);
5457
+ if (!openMatch) {
5458
+ return;
5459
+ }
5460
+ if (text.includes(":::") && text.lastIndexOf(":::") > openMatch[0].length) {
5461
+ const afterOpen = text.slice(openMatch[0].length);
5462
+ const closeIndex = afterOpen.lastIndexOf(":::");
5463
+ const content = afterOpen.slice(0, closeIndex);
5464
+ const parsed = parseMetaContent(content);
5465
+ Object.assign(metadata, parsed);
5466
+ nodesToRemove.push({ index, parent });
5467
+ return SKIP;
5468
+ }
5469
+ let fullText = text;
5470
+ for (let i = 1; i < node.children.length; i++) {
5471
+ const child = node.children[i];
5472
+ if (child.type === "text") {
5473
+ fullText += child.value;
5474
+ }
5475
+ }
5476
+ const afterOpenFull = fullText.slice(openMatch[0].length);
5477
+ const closeIndexFull = afterOpenFull.lastIndexOf(":::");
5478
+ if (closeIndexFull !== -1) {
5479
+ const content = afterOpenFull.slice(0, closeIndexFull);
5480
+ const parsed = parseMetaContent(content);
5481
+ Object.assign(metadata, parsed);
5482
+ nodesToRemove.push({ index, parent });
5483
+ return SKIP;
5385
5484
  }
5386
- );
5387
- return this.handleResponse(response);
5388
- }
5389
- async search(pattern, includeContent = true) {
5390
- const params = new URLSearchParams({
5391
- content: String(includeContent),
5392
- filename: pattern
5393
5485
  });
5394
- const response = await fetch(
5395
- `${this.config.baseUrl}/api/v1/files/search?${params}`,
5396
- { headers: this.headers }
5397
- );
5398
- if (response.status === 404) {
5399
- return [];
5486
+ for (let i = nodesToRemove.length - 1; i >= 0; i--) {
5487
+ const { index, parent } = nodesToRemove[i];
5488
+ parent.children.splice(index, 1);
5400
5489
  }
5401
- return this.handleResponse(response);
5490
+ };
5491
+ };
5492
+
5493
+ // src/ai-knowledge/generator/config.ts
5494
+ var currentConfig = null;
5495
+ function setConfig(config3) {
5496
+ currentConfig = config3;
5497
+ }
5498
+ function getConfig() {
5499
+ if (!currentConfig) {
5500
+ throw new Error("Config not initialized");
5402
5501
  }
5403
- async deleteAll() {
5404
- const response = await fetch(`${this.config.baseUrl}/api/v1/files/all`, {
5405
- headers: this.headers,
5406
- method: "DELETE"
5407
- });
5408
- return this.handleResponse(response);
5409
- }
5410
- async getById(id) {
5411
- const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
5412
- headers: this.headers
5413
- });
5414
- return this.handleResponse(response);
5502
+ return currentConfig;
5503
+ }
5504
+
5505
+ // src/ai-knowledge/generator/demo-plugin.ts
5506
+ import { readFile as readFile2 } from "node:fs/promises";
5507
+ import { basename, extname, join as join4 } from "node:path";
5508
+ import { visit as visit6 } from "unist-util-visit";
5509
+ import { kebabCase } from "@qualcomm-ui/utils/change-case";
5510
+
5511
+ // src/ai-knowledge/generator/utils.ts
5512
+ import { createHash as createHash2 } from "node:crypto";
5513
+ import { access, readFile } from "node:fs/promises";
5514
+ import { dirname, join as join3, resolve as resolve2 } from "node:path";
5515
+ import ts from "typescript";
5516
+ async function exists(dirPath) {
5517
+ return access(dirPath).then(() => true).catch(() => false);
5518
+ }
5519
+ function computeMd5(content) {
5520
+ return createHash2("md5").update(content).digest("hex");
5521
+ }
5522
+ function isPreviewLine(trimmedLine) {
5523
+ return trimmedLine === "// preview" || /^\{\s*\/\*\s*preview\s*\*\/\s*\}$/.test(trimmedLine) || /^<!--\s*preview\s*-->$/.test(trimmedLine);
5524
+ }
5525
+ function removePreviewLines(code) {
5526
+ return code.split("\n").filter((line) => !isPreviewLine(line.trim())).join("\n");
5527
+ }
5528
+ function getIntroLines(projectName, description) {
5529
+ const lines = [];
5530
+ if (projectName) {
5531
+ lines.push(`# ${projectName}`);
5415
5532
  }
5416
- async getProcessStatus(id) {
5417
- const response = await fetch(
5418
- `${this.config.baseUrl}/api/v1/files/${id}/process/status`,
5419
- { headers: this.headers }
5420
- );
5421
- return this.handleResponse(response);
5533
+ if (description) {
5534
+ lines.push("");
5535
+ lines.push(`> ${description}`);
5422
5536
  }
5423
- async waitForProcessing(id, options) {
5424
- const maxAttempts = options?.maxAttempts ?? 120;
5425
- const intervalMs = options?.intervalMs ?? 1e3;
5426
- for (let i = 0; i < maxAttempts; i++) {
5427
- const status = await this.getProcessStatus(id);
5428
- if (status.status === "completed" || status.status === "failed") {
5429
- return status;
5537
+ return lines.join("\n");
5538
+ }
5539
+ function extractRelativeImports(content) {
5540
+ const sourceFile = ts.createSourceFile(
5541
+ "temp.ts",
5542
+ content,
5543
+ ts.ScriptTarget.Latest,
5544
+ false,
5545
+ ts.ScriptKind.TSX
5546
+ );
5547
+ const imports = [];
5548
+ for (const statement of sourceFile.statements) {
5549
+ if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
5550
+ const path = statement.moduleSpecifier.text;
5551
+ if (path.startsWith(".")) {
5552
+ imports.push(path);
5430
5553
  }
5431
- await new Promise((resolve8) => setTimeout(resolve8, intervalMs));
5432
5554
  }
5433
- throw new Error(`File processing timed out after ${maxAttempts} attempts`);
5434
5555
  }
5435
- async getDataContent(id) {
5436
- const response = await fetch(
5437
- `${this.config.baseUrl}/api/v1/files/${id}/data/content`,
5438
- { headers: this.headers }
5439
- );
5440
- return this.handleResponse(response);
5556
+ return imports;
5557
+ }
5558
+ async function resolveModulePath(importPath, fromFile) {
5559
+ const fromDir = dirname(fromFile);
5560
+ const baseResolved = resolve2(fromDir, importPath);
5561
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ""];
5562
+ for (const ext of extensions) {
5563
+ const fullPath = baseResolved + ext;
5564
+ if (await exists(fullPath)) {
5565
+ return fullPath;
5566
+ }
5441
5567
  }
5442
- async updateDataContent(id, content) {
5443
- const response = await fetch(
5444
- `${this.config.baseUrl}/api/v1/files/${id}/data/content/update`,
5445
- {
5446
- body: JSON.stringify({ content }),
5447
- headers: this.jsonHeaders,
5448
- method: "POST"
5568
+ if (await exists(baseResolved)) {
5569
+ const indexPath = join3(baseResolved, "index.ts");
5570
+ if (await exists(indexPath)) {
5571
+ return indexPath;
5572
+ }
5573
+ }
5574
+ return null;
5575
+ }
5576
+ function extractMetadata(metadata) {
5577
+ return Object.entries(metadata ?? {});
5578
+ }
5579
+
5580
+ // src/ai-knowledge/generator/demo-plugin.ts
5581
+ async function collectDemoImports(demoCode, demoFilePath, visited = /* @__PURE__ */ new Set()) {
5582
+ const modules = [];
5583
+ const relativeImports = extractRelativeImports(demoCode);
5584
+ for (const importPath of relativeImports) {
5585
+ const resolvedPath = await resolveModulePath(importPath, demoFilePath);
5586
+ if (!resolvedPath || visited.has(resolvedPath)) {
5587
+ continue;
5588
+ }
5589
+ visited.add(resolvedPath);
5590
+ try {
5591
+ const importContent = await readFile2(resolvedPath, "utf-8");
5592
+ modules.push({
5593
+ content: importContent,
5594
+ path: resolvedPath
5595
+ });
5596
+ const nestedModules = await collectDemoImports(
5597
+ importContent,
5598
+ resolvedPath,
5599
+ visited
5600
+ );
5601
+ modules.push(...nestedModules);
5602
+ } catch {
5603
+ if (getConfig().verbose) {
5604
+ console.log(` Could not read import: ${resolvedPath}`);
5449
5605
  }
5450
- );
5451
- return this.handleResponse(response);
5606
+ }
5452
5607
  }
5453
- async getContent(id, asAttachment = false) {
5454
- const params = new URLSearchParams({ attachment: String(asAttachment) });
5455
- return fetch(
5456
- `${this.config.baseUrl}/api/v1/files/${id}/content?${params}`,
5457
- {
5458
- headers: this.headers
5608
+ return modules;
5609
+ }
5610
+ function formatDemos(demosFolder) {
5611
+ return () => async (tree) => {
5612
+ const promises = [];
5613
+ visit6(
5614
+ tree,
5615
+ "mdxJsxFlowElement",
5616
+ (node, index, parent) => {
5617
+ if (!node?.name || !["QdsDemo", "CodeDemo", "Demo"].includes(node.name)) {
5618
+ return;
5619
+ }
5620
+ const nameAttr = node.attributes?.find(
5621
+ (attr) => attr.type === "mdxJsxAttribute" && attr.name === "name"
5622
+ );
5623
+ const nodeAttr = node.attributes?.find(
5624
+ (attr) => attr.type === "mdxJsxAttribute" && attr.name === "node"
5625
+ );
5626
+ let demoName;
5627
+ if (nameAttr && typeof nameAttr.value === "string") {
5628
+ demoName = nameAttr.value;
5629
+ } else if (nodeAttr?.value && typeof nodeAttr.value !== "string") {
5630
+ const estree = nodeAttr.value.data?.estree;
5631
+ if (estree?.body?.[0]?.type === "ExpressionStatement") {
5632
+ const expression = estree.body[0].expression;
5633
+ if (expression.type === "MemberExpression" && expression.object.type === "Identifier" && expression.object.name === "Demo" && expression.property.type === "Identifier") {
5634
+ demoName = expression.property.name;
5635
+ }
5636
+ }
5637
+ }
5638
+ if (!demoName) {
5639
+ if (parent && index !== void 0) {
5640
+ parent.children.splice(index, 1);
5641
+ }
5642
+ return;
5643
+ }
5644
+ promises.push(
5645
+ (async () => {
5646
+ const kebabName = kebabCase(demoName);
5647
+ let filePath = `${kebabName}.tsx`;
5648
+ if (!demosFolder) {
5649
+ if (getConfig().verbose) {
5650
+ console.log(` No demos folder for ${demoName}`);
5651
+ }
5652
+ if (parent && index !== void 0) {
5653
+ parent.children.splice(index, 1);
5654
+ }
5655
+ return;
5656
+ }
5657
+ let demoFilePath = join4(demosFolder, filePath);
5658
+ let isAngularDemo = false;
5659
+ if (!await exists(demoFilePath)) {
5660
+ demoFilePath = join4(demosFolder, `${kebabName}.ts`);
5661
+ if (await exists(demoFilePath)) {
5662
+ isAngularDemo = true;
5663
+ filePath = `${kebabCase(demoName).replace("-component", ".component")}.ts`;
5664
+ demoFilePath = join4(demosFolder, filePath);
5665
+ } else {
5666
+ console.log(` Demo not found ${demoName}`);
5667
+ if (parent && index !== void 0) {
5668
+ parent.children.splice(index, 1);
5669
+ }
5670
+ return;
5671
+ }
5672
+ }
5673
+ try {
5674
+ const demoCode = await readFile2(demoFilePath, "utf-8");
5675
+ const cleanedCode = removePreviewLines(demoCode);
5676
+ if (getConfig().verbose) {
5677
+ console.log(` Replaced demo ${demoName} with source code`);
5678
+ }
5679
+ const demoCodeBlock = {
5680
+ lang: isAngularDemo ? "angular-ts" : "tsx",
5681
+ meta: null,
5682
+ type: "code",
5683
+ value: cleanedCode
5684
+ };
5685
+ const importedModules = await collectDemoImports(
5686
+ demoCode,
5687
+ demoFilePath
5688
+ );
5689
+ if (importedModules.length === 0 || !parent || index === void 0) {
5690
+ Object.assign(node, demoCodeBlock);
5691
+ } else {
5692
+ const nodesToInsert = [demoCodeBlock];
5693
+ for (const importedModule of importedModules) {
5694
+ const ext = extname(importedModule.path).slice(1);
5695
+ const filename = basename(importedModule.path);
5696
+ nodesToInsert.push({
5697
+ lang: ext,
5698
+ meta: `title="${filename}"`,
5699
+ type: "code",
5700
+ value: importedModule.content
5701
+ });
5702
+ }
5703
+ parent.children.splice(index, 1, ...nodesToInsert);
5704
+ if (getConfig().verbose) {
5705
+ console.log(
5706
+ ` Added ${importedModules.length} imported file(s) after demo`
5707
+ );
5708
+ }
5709
+ }
5710
+ } catch (error) {
5711
+ if (getConfig().verbose) {
5712
+ console.log(`Error reading demo ${demoName}`, error);
5713
+ }
5714
+ if (parent && index !== void 0) {
5715
+ parent.children.splice(index, 1);
5716
+ }
5717
+ }
5718
+ })()
5719
+ );
5459
5720
  }
5460
5721
  );
5722
+ await Promise.all(promises);
5723
+ };
5724
+ }
5725
+
5726
+ // src/ai-knowledge/generator/doc-props-plugin.ts
5727
+ import { readFile as readFile3 } from "node:fs/promises";
5728
+ import { dirname as dirname2, join as join5, resolve as resolve3 } from "node:path";
5729
+ import { visit as visit7 } from "unist-util-visit";
5730
+ function extractBestType(propInfo) {
5731
+ const type = propInfo.resolvedType?.prettyType || propInfo.type;
5732
+ return cleanType(type.startsWith("| ") ? type.substring(2) : type);
5733
+ }
5734
+ function extractRequired(propInfo, isPartial) {
5735
+ return Boolean(propInfo.resolvedType?.required && !isPartial);
5736
+ }
5737
+ function cleanType(type) {
5738
+ return type.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
5739
+ }
5740
+ function cleanDefaultValue(defaultValue) {
5741
+ return defaultValue.replace(/^\n+/, "").replace(/\n+$/, "").trim();
5742
+ }
5743
+ function escapeText(value) {
5744
+ return value.replace(/\n/g, " ");
5745
+ }
5746
+ function propsToDefinitionList(props) {
5747
+ if (props.length === 0) {
5748
+ return "";
5461
5749
  }
5462
- async getContentAsText(id) {
5463
- const response = await this.getContent(id);
5464
- if (!response.ok) {
5465
- const error = await response.json();
5466
- throw new Error(error.detail || "Failed to get file content");
5750
+ return props.map((prop) => {
5751
+ const parts = [`- **${prop.name}** (\`${escapeText(prop.type)}\``];
5752
+ if (prop.defaultValue) {
5753
+ parts.push(`, default: \`${escapeText(prop.defaultValue)}\``);
5467
5754
  }
5468
- return response.text();
5469
- }
5470
- async delete(id) {
5471
- const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
5472
- headers: this.headers,
5473
- method: "DELETE"
5474
- });
5475
- return this.handleResponse(response);
5476
- }
5477
- };
5478
- var KnowledgeApi = class {
5479
- config;
5480
- constructor(config2) {
5481
- this.config = config2;
5482
- }
5483
- get headers() {
5484
- return {
5485
- Authorization: `Bearer ${this.config.apiKey}`
5486
- };
5487
- }
5488
- get jsonHeaders() {
5489
- return {
5490
- ...this.headers,
5491
- "Content-Type": "application/json"
5492
- };
5493
- }
5494
- async handleResponse(response) {
5495
- const data = await response.json();
5496
- if (isErrorResponse(data)) {
5497
- throw new Error(data.detail);
5755
+ if (prop.required) {
5756
+ parts.push(", required");
5498
5757
  }
5499
- return data;
5500
- }
5501
- async list() {
5502
- const response = await fetch(`${this.config.baseUrl}/api/v1/knowledge/`, {
5503
- headers: this.headers
5504
- });
5505
- return this.handleResponse(response);
5506
- }
5507
- async listWritable() {
5508
- const response = await fetch(
5509
- `${this.config.baseUrl}/api/v1/knowledge/list`,
5510
- {
5511
- headers: this.headers
5758
+ parts.push(")");
5759
+ if (prop.description) {
5760
+ parts.push(` - ${escapeText(prop.description)}`);
5761
+ }
5762
+ return parts.join("");
5763
+ }).join("\n");
5764
+ }
5765
+ var PropFormatter = class {
5766
+ docProps = null;
5767
+ async loadDocProps() {
5768
+ if (this.docProps) {
5769
+ return this.docProps;
5770
+ }
5771
+ const config3 = getConfig();
5772
+ const resolvedDocPropsPath = config3.docPropsPath ? await exists(config3.docPropsPath) ? config3.docPropsPath : resolve3(process.cwd(), config3.docPropsPath) : join5(dirname2(config3.routeDir), "doc-props.json");
5773
+ if (!await exists(resolvedDocPropsPath)) {
5774
+ if (config3.verbose) {
5775
+ console.log(`Doc props file not found at: ${resolvedDocPropsPath}`);
5512
5776
  }
5513
- );
5514
- return this.handleResponse(response);
5515
- }
5516
- async create(form) {
5517
- const response = await fetch(
5518
- `${this.config.baseUrl}/api/v1/knowledge/create`,
5519
- {
5520
- body: JSON.stringify(form),
5521
- headers: this.jsonHeaders,
5522
- method: "POST"
5777
+ return null;
5778
+ }
5779
+ try {
5780
+ const content = await readFile3(resolvedDocPropsPath, "utf-8");
5781
+ const docProps = JSON.parse(content);
5782
+ if (config3.verbose) {
5783
+ console.log(`Loaded doc props from: ${resolvedDocPropsPath}`);
5784
+ console.log(
5785
+ `Found ${Object.keys(docProps.props).length} component types`
5786
+ );
5523
5787
  }
5524
- );
5525
- return this.handleResponse(response);
5526
- }
5527
- async reindex() {
5528
- const response = await fetch(
5529
- `${this.config.baseUrl}/api/v1/knowledge/reindex`,
5530
- {
5531
- headers: this.jsonHeaders,
5532
- method: "POST"
5788
+ this.docProps = docProps;
5789
+ return docProps;
5790
+ } catch (error) {
5791
+ if (config3.verbose) {
5792
+ console.log("Error loading doc props", error);
5533
5793
  }
5534
- );
5535
- return this.handleResponse(response);
5794
+ return null;
5795
+ }
5536
5796
  }
5537
- async getById(id) {
5538
- const response = await fetch(
5539
- `${this.config.baseUrl}/api/v1/knowledge/${id}`,
5540
- {
5541
- headers: this.headers
5542
- }
5543
- );
5544
- return this.handleResponse(response);
5545
- }
5546
- async update(id, form) {
5547
- const response = await fetch(
5548
- `${this.config.baseUrl}/api/v1/knowledge/${id}/update`,
5549
- {
5550
- body: JSON.stringify(form),
5551
- headers: this.jsonHeaders,
5552
- method: "POST"
5797
+ formatCommentParts(parts) {
5798
+ return parts.map((part) => {
5799
+ switch (part.kind) {
5800
+ case "text":
5801
+ return part.text;
5802
+ case "code":
5803
+ const codeText = part.text.replace(/```\w*\n?/g, "").replace(/\n?```/g, "").trim();
5804
+ if (codeText.includes("\n")) {
5805
+ return `\`\`\`
5806
+ ${codeText}
5807
+ \`\`\``;
5808
+ } else {
5809
+ return codeText;
5810
+ }
5811
+ default:
5812
+ if (getConfig().outputMode === "per-page" && "tag" in part && part.tag === "@link" && typeof part.target === "string") {
5813
+ if (part.text === "Learn more") {
5814
+ return "";
5815
+ }
5816
+ }
5817
+ return part.text;
5553
5818
  }
5554
- );
5555
- return this.handleResponse(response);
5819
+ }).join("").replace(/\n/g, " ").replace(/\s+/g, " ").trim();
5556
5820
  }
5557
- async addFile(knowledgeId, fileId) {
5558
- const response = await fetch(
5559
- `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/add`,
5560
- {
5561
- body: JSON.stringify({ file_id: fileId }),
5562
- headers: this.jsonHeaders,
5563
- method: "POST"
5821
+ formatComment(comment) {
5822
+ if (!comment) {
5823
+ return "";
5824
+ }
5825
+ const parts = [];
5826
+ if (comment.summary && comment.summary.length > 0) {
5827
+ const summaryText = this.formatCommentParts(comment.summary);
5828
+ if (summaryText.trim()) {
5829
+ parts.push(summaryText.trim());
5564
5830
  }
5565
- );
5566
- return this.handleResponse(response);
5567
- }
5568
- async updateFile(knowledgeId, fileId) {
5569
- const response = await fetch(
5570
- `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/update`,
5571
- {
5572
- body: JSON.stringify({ file_id: fileId }),
5573
- headers: this.jsonHeaders,
5574
- method: "POST"
5831
+ }
5832
+ if (comment.blockTags && comment.blockTags.length > 0) {
5833
+ for (const blockTag of comment.blockTags) {
5834
+ const tagContent = this.formatCommentParts(blockTag.content);
5835
+ if (tagContent.trim()) {
5836
+ const tagName = blockTag.tag.replace("@", "");
5837
+ if (tagName === "default" || tagName === "defaultValue") {
5838
+ continue;
5839
+ }
5840
+ if (tagName === "example") {
5841
+ parts.push(`**Example:**
5842
+ \`\`\`
5843
+ ${tagContent.trim()}
5844
+ \`\`\``);
5845
+ } else {
5846
+ parts.push(`**${tagName}:** ${tagContent.trim()}`);
5847
+ }
5848
+ }
5575
5849
  }
5576
- );
5577
- return this.handleResponse(response);
5850
+ }
5851
+ return parts.join("\n\n");
5578
5852
  }
5579
- async removeFile(knowledgeId, fileId, deleteFile = true) {
5580
- const params = new URLSearchParams({ delete_file: String(deleteFile) });
5581
- const response = await fetch(
5582
- `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/remove?${params}`,
5583
- {
5584
- body: JSON.stringify({ file_id: fileId }),
5585
- headers: this.jsonHeaders,
5586
- method: "POST"
5587
- }
5588
- );
5589
- return this.handleResponse(response);
5853
+ extractProps(props, isPartial) {
5854
+ const propsInfo = [];
5855
+ if (props.props?.length) {
5856
+ propsInfo.push(
5857
+ ...props.props.map((prop) => this.convertPropInfo(prop, isPartial))
5858
+ );
5859
+ }
5860
+ if (props.input?.length) {
5861
+ propsInfo.push(
5862
+ ...props.input.map(
5863
+ (prop) => this.convertPropInfo(prop, isPartial, "input")
5864
+ )
5865
+ );
5866
+ }
5867
+ if (props.output?.length) {
5868
+ propsInfo.push(
5869
+ ...props.output.map(
5870
+ (prop) => this.convertPropInfo(prop, isPartial, "output")
5871
+ )
5872
+ );
5873
+ }
5874
+ return propsInfo;
5590
5875
  }
5591
- async delete(id) {
5592
- const response = await fetch(
5593
- `${this.config.baseUrl}/api/v1/knowledge/${id}/delete`,
5594
- {
5595
- headers: this.headers,
5596
- method: "DELETE"
5597
- }
5598
- );
5599
- return this.handleResponse(response);
5876
+ convertPropInfo(propInfo, isPartial, propType = void 0) {
5877
+ return {
5878
+ name: propInfo.name,
5879
+ type: extractBestType(propInfo),
5880
+ ...propInfo.defaultValue && {
5881
+ defaultValue: cleanDefaultValue(propInfo.defaultValue)
5882
+ },
5883
+ description: this.formatComment(propInfo.comment || null),
5884
+ propType,
5885
+ required: extractRequired(propInfo, isPartial) || void 0
5886
+ };
5600
5887
  }
5601
- async reset(id) {
5602
- const response = await fetch(
5603
- `${this.config.baseUrl}/api/v1/knowledge/${id}/reset`,
5604
- {
5605
- headers: this.jsonHeaders,
5606
- method: "POST"
5607
- }
5608
- );
5609
- return this.handleResponse(response);
5888
+ /**
5889
+ * Creates a remark plugin that replaces TypeDocProps JSX elements with
5890
+ * Markdown tables containing component prop documentation.
5891
+ */
5892
+ formatTypeDocProps() {
5893
+ return () => (tree, _file, done) => {
5894
+ visit7(
5895
+ tree,
5896
+ "mdxJsxFlowElement",
5897
+ (node, index, parent) => {
5898
+ if (node?.name !== "TypeDocProps") {
5899
+ return;
5900
+ }
5901
+ const nameAttr = node.attributes?.find(
5902
+ (attr) => attr.type === "mdxJsxAttribute" && attr.name === "name"
5903
+ );
5904
+ const isPartial = node.attributes?.some(
5905
+ (attr) => attr.type === "mdxJsxAttribute" && attr.name === "partial"
5906
+ );
5907
+ if (!this.docProps || !nameAttr) {
5908
+ if (parent && index !== void 0) {
5909
+ parent.children.splice(index, 1);
5910
+ }
5911
+ return;
5912
+ }
5913
+ const propsNames = extractNamesFromAttribute(nameAttr);
5914
+ if (propsNames.length === 0) {
5915
+ if (parent && index !== void 0) {
5916
+ parent.children.splice(index, 1);
5917
+ }
5918
+ return;
5919
+ }
5920
+ const propsName = propsNames[0];
5921
+ const componentProps = this.docProps.props[propsName];
5922
+ if (!componentProps) {
5923
+ if (getConfig().verbose) {
5924
+ console.log(` TypeDocProps not found: ${propsName}`);
5925
+ }
5926
+ if (parent && index !== void 0) {
5927
+ parent.children.splice(index, 1);
5928
+ }
5929
+ return;
5930
+ }
5931
+ const propsDoc = this.extractProps(componentProps, Boolean(isPartial));
5932
+ if (getConfig().verbose) {
5933
+ console.log(
5934
+ ` Replaced TypeDocProps ${propsName} with API documentation`
5935
+ );
5936
+ }
5937
+ const regularProps = propsDoc.filter((p) => p.propType === void 0);
5938
+ const inputs = propsDoc.filter((p) => p.propType === "input");
5939
+ const outputs = propsDoc.filter((p) => p.propType === "output");
5940
+ const sections = [];
5941
+ if (regularProps.length > 0) {
5942
+ sections.push(propsToDefinitionList(regularProps));
5943
+ }
5944
+ if (inputs.length > 0) {
5945
+ sections.push(`**Inputs**
5946
+
5947
+ ${propsToDefinitionList(inputs)}`);
5948
+ }
5949
+ if (outputs.length > 0) {
5950
+ sections.push(`**Outputs**
5951
+
5952
+ ${propsToDefinitionList(outputs)}`);
5953
+ }
5954
+ const markdownContent = sections.join("\n\n");
5955
+ if (!markdownContent) {
5956
+ if (parent && index !== void 0) {
5957
+ parent.children.splice(index, 1);
5958
+ }
5959
+ return;
5960
+ }
5961
+ const propNames = propsDoc.map((p) => p.name);
5962
+ Object.assign(node, {
5963
+ data: { typeDocProps: { name: propsName, props: propNames } },
5964
+ lang: null,
5965
+ meta: null,
5966
+ type: "code",
5967
+ value: markdownContent
5968
+ });
5969
+ }
5970
+ );
5971
+ done();
5972
+ };
5610
5973
  }
5611
- async addFilesBatch(knowledgeId, fileIds) {
5612
- const response = await fetch(
5613
- `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/files/batch/add`,
5614
- {
5615
- body: JSON.stringify(fileIds.map((file_id) => ({ file_id }))),
5616
- headers: this.jsonHeaders,
5617
- method: "POST"
5974
+ };
5975
+
5976
+ // src/ai-knowledge/generator/npm-install-tabs-plugin.ts
5977
+ import { visit as visit8 } from "unist-util-visit";
5978
+ var formatNpmInstallTabs = () => {
5979
+ return (tree, _file, done) => {
5980
+ visit8(
5981
+ tree,
5982
+ "mdxJsxFlowElement",
5983
+ (node, index, parent) => {
5984
+ if (node?.name === "NpmInstallTabs") {
5985
+ const packages = node.attributes?.find(
5986
+ (attr) => attr.type === "mdxJsxAttribute" && attr.name === "packages"
5987
+ );
5988
+ const packageNames = packages ? extractNamesFromAttribute(packages) : [];
5989
+ if (packageNames.length === 0) {
5990
+ if (parent && index !== void 0) {
5991
+ parent.children.splice(index, 1);
5992
+ }
5993
+ return;
5994
+ }
5995
+ Object.assign(node, {
5996
+ lang: "shell",
5997
+ meta: null,
5998
+ type: "code",
5999
+ value: `npm install ${packageNames.join(" ")}`
6000
+ });
6001
+ }
5618
6002
  }
5619
6003
  );
5620
- return this.handleResponse(response);
5621
- }
6004
+ done();
6005
+ };
5622
6006
  };
5623
6007
 
5624
- // src/open-web-ui-knowledge/common.ts
5625
- import { config } from "dotenv";
5626
- function loadEnv() {
5627
- const options = program.optsWithGlobals();
5628
- if (options.env) {
5629
- config({ path: options.env });
5630
- } else {
5631
- config();
5632
- }
5633
- }
5634
- function getConfigFromEnv() {
5635
- const openWebUiUrl = process.env.WEB_UI_URL || process.env.OPEN_WEB_UI_URL;
5636
- const openWebUiKey = process.env.WEB_UI_KEY || process.env.OPEN_WEB_UI_API_KEY;
5637
- const knowledgeId = process.env.KNOWLEDGE_ID || process.env.OPEN_WEB_UI_KNOWLEDGE_ID;
5638
- if (!openWebUiUrl || !openWebUiKey || !knowledgeId) {
5639
- throw new Error("WEB_UI_URL, WEB_UI_KEY, and KNOWLEDGE_ID must be set");
6008
+ // src/ai-knowledge/generator/qds-theme-plugin.ts
6009
+ import { visit as visit9 } from "unist-util-visit";
6010
+ function themeDataToJson(data, cssPropertyName) {
6011
+ if (!data || typeof data !== "object") {
6012
+ return "";
5640
6013
  }
5641
- return {
5642
- knowledgeId,
5643
- webUiKey: openWebUiKey,
5644
- webUiUrl: openWebUiUrl
5645
- };
5646
- }
5647
- function loadOpenWebUiEnv(integration, integrationName) {
5648
- const envFilePath = integration.envFile ?? `.env.${integration.id}`;
5649
- config({ override: true, path: envFilePath });
5650
- const url = process.env.OPEN_WEB_UI_URL ?? process.env.WEB_UI_URL;
5651
- const apiKey = process.env.OPEN_WEB_UI_API_KEY ?? process.env.WEB_UI_KEY;
5652
- const knowledgeId = process.env.OPEN_WEB_UI_KNOWLEDGE_ID ?? process.env.KNOWLEDGE_ID;
5653
- if (!url) {
5654
- throw new Error(
5655
- `Missing OPEN_WEB_UI_URL for integration "${integrationName}" (env file: ${envFilePath})`
5656
- );
6014
+ if (cssPropertyName) {
6015
+ return JSON.stringify({ cssProperty: cssPropertyName, data }, null, 2);
5657
6016
  }
5658
- if (!apiKey) {
5659
- throw new Error(
5660
- `Missing OPEN_WEB_UI_API_KEY for integration "${integrationName}" (env file: ${envFilePath})`
5661
- );
6017
+ return JSON.stringify(data, null, 2);
6018
+ }
6019
+ function getPath(obj, path) {
6020
+ return path.split(".").reduce(
6021
+ (acc, key) => acc && typeof acc === "object" ? acc[key] : void 0,
6022
+ obj
6023
+ );
6024
+ }
6025
+ function getAttrExpression(node, name) {
6026
+ const attr = node.attributes?.find(
6027
+ (a) => a.type === "mdxJsxAttribute" && a.name === name
6028
+ );
6029
+ if (!attr?.value) {
6030
+ return null;
5662
6031
  }
5663
- if (!knowledgeId) {
5664
- throw new Error(
5665
- `Missing OPEN_WEB_UI_KNOWLEDGE_ID for integration "${integrationName}" (env file: ${envFilePath})`
5666
- );
6032
+ if (typeof attr.value === "string") {
6033
+ return attr.value;
6034
+ } else if (typeof attr.value === "object" && "value" in attr.value) {
6035
+ return attr.value.value;
5667
6036
  }
5668
- return { apiKey, knowledgeId, url };
6037
+ return null;
5669
6038
  }
5670
- function resolveOpenWebUiIntegration(name, integration, outputPath) {
5671
- const credentials = loadOpenWebUiEnv(integration, name);
5672
- return {
5673
- apiKey: credentials.apiKey,
5674
- environment: integration.id,
5675
- knowledgeId: credentials.knowledgeId,
5676
- name,
5677
- outputPath,
5678
- url: credentials.url
6039
+ async function formatThemeNodes() {
6040
+ let themes = null;
6041
+ try {
6042
+ themes = await import("@qualcomm-ui/tailwind-plugin/theme");
6043
+ } catch {
6044
+ return () => {
6045
+ };
6046
+ }
6047
+ const handlers = {
6048
+ ColorTable: (node) => {
6049
+ const path = getAttrExpression(node, "data");
6050
+ return path && getPath(themes, path);
6051
+ },
6052
+ FontTable: (node) => {
6053
+ const path = getAttrExpression(node, "data");
6054
+ return path && getPath(themes, path);
6055
+ },
6056
+ SpacingTable: (node) => {
6057
+ const path = getAttrExpression(node, "data");
6058
+ return path && getPath(themes, path);
6059
+ },
6060
+ ThemePropertyTable: (node) => {
6061
+ const path = getAttrExpression(node, "data");
6062
+ const property = getAttrExpression(node, "cssProperty");
6063
+ const data = path && getPath(themes, path);
6064
+ return path && property ? { cssPropertyName: property, data } : void 0;
6065
+ }
5679
6066
  };
5680
- }
5681
-
5682
- // src/open-web-ui-knowledge/download-knowledge.ts
5683
- function addDownloadKnowledgeCommand() {
5684
- program.command("download-knowledge").description("Download files from an Open Web UI knowledge base").requiredOption("-o, --output-dir <outputDir>", "Folder path").requiredOption("-e, --environment <environments>", "environment to load").action(async (opts) => {
5685
- loadEnv();
5686
- const env = opts.environment;
5687
- dotenv.config({ path: `.env.${env}` });
5688
- await mkdir(opts.outputDir, { recursive: true }).catch();
5689
- const config2 = getConfigFromEnv();
5690
- const apiConfig = { apiKey: config2.webUiKey, baseUrl: config2.webUiUrl };
5691
- const knowledgeApi = new KnowledgeApi(apiConfig);
5692
- const filesApi = new FilesApi(apiConfig);
5693
- const knowledge = await knowledgeApi.getById(config2.knowledgeId);
5694
- for (const file of knowledge.files ?? []) {
5695
- const fileName = file.meta?.name;
5696
- if (!fileName) {
5697
- continue;
6067
+ return () => (tree, _file, done) => {
6068
+ visit9(tree, "mdxJsxFlowElement", (node) => {
6069
+ const handler = node.name && handlers[node.name];
6070
+ if (!handler) {
6071
+ return;
5698
6072
  }
5699
- try {
5700
- const content = await filesApi.getDataContent(file.id);
5701
- if (content?.content) {
5702
- await writeFile2(
5703
- resolve2(opts.outputDir, fileName),
5704
- content.content,
5705
- "utf-8"
5706
- );
5707
- }
5708
- } catch {
5709
- console.warn(`Failed to download ${fileName}`);
6073
+ const data = handler(node);
6074
+ if (!data) {
6075
+ console.warn(`No theme data for ${node.name}`);
6076
+ return;
5710
6077
  }
5711
- }
5712
- });
6078
+ let markdownTable;
6079
+ if (typeof data === "object" && data !== null && "cssPropertyName" in data && "data" in data) {
6080
+ const { cssPropertyName, data: themeData } = data;
6081
+ markdownTable = themeDataToJson(themeData, cssPropertyName);
6082
+ } else {
6083
+ markdownTable = themeDataToJson(data);
6084
+ }
6085
+ if (!markdownTable) {
6086
+ return;
6087
+ }
6088
+ Object.assign(node, {
6089
+ lang: "json",
6090
+ meta: null,
6091
+ type: "code",
6092
+ value: markdownTable
6093
+ });
6094
+ });
6095
+ done();
6096
+ };
5713
6097
  }
5714
6098
 
5715
- // src/open-web-ui-knowledge/generate-knowledge.ts
5716
- import AdmZip from "adm-zip";
5717
- import chalk3 from "chalk";
5718
- import { minimatch } from "minimatch";
5719
- import { createHash as createHash2 } from "node:crypto";
5720
- import {
5721
- access,
5722
- mkdir as mkdir2,
5723
- readdir,
5724
- readFile,
5725
- rm,
5726
- stat,
5727
- writeFile as writeFile3
5728
- } from "node:fs/promises";
5729
- import { basename, dirname, extname, join as join3, relative as relative2, resolve as resolve4 } from "node:path";
5730
- import remarkFrontmatter2 from "remark-frontmatter";
5731
- import remarkMdx3 from "remark-mdx";
5732
- import remarkParse4 from "remark-parse";
5733
- import remarkParseFrontmatter2 from "remark-parse-frontmatter";
6099
+ // src/ai-knowledge/generator/section-extractor.ts
5734
6100
  import remarkStringify3 from "remark-stringify";
5735
6101
  import { unified as unified4 } from "unified";
5736
- import { visit as visit5 } from "unist-util-visit";
5737
- import { kebabCase } from "@qualcomm-ui/utils/change-case";
5738
-
5739
- // src/open-web-ui-knowledge/load-config-from-env.ts
5740
- import { existsSync } from "node:fs";
5741
- import { join as join2, resolve as resolve3 } from "node:path";
5742
- function parseCliMetadata(cliMetadata) {
5743
- if (!cliMetadata?.length) {
5744
- return void 0;
6102
+ import { visit as visit10 } from "unist-util-visit";
6103
+ import { kebabCase as kebabCase2 } from "@qualcomm-ui/utils/change-case";
6104
+ var SectionExtractor = class {
6105
+ depths;
6106
+ minContentLength;
6107
+ constructor(options) {
6108
+ const defaultDepths = [1, 2, 3, 4];
6109
+ this.depths = new Set(options?.depths ?? defaultDepths);
6110
+ this.minContentLength = options?.minContentLength ?? 0;
5745
6111
  }
5746
- return Object.fromEntries(cliMetadata.map((entry) => entry.split("=")));
5747
- }
5748
- function loadKnowledgeConfigFromEnv(options) {
5749
- const configLoader = new ConfigLoader({});
5750
- const resolvedConfig = configLoader.loadConfig();
5751
- const fileConfig = resolvedConfig.knowledge?.global;
5752
- const exclude = (options.exclude?.length ? options.exclude : void 0) ?? fileConfig?.exclude ?? (process.env.FILE_EXCLUDE_PATTERN ?? "").split(",").filter(Boolean);
5753
- const outputPath = options.outputPath ?? fileConfig?.outputPath ?? process.env.KNOWLEDGE_OUTPUT_PATH;
5754
- if (!outputPath) {
5755
- throw new Error("Missing required outputPath");
6112
+ /**
6113
+ * Extracts sections from a parsed AST.
6114
+ */
6115
+ extract(tree, pageInfo) {
6116
+ const sections = [];
6117
+ const headerStack = [{ depth: 1, text: pageInfo.title }];
6118
+ let pendingSection = null;
6119
+ const finalizeSection = () => {
6120
+ if (!pendingSection || pendingSection.nodes.length === 0) {
6121
+ return;
6122
+ }
6123
+ const entry = this.buildSectionEntry(pendingSection, pageInfo);
6124
+ if (entry && entry.content.length >= this.minContentLength) {
6125
+ sections.push(entry);
6126
+ }
6127
+ };
6128
+ for (let i = 0; i < tree.children.length; i++) {
6129
+ const node = tree.children[i];
6130
+ if (node.type === "heading") {
6131
+ const heading = node;
6132
+ if (!this.depths.has(heading.depth)) {
6133
+ if (pendingSection) {
6134
+ pendingSection.nodes.push(node);
6135
+ }
6136
+ continue;
6137
+ }
6138
+ finalizeSection();
6139
+ while (headerStack.length > 0 && headerStack[headerStack.length - 1].depth >= heading.depth) {
6140
+ headerStack.pop();
6141
+ }
6142
+ const headingText = this.getHeadingText(heading);
6143
+ headerStack.push({ depth: heading.depth, text: headingText });
6144
+ pendingSection = {
6145
+ headerPath: headerStack.map((h) => h.text),
6146
+ nodes: [],
6147
+ startIndex: i
6148
+ };
6149
+ } else if (pendingSection) {
6150
+ pendingSection.nodes.push(node);
6151
+ }
6152
+ }
6153
+ finalizeSection();
6154
+ return sections;
5756
6155
  }
5757
- const routeDir = join2(
5758
- resolvedConfig.appDirectory,
5759
- resolvedConfig.pageDirectory
5760
- );
5761
- if (!existsSync(resolve3(routeDir))) {
5762
- throw new Error(`Route directory ${routeDir} does not exist`);
6156
+ getHeadingText(heading) {
6157
+ let text = "";
6158
+ visit10(heading, "text", (node) => {
6159
+ text += node.value;
6160
+ });
6161
+ return text.trim();
5763
6162
  }
5764
- const cliMetadata = parseCliMetadata(options.metadata);
5765
- const mergedMetadata = fileConfig?.metadata || cliMetadata ? { ...fileConfig?.metadata, ...cliMetadata } : void 0;
5766
- return {
5767
- ...fileConfig,
5768
- ...options,
5769
- baseUrl: options.baseUrl ?? fileConfig?.baseUrl ?? process.env.DOCS_SITE_BASE_URL,
5770
- docPropsPath: resolvedConfig.typeDocProps,
5771
- exclude,
5772
- extraFiles: fileConfig?.extraFiles,
5773
- metadata: mergedMetadata,
5774
- outputPath,
5775
- pageTitlePrefix: options.pageTitlePrefix ?? fileConfig?.pageTitlePrefix ?? process.env.PAGE_TITLE_PREFIX,
5776
- routeDir
5777
- };
5778
- }
5779
- function mergeEnvironmentConfig(global, environment) {
5780
- return {
5781
- ...global,
5782
- ...environment,
5783
- extraFiles: environment.extraFiles ?? global?.extraFiles,
5784
- metadata: global?.metadata || environment.metadata ? { ...global?.metadata, ...environment.metadata } : void 0
5785
- };
5786
- }
5787
- function loadEnvironmentConfigs(options = {}) {
5788
- const configLoader = new ConfigLoader({});
5789
- const resolvedConfig = configLoader.loadConfig();
5790
- const knowledgeConfig = resolvedConfig.knowledge;
5791
- const globalConfig = knowledgeConfig?.global;
5792
- const environments = knowledgeConfig?.environments;
5793
- const routeDir = join2(
5794
- resolvedConfig.appDirectory,
5795
- resolvedConfig.pageDirectory
5796
- );
5797
- if (!existsSync(resolve3(routeDir))) {
5798
- throw new Error(`Route directory ${routeDir} does not exist`);
6163
+ buildSectionEntry(section, pageInfo) {
6164
+ const { metadata, nodes } = this.extractMetadata(section.nodes);
6165
+ if (nodes.length === 0) {
6166
+ return null;
6167
+ }
6168
+ const contentNodes = [];
6169
+ const codeExamples = [];
6170
+ for (const node of nodes) {
6171
+ if (node.type === "code") {
6172
+ const codeNode = node;
6173
+ if (codeNode.data?.typeDocProps) {
6174
+ const { name, props } = codeNode.data.typeDocProps;
6175
+ metadata.props = [...metadata.props ?? [], ...props];
6176
+ metadata.type = name;
6177
+ }
6178
+ codeExamples.push({
6179
+ code: codeNode.value,
6180
+ language: codeNode.lang ?? ""
6181
+ });
6182
+ } else {
6183
+ contentNodes.push(node);
6184
+ }
6185
+ }
6186
+ const rawContent = this.nodesToRawContent(nodes);
6187
+ const content = this.nodesToContent(contentNodes);
6188
+ const sectionId = this.generateSectionId(section.headerPath);
6189
+ const url = pageInfo.url ? `${pageInfo.url}#${this.generateAnchorId(section.headerPath.at(-1) ?? "")}` : void 0;
6190
+ const hashData = {
6191
+ headerPath: section.headerPath,
6192
+ metadata: Object.keys(metadata).length ? metadata : void 0,
6193
+ pageFrontmatter: Object.keys(pageInfo.frontmatter).length ? pageInfo.frontmatter : void 0,
6194
+ pageId: pageInfo.id,
6195
+ rawContent: rawContent.trim()
6196
+ };
6197
+ const sectionHash = JSON.stringify(hashData);
6198
+ return {
6199
+ ...hashData,
6200
+ codeExamples: codeExamples.length ? codeExamples : void 0,
6201
+ content: content.trim(),
6202
+ hash: sectionHash,
6203
+ sectionId,
6204
+ url
6205
+ };
5799
6206
  }
5800
- if (!environments || environments.length === 0) {
5801
- const legacyConfig = loadKnowledgeConfigFromEnv(
5802
- options.cliOptions ?? { outputMode: "per-page" }
5803
- );
5804
- return [legacyConfig];
6207
+ extractMetadata(nodes) {
6208
+ const metadata = {};
6209
+ const filteredNodes = [];
6210
+ for (const node of nodes) {
6211
+ if (node.type === "paragraph") {
6212
+ const firstChild = node.children?.[0];
6213
+ if (firstChild?.type === "text") {
6214
+ const text = firstChild.value;
6215
+ const metaMatch = text.match(/^:::\s*meta\s*/);
6216
+ if (metaMatch) {
6217
+ const parsed = this.parseMetaBlock(text);
6218
+ Object.assign(metadata, parsed);
6219
+ continue;
6220
+ }
6221
+ }
6222
+ }
6223
+ filteredNodes.push(node);
6224
+ }
6225
+ return { metadata, nodes: filteredNodes };
5805
6226
  }
5806
- let filteredEnvironments = environments;
5807
- if (options.environments?.length) {
5808
- const filterSet = new Set(options.environments);
5809
- filteredEnvironments = environments.filter((env) => filterSet.has(env.id));
6227
+ parseMetaBlock(text) {
6228
+ const metadata = {};
6229
+ const afterOpen = text.replace(/^:::\s*meta\s*/, "");
6230
+ const closeIndex = afterOpen.lastIndexOf(":::");
6231
+ const content = closeIndex !== -1 ? afterOpen.slice(0, closeIndex) : afterOpen;
6232
+ const lines = content.split("\n");
6233
+ for (const line of lines) {
6234
+ const trimmed = line.trim();
6235
+ if (!trimmed) {
6236
+ continue;
6237
+ }
6238
+ const colonIndex = trimmed.indexOf(":");
6239
+ if (colonIndex === -1) {
6240
+ continue;
6241
+ }
6242
+ const key = trimmed.slice(0, colonIndex).trim();
6243
+ const value = trimmed.slice(colonIndex + 1).trim();
6244
+ if (key && value) {
6245
+ metadata[key] = this.parseValue(value);
6246
+ }
6247
+ }
6248
+ return metadata;
5810
6249
  }
5811
- if (filteredEnvironments.length === 0) {
5812
- throw new Error(
5813
- `No matching environments found. Available: ${environments.map((e) => e.id).join(", ")}`
5814
- );
6250
+ parseValue(value) {
6251
+ const trimmed = value.trim();
6252
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
6253
+ const inner = trimmed.slice(1, -1);
6254
+ return inner.split(",").map((item) => item.trim()).filter(Boolean);
6255
+ }
6256
+ return trimmed;
5815
6257
  }
5816
- return filteredEnvironments.map((envConfig) => {
5817
- const merged = mergeEnvironmentConfig(globalConfig, envConfig);
5818
- const cliOpts = options.cliOptions;
5819
- const cliMetadata = parseCliMetadata(cliOpts?.metadata);
5820
- const mergedMetadata = merged.metadata || cliMetadata ? { ...merged.metadata, ...cliMetadata } : void 0;
5821
- return {
5822
- ...merged,
5823
- ...cliOpts,
5824
- baseUrl: cliOpts?.baseUrl ?? merged.baseUrl ?? process.env.DOCS_SITE_BASE_URL,
5825
- docPropsPath: resolvedConfig.typeDocProps,
5826
- environmentName: envConfig.id,
5827
- exclude: (cliOpts?.exclude?.length ? cliOpts.exclude : void 0) ?? merged.exclude ?? (process.env.FILE_EXCLUDE_PATTERN ?? "").split(",").filter(Boolean),
5828
- extraFiles: merged.extraFiles,
5829
- metadata: mergedMetadata,
5830
- outputMode: cliOpts?.outputMode ?? merged.outputMode ?? "per-page",
5831
- outputPath: merged.outputPath,
5832
- pageTitlePrefix: cliOpts?.pageTitlePrefix ?? merged.pageTitlePrefix ?? process.env.PAGE_TITLE_PREFIX,
5833
- routeDir
6258
+ /**
6259
+ * Convert links to inline code. URLs are not relevant for text embeddings
6260
+ * and will muddy the vector storage.
6261
+ */
6262
+ transformLinks() {
6263
+ return () => (tree) => {
6264
+ visit10(tree, "link", (node) => {
6265
+ let text = "";
6266
+ visit10(node, "text", (textNode) => {
6267
+ text += textNode.value;
6268
+ });
6269
+ Object.assign(node, {
6270
+ children: void 0,
6271
+ type: "inlineCode",
6272
+ url: void 0,
6273
+ value: text
6274
+ });
6275
+ });
5834
6276
  };
5835
- });
5836
- }
5837
- function loadOpenWebUiIntegrations(options = {}) {
5838
- const configLoader = new ConfigLoader({});
5839
- const resolvedConfig = configLoader.loadConfig();
5840
- const knowledgeConfig = resolvedConfig.knowledge;
5841
- const environments = knowledgeConfig?.environments;
5842
- const integrations = knowledgeConfig?.integrations?.openWebUi;
5843
- if (!integrations || integrations.length === 0) {
5844
- return [];
5845
6277
  }
5846
- let filteredIntegrations = integrations;
5847
- if (options.integrations?.length) {
5848
- const filterSet = new Set(options.integrations);
5849
- filteredIntegrations = integrations.filter(
5850
- (integration) => filterSet.has(integration.id)
5851
- );
6278
+ nodesToRawContent(nodes) {
6279
+ const tree = { children: nodes, type: "root" };
6280
+ const processor = unified4().use(remarkStringify3);
6281
+ return processor.stringify(tree);
5852
6282
  }
5853
- if (options.environments?.length) {
5854
- const filterSet = new Set(options.environments);
5855
- filteredIntegrations = filteredIntegrations.filter(
5856
- (integration) => filterSet.has(integration.id)
5857
- );
6283
+ nodesToContent(nodes) {
6284
+ const tree = { children: structuredClone(nodes), type: "root" };
6285
+ const processor = unified4().use(this.transformLinks()).use(remarkStringify3);
6286
+ const transformed = processor.runSync(tree);
6287
+ return processor.stringify(transformed);
5858
6288
  }
5859
- return filteredIntegrations.map((integration) => {
5860
- const envConfig = environments?.find((e) => e.id === integration.id);
5861
- if (!envConfig) {
5862
- throw new Error(
5863
- `Integration "${integration.id}" references unknown environment "${integration.id}". Available environments: ${environments?.map((e) => e.id).join(", ") || "none"}`
5864
- );
5865
- }
5866
- return {
5867
- integration,
5868
- name: integration.id,
5869
- outputPath: envConfig.outputPath
5870
- };
5871
- });
5872
- }
5873
-
5874
- // src/open-web-ui-knowledge/generate-knowledge.ts
5875
- async function exists(dirPath) {
5876
- return access(dirPath).then(() => true).catch(() => false);
5877
- }
5878
- function computeMd5(content) {
5879
- return createHash2("md5").update(content).digest("hex");
5880
- }
5881
- function extractBestType(propInfo) {
5882
- const type = propInfo.resolvedType?.prettyType || propInfo.type;
5883
- return cleanType(type.startsWith("| ") ? type.substring(2) : type);
5884
- }
5885
- function extractRequired(propInfo, isPartial) {
5886
- return Boolean(propInfo.resolvedType?.required && !isPartial);
5887
- }
5888
- function cleanType(type) {
5889
- return type.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
5890
- }
5891
- function cleanDefaultValue(defaultValue) {
5892
- return defaultValue.replace(/^\n+/, "").replace(/\n+$/, "").trim();
5893
- }
5894
- function isPreviewLine(trimmedLine) {
5895
- return trimmedLine === "// preview" || /^\{\s*\/\*\s*preview\s*\*\/\s*\}$/.test(trimmedLine) || /^<!--\s*preview\s*-->$/.test(trimmedLine);
5896
- }
5897
- function removePreviewLines(code) {
5898
- return code.split("\n").filter((line) => !isPreviewLine(line.trim())).join("\n");
5899
- }
5900
- function getIntroLines(projectName, description) {
5901
- const lines = [];
5902
- if (projectName) {
5903
- lines.push(`# ${projectName}`);
5904
- }
5905
- if (description) {
5906
- lines.push("");
5907
- lines.push(`> ${description}`);
5908
- }
5909
- return lines.join("\n");
5910
- }
5911
- function extractRelativeImports(content) {
5912
- const imports = [];
5913
- const importRegex = /^import\s+(?:{[^}]*}|[\w*]+|\*\s+as\s+\w+)?\s*(?:,\s*{[^}]*})?\s*from\s+["'](\.[^"']+)["']/gm;
5914
- let match;
5915
- while ((match = importRegex.exec(content)) !== null) {
5916
- imports.push(match[1]);
5917
- }
5918
- return imports;
5919
- }
5920
- async function resolveModulePath(importPath, fromFile) {
5921
- const fromDir = dirname(fromFile);
5922
- const baseResolved = resolve4(fromDir, importPath);
5923
- const extensions = [".ts", ".tsx", ".js", ".jsx", ""];
5924
- for (const ext of extensions) {
5925
- const fullPath = baseResolved + ext;
5926
- if (await exists(fullPath)) {
5927
- return fullPath;
5928
- }
6289
+ generateSectionId(headerPath) {
6290
+ return headerPath.map((h) => kebabCase2(h)).join("-");
5929
6291
  }
5930
- if (await exists(baseResolved)) {
5931
- const indexPath = join3(baseResolved, "index.ts");
5932
- if (await exists(indexPath)) {
5933
- return indexPath;
5934
- }
6292
+ generateAnchorId(headerText) {
6293
+ return kebabCase2(headerText);
5935
6294
  }
5936
- return null;
5937
- }
5938
- function extractMetadata(metadata) {
5939
- return Object.entries(metadata ?? {});
5940
- }
5941
- var replaceNpmInstallTabs = () => {
5942
- return (tree, _file, done) => {
5943
- visit5(tree, "mdxJsxFlowElement", (node) => {
5944
- if (node?.name === "NpmInstallTabs") {
5945
- const packages = node.attributes?.find(
5946
- (attr) => attr.type === "mdxJsxAttribute" && attr.name === "packages"
5947
- );
5948
- const packageNames = packages ? extractNamesFromAttribute(packages) : [];
5949
- Object.assign(node, {
5950
- lang: "shell",
5951
- meta: null,
5952
- type: "code",
5953
- value: `npm install ${packageNames.join(" ")}`
5954
- });
5955
- }
5956
- });
5957
- done();
5958
- };
5959
6295
  };
5960
- function getPath(obj, path) {
5961
- return path.split(".").reduce(
5962
- (acc, key) => acc && typeof acc === "object" ? acc[key] : void 0,
5963
- obj
5964
- );
5965
- }
5966
- function escapeText(value) {
5967
- return value.replace(/\n/g, " ");
5968
- }
5969
- function propsToDefinitionList(props) {
5970
- if (props.length === 0) {
5971
- return "";
5972
- }
5973
- return props.map((prop) => {
5974
- const parts = [`- **${prop.name}** (\`${escapeText(prop.type)}\``];
5975
- if (prop.defaultValue) {
5976
- parts.push(`, default: \`${escapeText(prop.defaultValue)}\``);
5977
- }
5978
- if (prop.required) {
5979
- parts.push(", required");
5980
- }
5981
- parts.push(")");
5982
- if (prop.description) {
5983
- parts.push(` - ${escapeText(prop.description)}`);
5984
- }
5985
- return parts.join("");
5986
- }).join("\n");
5987
- }
5988
- function themeDataToJson(data, cssPropertyName) {
5989
- if (!data || typeof data !== "object") {
5990
- return "";
5991
- }
5992
- if (cssPropertyName) {
5993
- return JSON.stringify({ cssProperty: cssPropertyName, data }, null, 2);
5994
- }
5995
- return JSON.stringify(data, null, 2);
5996
- }
6296
+
6297
+ // src/ai-knowledge/generator/knowledge-generator.ts
5997
6298
  var KnowledgeGenerator = class {
5998
6299
  config;
5999
- docProps = null;
6000
- constructor(config2) {
6001
- this.config = config2;
6300
+ propFormatter;
6301
+ constructor(config3) {
6302
+ setConfig(config3);
6303
+ this.config = getConfig();
6304
+ this.propFormatter = new PropFormatter();
6002
6305
  }
6003
6306
  async run() {
6004
6307
  const extractedMetadata = extractMetadata(this.config.metadata);
@@ -6008,11 +6311,10 @@ var KnowledgeGenerator = class {
6008
6311
  console.log(`Excluding patterns: ${this.config.exclude.join(", ")}`);
6009
6312
  }
6010
6313
  }
6011
- const [docProps, pages] = await Promise.all([
6012
- this.loadDocProps(),
6013
- this.scanPages()
6314
+ const [pages] = await Promise.all([
6315
+ this.scanPages(),
6316
+ this.propFormatter.loadDocProps()
6014
6317
  ]);
6015
- this.docProps = docProps;
6016
6318
  if (pages.length === 0) {
6017
6319
  console.log("No pages found.");
6018
6320
  return [];
@@ -6039,7 +6341,7 @@ var KnowledgeGenerator = class {
6039
6341
  if (this.config.outputMode === "aggregated") {
6040
6342
  await this.generateAggregatedOutput(processedPages, pages);
6041
6343
  } else {
6042
- await mkdir2(this.config.outputPath, { recursive: true }).catch(() => {
6344
+ await mkdir(this.config.outputPath, { recursive: true }).catch(() => {
6043
6345
  });
6044
6346
  await this.generatePerPageExports(
6045
6347
  pages,
@@ -6049,31 +6351,6 @@ var KnowledgeGenerator = class {
6049
6351
  }
6050
6352
  return pages;
6051
6353
  }
6052
- async loadDocProps() {
6053
- const resolvedDocPropsPath = this.config.docPropsPath ? await exists(this.config.docPropsPath) ? this.config.docPropsPath : resolve4(process.cwd(), this.config.docPropsPath) : join3(dirname(this.config.routeDir), "doc-props.json");
6054
- if (!await exists(resolvedDocPropsPath)) {
6055
- if (this.config.verbose) {
6056
- console.log(`Doc props file not found at: ${resolvedDocPropsPath}`);
6057
- }
6058
- return null;
6059
- }
6060
- try {
6061
- const content = await readFile(resolvedDocPropsPath, "utf-8");
6062
- const docProps = JSON.parse(content);
6063
- if (this.config.verbose) {
6064
- console.log(`Loaded doc props from: ${resolvedDocPropsPath}`);
6065
- console.log(
6066
- `Found ${Object.keys(docProps.props).length} component types`
6067
- );
6068
- }
6069
- return docProps;
6070
- } catch (error) {
6071
- if (this.config.verbose) {
6072
- console.log("Error loading doc props", error);
6073
- }
6074
- return null;
6075
- }
6076
- }
6077
6354
  async scanPages() {
6078
6355
  const components = [];
6079
6356
  const excludePatterns = this.config.exclude ?? [];
@@ -6097,14 +6374,14 @@ var KnowledgeGenerator = class {
6097
6374
  }
6098
6375
  const entries = await readdir(dirPath, { withFileTypes: true });
6099
6376
  const mdxFiles = entries.filter(
6100
- (f) => f.name.endsWith(".mdx") && !shouldExclude(join3(dirPath, f.name))
6377
+ (f) => f.name.endsWith(".mdx") && !shouldExclude(join6(dirPath, f.name))
6101
6378
  ) ?? [];
6102
6379
  const pageIdPrefix = this.config.pageIdPrefix ?? "";
6103
6380
  for (const mdxFile of mdxFiles) {
6104
6381
  const demosFolder = entries.find((f) => f.name === "demos");
6105
- const demosFolderPath = demosFolder ? join3(dirPath, demosFolder.name) : void 0;
6382
+ const demosFolderPath = demosFolder ? join6(dirPath, demosFolder.name) : void 0;
6106
6383
  const segments = getPathSegmentsFromFileName(
6107
- join3(dirPath, mdxFile.name),
6384
+ join6(dirPath, mdxFile.name),
6108
6385
  this.config.routeDir
6109
6386
  );
6110
6387
  const url = getPathnameFromPathSegments(segments);
@@ -6112,18 +6389,18 @@ var KnowledgeGenerator = class {
6112
6389
  demosFolder: demosFolderPath,
6113
6390
  filePath: dirPath,
6114
6391
  id: `${pageIdPrefix ? `${pageIdPrefix}-` : ""}${segments.join("-").trim()}`,
6115
- mdxFile: join3(dirPath, mdxFile.name),
6392
+ mdxFile: join6(dirPath, mdxFile.name),
6116
6393
  name: segments.at(-1),
6117
6394
  pathname: url,
6118
6395
  url: this.config.baseUrl ? new URL(url, this.config.baseUrl).toString() : void 0
6119
6396
  });
6120
6397
  if (this.config.verbose) {
6121
- console.log(`Found file: ${basename(dirPath)}`);
6398
+ console.log(`Found file: ${basename2(dirPath)}`);
6122
6399
  console.log(` Demos folder: ${demosFolderPath || "NOT FOUND"}`);
6123
6400
  }
6124
6401
  }
6125
6402
  for (const entry of entries) {
6126
- const fullPath = join3(dirPath, entry.name);
6403
+ const fullPath = join6(dirPath, entry.name);
6127
6404
  const stats = await stat(fullPath);
6128
6405
  if (stats.isDirectory()) {
6129
6406
  await scanDirectory(fullPath);
@@ -6133,807 +6410,892 @@ var KnowledgeGenerator = class {
6133
6410
  await scanDirectory(this.config.routeDir);
6134
6411
  return components;
6135
6412
  }
6136
- async collectRelativeImports(filePath, visited = /* @__PURE__ */ new Set()) {
6137
- const normalizedPath = resolve4(filePath);
6138
- if (visited.has(normalizedPath)) {
6139
- return [];
6140
- }
6141
- visited.add(normalizedPath);
6142
- const modules = [];
6143
- try {
6144
- const content = await readFile(normalizedPath, "utf-8");
6145
- const relativeImports = extractRelativeImports(content);
6146
- for (const importPath of relativeImports) {
6147
- const resolvedPath = await resolveModulePath(importPath, normalizedPath);
6148
- if (!resolvedPath) {
6149
- if (this.config.verbose) {
6150
- console.log(
6151
- ` Could not resolve import: ${importPath} from ${normalizedPath}`
6152
- );
6413
+ formatFrontmatterExpressions(frontmatter) {
6414
+ return () => (tree) => {
6415
+ visit11(
6416
+ tree,
6417
+ "mdxFlowExpression",
6418
+ (node, index, parent) => {
6419
+ if (node.value.trim() !== "frontmatter.description" || index === void 0 || !parent) {
6420
+ return;
6421
+ }
6422
+ if (frontmatter.description) {
6423
+ parent.children.splice(index, 1, {
6424
+ children: [{ type: "text", value: frontmatter.description }],
6425
+ type: "paragraph"
6426
+ });
6427
+ } else {
6428
+ parent.children.splice(index, 1);
6153
6429
  }
6154
- continue;
6155
6430
  }
6156
- const importContent = await readFile(resolvedPath, "utf-8");
6157
- modules.push({
6158
- content: importContent,
6159
- path: resolvedPath
6160
- });
6161
- const nestedModules = await this.collectRelativeImports(
6162
- resolvedPath,
6163
- visited
6431
+ );
6432
+ const root = tree;
6433
+ const h1Index = root.children.findIndex((node) => {
6434
+ if (node.type !== "heading" || node.depth !== 1) {
6435
+ return false;
6436
+ }
6437
+ return node.children?.some(
6438
+ (child) => child.type === "mdxTextExpression" && child.value?.includes("frontmatter")
6164
6439
  );
6165
- modules.push(...nestedModules);
6440
+ });
6441
+ if (h1Index >= 0) {
6442
+ root.children.splice(h1Index, 1);
6166
6443
  }
6167
- } catch (error) {
6168
- if (this.config.verbose) {
6169
- console.log(`Error processing ${normalizedPath}`, error);
6444
+ };
6445
+ }
6446
+ /**
6447
+ * Creates a remark plugin that transforms relative URLs to absolute URLs.
6448
+ */
6449
+ transformRelativeUrls(pageUrl) {
6450
+ const baseUrl = this.config.baseUrl;
6451
+ return () => (tree) => {
6452
+ if (!baseUrl || this.config.outputMode !== "per-page") {
6453
+ return;
6170
6454
  }
6455
+ visit11(tree, "link", (node) => {
6456
+ if (node.url.startsWith("/")) {
6457
+ node.url = `${baseUrl}${node.url}`;
6458
+ } else if (node.url.startsWith("./#") && pageUrl) {
6459
+ node.url = `${pageUrl}${node.url.slice(2)}`;
6460
+ }
6461
+ });
6462
+ };
6463
+ }
6464
+ applyPlugins(opts, processor) {
6465
+ if (this.config.mdxPlugins) {
6466
+ this.config.mdxPlugins.forEach((plugin) => {
6467
+ processor.use(plugin(opts));
6468
+ });
6171
6469
  }
6172
- return modules;
6173
6470
  }
6174
- extractProps(props, isPartial) {
6175
- const propsInfo = [];
6176
- if (props.props?.length) {
6177
- propsInfo.push(
6178
- ...props.props.map((prop) => this.convertPropInfo(prop, isPartial))
6179
- );
6471
+ filterFrontmatter(frontmatter) {
6472
+ if (!this.config.frontmatter?.include?.length) {
6473
+ return frontmatter;
6180
6474
  }
6181
- if (props.input?.length) {
6182
- propsInfo.push(
6183
- ...props.input.map(
6184
- (prop) => this.convertPropInfo(prop, isPartial, "input")
6185
- )
6475
+ const includePatterns = this.config.frontmatter.include;
6476
+ const excludePatterns = this.config.frontmatter.exclude ?? [];
6477
+ const filtered = {};
6478
+ for (const [field, value] of Object.entries(frontmatter)) {
6479
+ if (value === void 0) {
6480
+ continue;
6481
+ }
6482
+ const isIncluded = includePatterns.some(
6483
+ (pattern) => minimatch(field, pattern)
6186
6484
  );
6187
- }
6188
- if (props.output?.length) {
6189
- propsInfo.push(
6190
- ...props.output.map(
6191
- (prop) => this.convertPropInfo(prop, isPartial, "output")
6192
- )
6485
+ const isExcluded = excludePatterns.some(
6486
+ (pattern) => minimatch(field, pattern)
6193
6487
  );
6488
+ if (isIncluded && !isExcluded) {
6489
+ filtered[field] = value;
6490
+ }
6194
6491
  }
6195
- return propsInfo;
6492
+ return filtered;
6196
6493
  }
6197
- formatComment(comment) {
6198
- if (!comment) {
6199
- return "";
6200
- }
6201
- const parts = [];
6202
- if (comment.summary && comment.summary.length > 0) {
6203
- const summaryText = this.formatCommentParts(comment.summary);
6204
- if (summaryText.trim()) {
6205
- parts.push(summaryText.trim());
6494
+ /**
6495
+ * Processes MDX content by transforming JSX elements (TypeDocProps, demos)
6496
+ * into Markdown, resolving relative links, and cleaning up formatting.
6497
+ */
6498
+ async processMdxContent(mdxContent, pageInfo, frontmatter) {
6499
+ const processor = unified5().use(remarkParse4).use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(this.propFormatter.formatTypeDocProps()).use(this.formatFrontmatterExpressions(frontmatter)).use(await formatThemeNodes()).use(formatDemos(pageInfo.demosFolder)).use(this.transformRelativeUrls(pageInfo.url));
6500
+ this.applyPlugins(pageInfo, processor);
6501
+ processor.use(remarkStringify4);
6502
+ return await processor.run(processor.parse(mdxContent));
6503
+ }
6504
+ async processMdxPage(pageInfo) {
6505
+ try {
6506
+ const mdxContent = await readFile4(pageInfo.mdxFile, "utf-8");
6507
+ if (this.config.verbose) {
6508
+ console.log(`Processing page: ${pageInfo.name}`);
6206
6509
  }
6510
+ const processor = unified5().use(remarkParse4).use(remarkMdx3).use(formatNpmInstallTabs).use(remarkFrontmatter2, ["yaml"]).use(remarkParseFrontmatter2).use(remarkStringify4);
6511
+ const parsed = await processor.process(mdxContent);
6512
+ const frontmatter = parsed.data?.frontmatter || {};
6513
+ const ast = await this.processMdxContent(
6514
+ String(parsed),
6515
+ pageInfo,
6516
+ frontmatter
6517
+ );
6518
+ const removeJsxProcessor = unified5().use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(remarkRemoveJsx).use(remarkStringify4);
6519
+ const sectionAst = removeJsxProcessor.runSync(ast);
6520
+ const removedJsx = String(removeJsxProcessor.stringify(sectionAst));
6521
+ const rawContent = removedJsx.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/(^#{1,6} .*\\<[^>]+)>/gm, "$1\\>");
6522
+ const stripMetaProcessor = unified5().use(remarkParse4).use(remarkExtractMeta, {}).use(remarkStringify4);
6523
+ const strippedContent = String(
6524
+ await stripMetaProcessor.process(rawContent)
6525
+ );
6526
+ const title = frontmatter.title || pageInfo.name;
6527
+ return {
6528
+ content: strippedContent.trim(),
6529
+ frontmatter,
6530
+ rawContent: rawContent.trim(),
6531
+ sectionAst,
6532
+ title,
6533
+ url: pageInfo.url
6534
+ };
6535
+ } catch (error) {
6536
+ console.error(`Error processing component ${pageInfo.name}:`, error);
6537
+ throw error;
6207
6538
  }
6208
- if (comment.blockTags && comment.blockTags.length > 0) {
6209
- for (const blockTag of comment.blockTags) {
6210
- const tagContent = this.formatCommentParts(blockTag.content);
6211
- if (tagContent.trim()) {
6212
- const tagName = blockTag.tag.replace("@", "");
6213
- if (tagName === "default" || tagName === "defaultValue") {
6214
- continue;
6215
- }
6216
- if (tagName === "example") {
6217
- parts.push(`**Example:**
6218
- \`\`\`
6219
- ${tagContent.trim()}
6220
- \`\`\``);
6221
- } else {
6222
- parts.push(`**${tagName}:** ${tagContent.trim()}`);
6223
- }
6539
+ }
6540
+ generateLlmsTxt(pages) {
6541
+ const lines = [
6542
+ getIntroLines(this.config.name, this.config.description)
6543
+ ];
6544
+ lines.push("");
6545
+ for (const page of pages) {
6546
+ const content = page.content.split("\n").map((line) => {
6547
+ if (line.startsWith("#")) {
6548
+ return `#${line}`;
6224
6549
  }
6550
+ return line;
6551
+ });
6552
+ if (content.every((line) => !line.trim())) {
6553
+ continue;
6225
6554
  }
6555
+ lines.push(`## ${page.title}`);
6556
+ lines.push("");
6557
+ lines.push(content.join("\n"));
6558
+ lines.push("");
6226
6559
  }
6227
- return parts.join("\n\n");
6560
+ return lines.join("\n");
6228
6561
  }
6229
- formatCommentParts(parts) {
6230
- return parts.map((part) => {
6231
- switch (part.kind) {
6232
- case "text":
6233
- return part.text;
6234
- case "code":
6235
- const codeText = part.text.replace(/```\w*\n?/g, "").replace(/\n?```/g, "").trim();
6236
- if (codeText.includes("\n")) {
6237
- return `\`\`\`
6238
- ${codeText}
6239
- \`\`\``;
6240
- } else {
6241
- return codeText;
6242
- }
6243
- default:
6244
- if (this.config.outputMode === "per-page" && "tag" in part && part.tag === "@link" && typeof part.target === "string") {
6245
- if (part.text === "Learn more") {
6246
- return "";
6247
- }
6248
- }
6249
- return part.text;
6562
+ async generateAggregatedOutput(processedPages, pages) {
6563
+ const llmsTxtContent = this.generateLlmsTxt(processedPages);
6564
+ await mkdir(dirname3(this.config.outputPath), { recursive: true }).catch(
6565
+ () => {
6250
6566
  }
6251
- }).join("").replace(/\n/g, " ").replace(/\s+/g, " ").trim();
6252
- }
6253
- convertPropInfo(propInfo, isPartial, propType = void 0) {
6254
- return {
6255
- name: propInfo.name,
6256
- type: extractBestType(propInfo),
6257
- ...propInfo.defaultValue && {
6258
- defaultValue: cleanDefaultValue(propInfo.defaultValue)
6259
- },
6260
- description: this.formatComment(propInfo.comment || null),
6261
- propType,
6262
- required: extractRequired(propInfo, isPartial) || void 0
6263
- };
6567
+ );
6568
+ await writeFile(this.config.outputPath, llmsTxtContent, "utf-8");
6569
+ const outputStats = await stat(this.config.outputPath);
6570
+ const outputSizeKb = (outputStats.size / 1024).toFixed(1);
6571
+ console.log(
6572
+ `Generated ${this.config.outputPath} with ${pages.length} files(s) at: ${this.config.outputPath}`
6573
+ );
6574
+ console.log(`File size: ${outputSizeKb} KB`);
6264
6575
  }
6265
- /**
6266
- * Creates a remark plugin that replaces theme JSX elements with
6267
- * markdown tables containing theme data.
6268
- */
6269
- async replaceThemeNodes() {
6270
- let themes = null;
6271
- try {
6272
- themes = await import("@qualcomm-ui/tailwind-plugin/theme");
6273
- } catch {
6274
- return () => {
6275
- };
6576
+ async generateExtraFiles(metadata) {
6577
+ const start = performance.now();
6578
+ const extraFiles = this.config.extraFiles ?? [];
6579
+ if (extraFiles.length === 0) {
6580
+ return { count: 0, duration: 0, entries: [], totalSize: 0 };
6276
6581
  }
6277
- const handlers = {
6278
- ColorTable: (node) => {
6279
- const path = this.getAttrExpression(node, "data");
6280
- return path && getPath(themes, path);
6281
- },
6282
- FontTable: (node) => {
6283
- const path = this.getAttrExpression(node, "data");
6284
- return path && getPath(themes, path);
6285
- },
6286
- SpacingTable: (node) => {
6287
- const path = this.getAttrExpression(node, "data");
6288
- return path && getPath(themes, path);
6289
- },
6290
- ThemePropertyTable: (node) => {
6291
- const path = this.getAttrExpression(node, "data");
6292
- const property = this.getAttrExpression(node, "cssProperty");
6293
- const data = path && getPath(themes, path);
6294
- return path && property ? { cssPropertyName: property, data } : void 0;
6295
- }
6296
- };
6297
- return () => (tree, _file, done) => {
6298
- visit5(tree, "mdxJsxFlowElement", (node) => {
6299
- const handler = node.name && handlers[node.name];
6300
- if (!handler) {
6301
- return;
6302
- }
6303
- const data = handler(node);
6304
- if (!data) {
6305
- console.warn(`No theme data for ${node.name}`);
6306
- return;
6582
+ let totalSize = 0;
6583
+ const entries = [];
6584
+ await Promise.all(
6585
+ extraFiles.map(async (extraFile) => {
6586
+ let contents = extraFile.contents;
6587
+ if (extraFile.processAsMdx) {
6588
+ const removeJsxProcessor = unified5().use(remarkParse4).use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(remarkRemoveJsx).use(this.transformRelativeUrls()).use(remarkStringify4);
6589
+ contents = String(await removeJsxProcessor.process(contents));
6307
6590
  }
6308
- let markdownTable;
6309
- if (typeof data === "object" && data !== null && "cssPropertyName" in data && "data" in data) {
6310
- const { cssPropertyName, data: themeData } = data;
6311
- markdownTable = themeDataToJson(themeData, cssPropertyName);
6312
- } else {
6313
- markdownTable = themeDataToJson(data);
6591
+ const lines = [];
6592
+ if (metadata.length) {
6593
+ lines.push("---");
6594
+ for (const [key, value] of metadata) {
6595
+ lines.push(`${key}: ${value}`);
6596
+ }
6597
+ lines.push("---");
6598
+ lines.push("");
6314
6599
  }
6315
- if (!markdownTable) {
6316
- return;
6600
+ if (extraFile.title) {
6601
+ lines.push(`# ${extraFile.title}`);
6602
+ lines.push("");
6317
6603
  }
6318
- Object.assign(node, {
6319
- lang: "json",
6320
- meta: null,
6321
- type: "code",
6322
- value: markdownTable
6604
+ lines.push(contents);
6605
+ lines.push("");
6606
+ const fileContent = lines.join("\n");
6607
+ const fileName = `${kebabCase3(extraFile.id)}.md`;
6608
+ const outfile = `${resolve4(this.config.outputPath)}/${fileName}`;
6609
+ await writeFile(outfile, fileContent, "utf-8");
6610
+ const stats = await stat(outfile);
6611
+ totalSize += stats.size / 1024;
6612
+ entries.push({
6613
+ id: extraFile.id,
6614
+ md5: computeMd5(fileContent),
6615
+ path: fileName,
6616
+ size: stats.size,
6617
+ title: extraFile.title || extraFile.id
6323
6618
  });
6324
- });
6325
- done();
6326
- };
6327
- }
6328
- getAttrExpression(node, name) {
6329
- const attr = node.attributes?.find(
6330
- (a) => a.type === "mdxJsxAttribute" && a.name === name
6619
+ })
6331
6620
  );
6332
- if (!attr?.value) {
6333
- return null;
6334
- }
6335
- if (typeof attr.value === "string") {
6336
- return attr.value;
6337
- } else if (typeof attr.value === "object" && "value" in attr.value) {
6338
- return attr.value.value;
6339
- }
6340
- return null;
6621
+ return {
6622
+ count: extraFiles.length,
6623
+ duration: performance.now() - start,
6624
+ entries,
6625
+ totalSize
6626
+ };
6341
6627
  }
6342
- /**
6343
- * Creates a remark plugin that transforms relative URLs to absolute URLs.
6344
- */
6345
- transformRelativeUrls(pageUrl) {
6346
- const baseUrl = this.config.baseUrl;
6347
- return () => (tree) => {
6348
- if (!baseUrl || this.config.outputMode !== "per-page") {
6349
- return;
6628
+ async generatePerPageExports(pages, processedPages, metadata) {
6629
+ const start = performance.now();
6630
+ await mkdir(dirname3(this.config.outputPath), { recursive: true }).catch(
6631
+ () => {
6350
6632
  }
6351
- visit5(tree, "link", (node) => {
6352
- if (node.url.startsWith("/")) {
6353
- node.url = `${baseUrl}${node.url}`;
6354
- } else if (node.url.startsWith("./#") && pageUrl) {
6355
- node.url = `${pageUrl}${node.url.slice(2)}`;
6633
+ );
6634
+ const count = processedPages.length;
6635
+ let totalSize = 0;
6636
+ const manifestEntries = [];
6637
+ await Promise.all(
6638
+ processedPages.map(async (processedPage, index) => {
6639
+ const page = pages[index];
6640
+ const lines = [];
6641
+ const frontmatterEntries = [];
6642
+ if (page.url) {
6643
+ frontmatterEntries.push(["url", page.url]);
6356
6644
  }
6357
- });
6358
- };
6359
- }
6360
- /**
6361
- * Creates a remark plugin that replaces TypeDocProps JSX elements with
6362
- * markdown tables containing component prop documentation.
6363
- */
6364
- replaceTypeDocProps() {
6365
- return () => (tree, _file, done) => {
6366
- visit5(
6367
- tree,
6368
- "mdxJsxFlowElement",
6369
- (node, index, parent) => {
6370
- if (node?.name !== "TypeDocProps") {
6371
- return;
6372
- }
6373
- const nameAttr = node.attributes?.find(
6374
- (attr) => attr.type === "mdxJsxAttribute" && attr.name === "name"
6375
- );
6376
- const isPartial = node.attributes?.some(
6377
- (attr) => attr.type === "mdxJsxAttribute" && attr.name === "partial"
6378
- );
6379
- if (!this.docProps || !nameAttr) {
6380
- if (parent && index !== void 0) {
6381
- parent.children.splice(index, 1);
6382
- }
6383
- return;
6384
- }
6385
- const propsNames = extractNamesFromAttribute(nameAttr);
6386
- if (propsNames.length === 0) {
6387
- if (parent && index !== void 0) {
6388
- parent.children.splice(index, 1);
6389
- }
6390
- return;
6391
- }
6392
- const propsName = propsNames[0];
6393
- const componentProps = this.docProps.props[propsName];
6394
- if (!componentProps) {
6395
- if (this.config.verbose) {
6396
- console.log(` TypeDocProps not found: ${propsName}`);
6397
- }
6398
- if (parent && index !== void 0) {
6399
- parent.children.splice(index, 1);
6645
+ for (const [key, value] of metadata) {
6646
+ frontmatterEntries.push([key, value]);
6647
+ }
6648
+ if (this.config.frontmatter?.include?.length) {
6649
+ const includePatterns = this.config.frontmatter.include;
6650
+ const excludePatterns = this.config.frontmatter.exclude ?? [];
6651
+ for (const [field, value] of Object.entries(
6652
+ processedPage.frontmatter
6653
+ )) {
6654
+ if (value === void 0) {
6655
+ continue;
6400
6656
  }
6401
- return;
6402
- }
6403
- const propsDoc = this.extractProps(componentProps, Boolean(isPartial));
6404
- if (this.config.verbose) {
6405
- console.log(
6406
- ` Replaced TypeDocProps ${propsName} with API documentation`
6657
+ const isIncluded = includePatterns.some(
6658
+ (pattern) => minimatch(field, pattern)
6407
6659
  );
6660
+ const isExcluded = excludePatterns.some(
6661
+ (pattern) => minimatch(field, pattern)
6662
+ );
6663
+ if (isIncluded && !isExcluded) {
6664
+ frontmatterEntries.push([
6665
+ field,
6666
+ Array.isArray(value) ? value : String(value)
6667
+ ]);
6668
+ }
6408
6669
  }
6409
- const regularProps = propsDoc.filter((p) => p.propType === void 0);
6410
- const inputs = propsDoc.filter((p) => p.propType === "input");
6411
- const outputs = propsDoc.filter((p) => p.propType === "output");
6412
- const sections = [];
6413
- if (regularProps.length > 0) {
6414
- sections.push(propsToDefinitionList(regularProps));
6415
- }
6416
- if (inputs.length > 0) {
6417
- sections.push(`**Inputs**
6418
-
6419
- ${propsToDefinitionList(inputs)}`);
6420
- }
6421
- if (outputs.length > 0) {
6422
- sections.push(`**Outputs**
6423
-
6424
- ${propsToDefinitionList(outputs)}`);
6670
+ }
6671
+ if (this.config.frontmatter?.extraFields) {
6672
+ for (const [key, value] of Object.entries(
6673
+ this.config.frontmatter.extraFields
6674
+ )) {
6675
+ frontmatterEntries.push([key, value]);
6425
6676
  }
6426
- const markdownContent = sections.join("\n\n");
6427
- if (!markdownContent) {
6428
- if (parent && index !== void 0) {
6429
- parent.children.splice(index, 1);
6677
+ }
6678
+ if (frontmatterEntries.length > 0) {
6679
+ lines.push("---");
6680
+ for (const [key, value] of frontmatterEntries) {
6681
+ if (Array.isArray(value)) {
6682
+ lines.push(`${key}: [${value.join(", ")}]`);
6683
+ } else {
6684
+ lines.push(`${key}: ${value}`);
6430
6685
  }
6431
- return;
6432
6686
  }
6433
- Object.assign(node, {
6434
- lang: null,
6435
- meta: null,
6436
- type: "code",
6437
- value: markdownContent
6438
- });
6687
+ lines.push("---");
6688
+ lines.push("");
6439
6689
  }
6440
- );
6441
- done();
6442
- };
6443
- }
6444
- /**
6445
- * Creates a remark plugin that replaces demo JSX elements (QdsDemo, CodeDemo,
6446
- * Demo) with code blocks containing the demo source code from the demos folder.
6447
- */
6448
- replaceDemos(demosFolder, demoFiles) {
6449
- return () => async (tree) => {
6450
- const promises = [];
6451
- visit5(
6452
- tree,
6453
- "mdxJsxFlowElement",
6454
- (node, index, parent) => {
6455
- if (!node?.name || !["QdsDemo", "CodeDemo", "Demo"].includes(node.name)) {
6456
- return;
6457
- }
6458
- const nameAttr = node.attributes?.find(
6459
- (attr) => attr.type === "mdxJsxAttribute" && attr.name === "name"
6460
- );
6461
- const nodeAttr = node.attributes?.find(
6462
- (attr) => attr.type === "mdxJsxAttribute" && attr.name === "node"
6463
- );
6464
- let demoName;
6465
- if (nameAttr && typeof nameAttr.value === "string") {
6466
- demoName = nameAttr.value;
6467
- } else if (nodeAttr?.value && typeof nodeAttr.value !== "string") {
6468
- const estree = nodeAttr.value.data?.estree;
6469
- if (estree?.body?.[0]?.type === "ExpressionStatement") {
6470
- const expression = estree.body[0].expression;
6471
- if (expression.type === "MemberExpression" && expression.object.type === "Identifier" && expression.object.name === "Demo" && expression.property.type === "Identifier") {
6472
- demoName = expression.property.name;
6473
- }
6474
- }
6475
- }
6476
- if (!demoName) {
6477
- if (parent && index !== void 0) {
6478
- parent.children.splice(index, 1);
6479
- }
6480
- return;
6481
- }
6482
- promises.push(
6483
- (async () => {
6484
- const kebabName = kebabCase(demoName);
6485
- let filePath = `${kebabName}.tsx`;
6486
- if (!demosFolder) {
6487
- if (this.config.verbose) {
6488
- console.log(` No demos folder for ${demoName}`);
6489
- }
6490
- if (parent && index !== void 0) {
6491
- parent.children.splice(index, 1);
6492
- }
6493
- return;
6494
- }
6495
- let demoFilePath = join3(demosFolder, filePath);
6496
- let isAngularDemo = false;
6497
- if (!await exists(demoFilePath)) {
6498
- demoFilePath = join3(demosFolder, `${kebabName}.ts`);
6499
- if (await exists(demoFilePath)) {
6500
- isAngularDemo = true;
6501
- filePath = `${kebabCase(demoName).replace("-component", ".component")}.ts`;
6502
- demoFilePath = join3(demosFolder, filePath);
6503
- } else {
6504
- console.log(` Demo not found ${demoName}`);
6505
- if (parent && index !== void 0) {
6506
- parent.children.splice(index, 1);
6507
- }
6508
- return;
6509
- }
6510
- }
6511
- try {
6512
- const demoCode = await readFile(demoFilePath, "utf-8");
6513
- const cleanedCode = removePreviewLines(demoCode);
6514
- if (this.config.verbose) {
6515
- console.log(` Replaced demo ${demoName} with source code`);
6516
- }
6517
- demoFiles.push(demoFilePath);
6518
- Object.assign(node, {
6519
- lang: isAngularDemo ? "angular-ts" : "tsx",
6520
- meta: null,
6521
- type: "code",
6522
- value: cleanedCode
6523
- });
6524
- } catch (error) {
6525
- if (this.config.verbose) {
6526
- console.log(`Error reading demo ${demoName}`, error);
6527
- }
6528
- if (parent && index !== void 0) {
6529
- parent.children.splice(index, 1);
6530
- }
6531
- }
6532
- })()
6690
+ lines.push(`# ${processedPage.title}`);
6691
+ lines.push("");
6692
+ if (processedPage.frontmatter?.title) {
6693
+ page.name = processedPage.frontmatter.title;
6694
+ }
6695
+ let content = processedPage.content;
6696
+ content = content.replace(
6697
+ new RegExp(`^# ${processedPage.title}\\n+`, ""),
6698
+ ""
6699
+ );
6700
+ if (this.config.pageTitlePrefix) {
6701
+ content = content.replace(
6702
+ `# ${page.name}`,
6703
+ `# ${this.config.pageTitlePrefix} ${page.name}`
6533
6704
  );
6705
+ page.name = `${this.config.pageTitlePrefix} ${page.name}`;
6534
6706
  }
6707
+ lines.push(content);
6708
+ lines.push("");
6709
+ const fileContent = lines.join("\n");
6710
+ const fileName = `${kebabCase3(page.id || page.name)}.md`;
6711
+ const outfile = `${resolve4(this.config.outputPath)}/${fileName}`;
6712
+ await writeFile(outfile, fileContent, "utf-8");
6713
+ const stats = await stat(outfile);
6714
+ totalSize += stats.size / 1024;
6715
+ manifestEntries.push({
6716
+ id: page.id || kebabCase3(page.name),
6717
+ md5: computeMd5(fileContent),
6718
+ path: fileName,
6719
+ size: stats.size,
6720
+ title: processedPage.title,
6721
+ url: page.url
6722
+ });
6723
+ })
6724
+ );
6725
+ const extraFilesResult = await this.generateExtraFiles(metadata);
6726
+ manifestEntries.push(...extraFilesResult.entries);
6727
+ if (this.config.manifestOutputPath) {
6728
+ if (this.config.generateManifest !== false) {
6729
+ await this.generateManifest(
6730
+ this.config.manifestOutputPath,
6731
+ manifestEntries
6732
+ );
6733
+ }
6734
+ if (this.config.generateBulkZip !== false) {
6735
+ await this.generateBulkZip(
6736
+ this.config.manifestOutputPath,
6737
+ manifestEntries
6738
+ );
6739
+ }
6740
+ await this.generateSectionsOutput(
6741
+ processedPages,
6742
+ pages,
6743
+ this.config.manifestOutputPath
6535
6744
  );
6536
- await Promise.all(promises);
6745
+ }
6746
+ console.log(
6747
+ `Generated ${count + extraFilesResult.count} files(s) in ${chalk3.magenta.bold(`${Math.round(performance.now() - start + extraFilesResult.duration)}ms`)} at ${chalk3.blue.bold(this.config.outputPath)} - ${(totalSize + extraFilesResult.totalSize).toFixed(1)} KB`
6748
+ );
6749
+ }
6750
+ computeAggregateHash(entries) {
6751
+ const sortedHashes = entries.map((e) => e.md5).sort().join("");
6752
+ return computeMd5(sortedHashes);
6753
+ }
6754
+ async generateManifest(outputPath, entries) {
6755
+ const aggregateHash = this.computeAggregateHash(entries);
6756
+ const totalSize = entries.reduce((sum, e) => sum + e.size, 0);
6757
+ const manifest = {
6758
+ aggregateHash,
6759
+ baseUrl: this.config.baseUrl,
6760
+ files: entries,
6761
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6762
+ totalFiles: entries.length,
6763
+ totalSize,
6764
+ version: 1
6537
6765
  };
6766
+ await mkdir(outputPath, { recursive: true }).catch(() => {
6767
+ });
6768
+ await writeFile(
6769
+ join6(outputPath, "manifest.json"),
6770
+ JSON.stringify(manifest, null, 2),
6771
+ "utf-8"
6772
+ );
6773
+ return manifest;
6538
6774
  }
6539
- replaceFrontmatterExpressions(frontmatter) {
6540
- return () => (tree) => {
6541
- visit5(
6542
- tree,
6543
- "mdxFlowExpression",
6544
- (node, index, parent) => {
6545
- if (node.value.trim() !== "frontmatter.description" || index === void 0 || !parent) {
6546
- return;
6547
- }
6548
- if (frontmatter.description) {
6549
- parent.children.splice(index, 1, {
6550
- children: [{ type: "text", value: frontmatter.description }],
6551
- type: "paragraph"
6552
- });
6553
- } else {
6554
- parent.children.splice(index, 1);
6555
- }
6556
- }
6557
- );
6558
- const root = tree;
6559
- const h1Index = root.children.findIndex((node) => {
6560
- if (node.type !== "heading" || node.depth !== 1) {
6561
- return false;
6562
- }
6563
- return node.children?.some(
6564
- (child) => child.type === "mdxTextExpression" && child.value?.includes("frontmatter")
6565
- );
6775
+ async generateBulkZip(outputPath, entries) {
6776
+ await mkdir(outputPath, { recursive: true }).catch(() => {
6777
+ });
6778
+ const zipPath = join6(outputPath, "bulk.zip");
6779
+ const zip = new AdmZip();
6780
+ for (const entry of entries) {
6781
+ const filePath = join6(this.config.outputPath, entry.path);
6782
+ const content = await readFile4(filePath);
6783
+ zip.addFile(entry.path, content);
6784
+ }
6785
+ zip.writeZip(zipPath);
6786
+ }
6787
+ async generateSectionsOutput(processedPages, pages, outputPath) {
6788
+ const sectionsConfig = this.config.sections ?? {};
6789
+ const extractor = new SectionExtractor({
6790
+ depths: sectionsConfig.depths,
6791
+ minContentLength: sectionsConfig.minContentLength
6792
+ });
6793
+ const allSections = [];
6794
+ for (let i = 0; i < processedPages.length; i++) {
6795
+ const processed = processedPages[i];
6796
+ const page = pages[i];
6797
+ const filteredFrontmatter = this.filterFrontmatter(processed.frontmatter);
6798
+ const pageSections = extractor.extract(processed.sectionAst, {
6799
+ frontmatter: filteredFrontmatter,
6800
+ id: page.id,
6801
+ title: processed.title,
6802
+ url: processed.url
6566
6803
  });
6567
- if (h1Index >= 0) {
6568
- root.children.splice(h1Index, 1);
6804
+ allSections.push(...pageSections);
6805
+ }
6806
+ const hash = computeMd5(JSON.stringify(allSections));
6807
+ const output = {
6808
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6809
+ hash,
6810
+ sections: allSections,
6811
+ totalSections: allSections.length,
6812
+ version: 1
6813
+ };
6814
+ const sectionsPath = join6(
6815
+ outputPath,
6816
+ sectionsConfig.outputPath ?? "sections.json"
6817
+ );
6818
+ await mkdir(dirname3(sectionsPath), { recursive: true }).catch(() => {
6819
+ });
6820
+ await writeFile(sectionsPath, JSON.stringify(output, null, 2), "utf-8");
6821
+ if (this.config.verbose) {
6822
+ console.log(
6823
+ `Generated ${allSections.length} sections at ${chalk3.blue.bold(sectionsPath)}`
6824
+ );
6825
+ }
6826
+ }
6827
+ };
6828
+
6829
+ // src/ai-knowledge/generator/command.ts
6830
+ async function generate(config3) {
6831
+ const generator = new KnowledgeGenerator(config3);
6832
+ return generator.run();
6833
+ }
6834
+ function addGenerateKnowledgeCommand() {
6835
+ program.description("Generate llms.txt from QUI Docs documentation").command("generate-llms-txt").option("-n, --name <name>", "Project name for llms.txt header").requiredOption("-m, --output-mode <outputMode>").option("-o, --outputPath <outputPath>", "Output file or directory.").option(
6836
+ "-d, --description <description>",
6837
+ "Project description for llms.txt"
6838
+ ).option("-v, --verbose", "Enable verbose logging", false).option(
6839
+ "--exclude <patterns...>",
6840
+ "Glob patterns to exclude (e.g., **/internal/**, guide/drafts/*)",
6841
+ []
6842
+ ).option("--base-url <url>", "Base URL for component documentation links").option("--metadata <pairs...>", "metadata key-value pairs").option("--clean", "Clean the output path before generating").option("--include-imports", "Include relative import source files", true).option(
6843
+ "-e, --environment <environments>",
6844
+ "Comma-separated list of environments to generate (default: all)"
6845
+ ).action(async (options) => {
6846
+ loadEnv();
6847
+ const cliOptions = {
6848
+ ...options,
6849
+ outputMode: options.outputMode === "per-page" ? "per-page" : "aggregated"
6850
+ };
6851
+ const environmentFilter = options.environment?.split(",").map((e) => e.trim()).filter(Boolean);
6852
+ const configs = loadEnvironmentConfigs({
6853
+ cliOptions,
6854
+ environments: environmentFilter
6855
+ });
6856
+ for (const config3 of configs) {
6857
+ const envLabel = config3.environmentName ? `[${config3.environmentName}] ` : "";
6858
+ console.log(`${envLabel}Generating knowledge to ${config3.outputPath}`);
6859
+ await generate(config3);
6860
+ }
6861
+ if (configs.length > 1) {
6862
+ console.log(
6863
+ `
6864
+ Generated knowledge for ${configs.length} environment(s)`
6865
+ );
6866
+ }
6867
+ });
6868
+ }
6869
+
6870
+ // src/ai-knowledge/open-web-ui/download-knowledge.ts
6871
+ import dotenv from "dotenv";
6872
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
6873
+ import { resolve as resolve5 } from "node:path";
6874
+
6875
+ // src/ai-knowledge/open-web-ui/api.ts
6876
+ function isErrorResponse(response) {
6877
+ return typeof response === "object" && response !== null && "detail" in response;
6878
+ }
6879
+ var FilesApi = class {
6880
+ config;
6881
+ constructor(config3) {
6882
+ this.config = config3;
6883
+ }
6884
+ get headers() {
6885
+ return {
6886
+ Authorization: `Bearer ${this.config.apiKey}`
6887
+ };
6888
+ }
6889
+ get jsonHeaders() {
6890
+ return {
6891
+ ...this.headers,
6892
+ "Content-Type": "application/json"
6893
+ };
6894
+ }
6895
+ async handleResponse(response) {
6896
+ const data = await response.json();
6897
+ if (isErrorResponse(data)) {
6898
+ throw new Error(data.detail);
6899
+ }
6900
+ return data;
6901
+ }
6902
+ async upload(file, filename, options) {
6903
+ const formData = new FormData();
6904
+ let blob;
6905
+ if (file instanceof Blob) {
6906
+ blob = file;
6907
+ } else if (file instanceof ArrayBuffer) {
6908
+ blob = new Blob([file]);
6909
+ } else {
6910
+ const copy = new Uint8Array(file).buffer;
6911
+ blob = new Blob([copy]);
6912
+ }
6913
+ formData.append("file", blob, filename);
6914
+ if (options?.metadata) {
6915
+ formData.append("metadata", JSON.stringify(options.metadata));
6916
+ }
6917
+ const params = new URLSearchParams();
6918
+ if (options?.process !== void 0) {
6919
+ params.set("process", String(options.process));
6920
+ }
6921
+ if (options?.processInBackground !== void 0) {
6922
+ params.set("process_in_background", String(options.processInBackground));
6923
+ }
6924
+ const url = `${this.config.baseUrl}/api/v1/files/${params.toString() ? `?${params}` : ""}`;
6925
+ const response = await fetch(url, {
6926
+ body: formData,
6927
+ headers: this.headers,
6928
+ method: "POST"
6929
+ });
6930
+ return this.handleResponse(response);
6931
+ }
6932
+ async list(includeContent = true) {
6933
+ const params = new URLSearchParams({ content: String(includeContent) });
6934
+ const response = await fetch(
6935
+ `${this.config.baseUrl}/api/v1/files/?${params}`,
6936
+ {
6937
+ headers: this.headers
6569
6938
  }
6939
+ );
6940
+ return this.handleResponse(response);
6941
+ }
6942
+ async search(pattern, includeContent = true) {
6943
+ const params = new URLSearchParams({
6944
+ content: String(includeContent),
6945
+ filename: pattern
6946
+ });
6947
+ const response = await fetch(
6948
+ `${this.config.baseUrl}/api/v1/files/search?${params}`,
6949
+ { headers: this.headers }
6950
+ );
6951
+ if (response.status === 404) {
6952
+ return [];
6953
+ }
6954
+ return this.handleResponse(response);
6955
+ }
6956
+ async deleteAll() {
6957
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/all`, {
6958
+ headers: this.headers,
6959
+ method: "DELETE"
6960
+ });
6961
+ return this.handleResponse(response);
6962
+ }
6963
+ async getById(id) {
6964
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
6965
+ headers: this.headers
6966
+ });
6967
+ return this.handleResponse(response);
6968
+ }
6969
+ async getProcessStatus(id) {
6970
+ const response = await fetch(
6971
+ `${this.config.baseUrl}/api/v1/files/${id}/process/status`,
6972
+ { headers: this.headers }
6973
+ );
6974
+ return this.handleResponse(response);
6975
+ }
6976
+ async waitForProcessing(id, options) {
6977
+ const maxAttempts = options?.maxAttempts ?? 120;
6978
+ const intervalMs = options?.intervalMs ?? 1e3;
6979
+ for (let i = 0; i < maxAttempts; i++) {
6980
+ const status = await this.getProcessStatus(id);
6981
+ if (status.status === "completed" || status.status === "failed") {
6982
+ return status;
6983
+ }
6984
+ await new Promise((resolve10) => setTimeout(resolve10, intervalMs));
6985
+ }
6986
+ throw new Error(`File processing timed out after ${maxAttempts} attempts`);
6987
+ }
6988
+ async getDataContent(id) {
6989
+ const response = await fetch(
6990
+ `${this.config.baseUrl}/api/v1/files/${id}/data/content`,
6991
+ { headers: this.headers }
6992
+ );
6993
+ return this.handleResponse(response);
6994
+ }
6995
+ async updateDataContent(id, content) {
6996
+ const response = await fetch(
6997
+ `${this.config.baseUrl}/api/v1/files/${id}/data/content/update`,
6998
+ {
6999
+ body: JSON.stringify({ content }),
7000
+ headers: this.jsonHeaders,
7001
+ method: "POST"
7002
+ }
7003
+ );
7004
+ return this.handleResponse(response);
7005
+ }
7006
+ async getContent(id, asAttachment = false) {
7007
+ const params = new URLSearchParams({ attachment: String(asAttachment) });
7008
+ return fetch(
7009
+ `${this.config.baseUrl}/api/v1/files/${id}/content?${params}`,
7010
+ {
7011
+ headers: this.headers
7012
+ }
7013
+ );
7014
+ }
7015
+ async getContentAsText(id) {
7016
+ const response = await this.getContent(id);
7017
+ if (!response.ok) {
7018
+ const error = await response.json();
7019
+ throw new Error(error.detail || "Failed to get file content");
7020
+ }
7021
+ return response.text();
7022
+ }
7023
+ async delete(id) {
7024
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
7025
+ headers: this.headers,
7026
+ method: "DELETE"
7027
+ });
7028
+ return this.handleResponse(response);
7029
+ }
7030
+ };
7031
+ var KnowledgeApi = class {
7032
+ config;
7033
+ constructor(config3) {
7034
+ this.config = config3;
7035
+ }
7036
+ get headers() {
7037
+ return {
7038
+ Authorization: `Bearer ${this.config.apiKey}`
6570
7039
  };
6571
7040
  }
6572
- /**
6573
- * Processes MDX content by transforming JSX elements (TypeDocProps, demos)
6574
- * into markdown, resolving relative links, and cleaning up formatting.
6575
- */
6576
- async processMdxContent(mdxContent, pageUrl, demosFolder, frontmatter) {
6577
- const demoFiles = [];
6578
- const processor = unified4().use(remarkParse4).use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(this.replaceTypeDocProps()).use(this.replaceFrontmatterExpressions(frontmatter)).use(await this.replaceThemeNodes()).use(this.replaceDemos(demosFolder, demoFiles)).use(this.transformRelativeUrls(pageUrl)).use(remarkStringify3);
6579
- const processed = await processor.process(mdxContent);
6580
- const processedContent = String(processed).replace(/\n\s*\n\s*\n/g, "\n\n");
6581
- return { content: processedContent, demoFiles };
7041
+ get jsonHeaders() {
7042
+ return {
7043
+ ...this.headers,
7044
+ "Content-Type": "application/json"
7045
+ };
6582
7046
  }
6583
- async processMdxPage(pageInfo) {
6584
- try {
6585
- const mdxContent = await readFile(pageInfo.mdxFile, "utf-8");
6586
- if (this.config.verbose) {
6587
- console.log(`Processing page: ${pageInfo.name}`);
6588
- }
6589
- const processor = unified4().use(remarkParse4).use(remarkMdx3).use(replaceNpmInstallTabs).use(remarkFrontmatter2, ["yaml"]).use(remarkParseFrontmatter2).use(remarkStringify3);
6590
- const parsed = await processor.process(mdxContent);
6591
- const frontmatter = parsed.data?.frontmatter || {};
6592
- const { content: processedContent, demoFiles } = await this.processMdxContent(
6593
- String(parsed),
6594
- pageInfo.url,
6595
- pageInfo.demosFolder,
6596
- frontmatter
6597
- );
6598
- const removeJsxProcessor = unified4().use(remarkParse4).use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(remarkRemoveJsx).use(remarkStringify3);
6599
- const removedJsx = String(
6600
- await removeJsxProcessor.process(processedContent)
6601
- );
6602
- const contentWithoutFrontmatter = removedJsx.replace(/^---[\s\S]*?---\n/, "").replace(/(^#{1,6} .*\\<[^>]+)>/gm, "$1\\>");
6603
- const title = frontmatter.title || pageInfo.name;
6604
- return {
6605
- content: contentWithoutFrontmatter.trim(),
6606
- demoFiles,
6607
- frontmatter,
6608
- title,
6609
- url: pageInfo.url
6610
- };
6611
- } catch (error) {
6612
- console.error(`Error processing component ${pageInfo.name}:`, error);
6613
- throw error;
7047
+ async handleResponse(response) {
7048
+ const data = await response.json();
7049
+ if (isErrorResponse(data)) {
7050
+ throw new Error(data.detail);
6614
7051
  }
7052
+ return data;
6615
7053
  }
6616
- generateLlmsTxt(pages) {
6617
- const lines = [
6618
- getIntroLines(this.config.name, this.config.description)
6619
- ];
6620
- lines.push("");
6621
- for (const page of pages) {
6622
- const content = page.content.split("\n").map((line) => {
6623
- if (line.startsWith("#")) {
6624
- return `#${line}`;
6625
- }
6626
- return line;
6627
- });
6628
- if (content.every((line) => !line.trim())) {
6629
- continue;
7054
+ async list() {
7055
+ const response = await fetch(`${this.config.baseUrl}/api/v1/knowledge/`, {
7056
+ headers: this.headers
7057
+ });
7058
+ return this.handleResponse(response);
7059
+ }
7060
+ async listWritable() {
7061
+ const response = await fetch(
7062
+ `${this.config.baseUrl}/api/v1/knowledge/list`,
7063
+ {
7064
+ headers: this.headers
6630
7065
  }
6631
- lines.push(`## ${page.title}`);
6632
- lines.push("");
6633
- lines.push(content.join("\n"));
6634
- lines.push("");
6635
- }
6636
- return lines.join("\n");
7066
+ );
7067
+ return this.handleResponse(response);
6637
7068
  }
6638
- async generateAggregatedOutput(processedPages, pages) {
6639
- const llmsTxtContent = this.generateLlmsTxt(processedPages);
6640
- await mkdir2(dirname(this.config.outputPath), { recursive: true }).catch(
6641
- () => {
7069
+ async create(form) {
7070
+ const response = await fetch(
7071
+ `${this.config.baseUrl}/api/v1/knowledge/create`,
7072
+ {
7073
+ body: JSON.stringify(form),
7074
+ headers: this.jsonHeaders,
7075
+ method: "POST"
6642
7076
  }
6643
7077
  );
6644
- await writeFile3(this.config.outputPath, llmsTxtContent, "utf-8");
6645
- const outputStats = await stat(this.config.outputPath);
6646
- const outputSizeKb = (outputStats.size / 1024).toFixed(1);
6647
- console.log(
6648
- `Generated ${this.config.outputPath} with ${pages.length} files(s) at: ${this.config.outputPath}`
7078
+ return this.handleResponse(response);
7079
+ }
7080
+ async reindex() {
7081
+ const response = await fetch(
7082
+ `${this.config.baseUrl}/api/v1/knowledge/reindex`,
7083
+ {
7084
+ headers: this.jsonHeaders,
7085
+ method: "POST"
7086
+ }
6649
7087
  );
6650
- console.log(`File size: ${outputSizeKb} KB`);
7088
+ return this.handleResponse(response);
6651
7089
  }
6652
- async generateExtraFiles(metadata) {
6653
- const start = performance.now();
6654
- const extraFiles = this.config.extraFiles ?? [];
6655
- if (extraFiles.length === 0) {
6656
- return { count: 0, duration: 0, entries: [], totalSize: 0 };
6657
- }
6658
- let totalSize = 0;
6659
- const entries = [];
6660
- await Promise.all(
6661
- extraFiles.map(async (extraFile) => {
6662
- let contents = extraFile.contents;
6663
- if (extraFile.processAsMdx) {
6664
- const removeJsxProcessor = unified4().use(remarkParse4).use(remarkMdx3).use(remarkFrontmatter2, ["yaml"]).use(remarkRemoveJsx).use(this.transformRelativeUrls()).use(remarkStringify3);
6665
- contents = String(await removeJsxProcessor.process(contents));
6666
- }
6667
- const lines = [];
6668
- if (metadata.length) {
6669
- lines.push("---");
6670
- for (const [key, value] of metadata) {
6671
- lines.push(`${key}: ${value}`);
6672
- }
6673
- lines.push("---");
6674
- lines.push("");
6675
- }
6676
- if (extraFile.title) {
6677
- lines.push(`# ${extraFile.title}`);
6678
- lines.push("");
6679
- }
6680
- lines.push(contents);
6681
- lines.push("");
6682
- const fileContent = lines.join("\n");
6683
- const fileName = `${kebabCase(extraFile.id)}.md`;
6684
- const outfile = `${resolve4(this.config.outputPath)}/${fileName}`;
6685
- await writeFile3(outfile, fileContent, "utf-8");
6686
- const stats = await stat(outfile);
6687
- totalSize += stats.size / 1024;
6688
- entries.push({
6689
- id: extraFile.id,
6690
- md5: computeMd5(fileContent),
6691
- path: fileName,
6692
- size: stats.size,
6693
- title: extraFile.title || extraFile.id
6694
- });
6695
- })
7090
+ async getById(id) {
7091
+ const response = await fetch(
7092
+ `${this.config.baseUrl}/api/v1/knowledge/${id}`,
7093
+ {
7094
+ headers: this.headers
7095
+ }
6696
7096
  );
6697
- return {
6698
- count: extraFiles.length,
6699
- duration: performance.now() - start,
6700
- entries,
6701
- totalSize
6702
- };
7097
+ return this.handleResponse(response);
6703
7098
  }
6704
- async generatePerPageExports(pages, processedPages, metadata) {
6705
- const start = performance.now();
6706
- await mkdir2(dirname(this.config.outputPath), { recursive: true }).catch(
6707
- () => {
7099
+ async update(id, form) {
7100
+ const response = await fetch(
7101
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/update`,
7102
+ {
7103
+ body: JSON.stringify(form),
7104
+ headers: this.jsonHeaders,
7105
+ method: "POST"
6708
7106
  }
6709
7107
  );
6710
- const count = processedPages.length;
6711
- let totalSize = 0;
6712
- const manifestEntries = [];
6713
- await Promise.all(
6714
- processedPages.map(async (processedPage, index) => {
6715
- const page = pages[index];
6716
- const lines = [];
6717
- const frontmatterEntries = [];
6718
- if (page.url) {
6719
- frontmatterEntries.push(["url", page.url]);
6720
- }
6721
- for (const [key, value] of metadata) {
6722
- frontmatterEntries.push([key, value]);
6723
- }
6724
- if (this.config.frontmatterFields) {
6725
- if (typeof this.config.frontmatterFields === "function") {
6726
- const transformed = this.config.frontmatterFields(
6727
- processedPage.frontmatter,
6728
- page
6729
- );
6730
- for (const [key, value] of Object.entries(transformed)) {
6731
- if (value !== void 0) {
6732
- frontmatterEntries.push([key, String(value)]);
6733
- }
6734
- }
6735
- } else {
6736
- for (const field of this.config.frontmatterFields) {
6737
- const value = processedPage.frontmatter[field];
6738
- if (value !== void 0) {
6739
- frontmatterEntries.push([field, String(value)]);
6740
- }
6741
- }
6742
- }
6743
- }
6744
- if (frontmatterEntries.length > 0) {
6745
- lines.push("---");
6746
- for (const [key, value] of frontmatterEntries) {
6747
- lines.push(`${key}: ${value}`);
6748
- }
6749
- lines.push("---");
6750
- lines.push("");
6751
- }
6752
- lines.push(`# ${processedPage.title}`);
6753
- lines.push("");
6754
- if (processedPage.frontmatter?.title) {
6755
- page.name = processedPage.frontmatter.title;
6756
- }
6757
- let content = processedPage.content;
6758
- content = content.replace(
6759
- new RegExp(`^# ${processedPage.title}\\n+`, ""),
6760
- ""
6761
- );
6762
- if (this.config.pageTitlePrefix) {
6763
- content = content.replace(
6764
- `# ${page.name}`,
6765
- `# ${this.config.pageTitlePrefix} ${page.name}`
6766
- );
6767
- page.name = `${this.config.pageTitlePrefix} ${page.name}`;
6768
- }
6769
- lines.push(content);
6770
- lines.push("");
6771
- if (processedPage.demoFiles.length > 0) {
6772
- if (this.config.verbose) {
6773
- console.log(
6774
- `Collecting imports for ${page.name} from ${processedPage.demoFiles.length} demo files`
6775
- );
6776
- }
6777
- const allImports = [];
6778
- for (const demoFile of processedPage.demoFiles) {
6779
- const imports = await this.collectRelativeImports(
6780
- demoFile,
6781
- /* @__PURE__ */ new Set()
6782
- );
6783
- allImports.push(...imports);
6784
- }
6785
- const uniqueImports = Array.from(
6786
- new Map(allImports.map((m) => [m.path, m])).values()
6787
- );
6788
- if (this.config.verbose) {
6789
- console.log(
6790
- ` Collected ${uniqueImports.length} unique import modules`
6791
- );
6792
- }
6793
- if (uniqueImports.length > 0) {
6794
- lines.push("## Related Source Files");
6795
- lines.push("");
6796
- for (const importedModule of uniqueImports) {
6797
- const ext = extname(importedModule.path).slice(1);
6798
- lines.push(`### ${basename(importedModule.path)}`);
6799
- lines.push("");
6800
- lines.push(`\`\`\`${ext}`);
6801
- lines.push(importedModule.content);
6802
- lines.push("```");
6803
- lines.push("");
6804
- }
6805
- }
6806
- }
6807
- const fileContent = lines.join("\n");
6808
- const fileName = `${kebabCase(page.id || page.name)}.md`;
6809
- const outfile = `${resolve4(this.config.outputPath)}/${fileName}`;
6810
- await writeFile3(outfile, fileContent, "utf-8");
6811
- const stats = await stat(outfile);
6812
- totalSize += stats.size / 1024;
6813
- manifestEntries.push({
6814
- id: page.id || kebabCase(page.name),
6815
- md5: computeMd5(fileContent),
6816
- path: fileName,
6817
- size: stats.size,
6818
- title: processedPage.title,
6819
- url: page.url
6820
- });
6821
- })
7108
+ return this.handleResponse(response);
7109
+ }
7110
+ async addFile(knowledgeId, fileId) {
7111
+ const response = await fetch(
7112
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/add`,
7113
+ {
7114
+ body: JSON.stringify({ file_id: fileId }),
7115
+ headers: this.jsonHeaders,
7116
+ method: "POST"
7117
+ }
6822
7118
  );
6823
- const extraFilesResult = await this.generateExtraFiles(metadata);
6824
- manifestEntries.push(...extraFilesResult.entries);
6825
- if (this.config.manifestOutputPath) {
6826
- if (this.config.generateManifest !== false) {
6827
- await this.generateManifest(
6828
- this.config.manifestOutputPath,
6829
- manifestEntries
6830
- );
7119
+ return this.handleResponse(response);
7120
+ }
7121
+ async updateFile(knowledgeId, fileId) {
7122
+ const response = await fetch(
7123
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/update`,
7124
+ {
7125
+ body: JSON.stringify({ file_id: fileId }),
7126
+ headers: this.jsonHeaders,
7127
+ method: "POST"
6831
7128
  }
6832
- if (this.config.generateBulkZip !== false) {
6833
- await this.generateBulkZip(
6834
- this.config.manifestOutputPath,
6835
- manifestEntries
6836
- );
7129
+ );
7130
+ return this.handleResponse(response);
7131
+ }
7132
+ async removeFile(knowledgeId, fileId, deleteFile = true) {
7133
+ const params = new URLSearchParams({ delete_file: String(deleteFile) });
7134
+ const response = await fetch(
7135
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/remove?${params}`,
7136
+ {
7137
+ body: JSON.stringify({ file_id: fileId }),
7138
+ headers: this.jsonHeaders,
7139
+ method: "POST"
6837
7140
  }
6838
- }
6839
- console.log(
6840
- `Generated ${count + extraFilesResult.count} files(s) in ${chalk3.magenta.bold(`${Math.round(performance.now() - start + extraFilesResult.duration)}ms`)} at ${chalk3.blue.bold(this.config.outputPath)} - ${(totalSize + extraFilesResult.totalSize).toFixed(1)} KB`
6841
7141
  );
7142
+ return this.handleResponse(response);
6842
7143
  }
6843
- computeAggregateHash(entries) {
6844
- const sortedHashes = entries.map((e) => e.md5).sort().join("");
6845
- return computeMd5(sortedHashes);
7144
+ async delete(id) {
7145
+ const response = await fetch(
7146
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/delete`,
7147
+ {
7148
+ headers: this.headers,
7149
+ method: "DELETE"
7150
+ }
7151
+ );
7152
+ return this.handleResponse(response);
6846
7153
  }
6847
- async generateManifest(outputPath, entries) {
6848
- const aggregateHash = this.computeAggregateHash(entries);
6849
- const totalSize = entries.reduce((sum, e) => sum + e.size, 0);
6850
- const manifest = {
6851
- aggregateHash,
6852
- baseUrl: this.config.baseUrl,
6853
- files: entries,
6854
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6855
- totalFiles: entries.length,
6856
- totalSize,
6857
- version: 1
6858
- };
6859
- await mkdir2(outputPath, { recursive: true }).catch(() => {
6860
- });
6861
- await writeFile3(
6862
- join3(outputPath, "manifest.json"),
6863
- JSON.stringify(manifest, null, 2),
6864
- "utf-8"
7154
+ async reset(id) {
7155
+ const response = await fetch(
7156
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/reset`,
7157
+ {
7158
+ headers: this.jsonHeaders,
7159
+ method: "POST"
7160
+ }
6865
7161
  );
6866
- return manifest;
7162
+ return this.handleResponse(response);
6867
7163
  }
6868
- async generateBulkZip(outputPath, entries) {
6869
- await mkdir2(outputPath, { recursive: true }).catch(() => {
6870
- });
6871
- const zipPath = join3(outputPath, "bulk.zip");
6872
- const zip = new AdmZip();
6873
- for (const entry of entries) {
6874
- const filePath = join3(this.config.outputPath, entry.path);
6875
- const content = await readFile(filePath);
6876
- zip.addFile(entry.path, content);
6877
- }
6878
- zip.writeZip(zipPath);
7164
+ async addFilesBatch(knowledgeId, fileIds) {
7165
+ const response = await fetch(
7166
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/files/batch/add`,
7167
+ {
7168
+ body: JSON.stringify(fileIds.map((file_id) => ({ file_id }))),
7169
+ headers: this.jsonHeaders,
7170
+ method: "POST"
7171
+ }
7172
+ );
7173
+ return this.handleResponse(response);
6879
7174
  }
6880
7175
  };
6881
- async function generate(config2) {
6882
- const generator = new KnowledgeGenerator(config2);
6883
- return generator.run();
6884
- }
6885
- function addGenerateKnowledgeCommand() {
6886
- program.description("Generate llms.txt from QUI Docs documentation").command("generate-llms-txt").option("-n, --name <name>", "Project name for llms.txt header").requiredOption("-m, --output-mode <outputMode>").option("-o, --outputPath <outputPath>", "Output file or directory.").option(
6887
- "-d, --description <description>",
6888
- "Project description for llms.txt"
6889
- ).option("-v, --verbose", "Enable verbose logging", false).option(
6890
- "--exclude <patterns...>",
6891
- "Glob patterns to exclude (e.g., **/internal/**, guide/drafts/*)",
6892
- []
6893
- ).option("--base-url <url>", "Base URL for component documentation links").option("--metadata <pairs...>", "metadata key-value pairs").option("--clean", "Clean the output path before generating").option("--include-imports", "Include relative import source files", true).option(
6894
- "-e, --environment <environments>",
6895
- "Comma-separated list of environments to generate (default: all)"
6896
- ).action(async (options) => {
7176
+
7177
+ // src/ai-knowledge/open-web-ui/download-knowledge.ts
7178
+ function addDownloadKnowledgeCommand() {
7179
+ program.command("download-knowledge").description("Download files from an Open Web UI knowledge base").requiredOption("-o, --output-dir <outputDir>", "Folder path").requiredOption("-e, --environment <environments>", "environment to load").action(async (opts) => {
6897
7180
  loadEnv();
6898
- const cliOptions = {
6899
- ...options,
6900
- outputMode: options.outputMode === "per-page" ? "per-page" : "aggregated"
6901
- };
6902
- const environmentFilter = options.environment?.split(",").map((e) => e.trim()).filter(Boolean);
6903
- const configs = loadEnvironmentConfigs({
6904
- cliOptions,
6905
- environments: environmentFilter
6906
- });
6907
- for (const config2 of configs) {
6908
- const envLabel = config2.environmentName ? `[${config2.environmentName}] ` : "";
6909
- console.log(`${envLabel}Generating knowledge to ${config2.outputPath}`);
6910
- await generate(config2);
6911
- }
6912
- if (configs.length > 1) {
6913
- console.log(
6914
- `
6915
- Generated knowledge for ${configs.length} environment(s)`
6916
- );
7181
+ const env = opts.environment;
7182
+ dotenv.config({ path: `.env.${env}` });
7183
+ await mkdir2(opts.outputDir, { recursive: true }).catch();
7184
+ const config3 = getConfigFromEnv();
7185
+ const apiConfig = { apiKey: config3.webUiKey, baseUrl: config3.webUiUrl };
7186
+ const knowledgeApi = new KnowledgeApi(apiConfig);
7187
+ const filesApi = new FilesApi(apiConfig);
7188
+ const knowledge = await knowledgeApi.getById(config3.knowledgeId);
7189
+ for (const file of knowledge.files ?? []) {
7190
+ const fileName = file.meta?.name;
7191
+ if (!fileName) {
7192
+ continue;
7193
+ }
7194
+ try {
7195
+ const content = await filesApi.getDataContent(file.id);
7196
+ if (content?.content) {
7197
+ await writeFile2(
7198
+ resolve5(opts.outputDir, fileName),
7199
+ content.content,
7200
+ "utf-8"
7201
+ );
7202
+ }
7203
+ } catch {
7204
+ console.warn(`Failed to download ${fileName}`);
7205
+ }
6917
7206
  }
6918
7207
  });
6919
7208
  }
6920
7209
 
6921
- // src/open-web-ui-knowledge/upload-knowledge.ts
7210
+ // src/ai-knowledge/open-web-ui/upload-knowledge.ts
6922
7211
  import { createHash as createHash3 } from "node:crypto";
6923
7212
  import { writeFileSync } from "node:fs";
6924
- import { access as access2, readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
6925
- import { resolve as resolve5 } from "node:path";
7213
+ import { access as access2, readdir as readdir2, readFile as readFile5, stat as stat2 } from "node:fs/promises";
7214
+ import { resolve as resolve6 } from "node:path";
6926
7215
  import { setTimeout as setTimeout2 } from "node:timers/promises";
6927
7216
  import ora from "ora";
6928
7217
 
6929
- // src/open-web-ui-knowledge/knowledge-cleaner.ts
7218
+ // src/ai-knowledge/open-web-ui/common.ts
7219
+ import { config as config2 } from "dotenv";
7220
+ function loadOpenWebUiEnv(integration, integrationName) {
7221
+ const envFilePath = integration.envFile ?? `.env.${integration.id}`;
7222
+ config2({ override: true, path: envFilePath, quiet: true });
7223
+ const url = process.env.OPEN_WEB_UI_URL ?? process.env.WEB_UI_URL;
7224
+ const apiKey = process.env.OPEN_WEB_UI_API_KEY ?? process.env.WEB_UI_KEY;
7225
+ const knowledgeId = process.env.OPEN_WEB_UI_KNOWLEDGE_ID ?? process.env.KNOWLEDGE_ID;
7226
+ if (!url) {
7227
+ throw new Error(
7228
+ `Missing OPEN_WEB_UI_URL for integration "${integrationName}" (env file: ${envFilePath})`
7229
+ );
7230
+ }
7231
+ if (!apiKey) {
7232
+ throw new Error(
7233
+ `Missing OPEN_WEB_UI_API_KEY for integration "${integrationName}" (env file: ${envFilePath})`
7234
+ );
7235
+ }
7236
+ if (!knowledgeId) {
7237
+ throw new Error(
7238
+ `Missing OPEN_WEB_UI_KNOWLEDGE_ID for integration "${integrationName}" (env file: ${envFilePath})`
7239
+ );
7240
+ }
7241
+ return { apiKey, knowledgeId, url };
7242
+ }
7243
+ function resolveOpenWebUiIntegration(name, integration, outputPath) {
7244
+ const credentials = loadOpenWebUiEnv(integration, name);
7245
+ return {
7246
+ apiKey: credentials.apiKey,
7247
+ environment: integration.id,
7248
+ knowledgeId: credentials.knowledgeId,
7249
+ name,
7250
+ outputPath,
7251
+ url: credentials.url
7252
+ };
7253
+ }
7254
+ function loadOpenWebUiIntegrations(options = {}) {
7255
+ const configLoader = new ConfigLoader({});
7256
+ const resolvedConfig = configLoader.loadConfig();
7257
+ const knowledgeConfig = resolvedConfig.knowledge;
7258
+ const environments = knowledgeConfig?.environments;
7259
+ const integrations = knowledgeConfig?.integrations?.openWebUi;
7260
+ if (!integrations || integrations.length === 0) {
7261
+ return [];
7262
+ }
7263
+ let filteredIntegrations = integrations;
7264
+ if (options.integrations?.length) {
7265
+ const filterSet = new Set(options.integrations);
7266
+ filteredIntegrations = integrations.filter(
7267
+ (integration) => filterSet.has(integration.id)
7268
+ );
7269
+ }
7270
+ if (options.environments?.length) {
7271
+ const filterSet = new Set(options.environments);
7272
+ filteredIntegrations = filteredIntegrations.filter(
7273
+ (integration) => filterSet.has(integration.id)
7274
+ );
7275
+ }
7276
+ return filteredIntegrations.map((integration) => {
7277
+ const envConfig = environments?.find((e) => e.id === integration.id);
7278
+ if (!envConfig) {
7279
+ throw new Error(
7280
+ `Integration "${integration.id}" references unknown environment "${integration.id}". Available environments: ${environments?.map((e) => e.id).join(", ") || "none"}`
7281
+ );
7282
+ }
7283
+ return {
7284
+ integration,
7285
+ name: integration.id,
7286
+ outputPath: envConfig.outputPath
7287
+ };
7288
+ });
7289
+ }
7290
+
7291
+ // src/ai-knowledge/open-web-ui/knowledge-cleaner.ts
6930
7292
  var KnowledgeCleaner = class {
6931
7293
  filesApi;
6932
7294
  knowledgeApi;
6933
- constructor(config2) {
7295
+ constructor(config3) {
6934
7296
  const apiConfig = {
6935
- apiKey: config2.webUiKey,
6936
- baseUrl: config2.webUiUrl
7297
+ apiKey: config3.webUiKey,
7298
+ baseUrl: config3.webUiUrl
6937
7299
  };
6938
7300
  this.filesApi = new FilesApi(apiConfig);
6939
7301
  this.knowledgeApi = new KnowledgeApi(apiConfig);
@@ -6953,7 +7315,7 @@ var KnowledgeCleaner = class {
6953
7315
  }
6954
7316
  };
6955
7317
 
6956
- // src/open-web-ui-knowledge/upload-knowledge.ts
7318
+ // src/ai-knowledge/open-web-ui/upload-knowledge.ts
6957
7319
  function toKnowledgeFile(file) {
6958
7320
  return {
6959
7321
  id: file.id,
@@ -6971,15 +7333,15 @@ var Uploader = class {
6971
7333
  fileHashCache = /* @__PURE__ */ new Map();
6972
7334
  knowledgeFilesCache = null;
6973
7335
  cleaner;
6974
- constructor(config2) {
6975
- this.config = config2;
7336
+ constructor(config3) {
7337
+ this.config = config3;
6976
7338
  const apiConfig = {
6977
- apiKey: config2.webUiKey,
6978
- baseUrl: config2.webUiUrl
7339
+ apiKey: config3.webUiKey,
7340
+ baseUrl: config3.webUiUrl
6979
7341
  };
6980
7342
  this.knowledgeApi = new KnowledgeApi(apiConfig);
6981
7343
  this.filesApi = new FilesApi(apiConfig);
6982
- this.cleaner = new KnowledgeCleaner(config2);
7344
+ this.cleaner = new KnowledgeCleaner(config3);
6983
7345
  }
6984
7346
  async buildHashCache(files) {
6985
7347
  const results = await Promise.allSettled(
@@ -7048,11 +7410,15 @@ var Uploader = class {
7048
7410
  return { success: false };
7049
7411
  }
7050
7412
  async uploadDirectory() {
7051
- const fileNames = await readdir2(this.config.knowledgeFilePath);
7413
+ const allFileNames = await readdir2(this.config.knowledgeFilePath);
7414
+ const allowedExtensions = [".md", ".mdx", ".txt", ".pdf"];
7415
+ const fileNames = allFileNames.filter(
7416
+ (name) => allowedExtensions.some((ext) => name.endsWith(ext))
7417
+ );
7052
7418
  const files = await Promise.all(
7053
7419
  fileNames.map(async (name) => ({
7054
- contents: await readFile2(
7055
- resolve5(this.config.knowledgeFilePath, name),
7420
+ contents: await readFile5(
7421
+ resolve6(this.config.knowledgeFilePath, name),
7056
7422
  "utf-8"
7057
7423
  ),
7058
7424
  name
@@ -7103,8 +7469,8 @@ var Uploader = class {
7103
7469
  if (knowledgeFile) {
7104
7470
  try {
7105
7471
  const fileId = knowledgeFile.id;
7106
- const fileString = await readFile2(
7107
- resolve5(this.config.knowledgeFilePath, name),
7472
+ const fileString = await readFile5(
7473
+ resolve6(this.config.knowledgeFilePath, name),
7108
7474
  "utf-8"
7109
7475
  );
7110
7476
  const spinner2 = ora(`Updating ${name}`).start();
@@ -7118,8 +7484,8 @@ var Uploader = class {
7118
7484
  }
7119
7485
  }
7120
7486
  const spinner = ora(`Uploading ${name}`).start();
7121
- const fileBuffer = await readFile2(
7122
- resolve5(this.config.knowledgeFilePath, name)
7487
+ const fileBuffer = await readFile5(
7488
+ resolve6(this.config.knowledgeFilePath, name)
7123
7489
  );
7124
7490
  let uploadedFileId = void 0;
7125
7491
  try {
@@ -7157,7 +7523,7 @@ var Uploader = class {
7157
7523
  }
7158
7524
  }
7159
7525
  async uploadKnowledge() {
7160
- const resolvedPath = resolve5(this.config.knowledgeFilePath);
7526
+ const resolvedPath = resolve6(this.config.knowledgeFilePath);
7161
7527
  if (!await access2(resolvedPath).then(() => true).catch(() => false)) {
7162
7528
  throw new Error(`File or folder not found at ${resolvedPath}`);
7163
7529
  }
@@ -7244,7 +7610,7 @@ Uploaded to ${successCount} integration(s)${failureCount > 0 ? `, ${failureCount
7244
7610
  const files = await uploader.filesApi.search("*");
7245
7611
  console.debug(`found ${files.length} files`);
7246
7612
  writeFileSync(
7247
- resolve5(uploader.config.knowledgeFilePath, "files.json"),
7613
+ resolve6(uploader.config.knowledgeFilePath, "files.json"),
7248
7614
  JSON.stringify(files, null, 2),
7249
7615
  "utf-8"
7250
7616
  );
@@ -7286,18 +7652,72 @@ Uploaded to ${successCount} integration(s)${failureCount > 0 ? `, ${failureCount
7286
7652
  });
7287
7653
  }
7288
7654
 
7655
+ // src/docs-plugin/generate-page-map.ts
7656
+ import { glob } from "glob";
7657
+ import { writeFile as writeFile3 } from "node:fs/promises";
7658
+ import { resolve as resolve7 } from "node:path";
7659
+ import { cwd } from "node:process";
7660
+ function addGeneratePageMapCommand() {
7661
+ program.command("generate-page-map").description(
7662
+ "Invokes the docs-plugin once to build the site data and writes it to json"
7663
+ ).option(
7664
+ "-c, --config-file <configFile>",
7665
+ "Path to the qui-docs.config.ts config file"
7666
+ ).option(
7667
+ "-r, --routes-dir <routesDir>",
7668
+ "Path to the routes directory",
7669
+ "src/routes"
7670
+ ).option(
7671
+ "-o, --output <output>",
7672
+ "Output path for the site data json",
7673
+ "site-data.json"
7674
+ ).action(async (options) => {
7675
+ try {
7676
+ const configLoader = new ConfigLoader({ configFile: options.configFile });
7677
+ const resolvedConfig = configLoader.loadConfig();
7678
+ const routesDir = fixPath(
7679
+ resolve7(resolvedConfig.appDirectory, resolvedConfig.pageDirectory)
7680
+ );
7681
+ const indexer = new SearchIndexer({
7682
+ ...resolvedConfig,
7683
+ srcDir: fixPath(resolve7(cwd(), resolvedConfig.appDirectory)),
7684
+ typeDocProps: {}
7685
+ });
7686
+ const files = glob.sync(
7687
+ [`${routesDir}/**/*.mdx`, `${routesDir}/**/*.tsx`],
7688
+ {
7689
+ absolute: true,
7690
+ cwd: cwd()
7691
+ }
7692
+ );
7693
+ indexer.buildIndex(files, true);
7694
+ await writeFile3(
7695
+ resolve7(cwd(), options.output),
7696
+ JSON.stringify(indexer.pageMap, null, 2),
7697
+ "utf-8"
7698
+ );
7699
+ } catch (error) {
7700
+ console.error(
7701
+ "Generate Site Data Error:",
7702
+ error instanceof Error ? error.message : String(error)
7703
+ );
7704
+ process.exit(1);
7705
+ }
7706
+ });
7707
+ }
7708
+
7289
7709
  // src/react-demo-plugin/generate-lazy-demo-map.ts
7290
7710
  import { glob as glob2 } from "glob";
7291
7711
  import { uniqBy } from "lodash-es";
7292
7712
  import { writeFile as writeFile4 } from "node:fs/promises";
7293
- import { resolve as resolve7 } from "node:path";
7713
+ import { resolve as resolve9 } from "node:path";
7294
7714
  import { dedent } from "@qualcomm-ui/utils/dedent";
7295
7715
 
7296
7716
  // src/react-demo-plugin/demo-plugin-utils.ts
7297
7717
  import chalk4 from "chalk";
7298
7718
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
7299
- import { dirname as dirname2, join as join4, relative as relative3, resolve as resolve6, sep } from "node:path";
7300
- import * as ts from "typescript";
7719
+ import { dirname as dirname4, join as join7, relative as relative3, resolve as resolve8, sep } from "node:path";
7720
+ import * as ts2 from "typescript";
7301
7721
  import { pascalCase } from "@qualcomm-ui/utils/change-case";
7302
7722
  function extractPageId(filePath, routesDir) {
7303
7723
  const relativePath = relative3(routesDir, filePath);
@@ -7306,7 +7726,7 @@ function extractPageId(filePath, routesDir) {
7306
7726
  const demosIndex = pathParts.indexOf("demos");
7307
7727
  return pathParts.slice(0, demosIndex).join(sep);
7308
7728
  }
7309
- return dirname2(relativePath);
7729
+ return dirname4(relativePath);
7310
7730
  }
7311
7731
  function isDemoFile(filePath) {
7312
7732
  try {
@@ -7353,7 +7773,7 @@ function generateLazyDemoLoader(demoPages) {
7353
7773
  }
7354
7774
  async function generateLazyDemoMap(options) {
7355
7775
  const routesDir = options.routesDir;
7356
- const outputPath = resolve7(options.output);
7776
+ const outputPath = resolve9(options.output);
7357
7777
  console.log(`Scanning for demo pages in: ${routesDir}`);
7358
7778
  const demoPages = await scanForDemoPages({ routesDir });
7359
7779
  console.log(`Found ${demoPages.length} pages with demos`);