@farming-labs/docs 0.2.41 → 0.2.43

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 (42) hide show
  1. package/dist/{agent-WcXCJ4EN.mjs → agent-DXDOMJBw.mjs} +6 -6
  2. package/dist/{agents-BjTEkCsY.mjs → agents-DjhjZaNP.mjs} +5 -5
  3. package/dist/analytics-BZEwTK-8.mjs +177 -0
  4. package/dist/cli/index.mjs +27 -27
  5. package/dist/client/react.d.mts +45 -0
  6. package/dist/client/react.mjs +223 -0
  7. package/dist/{cloud-wcDf1YC0.mjs → cloud-Bk-eppay.mjs} +158 -14
  8. package/dist/cloud-analytics-Dlk_byos.mjs +132 -0
  9. package/dist/cloud-ask-ai-Cl-xaV8j.mjs +382 -0
  10. package/dist/cloud-ask-ai-DcMe6xOf.d.mts +23 -0
  11. package/dist/{codeblocks-DTlvGTH4.mjs → codeblocks-z9iT794h.mjs} +3 -3
  12. package/dist/{dev-DxVevOP_.mjs → dev-CBha7knT.mjs} +2 -2
  13. package/dist/docs-cloud-server.d.mts +70 -0
  14. package/dist/docs-cloud-server.mjs +310 -0
  15. package/dist/{doctor-VwhpJct8.mjs → doctor-O9o9kjN8.mjs} +7 -7
  16. package/dist/{downgrade-ghVFFHKi.mjs → downgrade-7PGER59g.mjs} +2 -2
  17. package/dist/index.d.mts +2 -2
  18. package/dist/index.mjs +4 -3
  19. package/dist/{init-DkcasCws.mjs → init-Ds7QM9po.mjs} +3 -3
  20. package/dist/{mcp-DfJiBC0f.mjs → mcp-DuCPNHS-.mjs} +4 -4
  21. package/dist/mcp.d.mts +1 -1
  22. package/dist/mcp.mjs +3 -2
  23. package/dist/{package-version-CeNAB4NZ.mjs → package-version-DPQSBBsX.mjs} +1 -1
  24. package/dist/{review-Bnd4NH9y.mjs → review-lm3dt7yy.mjs} +2 -2
  25. package/dist/{robots-BrBGgn1w.mjs → robots-DV7u4Ire.mjs} +4 -4
  26. package/dist/{robots-CVoC0w0i.mjs → robots-XVPs9lVz.mjs} +1 -1
  27. package/dist/{search-CA3H4Dfv.mjs → search-B6nEkx86.mjs} +4 -4
  28. package/dist/{search-BuOU4idW.mjs → search-BWuU70B0.mjs} +2 -306
  29. package/dist/{search-BEe1Gdu7.d.mts → search-DFEhf9-s.d.mts} +1 -1
  30. package/dist/server.d.mts +5 -23
  31. package/dist/server.mjs +8 -5
  32. package/dist/{sitemap-CUtSSoGG.mjs → sitemap-BEiKy4Iy.mjs} +5 -5
  33. package/dist/{sitemap-server-PLcNxmtk.mjs → sitemap-server-xEHtDUDB.mjs} +2 -382
  34. package/dist/{types-DKCNILor.d.mts → types-XqGLsmOD.d.mts} +17 -0
  35. package/dist/{upgrade-aexO-zom.mjs → upgrade-CkdKWRlk.mjs} +2 -2
  36. package/package.json +21 -1
  37. /package/dist/{agent-B6szuz_8.mjs → agent-CaOBIVFy.mjs} +0 -0
  38. /package/dist/{config-De5z-2LK.mjs → config-tiQCZ46q.mjs} +0 -0
  39. /package/dist/{errors-Dw5gMhEZ.mjs → errors-CW1LnxaQ.mjs} +0 -0
  40. /package/dist/{review-CJD85JTm.mjs → review-B7goPYUb.mjs} +0 -0
  41. /package/dist/{templates-BOoV0cIR.mjs → templates-CEcOVbV0.mjs} +0 -0
  42. /package/dist/{utils-CoHZJ1i4.mjs → utils-D-xTRNuh.mjs} +0 -0
