@farming-labs/docs 0.1.125 → 0.1.128
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-CBhuGaVu.mjs → agent-CJNYhzu7.mjs} +4 -4
- package/dist/{agents-BPX4YRPs.mjs → agents-BuL01_U0.mjs} +2 -2
- package/dist/cli/index.mjs +52 -19
- package/dist/cloud-88xTLXn3.mjs +529 -0
- package/dist/{codeblocks-B4QzFqNC.mjs → codeblocks-DbJrclYa.mjs} +1 -1
- package/dist/{config-B8wA38OC.mjs → config-CBoixmrv.mjs} +9 -1
- package/dist/{dev-1VFeAWUi.mjs → dev-DdlhqXNU.mjs} +7 -1
- package/dist/{doctor-lV9lhqGA.mjs → doctor-D_c7vJCb.mjs} +4 -4
- package/dist/{downgrade-BGANzvQb.mjs → downgrade-BayU3nlg.mjs} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{init-DzQWiVN1.mjs → init-BJqy-LK2.mjs} +1 -1
- package/dist/{mcp-BvERlrP_.mjs → mcp-CUAR3XfW.mjs} +2 -2
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +36 -0
- package/dist/{reading-time-HpTq-0VB.mjs → reading-time-DPAazAGu.mjs} +1 -0
- package/dist/{review-ClNDYRnn.mjs → review-CjyI-bVg.mjs} +1 -1
- package/dist/{robots-CNLiZCay.mjs → robots-Dx2uMF3g.mjs} +1 -1
- package/dist/{search-G0otDMfP.d.mts → search-CNoiftmI.d.mts} +1 -1
- package/dist/{search-BjyDm30M.mjs → search-Cekr2OyP.mjs} +2 -2
- package/dist/server.d.mts +13 -3
- package/dist/server.mjs +2 -2
- package/dist/{sitemap-BdSdcAmS.mjs → sitemap-BBfGr3tJ.mjs} +2 -2
- package/dist/{sitemap-server-BZHoJHqC.mjs → sitemap-server-idLUrmmU.mjs} +57 -1
- package/dist/{types-Dg82p0Fm.d.mts → types-C-C-9SMJ.d.mts} +65 -1
- package/dist/{upgrade-C7DvvfTR.mjs → upgrade-DDWolmHe.mjs} +1 -1
- package/package.json +1 -1
- /package/dist/{package-version-OrjpmqoR.mjs → package-version-CQm0KO-H.mjs} +0 -0
- /package/dist/{templates-CGaORZOY.mjs → templates-DomB3eeS.mjs} +0 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
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-CBoixmrv.mjs";
|
|
2
|
+
import { t as detectFramework } from "./utils-x5EtYWjC.mjs";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
//#region src/cli/cloud.ts
|
|
9
|
+
const DOCS_JSON_FILE = "docs.json";
|
|
10
|
+
const DOCS_CLOUD_SCHEMA_URL = "https://docs.farming-labs.dev/schema/docs.json";
|
|
11
|
+
const DOCS_CLOUD_DEFAULT_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
|
|
12
|
+
const DEFAULT_DOCS_CLOUD_API_BASE_URL = "https://docs-app.farming-labs.dev";
|
|
13
|
+
const DEFAULT_PREVIEW_TIMEOUT_MS = 300 * 1e3;
|
|
14
|
+
const DEFAULT_PREVIEW_POLL_INTERVAL_MS = 2e3;
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
17
|
+
}
|
|
18
|
+
function isJsonRecord(value) {
|
|
19
|
+
return isRecord(value);
|
|
20
|
+
}
|
|
21
|
+
function toJsonRecord(value) {
|
|
22
|
+
if (!isRecord(value)) return void 0;
|
|
23
|
+
return JSON.parse(JSON.stringify(value));
|
|
24
|
+
}
|
|
25
|
+
function readPackageName(rootDir) {
|
|
26
|
+
const packagePath = path.join(rootDir, "package.json");
|
|
27
|
+
if (!fs.existsSync(packagePath)) return void 0;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
30
|
+
return typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : void 0;
|
|
31
|
+
} catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function runGit(rootDir, args) {
|
|
36
|
+
try {
|
|
37
|
+
return execFileSync("git", [
|
|
38
|
+
"-C",
|
|
39
|
+
rootDir,
|
|
40
|
+
...args
|
|
41
|
+
], {
|
|
42
|
+
encoding: "utf-8",
|
|
43
|
+
stdio: [
|
|
44
|
+
"ignore",
|
|
45
|
+
"pipe",
|
|
46
|
+
"ignore"
|
|
47
|
+
]
|
|
48
|
+
}).trim();
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function parseGitHubRemote(remoteUrl) {
|
|
54
|
+
const trimmed = remoteUrl.trim();
|
|
55
|
+
const match = trimmed.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/) ?? trimmed.match(/^https:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?(?:\/)?$/);
|
|
56
|
+
if (!match?.[1] || !match[2]) return null;
|
|
57
|
+
return {
|
|
58
|
+
owner: match[1],
|
|
59
|
+
name: match[2].replace(/\.git$/i, "")
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function resolveGitRepositoryMetadata(rootDir) {
|
|
63
|
+
const gitRoot = runGit(rootDir, ["rev-parse", "--show-toplevel"]);
|
|
64
|
+
const remoteUrl = runGit(rootDir, [
|
|
65
|
+
"remote",
|
|
66
|
+
"get-url",
|
|
67
|
+
"origin"
|
|
68
|
+
]);
|
|
69
|
+
if (!gitRoot || !remoteUrl) return;
|
|
70
|
+
const parsedRemote = parseGitHubRemote(remoteUrl);
|
|
71
|
+
if (!parsedRemote) return;
|
|
72
|
+
const branch = runGit(rootDir, ["branch", "--show-current"]) ?? runGit(rootDir, [
|
|
73
|
+
"rev-parse",
|
|
74
|
+
"--abbrev-ref",
|
|
75
|
+
"HEAD"
|
|
76
|
+
]);
|
|
77
|
+
const resolvedGitRoot = fs.realpathSync(gitRoot);
|
|
78
|
+
const resolvedRootDir = fs.realpathSync(rootDir);
|
|
79
|
+
const rootDirectory = path.relative(resolvedGitRoot, resolvedRootDir).split(path.sep).filter(Boolean).join("/");
|
|
80
|
+
return {
|
|
81
|
+
...parsedRemote,
|
|
82
|
+
branch: branch && branch !== "HEAD" ? branch : void 0,
|
|
83
|
+
rootDirectory: rootDirectory || ".",
|
|
84
|
+
remoteUrl
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function titleFromPackageName(name) {
|
|
88
|
+
if (!name) return void 0;
|
|
89
|
+
const normalized = name.replace(/^@[^/]+\//, "").replace(/[-_]+/g, " ").trim();
|
|
90
|
+
if (!normalized) return void 0;
|
|
91
|
+
return normalized.replace(/\b\w/g, (match) => match.toUpperCase());
|
|
92
|
+
}
|
|
93
|
+
function tryResolveDocsConfigPath(rootDir, explicitPath) {
|
|
94
|
+
if (explicitPath) return resolveDocsConfigPath(rootDir, explicitPath);
|
|
95
|
+
try {
|
|
96
|
+
return resolveDocsConfigPath(rootDir);
|
|
97
|
+
} catch {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function loadDocsConfigSnapshot(rootDir, explicitPath) {
|
|
102
|
+
const configPath = tryResolveDocsConfigPath(rootDir, explicitPath);
|
|
103
|
+
if (!configPath) return {};
|
|
104
|
+
return {
|
|
105
|
+
path: configPath,
|
|
106
|
+
content: fs.readFileSync(configPath, "utf-8"),
|
|
107
|
+
config: (await loadDocsConfigModule(rootDir, configPath, { silent: true }))?.config
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function readExistingDocsJson(docsJsonPath) {
|
|
111
|
+
if (!fs.existsSync(docsJsonPath)) return void 0;
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(fs.readFileSync(docsJsonPath, "utf-8"));
|
|
114
|
+
if (!isJsonRecord(parsed)) throw new Error(`${DOCS_JSON_FILE} must contain a JSON object.`);
|
|
115
|
+
return parsed;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
118
|
+
throw new Error(`Could not parse ${DOCS_JSON_FILE}: ${message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function normalizeApiKeyConfig(apiKey) {
|
|
122
|
+
return { env: apiKey?.env?.trim() || DOCS_CLOUD_DEFAULT_API_KEY_ENV };
|
|
123
|
+
}
|
|
124
|
+
function normalizePreviewConfig(preview) {
|
|
125
|
+
return { enabled: preview?.enabled ?? true };
|
|
126
|
+
}
|
|
127
|
+
function normalizePublishConfig(publish) {
|
|
128
|
+
return {
|
|
129
|
+
mode: publish?.mode === "direct-commit" ? "direct-commit" : "draft-pr",
|
|
130
|
+
baseBranch: publish?.baseBranch?.trim() || "main"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function normalizeFeatureConfig(feature) {
|
|
134
|
+
if (!feature) return void 0;
|
|
135
|
+
return { enabled: feature.enabled ?? true };
|
|
136
|
+
}
|
|
137
|
+
function normalizeAnalyticsConfig(analytics) {
|
|
138
|
+
if (typeof analytics === "undefined") return void 0;
|
|
139
|
+
if (typeof analytics === "boolean") return analytics;
|
|
140
|
+
return {
|
|
141
|
+
enabled: analytics.enabled ?? true,
|
|
142
|
+
...typeof analytics.console !== "undefined" ? { console: analytics.console } : {},
|
|
143
|
+
includeInputs: analytics.includeInputs ?? false
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function normalizeCloudConfig(cloud) {
|
|
147
|
+
const normalized = {
|
|
148
|
+
apiKey: normalizeApiKeyConfig(cloud?.apiKey),
|
|
149
|
+
preview: normalizePreviewConfig(cloud?.preview),
|
|
150
|
+
publish: normalizePublishConfig(cloud?.publish)
|
|
151
|
+
};
|
|
152
|
+
if (cloud?.enabled === false) normalized.enabled = false;
|
|
153
|
+
const analytics = normalizeAnalyticsConfig(cloud?.analytics);
|
|
154
|
+
if (typeof analytics !== "undefined") normalized.analytics = analytics;
|
|
155
|
+
const ai = normalizeFeatureConfig(cloud?.ai);
|
|
156
|
+
if (ai) normalized.ai = ai;
|
|
157
|
+
const deploy = normalizeFeatureConfig(cloud?.deploy);
|
|
158
|
+
if (deploy) normalized.deploy = deploy;
|
|
159
|
+
return normalized;
|
|
160
|
+
}
|
|
161
|
+
function readStaticCloudConfig(content) {
|
|
162
|
+
if (!content || !extractNestedObjectLiteral(content, ["cloud"])) return void 0;
|
|
163
|
+
const cloudBlock = extractNestedObjectLiteral(content, ["cloud"]);
|
|
164
|
+
const apiKeyBlock = extractNestedObjectLiteral(content, ["cloud", "apiKey"]);
|
|
165
|
+
const previewBlock = extractNestedObjectLiteral(content, ["cloud", "preview"]);
|
|
166
|
+
const publishBlock = extractNestedObjectLiteral(content, ["cloud", "publish"]);
|
|
167
|
+
const analyticsBlock = extractNestedObjectLiteral(content, ["cloud", "analytics"]);
|
|
168
|
+
const aiBlock = extractNestedObjectLiteral(content, ["cloud", "ai"]);
|
|
169
|
+
const deployBlock = extractNestedObjectLiteral(content, ["cloud", "deploy"]);
|
|
170
|
+
const cloud = {};
|
|
171
|
+
const enabled = cloudBlock ? readTopLevelBooleanProperty(cloudBlock, "enabled") : void 0;
|
|
172
|
+
if (typeof enabled === "boolean") cloud.enabled = enabled;
|
|
173
|
+
const apiKeyEnv = apiKeyBlock ? readStringProperty(apiKeyBlock, "env") : void 0;
|
|
174
|
+
if (apiKeyEnv) cloud.apiKey = { env: apiKeyEnv };
|
|
175
|
+
const previewEnabled = previewBlock ? readTopLevelBooleanProperty(previewBlock, "enabled") : void 0;
|
|
176
|
+
if (typeof previewEnabled === "boolean") cloud.preview = { enabled: previewEnabled };
|
|
177
|
+
const publishMode = publishBlock ? readStringProperty(publishBlock, "mode") : void 0;
|
|
178
|
+
const baseBranch = publishBlock ? readStringProperty(publishBlock, "baseBranch") : void 0;
|
|
179
|
+
if (publishMode || baseBranch) cloud.publish = {
|
|
180
|
+
...publishMode === "direct-commit" || publishMode === "draft-pr" ? { mode: publishMode } : {},
|
|
181
|
+
...baseBranch ? { baseBranch } : {}
|
|
182
|
+
};
|
|
183
|
+
const analyticsEnabled = cloudBlock ? readTopLevelBooleanProperty(cloudBlock, "analytics") : void 0;
|
|
184
|
+
if (typeof analyticsEnabled === "boolean") cloud.analytics = analyticsEnabled;
|
|
185
|
+
else if (analyticsBlock) {
|
|
186
|
+
const analytics = {};
|
|
187
|
+
const enabledValue = readTopLevelBooleanProperty(analyticsBlock, "enabled");
|
|
188
|
+
const consoleBoolean = readTopLevelBooleanProperty(analyticsBlock, "console");
|
|
189
|
+
const consoleMode = readStringProperty(analyticsBlock, "console");
|
|
190
|
+
const includeInputs = readTopLevelBooleanProperty(analyticsBlock, "includeInputs");
|
|
191
|
+
if (typeof enabledValue === "boolean") analytics.enabled = enabledValue;
|
|
192
|
+
if (typeof consoleBoolean === "boolean") analytics.console = consoleBoolean;
|
|
193
|
+
else if (consoleMode === "log" || consoleMode === "info" || consoleMode === "debug") analytics.console = consoleMode;
|
|
194
|
+
if (typeof includeInputs === "boolean") analytics.includeInputs = includeInputs;
|
|
195
|
+
cloud.analytics = analytics;
|
|
196
|
+
}
|
|
197
|
+
const aiEnabled = aiBlock ? readTopLevelBooleanProperty(aiBlock, "enabled") : void 0;
|
|
198
|
+
if (typeof aiEnabled === "boolean") cloud.ai = { enabled: aiEnabled };
|
|
199
|
+
const deployEnabled = deployBlock ? readTopLevelBooleanProperty(deployBlock, "enabled") : void 0;
|
|
200
|
+
if (typeof deployEnabled === "boolean") cloud.deploy = { enabled: deployEnabled };
|
|
201
|
+
return cloud;
|
|
202
|
+
}
|
|
203
|
+
function resolveCloudConfig(snapshot, existing) {
|
|
204
|
+
const moduleCloud = snapshot.config?.cloud;
|
|
205
|
+
const staticCloud = readStaticCloudConfig(snapshot.content);
|
|
206
|
+
const existingCloud = existing?.cloud;
|
|
207
|
+
return normalizeCloudConfig(moduleCloud ?? staticCloud ?? existingCloud);
|
|
208
|
+
}
|
|
209
|
+
function resolveApiReferenceRoot(snapshot) {
|
|
210
|
+
const apiReference = snapshot.config?.apiReference;
|
|
211
|
+
if (isRecord(apiReference) && typeof apiReference.path === "string" && apiReference.path.trim()) return apiReference.path.trim();
|
|
212
|
+
const apiReferenceBlock = extractNestedObjectLiteral(snapshot.content ?? "", ["apiReference"]);
|
|
213
|
+
if (!apiReferenceBlock) return void 0;
|
|
214
|
+
return readStringProperty(apiReferenceBlock, "path");
|
|
215
|
+
}
|
|
216
|
+
function resolveSiteConfig(rootDir, snapshot, existing) {
|
|
217
|
+
const existingSite = toJsonRecord(existing?.site);
|
|
218
|
+
const navTitle = isRecord(snapshot.config?.nav) && typeof snapshot.config.nav.title === "string" ? snapshot.config.nav.title : readNavTitle(snapshot.content ?? "");
|
|
219
|
+
const metadata = isRecord(snapshot.config?.metadata) ? snapshot.config.metadata : void 0;
|
|
220
|
+
const metadataDescription = typeof metadata?.description === "string" && metadata.description.trim() ? metadata.description.trim() : void 0;
|
|
221
|
+
const packageTitle = titleFromPackageName(readPackageName(rootDir));
|
|
222
|
+
const name = navTitle?.trim() || existingSite?.name || packageTitle;
|
|
223
|
+
const description = metadataDescription || existingSite?.description;
|
|
224
|
+
if (!name && !description && !existingSite) return void 0;
|
|
225
|
+
return {
|
|
226
|
+
...existingSite ?? {},
|
|
227
|
+
...typeof name === "string" ? { name } : {},
|
|
228
|
+
...typeof description === "string" ? { description } : {}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function resolveDocsRoot(rootDir, snapshot, existing) {
|
|
232
|
+
const entry = snapshot.config?.entry ?? readTopLevelStringProperty(snapshot.content ?? "", "entry") ?? "docs";
|
|
233
|
+
if (snapshot.config?.contentDir) return snapshot.config.contentDir;
|
|
234
|
+
if (snapshot.content) return resolveDocsContentDir(rootDir, snapshot.content, entry);
|
|
235
|
+
if (typeof existing?.content?.docsRoot === "string") return existing.content.docsRoot;
|
|
236
|
+
return fs.existsSync(path.join(rootDir, "app", entry)) ? path.join("app", entry) : entry;
|
|
237
|
+
}
|
|
238
|
+
function resolveDocsBlock(rootDir, snapshot, existing) {
|
|
239
|
+
const detectedFramework = detectFramework(rootDir);
|
|
240
|
+
const existingDocs = existing?.docs;
|
|
241
|
+
const runtime = detectedFramework ?? existingDocs?.runtime ?? "nextjs";
|
|
242
|
+
if (!Boolean(snapshot.path || detectedFramework) && existingDocs) return existingDocs;
|
|
243
|
+
return {
|
|
244
|
+
mode: "framework",
|
|
245
|
+
runtime,
|
|
246
|
+
root: existingDocs?.root || "."
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function materializeDocsJsonObject(params) {
|
|
250
|
+
const cloud = resolveCloudConfig(params.snapshot, params.existing);
|
|
251
|
+
const docsRoot = resolveDocsRoot(params.rootDir, params.snapshot, params.existing);
|
|
252
|
+
const apiReferenceRoot = resolveApiReferenceRoot(params.snapshot);
|
|
253
|
+
const existingContent = toJsonRecord(params.existing?.content);
|
|
254
|
+
const site = resolveSiteConfig(params.rootDir, params.snapshot, params.existing);
|
|
255
|
+
const content = {
|
|
256
|
+
...existingContent ?? {},
|
|
257
|
+
docsRoot,
|
|
258
|
+
...apiReferenceRoot ? { apiReferenceRoot } : {}
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
...params.existing ?? {},
|
|
262
|
+
$schema: params.existing?.$schema ?? DOCS_CLOUD_SCHEMA_URL,
|
|
263
|
+
version: 1,
|
|
264
|
+
docs: resolveDocsBlock(params.rootDir, params.snapshot, params.existing),
|
|
265
|
+
content,
|
|
266
|
+
...site ? { site } : {},
|
|
267
|
+
cloud
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function serializeMaterializedDocsJson(config) {
|
|
271
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
272
|
+
}
|
|
273
|
+
async function materializeCloudConfig(options = {}) {
|
|
274
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
275
|
+
const docsJsonPath = path.join(rootDir, DOCS_JSON_FILE);
|
|
276
|
+
const existing = readExistingDocsJson(docsJsonPath);
|
|
277
|
+
const snapshot = await loadDocsConfigSnapshot(rootDir, options.configPath);
|
|
278
|
+
const config = materializeDocsJsonObject({
|
|
279
|
+
rootDir,
|
|
280
|
+
snapshot,
|
|
281
|
+
existing
|
|
282
|
+
});
|
|
283
|
+
const serialized = serializeMaterializedDocsJson(config);
|
|
284
|
+
const updated = (existing ? fs.readFileSync(docsJsonPath, "utf-8") : void 0) !== serialized;
|
|
285
|
+
if (updated) fs.writeFileSync(docsJsonPath, serialized, "utf-8");
|
|
286
|
+
return {
|
|
287
|
+
configPath: snapshot.path ?? docsJsonPath,
|
|
288
|
+
docsJsonPath,
|
|
289
|
+
config,
|
|
290
|
+
apiKeyEnv: config.cloud?.apiKey?.env ?? DOCS_CLOUD_DEFAULT_API_KEY_ENV,
|
|
291
|
+
created: !existing,
|
|
292
|
+
updated
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function resolveApiBaseUrl(options) {
|
|
296
|
+
return (options.apiBaseUrl ?? process.env.DOCS_CLOUD_API_URL ?? process.env.NEXT_PUBLIC_DOCS_CLOUD_URL ?? DEFAULT_DOCS_CLOUD_API_BASE_URL).replace(/\/+$/, "");
|
|
297
|
+
}
|
|
298
|
+
function resolveApiKey(options, rootDir, envName) {
|
|
299
|
+
if (options.apiKey?.trim()) return options.apiKey.trim();
|
|
300
|
+
const token = {
|
|
301
|
+
...loadProjectEnv(rootDir),
|
|
302
|
+
...process.env
|
|
303
|
+
}[envName]?.trim();
|
|
304
|
+
if (token) return token;
|
|
305
|
+
throw new Error(`Missing Docs Cloud API key. Set ${envName} in your shell or .env.local, or configure cloud.apiKey.env in docs.config.ts.`);
|
|
306
|
+
}
|
|
307
|
+
async function readJsonResponse(response) {
|
|
308
|
+
const text = await response.text();
|
|
309
|
+
if (!text.trim()) return {};
|
|
310
|
+
try {
|
|
311
|
+
return JSON.parse(text);
|
|
312
|
+
} catch {
|
|
313
|
+
return { message: text };
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function readResponseMessage(body, fallback) {
|
|
317
|
+
if (isRecord(body)) for (const key of [
|
|
318
|
+
"error",
|
|
319
|
+
"message",
|
|
320
|
+
"detail"
|
|
321
|
+
]) {
|
|
322
|
+
const value = body[key];
|
|
323
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
324
|
+
}
|
|
325
|
+
return fallback;
|
|
326
|
+
}
|
|
327
|
+
async function fetchCloudJson(params) {
|
|
328
|
+
const headers = new Headers(params.init?.headers);
|
|
329
|
+
headers.set("authorization", `Bearer ${params.apiKey}`);
|
|
330
|
+
headers.set("accept", "application/json");
|
|
331
|
+
if (params.init?.body && !headers.has("content-type")) headers.set("content-type", "application/json");
|
|
332
|
+
const response = await fetch(params.url, {
|
|
333
|
+
...params.init,
|
|
334
|
+
headers
|
|
335
|
+
});
|
|
336
|
+
const body = await readJsonResponse(response);
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
const requestPath = new URL(params.url).pathname;
|
|
339
|
+
if (response.status === 404 && requestPath === "/api/cloud/preview") throw new Error("Docs Cloud preview API is not available on this cloud host yet. The API key was validated, but the host did not expose /api/cloud/preview.");
|
|
340
|
+
const message = readResponseMessage(body, `Docs Cloud request failed with HTTP ${response.status}.`);
|
|
341
|
+
throw new Error(message);
|
|
342
|
+
}
|
|
343
|
+
return body;
|
|
344
|
+
}
|
|
345
|
+
function readPreviewUrl(body) {
|
|
346
|
+
if (!isRecord(body)) return void 0;
|
|
347
|
+
const direct = body.url ?? body.previewUrl;
|
|
348
|
+
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
349
|
+
const preview = body.preview;
|
|
350
|
+
if (isRecord(preview) && typeof preview.url === "string" && preview.url.trim()) return preview.url.trim();
|
|
351
|
+
}
|
|
352
|
+
function readStatusUrl(body, apiBaseUrl) {
|
|
353
|
+
if (!isRecord(body)) return void 0;
|
|
354
|
+
const statusUrl = body.statusUrl ?? body.pollUrl;
|
|
355
|
+
if (typeof statusUrl !== "string" || !statusUrl.trim()) return void 0;
|
|
356
|
+
return new URL(statusUrl, apiBaseUrl).toString();
|
|
357
|
+
}
|
|
358
|
+
function readPreviewStatus(body) {
|
|
359
|
+
if (!isRecord(body)) return void 0;
|
|
360
|
+
const status = body.status;
|
|
361
|
+
return typeof status === "string" ? status : void 0;
|
|
362
|
+
}
|
|
363
|
+
async function requestPreview(params) {
|
|
364
|
+
const initial = await fetchCloudJson({
|
|
365
|
+
url: `${params.apiBaseUrl}/api/cloud/preview`,
|
|
366
|
+
apiKey: params.apiKey,
|
|
367
|
+
init: {
|
|
368
|
+
method: "POST",
|
|
369
|
+
body: JSON.stringify({
|
|
370
|
+
config: params.config,
|
|
371
|
+
configPath: path.relative(params.rootDir, params.configPath) || DOCS_JSON_FILE,
|
|
372
|
+
repository: resolveGitRepositoryMetadata(params.rootDir)
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
const initialUrl = readPreviewUrl(initial);
|
|
377
|
+
if (initialUrl) return {
|
|
378
|
+
url: initialUrl,
|
|
379
|
+
response: initial
|
|
380
|
+
};
|
|
381
|
+
const statusUrl = readStatusUrl(initial, params.apiBaseUrl);
|
|
382
|
+
if (!statusUrl) throw new Error("Docs Cloud accepted the preview request but did not return a preview URL or status URL.");
|
|
383
|
+
const startedAt = Date.now();
|
|
384
|
+
while (Date.now() - startedAt < params.timeoutMs) {
|
|
385
|
+
await new Promise((resolve) => setTimeout(resolve, params.pollIntervalMs));
|
|
386
|
+
params.spinner.update("Waiting for Docs Cloud preview deployment");
|
|
387
|
+
const statusBody = await fetchCloudJson({
|
|
388
|
+
url: statusUrl,
|
|
389
|
+
apiKey: params.apiKey
|
|
390
|
+
});
|
|
391
|
+
const url = readPreviewUrl(statusBody);
|
|
392
|
+
if (url) return {
|
|
393
|
+
url,
|
|
394
|
+
response: statusBody
|
|
395
|
+
};
|
|
396
|
+
const status = readPreviewStatus(statusBody);
|
|
397
|
+
if (status === "failed" || status === "error") throw new Error(readResponseMessage(statusBody, "Docs Cloud preview failed."));
|
|
398
|
+
}
|
|
399
|
+
throw new Error("Docs Cloud preview timed out before a URL was ready.");
|
|
400
|
+
}
|
|
401
|
+
function createSpinner(initialMessage, options = {}) {
|
|
402
|
+
const interactive = Boolean(process.stdout.isTTY && !options.json && process.env.NODE_ENV !== "test");
|
|
403
|
+
const frames = [
|
|
404
|
+
"-",
|
|
405
|
+
"\\",
|
|
406
|
+
"|",
|
|
407
|
+
"/"
|
|
408
|
+
];
|
|
409
|
+
let frame = 0;
|
|
410
|
+
let message = initialMessage;
|
|
411
|
+
let timer;
|
|
412
|
+
const render = () => {
|
|
413
|
+
process.stdout.write(`\r${pc.cyan(frames[frame % frames.length])} ${message}`);
|
|
414
|
+
frame += 1;
|
|
415
|
+
};
|
|
416
|
+
if (interactive) {
|
|
417
|
+
render();
|
|
418
|
+
timer = setInterval(render, 90);
|
|
419
|
+
} else if (!options.json) console.log(`${pc.dim("-")} ${initialMessage}`);
|
|
420
|
+
const clear = () => {
|
|
421
|
+
if (timer) {
|
|
422
|
+
clearInterval(timer);
|
|
423
|
+
timer = void 0;
|
|
424
|
+
}
|
|
425
|
+
if (interactive) process.stdout.write("\r\x1B[K");
|
|
426
|
+
};
|
|
427
|
+
return {
|
|
428
|
+
update(nextMessage) {
|
|
429
|
+
message = nextMessage;
|
|
430
|
+
if (!interactive && !options.json) console.log(`${pc.dim("-")} ${nextMessage}`);
|
|
431
|
+
},
|
|
432
|
+
succeed(doneMessage) {
|
|
433
|
+
clear();
|
|
434
|
+
if (!options.json) console.log(`${pc.green("ok")} ${doneMessage}`);
|
|
435
|
+
},
|
|
436
|
+
fail(doneMessage) {
|
|
437
|
+
clear();
|
|
438
|
+
if (!options.json) console.log(`${pc.red("error")} ${doneMessage}`);
|
|
439
|
+
},
|
|
440
|
+
stop() {
|
|
441
|
+
clear();
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
async function syncCloudConfig(options = {}) {
|
|
446
|
+
const result = await materializeCloudConfig(options);
|
|
447
|
+
if (options.json) {
|
|
448
|
+
console.log(JSON.stringify(result, null, 2));
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
const action = result.created ? "Created" : result.updated ? "Synced" : "Checked";
|
|
452
|
+
console.log(`${pc.green("ok")} ${action} ${pc.cyan(path.relative(process.cwd(), result.docsJsonPath) || DOCS_JSON_FILE)}`);
|
|
453
|
+
console.log(`${pc.dim("api key env")} ${result.apiKeyEnv}`);
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
async function runCloudPreview(options = {}) {
|
|
457
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
458
|
+
const spinner = createSpinner("Preparing Docs Cloud preview", options);
|
|
459
|
+
try {
|
|
460
|
+
const materialized = await materializeCloudConfig({
|
|
461
|
+
...options,
|
|
462
|
+
rootDir
|
|
463
|
+
});
|
|
464
|
+
spinner.update(`${materialized.created ? "Created" : "Synced"} ${DOCS_JSON_FILE}`);
|
|
465
|
+
if (materialized.config.cloud?.enabled === false) throw new Error("Docs Cloud is disabled in cloud.enabled. Remove that override or set cloud.enabled: true before requesting a preview.");
|
|
466
|
+
if (materialized.config.cloud?.preview?.enabled === false) throw new Error("Docs Cloud preview is disabled in cloud.preview.enabled. Set it to true before requesting a preview.");
|
|
467
|
+
const apiKey = resolveApiKey(options, rootDir, materialized.apiKeyEnv);
|
|
468
|
+
const apiBaseUrl = resolveApiBaseUrl(options);
|
|
469
|
+
spinner.update("Validating Docs Cloud API key");
|
|
470
|
+
const identity = await fetchCloudJson({
|
|
471
|
+
url: `${apiBaseUrl}/api/cloud/me`,
|
|
472
|
+
apiKey
|
|
473
|
+
});
|
|
474
|
+
spinner.update("Requesting Docs Cloud preview deployment");
|
|
475
|
+
const preview = await requestPreview({
|
|
476
|
+
apiBaseUrl,
|
|
477
|
+
apiKey,
|
|
478
|
+
config: materialized.config,
|
|
479
|
+
configPath: materialized.docsJsonPath,
|
|
480
|
+
rootDir,
|
|
481
|
+
spinner,
|
|
482
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_PREVIEW_TIMEOUT_MS,
|
|
483
|
+
pollIntervalMs: options.pollIntervalMs ?? DEFAULT_PREVIEW_POLL_INTERVAL_MS
|
|
484
|
+
});
|
|
485
|
+
spinner.succeed("Docs Cloud preview is ready");
|
|
486
|
+
const result = {
|
|
487
|
+
url: preview.url,
|
|
488
|
+
docsJsonPath: materialized.docsJsonPath,
|
|
489
|
+
apiBaseUrl,
|
|
490
|
+
identity,
|
|
491
|
+
response: preview.response
|
|
492
|
+
};
|
|
493
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
494
|
+
else console.log(`${pc.bold("Preview")} ${pc.cyan(preview.url)}`);
|
|
495
|
+
return result;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
498
|
+
spinner.fail(message);
|
|
499
|
+
throw error;
|
|
500
|
+
} finally {
|
|
501
|
+
spinner.stop();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function printCloudHelp() {
|
|
505
|
+
console.log(`
|
|
506
|
+
${pc.bold("@farming-labs/docs cloud")}
|
|
507
|
+
|
|
508
|
+
${pc.dim("Usage:")}
|
|
509
|
+
${pc.cyan("docs preview")} Sync ${pc.dim("docs.config.ts")} to ${pc.dim("docs.json")} and request a cloud preview
|
|
510
|
+
${pc.cyan("docs cloud preview")} Same as ${pc.cyan("docs preview")}
|
|
511
|
+
${pc.cyan("docs cloud sync")} Only materialize cloud settings into ${pc.dim("docs.json")}
|
|
512
|
+
|
|
513
|
+
${pc.dim("Options:")}
|
|
514
|
+
${pc.cyan("--config <path>")} Use a custom docs config path
|
|
515
|
+
${pc.cyan("--api-base-url <url>")} Override Docs Cloud API base URL
|
|
516
|
+
${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
|
|
517
|
+
${pc.cyan("--json")} Print machine-readable output
|
|
518
|
+
|
|
519
|
+
${pc.dim("Config example:")}
|
|
520
|
+
cloud: {
|
|
521
|
+
apiKey: { env: "DOCS_CLOUD_API_KEY" },
|
|
522
|
+
preview: { enabled: true },
|
|
523
|
+
publish: { mode: "draft-pr", baseBranch: "main" },
|
|
524
|
+
}
|
|
525
|
+
`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
//#endregion
|
|
529
|
+
export { printCloudHelp, runCloudPreview, syncCloudConfig };
|
|
@@ -3,7 +3,7 @@ import { E as findDocsMarkdownPage, U as renderDocsMarkdownDocument } from "./ag
|
|
|
3
3
|
import "./index.mjs";
|
|
4
4
|
import { createFilesystemDocsMcpSource } from "./mcp.mjs";
|
|
5
5
|
import "./server.mjs";
|
|
6
|
-
import { a as loadProjectEnv, c as readNavTitle,
|
|
6
|
+
import { a as loadProjectEnv, c as readNavTitle, f as readTopLevelStringProperty, i as loadDocsConfigModule, l as readNumberProperty, m as resolveDocsContentDir, o as readBooleanProperty, p as resolveDocsConfigPath, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-CBoixmrv.mjs";
|
|
7
7
|
import matter from "gray-matter";
|
|
8
8
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
@@ -230,6 +230,14 @@ function readTopLevelStringProperty(content, key) {
|
|
|
230
230
|
if (match) return match[1];
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
|
+
function readTopLevelBooleanProperty(content, key) {
|
|
234
|
+
const source = extractTopLevelConfigObject(content) ?? content;
|
|
235
|
+
const propertyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*:\\s*(true|false)\\b`);
|
|
236
|
+
for (const property of splitTopLevelProperties(source)) {
|
|
237
|
+
const match = property.trim().match(propertyPattern);
|
|
238
|
+
if (match) return match[1] === "true";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
233
241
|
function readNavTitle(content) {
|
|
234
242
|
const block = extractObjectLiteral(extractTopLevelConfigObject(content) ?? content, "nav");
|
|
235
243
|
if (!block) return void 0;
|
|
@@ -295,4 +303,4 @@ async function loadDocsConfigModule(rootDir, explicitPath, options = {}) {
|
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
//#endregion
|
|
298
|
-
export { loadProjectEnv as a, readNavTitle as c,
|
|
306
|
+
export { loadProjectEnv as a, readNavTitle as c, readTopLevelBooleanProperty as d, readTopLevelStringProperty as f, loadDocsConfigModule as i, readNumberProperty as l, resolveDocsContentDir as m, extractObjectLiteral as n, readBooleanProperty as o, resolveDocsConfigPath as p, extractTopLevelConfigObject as r, readEnvReferenceProperty as s, extractNestedObjectLiteral as t, readStringProperty as u };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as detectPackageManagerFromLockfile } from "./utils-x5EtYWjC.mjs";
|
|
2
|
-
import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-
|
|
2
|
+
import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-DomB3eeS.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import pc from "picocolors";
|
|
@@ -203,6 +203,12 @@ const managedConfigSchema = z.object({
|
|
|
203
203
|
theme: z.object({ preset: z.string().optional() }).passthrough().optional(),
|
|
204
204
|
cloud: z.object({
|
|
205
205
|
enabled: z.boolean().optional(),
|
|
206
|
+
apiKey: z.object({ env: z.string().min(1).optional() }).passthrough().optional(),
|
|
207
|
+
preview: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
208
|
+
publish: z.object({
|
|
209
|
+
mode: z.enum(["draft-pr", "direct-commit"]).optional(),
|
|
210
|
+
baseBranch: z.string().min(1).optional()
|
|
211
|
+
}).passthrough().optional(),
|
|
206
212
|
analytics: z.union([z.boolean(), z.object({
|
|
207
213
|
enabled: z.boolean().optional(),
|
|
208
214
|
console: z.union([z.boolean(), z.enum([
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import "./reading-time-
|
|
1
|
+
import "./reading-time-DPAazAGu.mjs";
|
|
2
2
|
import "./search-BL7o2rXk.mjs";
|
|
3
3
|
import { a as DEFAULT_SITEMAP_XML_ROUTE, d as resolveDocsSitemapConfig, i as DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, r as DEFAULT_SITEMAP_MD_ROUTE } from "./sitemap-CMNj0Pt3.mjs";
|
|
4
4
|
import { T as buildDocsMcpEndpointCandidates, b as DEFAULT_SKILL_MD_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, d as DEFAULT_LLMS_FULL_TXT_ROUTE, g as DEFAULT_MCP_PUBLIC_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, m as DEFAULT_LLMS_TXT_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_MCP_WELL_KNOWN_ROUTE, x as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE } from "./agent-BS39vnhS.mjs";
|
|
5
5
|
import { a as analyzeDocsRobotsTxt, n as DEFAULT_ROBOTS_TXT_ROUTE, u as resolveDocsRobotsConfig } from "./robots-B2BfoyVM.mjs";
|
|
6
|
-
import "./sitemap-server-
|
|
6
|
+
import "./sitemap-server-idLUrmmU.mjs";
|
|
7
7
|
import { createFilesystemDocsMcpSource, resolveDocsMcpConfig } from "./mcp.mjs";
|
|
8
8
|
import "./server.mjs";
|
|
9
|
-
import { a as loadProjectEnv, c as readNavTitle,
|
|
9
|
+
import { a as loadProjectEnv, c as readNavTitle, f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, o as readBooleanProperty, p as resolveDocsConfigPath, r as extractTopLevelConfigObject, t as extractNestedObjectLiteral } from "./config-CBoixmrv.mjs";
|
|
10
10
|
import { t as detectFramework } from "./utils-x5EtYWjC.mjs";
|
|
11
|
-
import { n as inspectAgentCompactionState, o as scanDocsPageTargets } from "./codeblocks-
|
|
11
|
+
import { n as inspectAgentCompactionState, o as scanDocsPageTargets } from "./codeblocks-DbJrclYa.mjs";
|
|
12
12
|
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
import { LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as fileExists, o as exec, s as execOutput } from "./utils-x5EtYWjC.mjs";
|
|
2
|
-
import { a as getPackagesForFramework, c as resolveDocsPackageManager, l as validateUpgradeVersion, r as buildDocsPackageInstallCommand, s as resolveDocsPackageFramework } from "./package-version-
|
|
2
|
+
import { a as getPackagesForFramework, c as resolveDocsPackageManager, l as validateUpgradeVersion, r as buildDocsPackageInstallCommand, s as resolveDocsPackageFramework } from "./package-version-CQm0KO-H.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import pc from "picocolors";
|