@lukoweb/apitogo 0.1.81 → 0.1.82
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/cli.js +275 -116
- package/docs/cli/commands.md +2 -2
- package/docs/cli/overview.md +6 -5
- package/docs/cli/setup.md +5 -5
- package/docs/cli/shape.md +9 -7
- package/docs/configuration/authentication.md +1 -1
- package/docs/configuration/make-config.mdx +23 -15
- package/docs/configuration/manifest.md +1 -1
- package/docs/configuration/overview.md +1 -1
- package/docs/configuration/plans.md +4 -4
- package/docs/guides/custom-pages.md +2 -2
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -5633,7 +5633,7 @@ import {
|
|
|
5633
5633
|
// package.json
|
|
5634
5634
|
var package_default = {
|
|
5635
5635
|
name: "@lukoweb/apitogo",
|
|
5636
|
-
version: "0.1.
|
|
5636
|
+
version: "0.1.82",
|
|
5637
5637
|
type: "module",
|
|
5638
5638
|
sideEffects: [
|
|
5639
5639
|
"**/*.css",
|
|
@@ -7454,13 +7454,13 @@ var generateCode = (schema2, filePath) => {
|
|
|
7454
7454
|
|
|
7455
7455
|
// src/vite/api/SchemaManager.ts
|
|
7456
7456
|
var FALLBACK_VERSION = "default";
|
|
7457
|
-
var parseInputString = (
|
|
7458
|
-
const idx =
|
|
7457
|
+
var parseInputString = (input3) => {
|
|
7458
|
+
const idx = input3.indexOf("?");
|
|
7459
7459
|
if (idx === -1)
|
|
7460
|
-
return { filePath:
|
|
7460
|
+
return { filePath: input3, params: {} };
|
|
7461
7461
|
return {
|
|
7462
|
-
filePath:
|
|
7463
|
-
params: Object.fromEntries(new URLSearchParams(
|
|
7462
|
+
filePath: input3.slice(0, idx),
|
|
7463
|
+
params: Object.fromEntries(new URLSearchParams(input3.slice(idx)))
|
|
7464
7464
|
};
|
|
7465
7465
|
};
|
|
7466
7466
|
var canonicalParams = (params) => Object.entries(params).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join("&");
|
|
@@ -7496,8 +7496,8 @@ var SchemaManager = class {
|
|
|
7496
7496
|
...processors
|
|
7497
7497
|
];
|
|
7498
7498
|
}
|
|
7499
|
-
getPathForFile = (
|
|
7500
|
-
const filePath = path11.resolve(this.config.__meta.rootDir,
|
|
7499
|
+
getPathForFile = (input3, params) => {
|
|
7500
|
+
const filePath = path11.resolve(this.config.__meta.rootDir, input3);
|
|
7501
7501
|
const apis = ensureArray(this.config.apis ?? []);
|
|
7502
7502
|
for (const apiConfig of apis) {
|
|
7503
7503
|
if (!apiConfig || apiConfig.type !== "file" || !apiConfig.path) continue;
|
|
@@ -7507,12 +7507,12 @@ var SchemaManager = class {
|
|
|
7507
7507
|
if (match) return apiConfig.path;
|
|
7508
7508
|
}
|
|
7509
7509
|
};
|
|
7510
|
-
processSchema = async (
|
|
7511
|
-
const filePath = path11.resolve(this.config.__meta.rootDir,
|
|
7512
|
-
const params =
|
|
7513
|
-
const configuredPath = this.getPathForFile(
|
|
7510
|
+
processSchema = async (input3) => {
|
|
7511
|
+
const filePath = path11.resolve(this.config.__meta.rootDir, input3.input);
|
|
7512
|
+
const params = input3.params;
|
|
7513
|
+
const configuredPath = this.getPathForFile(input3.input, params);
|
|
7514
7514
|
if (!configuredPath) {
|
|
7515
|
-
console.warn(`No path found for file ${
|
|
7515
|
+
console.warn(`No path found for file ${input3.input}`);
|
|
7516
7516
|
return;
|
|
7517
7517
|
}
|
|
7518
7518
|
const parser = new $RefParser2();
|
|
@@ -7619,20 +7619,20 @@ var SchemaManager = class {
|
|
|
7619
7619
|
if (apiConfig.type !== "file" || !apiConfig.path) continue;
|
|
7620
7620
|
const inputs = normalizeInputs(apiConfig.input);
|
|
7621
7621
|
if (inputs.length === 0) throw new Error("No schema found");
|
|
7622
|
-
this.processedSchemas[apiConfig.path] = inputs.map((
|
|
7622
|
+
this.processedSchemas[apiConfig.path] = inputs.map((input3) => ({
|
|
7623
7623
|
schema: {},
|
|
7624
7624
|
version: "",
|
|
7625
|
-
path:
|
|
7626
|
-
label:
|
|
7627
|
-
inputPath: path11.resolve(this.config.__meta.rootDir,
|
|
7628
|
-
params:
|
|
7625
|
+
path: input3.path ?? "",
|
|
7626
|
+
label: input3.label,
|
|
7627
|
+
inputPath: path11.resolve(this.config.__meta.rootDir, input3.input),
|
|
7628
|
+
params: input3.params,
|
|
7629
7629
|
importKey: "",
|
|
7630
7630
|
downloadUrl: "",
|
|
7631
7631
|
processedJsonPath: "",
|
|
7632
7632
|
processedTime: 0
|
|
7633
7633
|
}));
|
|
7634
7634
|
const results = await Promise.allSettled(
|
|
7635
|
-
inputs.map((
|
|
7635
|
+
inputs.map((input3) => this.processSchema(input3))
|
|
7636
7636
|
);
|
|
7637
7637
|
const errors = results.flatMap(
|
|
7638
7638
|
(r) => r.status === "rejected" ? r.reason : []
|
|
@@ -7711,7 +7711,7 @@ import { readFile } from "node:fs/promises";
|
|
|
7711
7711
|
import matter from "gray-matter";
|
|
7712
7712
|
import { parse, stringify as stringify2 } from "yaml";
|
|
7713
7713
|
var yaml = {
|
|
7714
|
-
parse: (
|
|
7714
|
+
parse: (input3) => parse(input3) ?? {},
|
|
7715
7715
|
stringify: (obj) => stringify2(obj)
|
|
7716
7716
|
};
|
|
7717
7717
|
var readFrontmatter = async (filePath) => {
|
|
@@ -10520,6 +10520,7 @@ var build_default = {
|
|
|
10520
10520
|
import { constants as constants2 } from "node:fs";
|
|
10521
10521
|
import { access as access3, mkdir as mkdir7, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
|
|
10522
10522
|
import path36 from "node:path";
|
|
10523
|
+
import { stdin as input2 } from "node:process";
|
|
10523
10524
|
import { glob as glob5 } from "glob";
|
|
10524
10525
|
|
|
10525
10526
|
// src/cli/make-config/interactive.ts
|
|
@@ -10783,12 +10784,12 @@ function mergeDocumentationNavigation(navigation, docRoutePaths, options) {
|
|
|
10783
10784
|
}
|
|
10784
10785
|
return [documentation, ...rest];
|
|
10785
10786
|
}
|
|
10786
|
-
function applyOpenApiToJsonConfig(config2,
|
|
10787
|
+
function applyOpenApiToJsonConfig(config2, input3) {
|
|
10787
10788
|
const next = { ...config2 };
|
|
10788
10789
|
next.apis = [
|
|
10789
10790
|
{
|
|
10790
10791
|
type: "file",
|
|
10791
|
-
input:
|
|
10792
|
+
input: input3,
|
|
10792
10793
|
path: "api"
|
|
10793
10794
|
}
|
|
10794
10795
|
];
|
|
@@ -10831,15 +10832,15 @@ function buildLimitsJson(rateLimitCalls, monthlyQuota) {
|
|
|
10831
10832
|
quota: { calls: monthlyQuota, renewalPeriodDays: 30 }
|
|
10832
10833
|
});
|
|
10833
10834
|
}
|
|
10834
|
-
function buildPlanItemFromWizard(
|
|
10835
|
-
const sku =
|
|
10835
|
+
function buildPlanItemFromWizard(input3) {
|
|
10836
|
+
const sku = input3.sku ?? slugifyPlanSku(input3.displayName);
|
|
10836
10837
|
const item = {
|
|
10837
10838
|
sku,
|
|
10838
|
-
displayName:
|
|
10839
|
-
priceAmount:
|
|
10840
|
-
limitsJson: buildLimitsJson(
|
|
10839
|
+
displayName: input3.displayName,
|
|
10840
|
+
priceAmount: input3.priceAmount,
|
|
10841
|
+
limitsJson: buildLimitsJson(input3.rateLimitCalls, input3.monthlyQuota)
|
|
10841
10842
|
};
|
|
10842
|
-
if (
|
|
10843
|
+
if (input3.priceAmount > 0) {
|
|
10843
10844
|
item.priceCurrency = "usd";
|
|
10844
10845
|
item.billingPeriod = "month";
|
|
10845
10846
|
}
|
|
@@ -10870,6 +10871,45 @@ function applyPlansToJsonConfig(config2, planItems) {
|
|
|
10870
10871
|
modules.userPanel = userPanel;
|
|
10871
10872
|
return { ...config2, modules };
|
|
10872
10873
|
}
|
|
10874
|
+
var BACKEND_RUNTIMES = ["dotnet", "nodejs", "python"];
|
|
10875
|
+
var RUNTIME_VERSIONS_BY_RUNTIME = {
|
|
10876
|
+
nodejs: ["NODE_22_LTS", "NODE_24_LTS"],
|
|
10877
|
+
python: [
|
|
10878
|
+
"PYTHON_310",
|
|
10879
|
+
"PYTHON_311",
|
|
10880
|
+
"PYTHON_312",
|
|
10881
|
+
"PYTHON_313",
|
|
10882
|
+
"PYTHON_314"
|
|
10883
|
+
],
|
|
10884
|
+
dotnet: ["DOTNET_8", "DOTNET_9", "DOTNET_10"]
|
|
10885
|
+
};
|
|
10886
|
+
function parseBackendRuntime(raw) {
|
|
10887
|
+
const value = raw.trim().toLowerCase();
|
|
10888
|
+
if (value === "node" || value === "nodejs") {
|
|
10889
|
+
return "nodejs";
|
|
10890
|
+
}
|
|
10891
|
+
if (value === "dotnet" || value === "python") {
|
|
10892
|
+
return value;
|
|
10893
|
+
}
|
|
10894
|
+
return null;
|
|
10895
|
+
}
|
|
10896
|
+
function parseRuntimeVersion(runtime, raw) {
|
|
10897
|
+
const value = raw.trim().toUpperCase();
|
|
10898
|
+
const allowed = RUNTIME_VERSIONS_BY_RUNTIME[runtime];
|
|
10899
|
+
return allowed.includes(value) ? value : null;
|
|
10900
|
+
}
|
|
10901
|
+
function applyBackendToJsonConfig(config2, backend) {
|
|
10902
|
+
const existing = isPlainObject2(config2.backend) ? config2.backend : {};
|
|
10903
|
+
return {
|
|
10904
|
+
...config2,
|
|
10905
|
+
backend: {
|
|
10906
|
+
...existing,
|
|
10907
|
+
sourceDirectory: backend.sourceDirectory,
|
|
10908
|
+
runtime: backend.runtime,
|
|
10909
|
+
runtimeVersion: backend.runtimeVersion
|
|
10910
|
+
}
|
|
10911
|
+
};
|
|
10912
|
+
}
|
|
10873
10913
|
function toRoutePath(relativeFile, pagesDir) {
|
|
10874
10914
|
const relativeNoExt = relativeFile.replace(/\.(mdx?|tsx|jsx)$/i, "");
|
|
10875
10915
|
const escaped = pagesDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -11022,45 +11062,95 @@ var parseLoopChoice = (raw) => {
|
|
|
11022
11062
|
if (!value || value === "done") {
|
|
11023
11063
|
return "done";
|
|
11024
11064
|
}
|
|
11025
|
-
if (value === "openapi" || value === "page" || value === "plan") {
|
|
11065
|
+
if (value === "backend" || value === "openapi" || value === "page" || value === "plan") {
|
|
11026
11066
|
return value;
|
|
11027
11067
|
}
|
|
11028
11068
|
return null;
|
|
11029
11069
|
};
|
|
11030
|
-
async function promptMakeConfigInteractive(
|
|
11070
|
+
async function promptMakeConfigInteractive(options) {
|
|
11031
11071
|
const rl = createInterface({ input, output });
|
|
11032
|
-
const customPages = [];
|
|
11033
|
-
const planItems = [];
|
|
11034
11072
|
let hasPaidPlans = false;
|
|
11035
|
-
let openApiPath;
|
|
11036
11073
|
try {
|
|
11037
|
-
const title = await ask(rl, "Site title", defaults.title);
|
|
11038
11074
|
while (true) {
|
|
11039
11075
|
const choiceRaw = await ask(
|
|
11040
11076
|
rl,
|
|
11041
|
-
"
|
|
11077
|
+
"What do you want to configure? (backend / openapi / page / plan / done)",
|
|
11042
11078
|
"done"
|
|
11043
11079
|
);
|
|
11044
11080
|
const choice = parseLoopChoice(choiceRaw);
|
|
11045
11081
|
if (!choice) {
|
|
11046
|
-
printDiagnosticsToConsole(
|
|
11082
|
+
printDiagnosticsToConsole(
|
|
11083
|
+
"Choose backend, openapi, page, plan, or done."
|
|
11084
|
+
);
|
|
11047
11085
|
continue;
|
|
11048
11086
|
}
|
|
11049
11087
|
if (choice === "done") {
|
|
11050
11088
|
break;
|
|
11051
11089
|
}
|
|
11090
|
+
if (choice === "backend") {
|
|
11091
|
+
const sourceDirectory = await ask(
|
|
11092
|
+
rl,
|
|
11093
|
+
"Backend source directory (relative to portal)",
|
|
11094
|
+
".."
|
|
11095
|
+
);
|
|
11096
|
+
if (!sourceDirectory.trim()) {
|
|
11097
|
+
printDiagnosticsToConsole("Backend source directory is required.");
|
|
11098
|
+
continue;
|
|
11099
|
+
}
|
|
11100
|
+
let runtime = null;
|
|
11101
|
+
while (!runtime) {
|
|
11102
|
+
const runtimeRaw = await ask(
|
|
11103
|
+
rl,
|
|
11104
|
+
`Runtime (${BACKEND_RUNTIMES.join(" / ")})`,
|
|
11105
|
+
"nodejs"
|
|
11106
|
+
);
|
|
11107
|
+
runtime = parseBackendRuntime(runtimeRaw);
|
|
11108
|
+
if (!runtime) {
|
|
11109
|
+
printDiagnosticsToConsole(
|
|
11110
|
+
`Choose one of: ${BACKEND_RUNTIMES.join(", ")} (node \u2192 nodejs).`
|
|
11111
|
+
);
|
|
11112
|
+
}
|
|
11113
|
+
}
|
|
11114
|
+
const versions = RUNTIME_VERSIONS_BY_RUNTIME[runtime];
|
|
11115
|
+
let runtimeVersion = null;
|
|
11116
|
+
while (!runtimeVersion) {
|
|
11117
|
+
const versionRaw = await ask(
|
|
11118
|
+
rl,
|
|
11119
|
+
`Runtime version (${versions.join(" / ")})`,
|
|
11120
|
+
versions[0]
|
|
11121
|
+
);
|
|
11122
|
+
runtimeVersion = parseRuntimeVersion(runtime, versionRaw);
|
|
11123
|
+
if (!runtimeVersion) {
|
|
11124
|
+
printDiagnosticsToConsole(
|
|
11125
|
+
`Choose one of: ${versions.join(", ")}.`
|
|
11126
|
+
);
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
const backend = {
|
|
11130
|
+
sourceDirectory: sourceDirectory.trim(),
|
|
11131
|
+
runtime,
|
|
11132
|
+
runtimeVersion
|
|
11133
|
+
};
|
|
11134
|
+
await options.onApplied({ kind: "backend", backend });
|
|
11135
|
+
printDiagnosticsToConsole(
|
|
11136
|
+
`Backend saved: ${backend.runtime} ${backend.runtimeVersion} (sourceDirectory: ${backend.sourceDirectory}).`
|
|
11137
|
+
);
|
|
11138
|
+
continue;
|
|
11139
|
+
}
|
|
11052
11140
|
if (choice === "openapi") {
|
|
11053
11141
|
const entered = await ask(
|
|
11054
11142
|
rl,
|
|
11055
11143
|
"OpenAPI file path (leave empty to skip)",
|
|
11056
|
-
|
|
11144
|
+
options.discoveredOpenApi ?? ""
|
|
11057
11145
|
);
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
`OpenAPI will be linked as a relative path in apitogo.config.json.`
|
|
11062
|
-
);
|
|
11146
|
+
if (!entered) {
|
|
11147
|
+
printDiagnosticsToConsole("OpenAPI skipped.");
|
|
11148
|
+
continue;
|
|
11063
11149
|
}
|
|
11150
|
+
await options.onApplied({ kind: "openapi", openApiPath: entered });
|
|
11151
|
+
printDiagnosticsToConsole(
|
|
11152
|
+
`OpenAPI linked as a relative path in apitogo.config.json.`
|
|
11153
|
+
);
|
|
11064
11154
|
continue;
|
|
11065
11155
|
}
|
|
11066
11156
|
if (choice === "page") {
|
|
@@ -11079,14 +11169,14 @@ async function promptMakeConfigInteractive(defaults) {
|
|
|
11079
11169
|
continue;
|
|
11080
11170
|
}
|
|
11081
11171
|
const routePath = `/${slug}`;
|
|
11082
|
-
const relativePath = path34.posix.join(
|
|
11172
|
+
const relativePath = path34.posix.join(options.pagesDir, `${slug}.tsx`);
|
|
11083
11173
|
const page = {
|
|
11084
11174
|
slug,
|
|
11085
11175
|
title: pageTitle.trim(),
|
|
11086
11176
|
routePath,
|
|
11087
11177
|
relativePath
|
|
11088
11178
|
};
|
|
11089
|
-
|
|
11179
|
+
await options.onApplied({ kind: "page", page });
|
|
11090
11180
|
printPageAddedHint(relativePath, routePath);
|
|
11091
11181
|
continue;
|
|
11092
11182
|
}
|
|
@@ -11116,25 +11206,15 @@ async function promptMakeConfigInteractive(defaults) {
|
|
|
11116
11206
|
rateLimitCalls,
|
|
11117
11207
|
monthlyQuota
|
|
11118
11208
|
});
|
|
11119
|
-
|
|
11120
|
-
if (
|
|
11209
|
+
const paid = priceAmount > 0;
|
|
11210
|
+
if (paid) {
|
|
11121
11211
|
hasPaidPlans = true;
|
|
11122
11212
|
printPaidPlanPlatformWarning();
|
|
11123
11213
|
}
|
|
11214
|
+
await options.onApplied({ kind: "plan", planItem, paid });
|
|
11124
11215
|
printPlanAddedHint(displayName.trim());
|
|
11125
11216
|
}
|
|
11126
|
-
return {
|
|
11127
|
-
title,
|
|
11128
|
-
site: { title },
|
|
11129
|
-
authentication: {
|
|
11130
|
-
enabled: true,
|
|
11131
|
-
type: "dev-portal"
|
|
11132
|
-
},
|
|
11133
|
-
openApiPath,
|
|
11134
|
-
customPages,
|
|
11135
|
-
planItems,
|
|
11136
|
-
hasPaidPlans
|
|
11137
|
-
};
|
|
11217
|
+
return { hasPaidPlans };
|
|
11138
11218
|
} finally {
|
|
11139
11219
|
rl.close();
|
|
11140
11220
|
}
|
|
@@ -11414,11 +11494,65 @@ var readJsonConfig = async (filePath) => {
|
|
|
11414
11494
|
}
|
|
11415
11495
|
return parsed;
|
|
11416
11496
|
};
|
|
11497
|
+
function shouldRunInteractive(argv) {
|
|
11498
|
+
if (argv.interactive === true) {
|
|
11499
|
+
return true;
|
|
11500
|
+
}
|
|
11501
|
+
if (argv.interactive === false) {
|
|
11502
|
+
return false;
|
|
11503
|
+
}
|
|
11504
|
+
const hasPatchFlags = argv.title !== void 0 || argv.site !== void 0 || argv.theme !== void 0 || argv.authentication !== void 0 || argv.force === true;
|
|
11505
|
+
if (hasPatchFlags) {
|
|
11506
|
+
return false;
|
|
11507
|
+
}
|
|
11508
|
+
return input2.isTTY === true;
|
|
11509
|
+
}
|
|
11510
|
+
async function persistConfig(config2, ctx) {
|
|
11511
|
+
const pageFiles = await glob5(`${ctx.pagesDir}/**/*.{md,mdx,tsx,jsx}`, {
|
|
11512
|
+
cwd: ctx.dir,
|
|
11513
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
11514
|
+
posix: true
|
|
11515
|
+
});
|
|
11516
|
+
const markdownFiles = pageFiles.filter((file) => /\.mdx?$/i.test(file));
|
|
11517
|
+
const tsxFiles = pageFiles.filter((file) => /\.(tsx|jsx)$/i.test(file));
|
|
11518
|
+
const markdownRoutes = markdownFiles.map(
|
|
11519
|
+
(file) => toRoutePath(file, ctx.pagesDir)
|
|
11520
|
+
);
|
|
11521
|
+
const tsxRoutes = tsxFiles.map((file) => toRoutePath(file, ctx.pagesDir));
|
|
11522
|
+
if (markdownRoutes.length === 0 && tsxRoutes.length === 0) {
|
|
11523
|
+
printDiagnosticsToConsole(
|
|
11524
|
+
`No docs pages under '${ctx.pagesDir}/' \u2014 skipping documentation nav scan.`
|
|
11525
|
+
);
|
|
11526
|
+
}
|
|
11527
|
+
const next = {
|
|
11528
|
+
...config2,
|
|
11529
|
+
navigation: mergeDocumentationNavigation(
|
|
11530
|
+
config2.navigation,
|
|
11531
|
+
markdownRoutes,
|
|
11532
|
+
{ force: ctx.force }
|
|
11533
|
+
)
|
|
11534
|
+
};
|
|
11535
|
+
await writeFile7(ctx.jsonPath, `${JSON.stringify(next, null, 2)}
|
|
11536
|
+
`, "utf8");
|
|
11537
|
+
const tsxPath = path36.join(ctx.dir, "apitogo.config.tsx");
|
|
11538
|
+
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
11539
|
+
const tsxSource = await readFile5(tsxPath, "utf8");
|
|
11540
|
+
if (isThinConfigWrapper(tsxSource)) {
|
|
11541
|
+
await writeFile7(
|
|
11542
|
+
tsxPath,
|
|
11543
|
+
buildThinWrapperWithCustomPages(tsxRoutes),
|
|
11544
|
+
"utf8"
|
|
11545
|
+
);
|
|
11546
|
+
}
|
|
11547
|
+
}
|
|
11548
|
+
return next;
|
|
11549
|
+
}
|
|
11417
11550
|
async function makeConfig(argv) {
|
|
11418
11551
|
const dir = path36.resolve(process.cwd(), argv.dir);
|
|
11419
11552
|
const pagesDir = toPosixPath2(
|
|
11420
11553
|
argv.pagesDir.replace(/^[./]+/, "").replace(/\/+$/, "")
|
|
11421
11554
|
);
|
|
11555
|
+
const interactive = shouldRunInteractive(argv);
|
|
11422
11556
|
try {
|
|
11423
11557
|
const discoveredOpenApi = await findOpenApiSpecPath(dir);
|
|
11424
11558
|
const createdPackageJson = await ensurePackageJson(dir);
|
|
@@ -11455,68 +11589,93 @@ async function makeConfig(argv) {
|
|
|
11455
11589
|
authentication: parseJsonObjectFlag(argv.authentication, "authentication"),
|
|
11456
11590
|
title: argv.title
|
|
11457
11591
|
};
|
|
11458
|
-
|
|
11592
|
+
const persistCtx = {
|
|
11593
|
+
dir,
|
|
11594
|
+
pagesDir,
|
|
11595
|
+
jsonPath,
|
|
11596
|
+
force: argv.force === true
|
|
11597
|
+
};
|
|
11459
11598
|
let interactiveHasPaidPlans = false;
|
|
11460
|
-
if (
|
|
11461
|
-
|
|
11599
|
+
if (interactive) {
|
|
11600
|
+
config2 = await persistConfig(config2, persistCtx);
|
|
11462
11601
|
const prompted = await promptMakeConfigInteractive({
|
|
11463
|
-
title: flagPatches.title ?? (typeof site.title === "string" ? site.title : titleFromDirectoryName(path36.basename(dir))),
|
|
11464
11602
|
discoveredOpenApi,
|
|
11465
|
-
pagesDir
|
|
11603
|
+
pagesDir,
|
|
11604
|
+
onApplied: async (action2) => {
|
|
11605
|
+
if (action2.kind === "backend") {
|
|
11606
|
+
config2 = applyBackendToJsonConfig(config2, action2.backend);
|
|
11607
|
+
config2 = await persistConfig(config2, persistCtx);
|
|
11608
|
+
return;
|
|
11609
|
+
}
|
|
11610
|
+
if (action2.kind === "openapi") {
|
|
11611
|
+
const openApiInput = toRelativeOpenApiInput(
|
|
11612
|
+
dir,
|
|
11613
|
+
action2.openApiPath
|
|
11614
|
+
);
|
|
11615
|
+
config2 = applyOpenApiToJsonConfig(config2, openApiInput);
|
|
11616
|
+
config2 = await persistConfig(config2, persistCtx);
|
|
11617
|
+
return;
|
|
11618
|
+
}
|
|
11619
|
+
if (action2.kind === "page") {
|
|
11620
|
+
const pagePath = path36.join(dir, action2.page.relativePath);
|
|
11621
|
+
await mkdir7(path36.dirname(pagePath), { recursive: true });
|
|
11622
|
+
await writeFile7(
|
|
11623
|
+
pagePath,
|
|
11624
|
+
buildStarterDocsPageTsx(action2.page.slug, action2.page.title),
|
|
11625
|
+
"utf8"
|
|
11626
|
+
);
|
|
11627
|
+
config2 = await persistConfig(config2, persistCtx);
|
|
11628
|
+
return;
|
|
11629
|
+
}
|
|
11630
|
+
if (action2.kind === "plan") {
|
|
11631
|
+
config2 = applyPlansToJsonConfig(config2, [action2.planItem]);
|
|
11632
|
+
config2 = await persistConfig(config2, persistCtx);
|
|
11633
|
+
}
|
|
11634
|
+
}
|
|
11466
11635
|
});
|
|
11467
|
-
openApiOverride = prompted.openApiPath;
|
|
11468
11636
|
interactiveHasPaidPlans = prompted.hasPaidPlans;
|
|
11469
|
-
|
|
11470
|
-
config2 =
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11637
|
+
} else {
|
|
11638
|
+
config2 = applyConfigPatches(config2, flagPatches);
|
|
11639
|
+
const pageFiles = await glob5(`${pagesDir}/**/*.{md,mdx,tsx,jsx}`, {
|
|
11640
|
+
cwd: dir,
|
|
11641
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
11642
|
+
posix: true
|
|
11643
|
+
});
|
|
11644
|
+
const markdownFiles = pageFiles.filter((file) => /\.mdx?$/i.test(file));
|
|
11645
|
+
const tsxFiles = pageFiles.filter((file) => /\.(tsx|jsx)$/i.test(file));
|
|
11646
|
+
const markdownRoutes = markdownFiles.map(
|
|
11647
|
+
(file) => toRoutePath(file, pagesDir)
|
|
11648
|
+
);
|
|
11649
|
+
const tsxRoutes = tsxFiles.map((file) => toRoutePath(file, pagesDir));
|
|
11650
|
+
if (markdownRoutes.length === 0 && tsxRoutes.length === 0) {
|
|
11651
|
+
printDiagnosticsToConsole(
|
|
11652
|
+
`No docs pages under '${pagesDir}/' \u2014 skipping documentation nav scan.`
|
|
11478
11653
|
);
|
|
11479
11654
|
}
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
11485
|
-
posix: true
|
|
11486
|
-
});
|
|
11487
|
-
const markdownFiles = pageFiles.filter((file) => /\.mdx?$/i.test(file));
|
|
11488
|
-
const tsxFiles = pageFiles.filter((file) => /\.(tsx|jsx)$/i.test(file));
|
|
11489
|
-
const markdownRoutes = markdownFiles.map(
|
|
11490
|
-
(file) => toRoutePath(file, pagesDir)
|
|
11491
|
-
);
|
|
11492
|
-
const tsxRoutes = tsxFiles.map((file) => toRoutePath(file, pagesDir));
|
|
11493
|
-
if (markdownRoutes.length === 0 && tsxRoutes.length === 0) {
|
|
11494
|
-
printDiagnosticsToConsole(
|
|
11495
|
-
`No docs pages under '${pagesDir}/' \u2014 skipping documentation nav scan.`
|
|
11655
|
+
config2.navigation = mergeDocumentationNavigation(
|
|
11656
|
+
config2.navigation,
|
|
11657
|
+
markdownRoutes,
|
|
11658
|
+
{ force: argv.force === true }
|
|
11496
11659
|
);
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
{ force: argv.force === true }
|
|
11502
|
-
);
|
|
11503
|
-
const openApiRel = openApiOverride === void 0 ? discoveredOpenApi : openApiOverride;
|
|
11504
|
-
if (openApiRel) {
|
|
11505
|
-
const openApiInput = argv.interactive ? toRelativeOpenApiInput(dir, openApiRel) : openapiInputForConfig(openApiRel);
|
|
11506
|
-
config2 = applyOpenApiToJsonConfig(config2, openApiInput);
|
|
11507
|
-
}
|
|
11508
|
-
await writeFile7(jsonPath, `${JSON.stringify(config2, null, 2)}
|
|
11509
|
-
`, "utf8");
|
|
11510
|
-
const tsxPath = path36.join(dir, "apitogo.config.tsx");
|
|
11511
|
-
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
11512
|
-
const tsxSource = await readFile5(tsxPath, "utf8");
|
|
11513
|
-
if (isThinConfigWrapper(tsxSource)) {
|
|
11514
|
-
await writeFile7(
|
|
11515
|
-
tsxPath,
|
|
11516
|
-
buildThinWrapperWithCustomPages(tsxRoutes),
|
|
11517
|
-
"utf8"
|
|
11660
|
+
if (discoveredOpenApi) {
|
|
11661
|
+
config2 = applyOpenApiToJsonConfig(
|
|
11662
|
+
config2,
|
|
11663
|
+
openapiInputForConfig(discoveredOpenApi)
|
|
11518
11664
|
);
|
|
11519
11665
|
}
|
|
11666
|
+
await writeFile7(jsonPath, `${JSON.stringify(config2, null, 2)}
|
|
11667
|
+
`, "utf8");
|
|
11668
|
+
const tsxPath = path36.join(dir, "apitogo.config.tsx");
|
|
11669
|
+
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
11670
|
+
const tsxSource = await readFile5(tsxPath, "utf8");
|
|
11671
|
+
if (isThinConfigWrapper(tsxSource)) {
|
|
11672
|
+
await writeFile7(
|
|
11673
|
+
tsxPath,
|
|
11674
|
+
buildThinWrapperWithCustomPages(tsxRoutes),
|
|
11675
|
+
"utf8"
|
|
11676
|
+
);
|
|
11677
|
+
}
|
|
11678
|
+
}
|
|
11520
11679
|
}
|
|
11521
11680
|
const action = createdJson ? "Initialized" : "Updated";
|
|
11522
11681
|
printResultToConsole(
|
|
@@ -11527,7 +11686,7 @@ async function makeConfig(argv) {
|
|
|
11527
11686
|
"Next: run npm install (or pnpm install / yarn) in this directory, then npm run dev."
|
|
11528
11687
|
);
|
|
11529
11688
|
}
|
|
11530
|
-
if (
|
|
11689
|
+
if (interactive) {
|
|
11531
11690
|
printInteractiveWrapUp(interactiveHasPaidPlans);
|
|
11532
11691
|
}
|
|
11533
11692
|
} catch (error) {
|
|
@@ -11539,7 +11698,7 @@ async function makeConfig(argv) {
|
|
|
11539
11698
|
|
|
11540
11699
|
// src/cli/cmds/config.ts
|
|
11541
11700
|
var config_default = {
|
|
11542
|
-
desc: "Create or update apitogo.config.json
|
|
11701
|
+
desc: "Create or update apitogo.config.json (interactive on TTY: backend, OpenAPI, pages, plans)",
|
|
11543
11702
|
command: "config",
|
|
11544
11703
|
aliases: ["make-config"],
|
|
11545
11704
|
builder: (yargs2) => yargs2.option("dir", {
|
|
@@ -11555,8 +11714,7 @@ var config_default = {
|
|
|
11555
11714
|
}).option("interactive", {
|
|
11556
11715
|
alias: "i",
|
|
11557
11716
|
type: "boolean",
|
|
11558
|
-
describe: "Interactive wizard
|
|
11559
|
-
default: false
|
|
11717
|
+
describe: "Interactive wizard (default on TTY). Apply backend, OpenAPI, pages, and plans immediately. Use --no-interactive for CI/flags-only."
|
|
11560
11718
|
}).option("site", {
|
|
11561
11719
|
type: "string",
|
|
11562
11720
|
describe: "JSON object merged into site"
|
|
@@ -12023,10 +12181,11 @@ async function init2(argv) {
|
|
|
12023
12181
|
dir,
|
|
12024
12182
|
pagesDir: "pages",
|
|
12025
12183
|
title: argv.name,
|
|
12026
|
-
force: false
|
|
12184
|
+
force: false,
|
|
12185
|
+
interactive: false
|
|
12027
12186
|
});
|
|
12028
12187
|
printResultToConsole(
|
|
12029
|
-
`Portal scaffolded in ${dir}. Next: apitogo config
|
|
12188
|
+
`Portal scaffolded in ${dir}. Next: apitogo config, apitogo dev, then apitogo apply.`
|
|
12030
12189
|
);
|
|
12031
12190
|
} catch (error) {
|
|
12032
12191
|
await printCriticalFailureToConsoleAndExit(
|
package/docs/cli/commands.md
CHANGED
|
@@ -12,7 +12,7 @@ Commands are exposed by the **`apitogo`** binary (`@lukoweb/apitogo`). Platform
|
|
|
12
12
|
| Command | Role |
|
|
13
13
|
| ------- | ---- |
|
|
14
14
|
| `init` | Scaffold portal locally (no platform calls) |
|
|
15
|
-
| `config` / `make-config` | Write or update `apitogo.config.json` (interactive: OpenAPI, pages, plans) |
|
|
15
|
+
| `config` / `make-config` | Write or update `apitogo.config.json` (interactive: backend, OpenAPI, pages, plans) |
|
|
16
16
|
| `dev` | Local preview server |
|
|
17
17
|
| `build` | Production build (`--preview` to serve locally) |
|
|
18
18
|
| `apply` | Publish config to platform (apihub + portal) |
|
|
@@ -24,7 +24,7 @@ Commands are exposed by the **`apitogo`** binary (`@lukoweb/apitogo`). Platform
|
|
|
24
24
|
| Command | Role |
|
|
25
25
|
| ------- | ---- |
|
|
26
26
|
| `init [dir]` | Create `package.json`, stubs, and initial `apitogo.config.json` |
|
|
27
|
-
| `config` |
|
|
27
|
+
| `config` | Interactive on TTY: backend, OpenAPI, pages, plans (apply each immediately); flags for site/theme/auth |
|
|
28
28
|
|
|
29
29
|
`npm create @lukoweb/apitogo@latest` is an interactive alternative to `init` for new folders.
|
|
30
30
|
|
package/docs/cli/overview.md
CHANGED
|
@@ -14,7 +14,7 @@ local files, preview with `apitogo dev`, and publish to APItoGo hosting with `ap
|
|
|
14
14
|
## What you get
|
|
15
15
|
|
|
16
16
|
- Scaffold a local portal site (`npm create @lukoweb/apitogo@latest` or `apitogo init`)
|
|
17
|
-
- Shape OpenAPI, plans, docs pages
|
|
17
|
+
- Shape backend, OpenAPI, plans, and docs pages via `apitogo config` and file edits
|
|
18
18
|
- Preview locally (`apitogo dev`)
|
|
19
19
|
- Publish to APItoGo hosting (`apitogo apply`) with platform plans **Free**, **Starter**, or
|
|
20
20
|
**Professional**
|
|
@@ -47,10 +47,11 @@ Common CLI shape tasks:
|
|
|
47
47
|
|
|
48
48
|
| Change | How |
|
|
49
49
|
| ----------------------------------- | ------------------------------------------------ |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
50
|
+
| Backend runtime | `apitogo config` (backend) |
|
|
51
|
+
| OpenAPI + API Reference nav | `apitogo config` (openapi) |
|
|
52
|
+
| Custom docs pages | `apitogo config` (page) or add under `pages/` |
|
|
53
|
+
| Gateway / API plans | `apitogo config` (plan) or edit manifest |
|
|
54
|
+
| Site title | `--title` or edit `apitogo.config.json` |
|
|
54
55
|
| Theme, fonts, branding, projectId | Edit `apitogo.config.json` |
|
|
55
56
|
|
|
56
57
|
Home page content often lives in `landing/LandingPage.tsx`. Site footer can live under
|
package/docs/cli/setup.md
CHANGED
|
@@ -28,7 +28,7 @@ need a global install — scaffold commands add it to your project and `npm inst
|
|
|
28
28
|
```bash
|
|
29
29
|
npm create @lukoweb/apitogo@latest
|
|
30
30
|
cd your-project-name
|
|
31
|
-
npx apitogo config
|
|
31
|
+
npx apitogo config # interactive: backend, OpenAPI, pages, plans
|
|
32
32
|
npm run dev
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -40,7 +40,7 @@ Non-interactive example:
|
|
|
40
40
|
```bash
|
|
41
41
|
npm create @lukoweb/apitogo@latest my-portal -- --yes --typescript
|
|
42
42
|
cd my-portal
|
|
43
|
-
npx apitogo config
|
|
43
|
+
npx apitogo config
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
### Scaffold into an existing folder
|
|
@@ -52,18 +52,18 @@ before other `apitogo` commands:
|
|
|
52
52
|
npx @lukoweb/apitogo init ./dev-portal
|
|
53
53
|
cd ./dev-portal
|
|
54
54
|
npm install
|
|
55
|
-
npx apitogo config
|
|
55
|
+
npx apitogo config
|
|
56
56
|
npm run dev
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
`apitogo init` is local only — it does not call the platform. After `config
|
|
59
|
+
`apitogo init` is local only — it does not call the platform. After `config`, run `npm run dev`
|
|
60
60
|
to preview, then `apitogo apply` when ready to go live.
|
|
61
61
|
|
|
62
62
|
### How to run `apitogo`
|
|
63
63
|
|
|
64
64
|
| Situation | Command |
|
|
65
65
|
| --------- | ------- |
|
|
66
|
-
| After `npm create` or `npm install` in the project | `npx apitogo <command>` (e.g. `npx apitogo config
|
|
66
|
+
| After `npm create` or `npm install` in the project | `npx apitogo <command>` (e.g. `npx apitogo config`) |
|
|
67
67
|
| Same, if `node_modules/.bin` is on your PATH | `apitogo <command>` |
|
|
68
68
|
| One-off, no project yet | `npx @lukoweb/apitogo <command>` |
|
|
69
69
|
| Optional global install | `npm install -g @lukoweb/apitogo` then `apitogo <command>` anywhere |
|
package/docs/cli/shape.md
CHANGED
|
@@ -18,7 +18,7 @@ Requires the **`apitogo`** CLI (`@lukoweb/apitogo`). If you have not installed i
|
|
|
18
18
|
```bash
|
|
19
19
|
npm create @lukoweb/apitogo@latest my-portal -- --yes
|
|
20
20
|
cd my-portal
|
|
21
|
-
npx apitogo config
|
|
21
|
+
npx apitogo config # backend, OpenAPI, pages, plans (apply each immediately)
|
|
22
22
|
npm run dev # http://localhost:3000
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -28,18 +28,20 @@ Or scaffold into an existing folder (`npm install` is required after `init`):
|
|
|
28
28
|
npx @lukoweb/apitogo init ./dev-portal --name "Books API"
|
|
29
29
|
cd ./dev-portal
|
|
30
30
|
npm install
|
|
31
|
-
npx apitogo config
|
|
31
|
+
npx apitogo config
|
|
32
32
|
npm run dev
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
`apitogo init` does **not** call the platform. Run `apitogo apply` when you are ready to go live.
|
|
36
36
|
|
|
37
|
-
## Interactive config (`apitogo config
|
|
37
|
+
## Interactive config (`apitogo config`)
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
On a TTY, bare `apitogo config` opens the wizard. Each choice is written immediately; the loop
|
|
40
|
+
continues until you type `done`:
|
|
40
41
|
|
|
41
42
|
| Choice | What it does |
|
|
42
43
|
| ------ | ------------ |
|
|
44
|
+
| `backend` | Set `backend.sourceDirectory`, `runtime`, and platform `runtimeVersion` |
|
|
43
45
|
| `openapi` | Link an OpenAPI file by **relative path** in `apitogo.config.json` |
|
|
44
46
|
| `page` | Create `pages/<slug>.tsx` and wire it in navigation |
|
|
45
47
|
| `plan` | Add a gateway plan (title, price, rate limit, monthly quota) |
|
|
@@ -64,14 +66,14 @@ stubs when missing. Scans `pages/` for navigation and wires OpenAPI when a spec
|
|
|
64
66
|
npx apitogo config
|
|
65
67
|
npx apitogo config --title "Books API"
|
|
66
68
|
npx apitogo config --theme '{"light":{"primary":"#111111"}}'
|
|
67
|
-
npx apitogo config -
|
|
69
|
+
npx apitogo config --no-interactive
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
Common flags:
|
|
71
73
|
|
|
72
74
|
| Flag | Purpose |
|
|
73
75
|
| ---- | ------- |
|
|
74
|
-
| `--interactive` / `-i` |
|
|
76
|
+
| `--interactive` / `-i` | Force wizard (default on TTY). `--no-interactive` skips it |
|
|
75
77
|
| `--title` | Set `site.title` |
|
|
76
78
|
| `--theme`, `--site`, `--authentication` | Deep-merge JSON into config |
|
|
77
79
|
| `--pages-dir` | Folder to scan (default: `pages`) |
|
|
@@ -112,7 +114,7 @@ If you already have `pages/` and an OpenAPI spec:
|
|
|
112
114
|
```bash
|
|
113
115
|
npx @lukoweb/apitogo init .
|
|
114
116
|
npm install
|
|
115
|
-
npx apitogo config
|
|
117
|
+
npx apitogo config
|
|
116
118
|
npm run dev
|
|
117
119
|
```
|
|
118
120
|
|
|
@@ -30,7 +30,7 @@ Backend API URL (`apitogo apply` writes this to `.env` after the portal backend
|
|
|
30
30
|
VITE_APITOGO_AUTHENTICATION_API_BASE_URL=https://your-dev-portal-api.example.com
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
You do not need to enter this URL in `apitogo config
|
|
33
|
+
You do not need to enter this URL in `apitogo config`. Or set `authentication.apiBaseUrl` in JSON /
|
|
34
34
|
`APITOGO_CONFIG_AUTHENTICATION__API_BASE_URL` in `.env` manually.
|
|
35
35
|
|
|
36
36
|
### Local demo mode
|
|
@@ -10,7 +10,8 @@ description:
|
|
|
10
10
|
The **`apitogo config`** (alias: `make-config`) command writes **`apitogo.config.json`**. It can scaffold a portal
|
|
11
11
|
project (package.json, tsconfig, landing and user-panel stubs, thin `apitogo.config.tsx`) when those
|
|
12
12
|
files are missing, merge **site / theme / authentication** from flags, run an **interactive wizard**
|
|
13
|
-
|
|
13
|
+
on a TTY (apply each change immediately), scan `pages/` for navigation, and wire OpenAPI plus an
|
|
14
|
+
**API Reference** link.
|
|
14
15
|
|
|
15
16
|
Landing and user panel are **always enabled** on new configs. **Authentication is enabled by default**
|
|
16
17
|
on new scaffolds. The command does **not** change `redirects`.
|
|
@@ -34,31 +35,37 @@ Run config from the project root with `npx apitogo config` (or `apitogo config`
|
|
|
34
35
|
**`apitogo.config.tsx`** wrapper only if that file does not already exist (existing TSX is left
|
|
35
36
|
alone).
|
|
36
37
|
3. **Patches site, theme, and auth** from `--site`, `--theme`, `--authentication`, `--title`, or
|
|
37
|
-
|
|
38
|
+
the interactive wizard.
|
|
38
39
|
4. **Merges `navigation`** from `.md` / `.mdx` / `.tsx` files under your pages directory into a
|
|
39
40
|
Documentation category when those files exist. **Missing or empty `pages/` is fine** (common for
|
|
40
41
|
portal-only sites) — existing navigation is left alone. Other nav items are kept. Use
|
|
41
42
|
**`--force`** to rebuild Documentation items from disk when pages are present (API Reference is
|
|
42
43
|
still kept when OpenAPI is wired).
|
|
43
|
-
5. **Wires OpenAPI** when a spec is found (non-interactive) or when you enter a path in
|
|
44
|
+
5. **Wires OpenAPI** when a spec is found (non-interactive) or when you enter a path in the wizard:
|
|
44
45
|
`apis: [{ type: "file", input, path: "api" }]` and a top-level **API Reference** link to `/api` if
|
|
45
46
|
that link is missing. Interactive mode stores a **relative file path** in `apis[0].input` (for
|
|
46
47
|
example `./openapi.yaml` or `../api/openapi.yaml`).
|
|
47
|
-
6. **Interactive only** — can
|
|
48
|
-
|
|
48
|
+
6. **Interactive only** — can set **backend** (`sourceDirectory`, `runtime`, `runtimeVersion`),
|
|
49
|
+
scaffold **custom pages** under `pages/`, and append **gateway plans** to
|
|
50
|
+
`modules.userPanel.plans.items`. Each choice is written immediately.
|
|
49
51
|
|
|
50
|
-
## Interactive wizard
|
|
52
|
+
## Interactive wizard
|
|
53
|
+
|
|
54
|
+
On a TTY, bare `apitogo config` opens the wizard (no `-i` required). `-i` still works as an alias.
|
|
55
|
+
Use `--no-interactive` (or pass flags like `--title`) for CI / non-TTY.
|
|
51
56
|
|
|
52
57
|
```bash
|
|
53
|
-
npx apitogo config
|
|
58
|
+
npx apitogo config
|
|
54
59
|
```
|
|
55
60
|
|
|
56
|
-
1. **
|
|
57
|
-
|
|
61
|
+
1. **Loop** until you type `done` (default):
|
|
62
|
+
- **`backend`** — source directory, runtime (`dotnet` / `nodejs` / `python`), platform
|
|
63
|
+
`runtimeVersion` (writes `backend` in `apitogo.config.json` immediately)
|
|
58
64
|
- **`openapi`** — path to your OpenAPI file (relative link in config; empty skips)
|
|
59
|
-
- **`page`** — page title + URL path (writes `pages/<slug>.tsx` and wires navigation)
|
|
60
|
-
- **`plan`** — plan title, price (USD/month), rate limit (calls/min), monthly quota
|
|
61
|
-
|
|
65
|
+
- **`page`** — page title + URL path (writes `pages/<slug>.tsx` and wires navigation immediately)
|
|
66
|
+
- **`plan`** — plan title, price (USD/month), rate limit (calls/min), monthly quota (writes
|
|
67
|
+
immediately)
|
|
68
|
+
2. **Wrap-up** — links to [docs.apitogo.com](https://docs.apitogo.com) for further configuration
|
|
62
69
|
|
|
63
70
|
The wizard does **not** ask for fonts, colors, logo alt text, or the auth API base URL. Authentication
|
|
64
71
|
stays **enabled**; `apitogo apply` writes `VITE_APITOGO_AUTHENTICATION_API_BASE_URL` to `.env` after
|
|
@@ -83,8 +90,8 @@ npx apitogo config
|
|
|
83
90
|
| ------------------------- | --------------------------------------------------------------------------- |
|
|
84
91
|
| `--pages-dir <dir>` | Folder to scan for `.md` / `.mdx` / `.tsx` (default: `pages`) |
|
|
85
92
|
| `--dir <path>` | Project root (default: current directory) |
|
|
86
|
-
| `--interactive` / `-i` |
|
|
87
|
-
| `--title <string>` | Set `site.title`
|
|
93
|
+
| `--interactive` / `-i` | Force the wizard (default on TTY). Use `--no-interactive` to skip. |
|
|
94
|
+
| `--title <string>` | Set `site.title` (non-interactive) |
|
|
88
95
|
| `--site <json>` | Deep-merge a JSON object into `site` |
|
|
89
96
|
| `--theme <json>` | Deep-merge a JSON object into `theme` |
|
|
90
97
|
| `--authentication <json>` | Deep-merge a JSON object into `authentication` (secrets stay in `.env`) |
|
|
@@ -93,9 +100,10 @@ npx apitogo config
|
|
|
93
100
|
Examples:
|
|
94
101
|
|
|
95
102
|
```bash
|
|
103
|
+
npx apitogo config
|
|
96
104
|
npx apitogo config --title "Books API"
|
|
97
105
|
npx apitogo config --theme '{"light":{"primary":"#111111"}}'
|
|
98
|
-
npx apitogo config -
|
|
106
|
+
npx apitogo config --no-interactive
|
|
99
107
|
```
|
|
100
108
|
|
|
101
109
|
Theme and fonts are edited in `apitogo.config.json` or via MCP — not in the interactive wizard. See
|
|
@@ -57,7 +57,7 @@ metadata, with optional `.env` overrides for secrets and local tweaks.
|
|
|
57
57
|
- **Site name / logo:** only `site.title` and `site.logo` (platform branding is derived at publish).
|
|
58
58
|
- **OpenAPI path:** `backend.openApiPath` is derived from `apis[0].input`.
|
|
59
59
|
- **Billing / account plans:** use `modules.userPanel.plans.enabled` and store the plan catalog in
|
|
60
|
-
`modules.userPanel.plans.items`. Add plans with `apitogo config
|
|
60
|
+
`modules.userPanel.plans.items`. Add plans with `apitogo config` or edit the manifest by hand.
|
|
61
61
|
See [Gateway plans](./plans.md). Legacy root `plans` is migrated on load.
|
|
62
62
|
- **Docs / API keys:** configure under `modules.docs` and `modules.userPanel.apiKeys` only.
|
|
63
63
|
|
|
@@ -160,7 +160,7 @@ Put navigation in `apitogo.config.json`, or keep large trees in a separate modul
|
|
|
160
160
|
## Related
|
|
161
161
|
|
|
162
162
|
- [Manifest (`apitogo.config.json`)](./manifest.md)
|
|
163
|
-
- [Make config (CLI)](./make-config.mdx) — interactive wizard (`apitogo config
|
|
163
|
+
- [Make config (CLI)](./make-config.mdx) — interactive wizard (`apitogo config`)
|
|
164
164
|
- [Gateway plans](./plans.md)
|
|
165
165
|
- [Authentication](./authentication.md)
|
|
166
166
|
- [MCP overview](../mcp/overview.md)
|
|
@@ -14,12 +14,12 @@ Store the catalog under `modules.userPanel.plans.items` in `apitogo.config.json`
|
|
|
14
14
|
|
|
15
15
|
## Shape with the CLI
|
|
16
16
|
|
|
17
|
-
`apitogo config
|
|
18
|
-
also edit the manifest by hand or use MCP `update_dev_portal_manifest`.
|
|
17
|
+
`apitogo config` can add plans interactively (title, price, rate limit, monthly quota). Each plan is
|
|
18
|
+
written immediately. You can also edit the manifest by hand or use MCP `update_dev_portal_manifest`.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx apitogo config
|
|
22
|
-
#
|
|
21
|
+
npx apitogo config
|
|
22
|
+
# choose plan → enter title, price, policies → done when finished
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Canonical shape
|
|
@@ -13,8 +13,8 @@ Start by creating the page you want to add.
|
|
|
13
13
|
|
|
14
14
|
## CLI scaffold
|
|
15
15
|
|
|
16
|
-
`apitogo config
|
|
17
|
-
`page` in the wizard loop, then edit the generated `.tsx` file.
|
|
16
|
+
`apitogo config` can create a starter page under `pages/` and wire it into navigation immediately.
|
|
17
|
+
Choose `page` in the wizard loop, then edit the generated `.tsx` file.
|
|
18
18
|
|
|
19
19
|
Predefined UI components (Callout, Card, Button, Heading, and more) are documented under
|
|
20
20
|
[/docs/components](/docs/components).
|