@@ -1,6 +1,6 @@
1
- import { n as markCliErrorReported } from "./errors-Dw5gMhEZ.mjs";
2
- import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelBooleanProperty, f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, p as resolveDocsConfigPath, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-De5z-2LK.mjs";
3
- import { t as detectFramework } from "./utils-CoHZJ1i4.mjs";
1
+ import { n as markCliErrorReported } from "./errors-CW1LnxaQ.mjs";
2
+ import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelBooleanProperty, f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, p as resolveDocsConfigPath, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-tiQCZ46q.mjs";
3
+ import { i as detectPackageManagerFromProject, t as detectFramework } from "./utils-D-xTRNuh.mjs";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import pc from "picocolors";
@@ -31,6 +31,7 @@ const CLOUD_CHECK_TARGETS = [
31
31
  "analytics",
32
32
  "ask-ai"
33
33
  ];
34
+ const FUMADOCS_CONNECT_MARKER = "@farming-labs/docs cloud connect: fumadocs";
34
35
  function isRecord(value) {
35
36
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
36
37
  }
@@ -51,6 +52,100 @@ function readPackageName(rootDir) {
51
52
  return;
52
53
  }
53
54
  }
55
+ function readPackageJsonRecord(rootDir) {
56
+ const packagePath = path.join(rootDir, "package.json");
57
+ if (!fs.existsSync(packagePath)) return void 0;
58
+ try {
59
+ const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
60
+ return isRecord(parsed) ? parsed : void 0;
61
+ } catch {
62
+ return;
63
+ }
64
+ }
65
+ function readPackageDependencies(rootDir) {
66
+ const packageJson = readPackageJsonRecord(rootDir);
67
+ const names = /* @__PURE__ */ new Set();
68
+ for (const key of [
69
+ "dependencies",
70
+ "devDependencies",
71
+ "peerDependencies",
72
+ "optionalDependencies"
73
+ ]) {
74
+ const dependencies = packageJson?.[key];
75
+ if (!isRecord(dependencies)) continue;
76
+ for (const name of Object.keys(dependencies)) names.add(name);
77
+ }
78
+ return names;
79
+ }
80
+ function hasFumadocsDependency(dependencies) {
81
+ return [...dependencies].some((name) => name === "fumadocs-core" || name === "fumadocs-ui" || name === "fumadocs-mdx" || name === "fumadocs-openapi" || name === "fumadocs-docgen");
82
+ }
83
+ function firstExistingFile(rootDir, candidates) {
84
+ return candidates.find((candidate) => fs.existsSync(path.join(rootDir, candidate)));
85
+ }
86
+ function hasMarkdownDescendant(rootDir, relativeDir) {
87
+ const absoluteDir = path.join(rootDir, relativeDir);
88
+ if (!fs.existsSync(absoluteDir)) return false;
89
+ const queue = [absoluteDir];
90
+ while (queue.length > 0) {
91
+ const current = queue.shift();
92
+ let entries;
93
+ try {
94
+ entries = fs.readdirSync(current, { withFileTypes: true });
95
+ } catch {
96
+ continue;
97
+ }
98
+ for (const entry of entries) {
99
+ const fullPath = path.join(current, entry.name);
100
+ if (entry.isDirectory()) {
101
+ queue.push(fullPath);
102
+ continue;
103
+ }
104
+ if (entry.isFile() && /\.(?:md|mdx)$/i.test(entry.name)) return true;
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ function detectFumadocsContentRoots(rootDir) {
110
+ const roots = [
111
+ "content/docs",
112
+ "src/content/docs",
113
+ "docs",
114
+ "content"
115
+ ].filter((candidate) => hasMarkdownDescendant(rootDir, candidate));
116
+ return roots.filter((root) => !roots.some((other) => other !== root && other.startsWith(`${root}/`)));
117
+ }
118
+ function detectConnectedFumadocsProfile(rootDir) {
119
+ const dependencies = readPackageDependencies(rootDir);
120
+ const sourceConfig = firstExistingFile(rootDir, [
121
+ "source.config.ts",
122
+ "source.config.tsx",
123
+ "source.config.js",
124
+ "source.config.mjs",
125
+ "source.config.cjs"
126
+ ]);
127
+ const sourceFile = firstExistingFile(rootDir, [
128
+ "lib/source.ts",
129
+ "lib/source.tsx",
130
+ "src/lib/source.ts",
131
+ "src/lib/source.tsx"
132
+ ]);
133
+ const contentRoots = detectFumadocsContentRoots(rootDir);
134
+ if (!(hasFumadocsDependency(dependencies) || Boolean(sourceConfig) || Boolean(sourceFile)) || contentRoots.length === 0) return;
135
+ const framework = detectFramework(rootDir) ?? "nextjs";
136
+ const configFiles = [sourceConfig, sourceFile].filter((file) => Boolean(file));
137
+ const packageManager = detectPackageManagerFromProject(rootDir)?.packageManager;
138
+ return {
139
+ engine: "fumadocs",
140
+ runtime: framework,
141
+ appRoot: ".",
142
+ contentRoots,
143
+ configFiles,
144
+ ...packageManager ? { packageManager } : {},
145
+ confidence: sourceConfig || sourceFile ? "high" : "medium",
146
+ reason: sourceConfig ? `Detected Fumadocs source config at ${sourceConfig}.` : "Detected Fumadocs dependencies and markdown content."
147
+ };
148
+ }
54
149
  function runGit(rootDir, args) {
55
150
  try {
56
151
  return execFileSync("git", [
@@ -363,6 +458,19 @@ ${renderCloudConfigProperty(" ", apiKeyEnv)}
363
458
  });
364
459
  `;
365
460
  }
461
+ function renderFumadocsConnectDocsConfig(apiKeyEnv, profile) {
462
+ const contentDir = profile.contentRoots[0] ?? "content/docs";
463
+ return `// ${FUMADOCS_CONNECT_MARKER}
464
+ import { defineDocs } from "@farming-labs/docs";
465
+
466
+ export default defineDocs({
467
+ entry: "docs",
468
+ contentDir: ${JSON.stringify(contentDir)},
469
+ ${renderAnalyticsConfigProperty(" ")}
470
+ ${renderCloudConfigProperty(" ", apiKeyEnv)}
471
+ });
472
+ `;
473
+ }
366
474
  function normalizeEnvName(value, fallback) {
367
475
  const normalized = value?.trim();
368
476
  if (!normalized) return fallback;
@@ -373,7 +481,7 @@ function ensureDocsConfigCloudInit(options) {
373
481
  const resolvedConfigPath = tryResolveDocsConfigPath(options.rootDir, options.configPath);
374
482
  const configPath = resolvedConfigPath ?? path.join(options.rootDir, "docs.config.ts");
375
483
  if (!resolvedConfigPath) {
376
- fs.writeFileSync(configPath, renderCloudInitDocsConfig(options.apiKeyEnv), "utf-8");
484
+ fs.writeFileSync(configPath, options.docsInfraProfile ? renderFumadocsConnectDocsConfig(options.apiKeyEnv, options.docsInfraProfile) : renderCloudInitDocsConfig(options.apiKeyEnv), "utf-8");
377
485
  return {
378
486
  configPath,
379
487
  created: true,
@@ -525,6 +633,14 @@ function resolveApiReferenceRoot(snapshot) {
525
633
  if (!apiReferenceBlock) return void 0;
526
634
  return readStringProperty(apiReferenceBlock, "path");
527
635
  }
636
+ function resolveConnectedDocsProfile(params) {
637
+ if (params.explicit) return params.explicit;
638
+ if (!(params.snapshot.content?.includes(FUMADOCS_CONNECT_MARKER) || !params.snapshot.path)) return void 0;
639
+ const detectedProfile = detectConnectedFumadocsProfile(params.rootDir);
640
+ if (detectedProfile) return detectedProfile;
641
+ const existingProfile = params.existing?.extensions?.docsInfraProfile;
642
+ if (isRecord(existingProfile) && existingProfile.engine === "fumadocs" && Array.isArray(existingProfile.contentRoots)) return existingProfile;
643
+ }
528
644
  function resolveSiteConfig(rootDir, snapshot, existing) {
529
645
  const existingSite = toJsonRecord(existing?.site);
530
646
  const navTitle = isRecord(snapshot.config?.nav) && typeof snapshot.config.nav.title === "string" ? snapshot.config.nav.title : readNavTitle(snapshot.content ?? "");
@@ -540,17 +656,18 @@ function resolveSiteConfig(rootDir, snapshot, existing) {
540
656
  ...typeof description === "string" ? { description } : {}
541
657
  };
542
658
  }
543
- function resolveDocsRoot(rootDir, snapshot, existing) {
659
+ function resolveDocsRoot(rootDir, snapshot, existing, docsInfraProfile) {
660
+ if (docsInfraProfile?.contentRoots[0]) return docsInfraProfile.contentRoots[0];
544
661
  const entry = snapshot.config?.entry ?? readTopLevelStringProperty(snapshot.content ?? "", "entry") ?? "docs";
545
662
  if (snapshot.config?.contentDir) return snapshot.config.contentDir;
546
663
  if (snapshot.content) return resolveDocsContentDir(rootDir, snapshot.content, entry);
547
664
  if (typeof existing?.content?.docsRoot === "string") return existing.content.docsRoot;
548
665
  return fs.existsSync(path.join(rootDir, "app", entry)) ? path.join("app", entry) : entry;
549
666
  }
550
- function resolveDocsBlock(rootDir, snapshot, existing) {
667
+ function resolveDocsBlock(rootDir, snapshot, existing, docsInfraProfile) {
551
668
  const detectedFramework = detectFramework(rootDir);
552
669
  const existingDocs = existing?.docs;
553
- const runtime = detectedFramework ?? existingDocs?.runtime ?? "nextjs";
670
+ const runtime = detectedFramework ?? docsInfraProfile?.runtime ?? existingDocs?.runtime ?? "nextjs";
554
671
  if (!Boolean(snapshot.path || detectedFramework) && existingDocs) return existingDocs;
555
672
  return {
556
673
  mode: "framework",
@@ -558,12 +675,31 @@ function resolveDocsBlock(rootDir, snapshot, existing) {
558
675
  root: existingDocs?.root || "."
559
676
  };
560
677
  }
678
+ function resolveExtensions(existing, docsInfraProfile) {
679
+ const existingExtensions = toJsonRecord(existing?.extensions);
680
+ if (!docsInfraProfile) {
681
+ if (!existingExtensions?.docsInfraProfile) return existingExtensions;
682
+ const { docsInfraProfile: _staleDocsInfraProfile, ...rest } = existingExtensions;
683
+ return Object.keys(rest).length > 0 ? rest : void 0;
684
+ }
685
+ return {
686
+ ...existingExtensions,
687
+ docsInfraProfile: toJsonRecord(docsInfraProfile)
688
+ };
689
+ }
561
690
  function materializeDocsJsonObject(params) {
562
691
  const cloud = resolveCloudConfig(params.snapshot, params.existing);
563
- const docsRoot = resolveDocsRoot(params.rootDir, params.snapshot, params.existing);
692
+ const docsInfraProfile = resolveConnectedDocsProfile({
693
+ rootDir: params.rootDir,
694
+ snapshot: params.snapshot,
695
+ existing: params.existing,
696
+ explicit: params.docsInfraProfile
697
+ });
698
+ const docsRoot = resolveDocsRoot(params.rootDir, params.snapshot, params.existing, docsInfraProfile);
564
699
  const apiReferenceRoot = resolveApiReferenceRoot(params.snapshot);
565
700
  const existingContent = toJsonRecord(params.existing?.content);
566
701
  const site = resolveSiteConfig(params.rootDir, params.snapshot, params.existing);
702
+ const extensions = resolveExtensions(params.existing, docsInfraProfile);
567
703
  const content = {
568
704
  ...existingContent,
569
705
  docsRoot,
@@ -573,10 +709,11 @@ function materializeDocsJsonObject(params) {
573
709
  ...params.existing,
574
710
  $schema: params.existing?.$schema ?? DOCS_CLOUD_SCHEMA_URL,
575
711
  version: 1,
576
- docs: resolveDocsBlock(params.rootDir, params.snapshot, params.existing),
712
+ docs: resolveDocsBlock(params.rootDir, params.snapshot, params.existing, docsInfraProfile),
577
713
  content,
578
714
  ...site ? { site } : {},
579
- cloud
715
+ cloud,
716
+ ...extensions ? { extensions } : {}
580
717
  };
581
718
  }
582
719
  function serializeMaterializedDocsJson(config) {
@@ -590,7 +727,8 @@ async function materializeCloudConfig(options = {}) {
590
727
  const config = materializeDocsJsonObject({
591
728
  rootDir,
592
729
  snapshot,
593
- existing
730
+ existing,
731
+ docsInfraProfile: options.docsInfraProfile
594
732
  });
595
733
  const serialized = serializeMaterializedDocsJson(config);
596
734
  const updated = (existing ? fs.readFileSync(docsJsonPath, "utf-8") : void 0) !== serialized;
@@ -1278,15 +1416,19 @@ async function syncCloudConfig(options = {}) {
1278
1416
  async function initCloudConfig(options = {}) {
1279
1417
  const rootDir = options.rootDir ?? process.cwd();
1280
1418
  const apiKeyEnv = normalizeEnvName(options.apiKeyEnv, DOCS_CLOUD_DEFAULT_API_KEY_ENV);
1419
+ const existingConfigPath = tryResolveDocsConfigPath(rootDir, options.configPath);
1420
+ const docsInfraProfile = options.docsInfraProfile ?? (existingConfigPath ? void 0 : detectConnectedFumadocsProfile(rootDir));
1281
1421
  const configUpdate = ensureDocsConfigCloudInit({
1282
1422
  rootDir,
1283
1423
  configPath: options.configPath,
1284
- apiKeyEnv
1424
+ apiKeyEnv,
1425
+ docsInfraProfile
1285
1426
  });
1286
1427
  const materialized = await materializeCloudConfig({
1287
1428
  ...options,
1288
1429
  rootDir,
1289
- configPath: path.relative(rootDir, configUpdate.configPath)
1430
+ configPath: path.relative(rootDir, configUpdate.configPath),
1431
+ docsInfraProfile
1290
1432
  });
1291
1433
  return {
1292
1434
  configPath: configUpdate.configPath,
@@ -1296,7 +1438,8 @@ async function initCloudConfig(options = {}) {
1296
1438
  configCreated: configUpdate.created,
1297
1439
  configUpdated: configUpdate.updated,
1298
1440
  docsJsonCreated: materialized.created,
1299
- docsJsonUpdated: materialized.updated
1441
+ docsJsonUpdated: materialized.updated,
1442
+ ...docsInfraProfile ? { docsInfraProfile } : {}
1300
1443
  };
1301
1444
  }
1302
1445
  async function runCloudInit(options = {}) {
@@ -1311,6 +1454,7 @@ async function runCloudInit(options = {}) {
1311
1454
  const docsJsonAction = result.docsJsonCreated ? "created" : result.docsJsonUpdated ? "updated" : "checked";
1312
1455
  console.log(`${pc.green("ok")} ${configAction} ${pc.cyan(relativeConfigPath)}`);
1313
1456
  console.log(`${pc.green("ok")} ${docsJsonAction} ${pc.cyan(relativeDocsJsonPath)}`);
1457
+ if (result.docsInfraProfile?.engine === "fumadocs") console.log(`${pc.green("ok")} Connected existing ${pc.cyan("Fumadocs")} content at ${pc.cyan(result.docsInfraProfile.contentRoots.join(", "))}`);
1314
1458
  console.log();
1315
1459
  console.log(pc.bold("Add these env vars"));
1316
1460
  console.log(`${pc.cyan(result.apiKeyEnv)}=${pc.dim("paste_your_docs_cloud_api_key")}`);
@@ -0,0 +1,132 @@
1
+ //#region src/cloud-analytics.ts
2
+ const DEFAULT_DOCS_CLOUD_ANALYTICS_ENDPOINT = "https://api.farming-labs.dev/v1/analytics/events";
3
+ function normalizeRuntimeEnvValue(value) {
4
+ const normalized = value?.trim();
5
+ return normalized ? normalized : void 0;
6
+ }
7
+ function readRuntimeEnv(name) {
8
+ if (typeof process === "undefined" || !process.env) return;
9
+ switch (name) {
10
+ case "PUBLIC_DOCS_CLOUD_PROJECT_ID": return normalizeRuntimeEnvValue(process.env.PUBLIC_DOCS_CLOUD_PROJECT_ID);
11
+ case "NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID": return normalizeRuntimeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID);
12
+ case "DOCS_CLOUD_PROJECT_ID": return normalizeRuntimeEnvValue(process.env.DOCS_CLOUD_PROJECT_ID);
13
+ case "PUBLIC_DOCS_CLOUD_ANALYTICS_KEY": return normalizeRuntimeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_KEY);
14
+ case "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_KEY": return normalizeRuntimeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_KEY);
15
+ case "DOCS_CLOUD_ANALYTICS_KEY": return normalizeRuntimeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_KEY);
16
+ case "PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED": return normalizeRuntimeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED);
17
+ case "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED": return normalizeRuntimeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED);
18
+ case "DOCS_CLOUD_ANALYTICS_ENABLED": return normalizeRuntimeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_ENABLED);
19
+ case "PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT": return normalizeRuntimeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT);
20
+ case "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT": return normalizeRuntimeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT);
21
+ case "DOCS_CLOUD_ANALYTICS_ENDPOINT": return normalizeRuntimeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_ENDPOINT);
22
+ default: return;
23
+ }
24
+ }
25
+ function isFalsyEnv(value) {
26
+ if (!value) return false;
27
+ return /^(0|false|no|off)$/i.test(value);
28
+ }
29
+ function resolveDocsCloudAnalyticsOptions(analytics) {
30
+ if (analytics === false || analytics && typeof analytics === "object" && (analytics.enabled === false || analytics.cloud === false)) return null;
31
+ const projectId = readRuntimeEnv("PUBLIC_DOCS_CLOUD_PROJECT_ID") ?? readRuntimeEnv("NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID") ?? readRuntimeEnv("DOCS_CLOUD_PROJECT_ID");
32
+ const apiKey = readRuntimeEnv("PUBLIC_DOCS_CLOUD_ANALYTICS_KEY") ?? readRuntimeEnv("NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_KEY") ?? readRuntimeEnv("DOCS_CLOUD_ANALYTICS_KEY");
33
+ const enabled = readRuntimeEnv("PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED") ?? readRuntimeEnv("NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED") ?? readRuntimeEnv("DOCS_CLOUD_ANALYTICS_ENABLED");
34
+ const endpoint = readRuntimeEnv("PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT") ?? readRuntimeEnv("NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT") ?? readRuntimeEnv("DOCS_CLOUD_ANALYTICS_ENDPOINT") ?? DEFAULT_DOCS_CLOUD_ANALYTICS_ENDPOINT;
35
+ if (isFalsyEnv(enabled)) return null;
36
+ if (!projectId) return null;
37
+ return {
38
+ endpoint,
39
+ projectId,
40
+ apiKey
41
+ };
42
+ }
43
+ function asRecord(value) {
44
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
45
+ }
46
+ function asString(value) {
47
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
48
+ }
49
+ function normalizeAnalyticsLabel(value) {
50
+ return value?.trim().toLowerCase().replace(/[-\s]+/g, "_") ?? "";
51
+ }
52
+ function isProtocolAgentEvent(event) {
53
+ const type = normalizeAnalyticsLabel(event.type);
54
+ return normalizeAnalyticsLabel(event.source) === "mcp" || type.startsWith("mcp_");
55
+ }
56
+ function inferAgentProvider(event) {
57
+ const type = normalizeAnalyticsLabel(event.type);
58
+ if (normalizeAnalyticsLabel(event.source) === "mcp" || type.startsWith("mcp_")) return "MCP client";
59
+ if (type.startsWith("agent_") || type === "agents_request") return "Docs agent";
60
+ if ([
61
+ "llms_request",
62
+ "markdown_request",
63
+ "skill_request"
64
+ ].includes(type)) return "Docs reader";
65
+ }
66
+ function detectAgentProviderFromUserAgent(userAgent) {
67
+ const value = userAgent?.toLowerCase() ?? "";
68
+ if (!value) return;
69
+ for (const [pattern, provider] of [
70
+ [/cursor/i, "Cursor"],
71
+ [/codex/i, "Codex"],
72
+ [/chatgpt-user|chatgpt/i, "ChatGPT"],
73
+ [/gptbot/i, "GPTBot"],
74
+ [/oai-searchbot|openai-search/i, "ChatGPT Search"],
75
+ [/openai/i, "ChatGPT"],
76
+ [/github-copilot|githubcopilot|copilot/i, "GitHub Copilot"],
77
+ [/claudebot|claude-user|anthropic/i, "Claude"],
78
+ [/perplexitybot|perplexity-user/i, "Perplexity"],
79
+ [/google-extended|googlebot|apis-google/i, "Google"],
80
+ [/bingbot|msnbot/i, "Bing"],
81
+ [/duckduckbot/i, "DuckDuckGo"],
82
+ [/applebot/i, "Apple"],
83
+ [/bytespider|bytedance/i, "ByteDance"],
84
+ [/ccbot|common crawl/i, "Common Crawl"],
85
+ [/ahrefsbot/i, "Ahrefs"],
86
+ [/semrushbot/i, "Semrush"]
87
+ ]) if (pattern.test(value)) return provider;
88
+ if (/bot|crawler|spider|slurp|facebookexternalhit|ia_archiver/.test(value)) return "Other bot";
89
+ }
90
+ function withDocsCloudAnalyticsHints(event) {
91
+ const properties = asRecord(event.properties);
92
+ const detectedAgent = detectAgentProviderFromUserAgent(asString(properties.userAgent) ?? asString(properties.user_agent));
93
+ const protocolAgent = isProtocolAgentEvent(event);
94
+ const incomingTrafficType = asString(properties.trafficType)?.toLowerCase();
95
+ const explicitAgent = incomingTrafficType === "agent" || incomingTrafficType === "bot";
96
+ const agentProvider = asString(properties.agentName) ?? asString(properties.agent) ?? asString(properties.botProvider) ?? asString(properties.provider) ?? asString(properties.crawler) ?? asString(asRecord(properties.bot).provider) ?? detectedAgent ?? (protocolAgent || explicitAgent ? inferAgentProvider(event) : void 0);
97
+ if (!explicitAgent && !protocolAgent && !detectedAgent && !agentProvider) return event;
98
+ return {
99
+ ...event,
100
+ properties: {
101
+ ...properties,
102
+ trafficType: "agent",
103
+ ...agentProvider && !asString(properties.agentName) ? { agentName: agentProvider } : {},
104
+ ...agentProvider && !asString(properties.botProvider) ? { botProvider: agentProvider } : {}
105
+ }
106
+ };
107
+ }
108
+ async function sendDocsCloudAnalyticsEvent(options, event) {
109
+ const fetcher = options.fetch ?? (typeof fetch === "function" ? fetch : void 0);
110
+ if (!fetcher) return;
111
+ const endpoint = options.endpoint?.trim() || DEFAULT_DOCS_CLOUD_ANALYTICS_ENDPOINT;
112
+ const projectId = options.projectId?.trim();
113
+ if (!endpoint || !projectId) return;
114
+ try {
115
+ const normalizedEvent = withDocsCloudAnalyticsHints(event);
116
+ await fetcher(endpoint, {
117
+ method: "POST",
118
+ headers: {
119
+ "content-type": "application/json",
120
+ ...options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {}
121
+ },
122
+ body: JSON.stringify({
123
+ projectId,
124
+ event: normalizedEvent
125
+ }),
126
+ keepalive: true
127
+ });
128
+ } catch {}
129
+ }
130
+
131
+ //#endregion
132
+ export { sendDocsCloudAnalyticsEvent as n, resolveDocsCloudAnalyticsOptions as t };