@farming-labs/docs 0.1.138 → 0.1.140
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/cli/index.mjs
CHANGED
|
@@ -81,6 +81,7 @@ async function main() {
|
|
|
81
81
|
configPath: typeof flags.config === "string" ? flags.config : void 0,
|
|
82
82
|
apiBaseUrl: typeof flags["api-base-url"] === "string" ? flags["api-base-url"] : typeof flags.url === "string" ? flags.url : void 0,
|
|
83
83
|
apiKey: typeof flags["api-key"] === "string" ? flags["api-key"] : void 0,
|
|
84
|
+
apiKeyEnv: typeof flags["api-key-env"] === "string" ? flags["api-key-env"] : void 0,
|
|
84
85
|
json: typeof flags.json === "boolean" ? flags.json : void 0
|
|
85
86
|
};
|
|
86
87
|
if (!parsedCommand.command || parsedCommand.command === "init") {
|
|
@@ -90,24 +91,27 @@ async function main() {
|
|
|
90
91
|
const { dev } = await import("../dev-DdlhqXNU.mjs");
|
|
91
92
|
await dev(devOptions);
|
|
92
93
|
} else if (parsedCommand.command === "deploy") {
|
|
93
|
-
const { runCloudDeploy } = await import("../cloud-
|
|
94
|
+
const { runCloudDeploy } = await import("../cloud-DcsDg7Ze.mjs");
|
|
94
95
|
await runCloudDeploy(cloudOptions);
|
|
95
96
|
} else if (parsedCommand.command === "preview") {
|
|
96
|
-
const { runCloudPreview } = await import("../cloud-
|
|
97
|
+
const { runCloudPreview } = await import("../cloud-DcsDg7Ze.mjs");
|
|
97
98
|
await runCloudPreview(cloudOptions);
|
|
98
99
|
} else if (parsedCommand.command === "cloud" && subcommand === "deploy") {
|
|
99
|
-
const { runCloudDeploy } = await import("../cloud-
|
|
100
|
+
const { runCloudDeploy } = await import("../cloud-DcsDg7Ze.mjs");
|
|
100
101
|
await runCloudDeploy(cloudOptions);
|
|
101
102
|
} else if (parsedCommand.command === "cloud" && subcommand === "preview") {
|
|
102
|
-
const { runCloudPreview } = await import("../cloud-
|
|
103
|
+
const { runCloudPreview } = await import("../cloud-DcsDg7Ze.mjs");
|
|
103
104
|
await runCloudPreview(cloudOptions);
|
|
105
|
+
} else if (parsedCommand.command === "cloud" && subcommand === "init") {
|
|
106
|
+
const { runCloudInit } = await import("../cloud-DcsDg7Ze.mjs");
|
|
107
|
+
await runCloudInit(cloudOptions);
|
|
104
108
|
} else if (parsedCommand.command === "cloud" && subcommand === "sync") {
|
|
105
|
-
const { syncCloudConfig } = await import("../cloud-
|
|
109
|
+
const { syncCloudConfig } = await import("../cloud-DcsDg7Ze.mjs");
|
|
106
110
|
await syncCloudConfig(cloudOptions);
|
|
107
111
|
} else if (parsedCommand.command === "cloud") {
|
|
108
112
|
console.error(pc.red(`Unknown cloud subcommand: ${subcommand ?? "(missing)"}`));
|
|
109
113
|
console.error();
|
|
110
|
-
const { printCloudHelp } = await import("../cloud-
|
|
114
|
+
const { printCloudHelp } = await import("../cloud-DcsDg7Ze.mjs");
|
|
111
115
|
printCloudHelp();
|
|
112
116
|
process.exit(1);
|
|
113
117
|
} else if (parsedCommand.command === "mcp") {
|
|
@@ -246,7 +250,7 @@ ${pc.dim("Commands:")}
|
|
|
246
250
|
${pc.cyan("dev")} Run frameworkless docs locally from ${pc.dim("docs.json")}
|
|
247
251
|
${pc.cyan("deploy")} Sync cloud config and deploy hosted preview docs
|
|
248
252
|
${pc.cyan("preview")} Alias for ${pc.cyan("deploy")}
|
|
249
|
-
${pc.cyan("cloud")} Docs Cloud utilities (${pc.dim("deploy")}, ${pc.dim("preview")}, ${pc.dim("sync")})
|
|
253
|
+
${pc.cyan("cloud")} Docs Cloud utilities (${pc.dim("init")}, ${pc.dim("deploy")}, ${pc.dim("preview")}, ${pc.dim("sync")})
|
|
250
254
|
${pc.cyan("agent")} Agent utilities (${pc.dim("compact")} to generate sibling agent.md files)
|
|
251
255
|
${pc.cyan("agents")} AGENTS.md utilities (${pc.dim("generate")} for static agent instructions)
|
|
252
256
|
${pc.cyan("doctor")} Inspect and score agent or reader-facing docs quality
|
|
@@ -281,12 +285,14 @@ ${pc.dim("Options for dev:")}
|
|
|
281
285
|
${pc.cyan("--verbose")} Show raw runtime logs in addition to branded CLI output
|
|
282
286
|
|
|
283
287
|
${pc.dim("Options for cloud deploy:")}
|
|
288
|
+
${pc.cyan("cloud init")} Add Docs Cloud config to ${pc.dim("docs.config.ts")} and ${pc.dim("docs.json")}
|
|
284
289
|
${pc.cyan("deploy")} Sync ${pc.dim("docs.config.ts")} into ${pc.dim("docs.json")} and deploy hosted preview docs
|
|
285
290
|
${pc.cyan("cloud deploy")} Same as ${pc.cyan("deploy")}
|
|
286
291
|
${pc.cyan("preview")} Alias for ${pc.cyan("deploy")}
|
|
287
292
|
${pc.cyan("cloud preview")} Compatibility alias for ${pc.cyan("cloud deploy")}
|
|
288
293
|
${pc.cyan("cloud sync")} Only materialize cloud settings into ${pc.dim("docs.json")}
|
|
289
294
|
${pc.cyan("--config <path>")} Use a custom docs config path
|
|
295
|
+
${pc.cyan("--api-key-env <name>")} Env var that stores the Docs Cloud API key
|
|
290
296
|
${pc.cyan("--api-base-url <url>")} Override the Docs Cloud API base URL
|
|
291
297
|
${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
|
|
292
298
|
${pc.cyan("--json")} Print machine-readable output
|
|
@@ -9,6 +9,7 @@ import { execFileSync } from "node:child_process";
|
|
|
9
9
|
const DOCS_JSON_FILE = "docs.json";
|
|
10
10
|
const DOCS_CLOUD_SCHEMA_URL = "https://docs.farming-labs.dev/schema/docs.json";
|
|
11
11
|
const DOCS_CLOUD_DEFAULT_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
|
|
12
|
+
const DOCS_CLOUD_DEFAULT_ANALYTICS_PROJECT_ID_ENV = "NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID";
|
|
12
13
|
const DEFAULT_DOCS_CLOUD_API_BASE_URL = "https://docs-app.farming-labs.dev";
|
|
13
14
|
const DEFAULT_PREVIEW_TIMEOUT_MS = 300 * 1e3;
|
|
14
15
|
const DEFAULT_PREVIEW_POLL_INTERVAL_MS = 2e3;
|
|
@@ -112,6 +113,299 @@ async function loadDocsConfigSnapshot(rootDir, explicitPath) {
|
|
|
112
113
|
config: (await loadDocsConfigModule(rootDir, configPath, { silent: true }))?.config
|
|
113
114
|
};
|
|
114
115
|
}
|
|
116
|
+
function escapeRegExp(value) {
|
|
117
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
118
|
+
}
|
|
119
|
+
function findBalancedBraceEnd(content, braceStart) {
|
|
120
|
+
let depth = 0;
|
|
121
|
+
let stringQuote = null;
|
|
122
|
+
let escaped = false;
|
|
123
|
+
let lineComment = false;
|
|
124
|
+
let blockComment = false;
|
|
125
|
+
for (let index = braceStart; index < content.length; index += 1) {
|
|
126
|
+
const char = content[index];
|
|
127
|
+
const next = content[index + 1];
|
|
128
|
+
if (lineComment) {
|
|
129
|
+
if (char === "\n") lineComment = false;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (blockComment) {
|
|
133
|
+
if (char === "*" && next === "/") {
|
|
134
|
+
blockComment = false;
|
|
135
|
+
index += 1;
|
|
136
|
+
}
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (stringQuote) {
|
|
140
|
+
if (escaped) {
|
|
141
|
+
escaped = false;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (char === "\\") {
|
|
145
|
+
escaped = true;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (char === stringQuote) stringQuote = null;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (char === "/" && next === "/") {
|
|
152
|
+
lineComment = true;
|
|
153
|
+
index += 1;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (char === "/" && next === "*") {
|
|
157
|
+
blockComment = true;
|
|
158
|
+
index += 1;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
162
|
+
stringQuote = char;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (char === "{") {
|
|
166
|
+
depth += 1;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (char !== "}") continue;
|
|
170
|
+
depth -= 1;
|
|
171
|
+
if (depth === 0) return index;
|
|
172
|
+
}
|
|
173
|
+
return -1;
|
|
174
|
+
}
|
|
175
|
+
function findConfigObjectRange(content) {
|
|
176
|
+
for (const marker of ["defineDocs(", "export default"]) {
|
|
177
|
+
const markerIndex = content.indexOf(marker);
|
|
178
|
+
if (markerIndex === -1) continue;
|
|
179
|
+
const braceStart = content.indexOf("{", markerIndex);
|
|
180
|
+
if (braceStart === -1) continue;
|
|
181
|
+
const braceEnd = findBalancedBraceEnd(content, braceStart);
|
|
182
|
+
if (braceEnd === -1) continue;
|
|
183
|
+
return {
|
|
184
|
+
braceStart,
|
|
185
|
+
braceEnd,
|
|
186
|
+
bodyStart: braceStart + 1,
|
|
187
|
+
bodyEnd: braceEnd
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function stripLeadingPropertyTrivia(content) {
|
|
192
|
+
let current = content;
|
|
193
|
+
while (true) {
|
|
194
|
+
const trimmed = current.replace(/^\s+/, "");
|
|
195
|
+
if (trimmed.startsWith("//")) {
|
|
196
|
+
const lineEnd = trimmed.indexOf("\n");
|
|
197
|
+
current = lineEnd === -1 ? "" : trimmed.slice(lineEnd + 1);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (trimmed.startsWith("/*")) {
|
|
201
|
+
const blockEnd = trimmed.indexOf("*/");
|
|
202
|
+
current = blockEnd === -1 ? "" : trimmed.slice(blockEnd + 2);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
return trimmed;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function propertyStartsWithKey(property, key) {
|
|
209
|
+
return new RegExp(`^${escapeRegExp(key)}\\s*:`).test(stripLeadingPropertyTrivia(property));
|
|
210
|
+
}
|
|
211
|
+
function findTopLevelPropertyRange(content, bodyStart, bodyEnd, key) {
|
|
212
|
+
let start = bodyStart;
|
|
213
|
+
let stringQuote = null;
|
|
214
|
+
let escaped = false;
|
|
215
|
+
let lineComment = false;
|
|
216
|
+
let blockComment = false;
|
|
217
|
+
let braceDepth = 0;
|
|
218
|
+
let bracketDepth = 0;
|
|
219
|
+
let parenDepth = 0;
|
|
220
|
+
const maybeMatch = (end) => {
|
|
221
|
+
if (!propertyStartsWithKey(content.slice(start, end), key)) return void 0;
|
|
222
|
+
return {
|
|
223
|
+
start,
|
|
224
|
+
end
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
for (let index = bodyStart; index <= bodyEnd; index += 1) {
|
|
228
|
+
const char = content[index];
|
|
229
|
+
const next = content[index + 1];
|
|
230
|
+
if (index === bodyEnd) return maybeMatch(index);
|
|
231
|
+
if (lineComment) {
|
|
232
|
+
if (char === "\n") lineComment = false;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (blockComment) {
|
|
236
|
+
if (char === "*" && next === "/") {
|
|
237
|
+
blockComment = false;
|
|
238
|
+
index += 1;
|
|
239
|
+
}
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (stringQuote) {
|
|
243
|
+
if (escaped) {
|
|
244
|
+
escaped = false;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (char === "\\") {
|
|
248
|
+
escaped = true;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (char === stringQuote) stringQuote = null;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (char === "/" && next === "/") {
|
|
255
|
+
lineComment = true;
|
|
256
|
+
index += 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (char === "/" && next === "*") {
|
|
260
|
+
blockComment = true;
|
|
261
|
+
index += 1;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
265
|
+
stringQuote = char;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (char === "{") {
|
|
269
|
+
braceDepth += 1;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (char === "}") {
|
|
273
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (char === "[") {
|
|
277
|
+
bracketDepth += 1;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (char === "]") {
|
|
281
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (char === "(") {
|
|
285
|
+
parenDepth += 1;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (char === ")") {
|
|
289
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (char !== "," || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0) continue;
|
|
293
|
+
const match = maybeMatch(index);
|
|
294
|
+
if (match) return match;
|
|
295
|
+
start = index + 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function findObjectPropertyRange(content, object, key) {
|
|
299
|
+
const property = findTopLevelPropertyRange(content, object.bodyStart, object.bodyEnd, key);
|
|
300
|
+
if (!property) return void 0;
|
|
301
|
+
const colon = content.indexOf(":", property.start);
|
|
302
|
+
const braceStart = content.indexOf("{", colon);
|
|
303
|
+
if (colon === -1 || braceStart === -1 || braceStart > property.end) return void 0;
|
|
304
|
+
const braceEnd = findBalancedBraceEnd(content, braceStart);
|
|
305
|
+
if (braceEnd === -1 || braceEnd > property.end) return void 0;
|
|
306
|
+
return {
|
|
307
|
+
braceStart,
|
|
308
|
+
braceEnd,
|
|
309
|
+
bodyStart: braceStart + 1,
|
|
310
|
+
bodyEnd: braceEnd
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function lineIndentAt(content, index) {
|
|
314
|
+
const lineStart = content.lastIndexOf("\n", index - 1) + 1;
|
|
315
|
+
return content.slice(lineStart, index).match(/^\s*/)?.[0] ?? "";
|
|
316
|
+
}
|
|
317
|
+
function insertObjectProperties(content, object, properties) {
|
|
318
|
+
if (properties.length === 0) return content;
|
|
319
|
+
const body = content.slice(object.bodyStart, object.bodyEnd);
|
|
320
|
+
const closingIndent = lineIndentAt(content, object.braceEnd);
|
|
321
|
+
const beforeObjectClose = content.slice(0, object.bodyEnd).replace(/\s*$/, "");
|
|
322
|
+
const suffix = content.slice(object.bodyEnd);
|
|
323
|
+
return `${beforeObjectClose}${body.trim().length > 0 && !beforeObjectClose.endsWith(",") ? "," : ""}\n${properties.join("\n")}\n${closingIndent}${suffix}`;
|
|
324
|
+
}
|
|
325
|
+
function renderAnalyticsConfigProperty(indent) {
|
|
326
|
+
return `${indent}analytics: {
|
|
327
|
+
${indent} enabled: true,
|
|
328
|
+
${indent} console: false,
|
|
329
|
+
${indent} includeInputs: false,
|
|
330
|
+
${indent}},`;
|
|
331
|
+
}
|
|
332
|
+
function renderCloudConfigProperty(indent, apiKeyEnv) {
|
|
333
|
+
return `${indent}cloud: {
|
|
334
|
+
${indent} apiKey: { env: ${JSON.stringify(apiKeyEnv)} },
|
|
335
|
+
${indent} deploy: { enabled: true },
|
|
336
|
+
${indent} analytics: {
|
|
337
|
+
${indent} enabled: true,
|
|
338
|
+
${indent} console: false,
|
|
339
|
+
${indent} includeInputs: false,
|
|
340
|
+
${indent} },
|
|
341
|
+
${indent} publish: { mode: "draft-pr", baseBranch: "main" },
|
|
342
|
+
${indent}},`;
|
|
343
|
+
}
|
|
344
|
+
function renderCloudInitDocsConfig(apiKeyEnv) {
|
|
345
|
+
return `import { defineDocs } from "@farming-labs/docs";
|
|
346
|
+
|
|
347
|
+
export default defineDocs({
|
|
348
|
+
${renderAnalyticsConfigProperty(" ")}
|
|
349
|
+
${renderCloudConfigProperty(" ", apiKeyEnv)}
|
|
350
|
+
});
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
function normalizeEnvName(value, fallback) {
|
|
354
|
+
const normalized = value?.trim();
|
|
355
|
+
if (!normalized) return fallback;
|
|
356
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) throw new Error(`Invalid environment variable name: ${normalized}`);
|
|
357
|
+
return normalized;
|
|
358
|
+
}
|
|
359
|
+
function ensureDocsConfigCloudInit(options) {
|
|
360
|
+
const resolvedConfigPath = tryResolveDocsConfigPath(options.rootDir, options.configPath);
|
|
361
|
+
const configPath = resolvedConfigPath ?? path.join(options.rootDir, "docs.config.ts");
|
|
362
|
+
if (!resolvedConfigPath) {
|
|
363
|
+
fs.writeFileSync(configPath, renderCloudInitDocsConfig(options.apiKeyEnv), "utf-8");
|
|
364
|
+
return {
|
|
365
|
+
configPath,
|
|
366
|
+
created: true,
|
|
367
|
+
updated: true
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const original = fs.readFileSync(configPath, "utf-8");
|
|
371
|
+
let content = original;
|
|
372
|
+
let configObject = findConfigObjectRange(content);
|
|
373
|
+
if (!configObject) throw new Error(`Could not find an object export in ${path.relative(options.rootDir, configPath)}. Use defineDocs({ ... }) or export default { ... } before running cloud init.`);
|
|
374
|
+
const topLevelIndent = `${lineIndentAt(content, configObject.braceEnd)} `;
|
|
375
|
+
const cloudProperty = findTopLevelPropertyRange(content, configObject.bodyStart, configObject.bodyEnd, "cloud");
|
|
376
|
+
let cloudObject = findObjectPropertyRange(content, configObject, "cloud");
|
|
377
|
+
if (!cloudProperty) content = insertObjectProperties(content, configObject, [renderCloudConfigProperty(topLevelIndent, options.apiKeyEnv)]);
|
|
378
|
+
else if (!cloudObject) throw new Error(`Could not update cloud config in ${path.relative(options.rootDir, configPath)} because cloud is not an object literal.`);
|
|
379
|
+
configObject = findConfigObjectRange(content);
|
|
380
|
+
if (!configObject) throw new Error(`Could not re-read ${path.relative(options.rootDir, configPath)}.`);
|
|
381
|
+
cloudObject = findObjectPropertyRange(content, configObject, "cloud");
|
|
382
|
+
if (cloudObject) {
|
|
383
|
+
const cloudIndent = `${lineIndentAt(content, cloudObject.braceEnd)} `;
|
|
384
|
+
const missingCloudProperties = [];
|
|
385
|
+
if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "apiKey")) missingCloudProperties.push(`${cloudIndent}apiKey: { env: ${JSON.stringify(options.apiKeyEnv)} },`);
|
|
386
|
+
else {
|
|
387
|
+
const apiKeyObject = findObjectPropertyRange(content, cloudObject, "apiKey");
|
|
388
|
+
if (apiKeyObject && !findTopLevelPropertyRange(content, apiKeyObject.bodyStart, apiKeyObject.bodyEnd, "env")) {
|
|
389
|
+
content = insertObjectProperties(content, apiKeyObject, [`${lineIndentAt(content, apiKeyObject.braceEnd)} env: ${JSON.stringify(options.apiKeyEnv)},`]);
|
|
390
|
+
configObject = findConfigObjectRange(content);
|
|
391
|
+
cloudObject = findObjectPropertyRange(content, configObject, "cloud");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "deploy")) missingCloudProperties.push(`${cloudIndent}deploy: { enabled: true },`);
|
|
395
|
+
if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "analytics")) missingCloudProperties.push(renderAnalyticsConfigProperty(cloudIndent));
|
|
396
|
+
if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "publish")) missingCloudProperties.push(`${cloudIndent}publish: { mode: "draft-pr", baseBranch: "main" },`);
|
|
397
|
+
content = insertObjectProperties(content, cloudObject, missingCloudProperties);
|
|
398
|
+
}
|
|
399
|
+
configObject = findConfigObjectRange(content);
|
|
400
|
+
if (!configObject) throw new Error(`Could not re-read ${path.relative(options.rootDir, configPath)}.`);
|
|
401
|
+
if (!findTopLevelPropertyRange(content, configObject.bodyStart, configObject.bodyEnd, "analytics")) content = insertObjectProperties(content, configObject, [renderAnalyticsConfigProperty(`${lineIndentAt(content, configObject.braceEnd)} `)]);
|
|
402
|
+
if (content !== original) fs.writeFileSync(configPath, content, "utf-8");
|
|
403
|
+
return {
|
|
404
|
+
configPath,
|
|
405
|
+
created: false,
|
|
406
|
+
updated: content !== original
|
|
407
|
+
};
|
|
408
|
+
}
|
|
115
409
|
function readExistingDocsJson(docsJsonPath) {
|
|
116
410
|
if (!fs.existsSync(docsJsonPath)) return void 0;
|
|
117
411
|
try {
|
|
@@ -564,6 +858,51 @@ async function syncCloudConfig(options = {}) {
|
|
|
564
858
|
console.log(`${pc.dim("api key env")} ${result.apiKeyEnv}`);
|
|
565
859
|
return result;
|
|
566
860
|
}
|
|
861
|
+
async function initCloudConfig(options = {}) {
|
|
862
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
863
|
+
const apiKeyEnv = normalizeEnvName(options.apiKeyEnv, DOCS_CLOUD_DEFAULT_API_KEY_ENV);
|
|
864
|
+
const configUpdate = ensureDocsConfigCloudInit({
|
|
865
|
+
rootDir,
|
|
866
|
+
configPath: options.configPath,
|
|
867
|
+
apiKeyEnv
|
|
868
|
+
});
|
|
869
|
+
const materialized = await materializeCloudConfig({
|
|
870
|
+
...options,
|
|
871
|
+
rootDir,
|
|
872
|
+
configPath: path.relative(rootDir, configUpdate.configPath)
|
|
873
|
+
});
|
|
874
|
+
return {
|
|
875
|
+
configPath: configUpdate.configPath,
|
|
876
|
+
docsJsonPath: materialized.docsJsonPath,
|
|
877
|
+
apiKeyEnv: materialized.apiKeyEnv,
|
|
878
|
+
analyticsProjectIdEnv: DOCS_CLOUD_DEFAULT_ANALYTICS_PROJECT_ID_ENV,
|
|
879
|
+
configCreated: configUpdate.created,
|
|
880
|
+
configUpdated: configUpdate.updated,
|
|
881
|
+
docsJsonCreated: materialized.created,
|
|
882
|
+
docsJsonUpdated: materialized.updated
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
async function runCloudInit(options = {}) {
|
|
886
|
+
const result = await initCloudConfig(options);
|
|
887
|
+
if (options.json) {
|
|
888
|
+
console.log(JSON.stringify(result, null, 2));
|
|
889
|
+
return result;
|
|
890
|
+
}
|
|
891
|
+
const relativeConfigPath = path.relative(process.cwd(), result.configPath) || "docs.config.ts";
|
|
892
|
+
const relativeDocsJsonPath = path.relative(process.cwd(), result.docsJsonPath) || DOCS_JSON_FILE;
|
|
893
|
+
const configAction = result.configCreated ? "Created" : result.configUpdated ? "Updated" : "Checked";
|
|
894
|
+
const docsJsonAction = result.docsJsonCreated ? "created" : result.docsJsonUpdated ? "updated" : "checked";
|
|
895
|
+
console.log(`${pc.green("ok")} ${configAction} ${pc.cyan(relativeConfigPath)}`);
|
|
896
|
+
console.log(`${pc.green("ok")} ${docsJsonAction} ${pc.cyan(relativeDocsJsonPath)}`);
|
|
897
|
+
console.log();
|
|
898
|
+
console.log(pc.bold("Add these env vars"));
|
|
899
|
+
console.log(`${pc.cyan(result.apiKeyEnv)}=${pc.dim("paste_your_docs_cloud_api_key")}`);
|
|
900
|
+
console.log(`${pc.cyan(result.analyticsProjectIdEnv)}=${pc.dim("paste_your_docs_cloud_project_id")}`);
|
|
901
|
+
console.log();
|
|
902
|
+
console.log(pc.dim("Use the same env vars in production. The API key value is never written to config."));
|
|
903
|
+
console.log(pc.dim(`Then run ${pc.cyan("pnpm dlx @farming-labs/docs deploy")}.`));
|
|
904
|
+
return result;
|
|
905
|
+
}
|
|
567
906
|
async function runCloudDeployment(options = {}) {
|
|
568
907
|
const rootDir = options.rootDir ?? process.cwd();
|
|
569
908
|
const spinner = createSpinner("Preparing Docs Cloud deployment", options);
|
|
@@ -625,6 +964,7 @@ function printCloudHelp() {
|
|
|
625
964
|
${pc.bold("@farming-labs/docs cloud")}
|
|
626
965
|
|
|
627
966
|
${pc.dim("Usage:")}
|
|
967
|
+
${pc.cyan("docs cloud init")} Add Docs Cloud config to ${pc.dim("docs.config.ts")} and ${pc.dim("docs.json")}
|
|
628
968
|
${pc.cyan("docs deploy")} Sync ${pc.dim("docs.config.ts")} to ${pc.dim("docs.json")} and deploy hosted preview docs
|
|
629
969
|
${pc.cyan("docs cloud deploy")} Same as ${pc.cyan("docs deploy")}
|
|
630
970
|
${pc.cyan("docs preview")} Compatibility alias for ${pc.cyan("docs deploy")}
|
|
@@ -633,6 +973,7 @@ ${pc.dim("Usage:")}
|
|
|
633
973
|
|
|
634
974
|
${pc.dim("Options:")}
|
|
635
975
|
${pc.cyan("--config <path>")} Use a custom docs config path
|
|
976
|
+
${pc.cyan("--api-key-env <name>")} Env var that stores the Docs Cloud API key
|
|
636
977
|
${pc.cyan("--api-base-url <url>")} Override Docs Cloud API base URL
|
|
637
978
|
${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
|
|
638
979
|
${pc.cyan("--json")} Print machine-readable output
|
|
@@ -644,10 +985,11 @@ ${pc.dim("Config example:")}
|
|
|
644
985
|
cloud: {
|
|
645
986
|
apiKey: { env: "DOCS_CLOUD_API_KEY" },
|
|
646
987
|
deploy: { enabled: true },
|
|
988
|
+
analytics: { enabled: true, console: false, includeInputs: false },
|
|
647
989
|
publish: { mode: "draft-pr", baseBranch: "main" },
|
|
648
990
|
}
|
|
649
991
|
`);
|
|
650
992
|
}
|
|
651
993
|
|
|
652
994
|
//#endregion
|
|
653
|
-
export { printCloudHelp, runCloudDeploy, runCloudPreview, syncCloudConfig };
|
|
995
|
+
export { printCloudHelp, runCloudDeploy, runCloudInit, runCloudPreview, syncCloudConfig };
|