@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.
- package/dist/{agent-WcXCJ4EN.mjs → agent-DXDOMJBw.mjs} +6 -6
- package/dist/{agents-BjTEkCsY.mjs → agents-DjhjZaNP.mjs} +5 -5
- package/dist/analytics-BZEwTK-8.mjs +177 -0
- package/dist/cli/index.mjs +27 -27
- package/dist/client/react.d.mts +45 -0
- package/dist/client/react.mjs +223 -0
- package/dist/{cloud-wcDf1YC0.mjs → cloud-Bk-eppay.mjs} +158 -14
- package/dist/cloud-analytics-Dlk_byos.mjs +132 -0
- package/dist/cloud-ask-ai-Cl-xaV8j.mjs +382 -0
- package/dist/cloud-ask-ai-DcMe6xOf.d.mts +23 -0
- package/dist/{codeblocks-DTlvGTH4.mjs → codeblocks-z9iT794h.mjs} +3 -3
- package/dist/{dev-DxVevOP_.mjs → dev-CBha7knT.mjs} +2 -2
- package/dist/docs-cloud-server.d.mts +70 -0
- package/dist/docs-cloud-server.mjs +310 -0
- package/dist/{doctor-VwhpJct8.mjs → doctor-O9o9kjN8.mjs} +7 -7
- package/dist/{downgrade-ghVFFHKi.mjs → downgrade-7PGER59g.mjs} +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +4 -3
- package/dist/{init-DkcasCws.mjs → init-Ds7QM9po.mjs} +3 -3
- package/dist/{mcp-DfJiBC0f.mjs → mcp-DuCPNHS-.mjs} +4 -4
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +3 -2
- package/dist/{package-version-CeNAB4NZ.mjs → package-version-DPQSBBsX.mjs} +1 -1
- package/dist/{review-Bnd4NH9y.mjs → review-lm3dt7yy.mjs} +2 -2
- package/dist/{robots-BrBGgn1w.mjs → robots-DV7u4Ire.mjs} +4 -4
- package/dist/{robots-CVoC0w0i.mjs → robots-XVPs9lVz.mjs} +1 -1
- package/dist/{search-CA3H4Dfv.mjs → search-B6nEkx86.mjs} +4 -4
- package/dist/{search-BuOU4idW.mjs → search-BWuU70B0.mjs} +2 -306
- package/dist/{search-BEe1Gdu7.d.mts → search-DFEhf9-s.d.mts} +1 -1
- package/dist/server.d.mts +5 -23
- package/dist/server.mjs +8 -5
- package/dist/{sitemap-CUtSSoGG.mjs → sitemap-BEiKy4Iy.mjs} +5 -5
- package/dist/{sitemap-server-PLcNxmtk.mjs → sitemap-server-xEHtDUDB.mjs} +2 -382
- package/dist/{types-DKCNILor.d.mts → types-XqGLsmOD.d.mts} +17 -0
- package/dist/{upgrade-aexO-zom.mjs → upgrade-CkdKWRlk.mjs} +2 -2
- package/package.json +21 -1
- /package/dist/{agent-B6szuz_8.mjs → agent-CaOBIVFy.mjs} +0 -0
- /package/dist/{config-De5z-2LK.mjs → config-tiQCZ46q.mjs} +0 -0
- /package/dist/{errors-Dw5gMhEZ.mjs → errors-CW1LnxaQ.mjs} +0 -0
- /package/dist/{review-CJD85JTm.mjs → review-B7goPYUb.mjs} +0 -0
- /package/dist/{templates-BOoV0cIR.mjs → templates-CEcOVbV0.mjs} +0 -0
- /package/dist/{utils-CoHZJ1i4.mjs → utils-D-xTRNuh.mjs} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as markCliErrorReported } from "./errors-
|
|
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-
|
|
3
|
-
import { t as detectFramework } from "./utils-
|
|
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
|
|
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 };
|