@lukoweb/apitogo 0.1.75 → 0.1.76
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 +692 -422
- package/docs/configuration/make-config.mdx +42 -64
- package/docs/configuration/navigation.mdx +5 -6
- package/docs/quickstart.md +2 -3
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -4940,12 +4940,12 @@ async function loadZudokuConfigWithMeta(rootDir) {
|
|
|
4940
4940
|
};
|
|
4941
4941
|
return configWithMetadata;
|
|
4942
4942
|
}
|
|
4943
|
-
function findOutputPathOfServerConfig(
|
|
4944
|
-
if (Array.isArray(
|
|
4943
|
+
function findOutputPathOfServerConfig(output2) {
|
|
4944
|
+
if (Array.isArray(output2)) {
|
|
4945
4945
|
throw new Error("Expected a single output, but got an array");
|
|
4946
4946
|
}
|
|
4947
|
-
if ("output" in
|
|
4948
|
-
const result =
|
|
4947
|
+
if ("output" in output2) {
|
|
4948
|
+
const result = output2.output.find(
|
|
4949
4949
|
(o) => "isEntry" in o && o.isEntry && serverConfigOutputFiles.includes(o.fileName)
|
|
4950
4950
|
);
|
|
4951
4951
|
if (result) {
|
|
@@ -5644,7 +5644,7 @@ import {
|
|
|
5644
5644
|
// package.json
|
|
5645
5645
|
var package_default = {
|
|
5646
5646
|
name: "@lukoweb/apitogo",
|
|
5647
|
-
version: "0.1.
|
|
5647
|
+
version: "0.1.76",
|
|
5648
5648
|
type: "module",
|
|
5649
5649
|
sideEffects: [
|
|
5650
5650
|
"**/*.css",
|
|
@@ -7465,13 +7465,13 @@ var generateCode = (schema2, filePath) => {
|
|
|
7465
7465
|
|
|
7466
7466
|
// src/vite/api/SchemaManager.ts
|
|
7467
7467
|
var FALLBACK_VERSION = "default";
|
|
7468
|
-
var parseInputString = (
|
|
7469
|
-
const idx =
|
|
7468
|
+
var parseInputString = (input2) => {
|
|
7469
|
+
const idx = input2.indexOf("?");
|
|
7470
7470
|
if (idx === -1)
|
|
7471
|
-
return { filePath:
|
|
7471
|
+
return { filePath: input2, params: {} };
|
|
7472
7472
|
return {
|
|
7473
|
-
filePath:
|
|
7474
|
-
params: Object.fromEntries(new URLSearchParams(
|
|
7473
|
+
filePath: input2.slice(0, idx),
|
|
7474
|
+
params: Object.fromEntries(new URLSearchParams(input2.slice(idx)))
|
|
7475
7475
|
};
|
|
7476
7476
|
};
|
|
7477
7477
|
var canonicalParams = (params) => Object.entries(params).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join("&");
|
|
@@ -7507,8 +7507,8 @@ var SchemaManager = class {
|
|
|
7507
7507
|
...processors
|
|
7508
7508
|
];
|
|
7509
7509
|
}
|
|
7510
|
-
getPathForFile = (
|
|
7511
|
-
const filePath = path11.resolve(this.config.__meta.rootDir,
|
|
7510
|
+
getPathForFile = (input2, params) => {
|
|
7511
|
+
const filePath = path11.resolve(this.config.__meta.rootDir, input2);
|
|
7512
7512
|
const apis = ensureArray(this.config.apis ?? []);
|
|
7513
7513
|
for (const apiConfig of apis) {
|
|
7514
7514
|
if (!apiConfig || apiConfig.type !== "file" || !apiConfig.path) continue;
|
|
@@ -7518,12 +7518,12 @@ var SchemaManager = class {
|
|
|
7518
7518
|
if (match) return apiConfig.path;
|
|
7519
7519
|
}
|
|
7520
7520
|
};
|
|
7521
|
-
processSchema = async (
|
|
7522
|
-
const filePath = path11.resolve(this.config.__meta.rootDir,
|
|
7523
|
-
const params =
|
|
7524
|
-
const configuredPath = this.getPathForFile(
|
|
7521
|
+
processSchema = async (input2) => {
|
|
7522
|
+
const filePath = path11.resolve(this.config.__meta.rootDir, input2.input);
|
|
7523
|
+
const params = input2.params;
|
|
7524
|
+
const configuredPath = this.getPathForFile(input2.input, params);
|
|
7525
7525
|
if (!configuredPath) {
|
|
7526
|
-
console.warn(`No path found for file ${
|
|
7526
|
+
console.warn(`No path found for file ${input2.input}`);
|
|
7527
7527
|
return;
|
|
7528
7528
|
}
|
|
7529
7529
|
const parser = new $RefParser2();
|
|
@@ -7630,20 +7630,20 @@ var SchemaManager = class {
|
|
|
7630
7630
|
if (apiConfig.type !== "file" || !apiConfig.path) continue;
|
|
7631
7631
|
const inputs = normalizeInputs(apiConfig.input);
|
|
7632
7632
|
if (inputs.length === 0) throw new Error("No schema found");
|
|
7633
|
-
this.processedSchemas[apiConfig.path] = inputs.map((
|
|
7633
|
+
this.processedSchemas[apiConfig.path] = inputs.map((input2) => ({
|
|
7634
7634
|
schema: {},
|
|
7635
7635
|
version: "",
|
|
7636
|
-
path:
|
|
7637
|
-
label:
|
|
7638
|
-
inputPath: path11.resolve(this.config.__meta.rootDir,
|
|
7639
|
-
params:
|
|
7636
|
+
path: input2.path ?? "",
|
|
7637
|
+
label: input2.label,
|
|
7638
|
+
inputPath: path11.resolve(this.config.__meta.rootDir, input2.input),
|
|
7639
|
+
params: input2.params,
|
|
7640
7640
|
importKey: "",
|
|
7641
7641
|
downloadUrl: "",
|
|
7642
7642
|
processedJsonPath: "",
|
|
7643
7643
|
processedTime: 0
|
|
7644
7644
|
}));
|
|
7645
7645
|
const results = await Promise.allSettled(
|
|
7646
|
-
inputs.map((
|
|
7646
|
+
inputs.map((input2) => this.processSchema(input2))
|
|
7647
7647
|
);
|
|
7648
7648
|
const errors = results.flatMap(
|
|
7649
7649
|
(r) => r.status === "rejected" ? r.reason : []
|
|
@@ -7722,7 +7722,7 @@ import { readFile } from "node:fs/promises";
|
|
|
7722
7722
|
import matter from "gray-matter";
|
|
7723
7723
|
import { parse, stringify as stringify2 } from "yaml";
|
|
7724
7724
|
var yaml = {
|
|
7725
|
-
parse: (
|
|
7725
|
+
parse: (input2) => parse(input2) ?? {},
|
|
7726
7726
|
stringify: (obj) => stringify2(obj)
|
|
7727
7727
|
};
|
|
7728
7728
|
var readFrontmatter = async (filePath) => {
|
|
@@ -9938,25 +9938,25 @@ function generateOutput({
|
|
|
9938
9938
|
});
|
|
9939
9939
|
}
|
|
9940
9940
|
}
|
|
9941
|
-
const
|
|
9941
|
+
const output2 = {
|
|
9942
9942
|
version: 3,
|
|
9943
9943
|
framework: {
|
|
9944
9944
|
version: pkgJson.version
|
|
9945
9945
|
},
|
|
9946
9946
|
routes
|
|
9947
9947
|
};
|
|
9948
|
-
return
|
|
9948
|
+
return output2;
|
|
9949
9949
|
}
|
|
9950
9950
|
async function writeOutput(dir, {
|
|
9951
9951
|
config: config2,
|
|
9952
9952
|
redirects,
|
|
9953
9953
|
rewrites
|
|
9954
9954
|
}) {
|
|
9955
|
-
const
|
|
9955
|
+
const output2 = generateOutput({ config: config2, redirects, rewrites });
|
|
9956
9956
|
const outputDir = process.env.VERCEL ? path26.join(dir, ".vercel/output") : path26.join(dir, "dist/.output");
|
|
9957
9957
|
await mkdir3(outputDir, { recursive: true });
|
|
9958
9958
|
const outputFile = path26.join(outputDir, "config.json");
|
|
9959
|
-
await writeFile3(outputFile, JSON.stringify(
|
|
9959
|
+
await writeFile3(outputFile, JSON.stringify(output2, null, 2), "utf-8");
|
|
9960
9960
|
if (process.env.VERCEL) {
|
|
9961
9961
|
console.log("Wrote Vercel output to", outputDir);
|
|
9962
9962
|
}
|
|
@@ -9977,12 +9977,12 @@ init_invariant();
|
|
|
9977
9977
|
init_joinUrl();
|
|
9978
9978
|
|
|
9979
9979
|
// src/vite/reporter.ts
|
|
9980
|
-
function writeLine(
|
|
9980
|
+
function writeLine(output2) {
|
|
9981
9981
|
clearLine();
|
|
9982
|
-
if (
|
|
9983
|
-
process.stdout.write(
|
|
9982
|
+
if (output2.length < process.stdout.columns) {
|
|
9983
|
+
process.stdout.write(output2);
|
|
9984
9984
|
} else {
|
|
9985
|
-
process.stdout.write(
|
|
9985
|
+
process.stdout.write(output2.substring(0, process.stdout.columns - 1));
|
|
9986
9986
|
}
|
|
9987
9987
|
}
|
|
9988
9988
|
function isTTY() {
|
|
@@ -11240,14 +11240,473 @@ var dev_default = {
|
|
|
11240
11240
|
|
|
11241
11241
|
// src/cli/make-config/handler.ts
|
|
11242
11242
|
import { constants as constants3 } from "node:fs";
|
|
11243
|
-
import { access as access5, mkdir as
|
|
11243
|
+
import { access as access5, mkdir as mkdir8, readFile as readFile6, writeFile as writeFile8 } from "node:fs/promises";
|
|
11244
11244
|
import path39 from "node:path";
|
|
11245
11245
|
import { glob as glob5 } from "glob";
|
|
11246
11246
|
|
|
11247
|
+
// src/cli/make-config/interactive.ts
|
|
11248
|
+
import { createInterface } from "node:readline/promises";
|
|
11249
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
11250
|
+
var ask = async (rl, question, defaultValue) => {
|
|
11251
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
11252
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
11253
|
+
return answer || defaultValue || "";
|
|
11254
|
+
};
|
|
11255
|
+
var askYesNo = async (rl, question, defaultYes) => {
|
|
11256
|
+
const hint = defaultYes ? "Y/n" : "y/N";
|
|
11257
|
+
const answer = (await rl.question(`${question} (${hint}): `)).trim().toLowerCase();
|
|
11258
|
+
if (!answer) {
|
|
11259
|
+
return defaultYes;
|
|
11260
|
+
}
|
|
11261
|
+
return answer === "y" || answer === "yes";
|
|
11262
|
+
};
|
|
11263
|
+
async function promptMakeConfigInteractive(defaults) {
|
|
11264
|
+
const rl = createInterface({ input, output });
|
|
11265
|
+
try {
|
|
11266
|
+
const title = await ask(rl, "Site title", defaults.title);
|
|
11267
|
+
const logoAlt = await ask(rl, "Logo alt text", defaults.logoAlt);
|
|
11268
|
+
const authEnabled = await askYesNo(
|
|
11269
|
+
rl,
|
|
11270
|
+
"Enable authentication",
|
|
11271
|
+
defaults.authEnabled
|
|
11272
|
+
);
|
|
11273
|
+
let apiBaseUrl = defaults.apiBaseUrl ?? "";
|
|
11274
|
+
if (authEnabled) {
|
|
11275
|
+
apiBaseUrl = await ask(
|
|
11276
|
+
rl,
|
|
11277
|
+
"Auth API base URL (optional; secrets stay in .env)",
|
|
11278
|
+
apiBaseUrl
|
|
11279
|
+
);
|
|
11280
|
+
}
|
|
11281
|
+
const sans = await ask(rl, "Sans font", defaults.sans ?? "IBM Plex Sans");
|
|
11282
|
+
const mono = await ask(
|
|
11283
|
+
rl,
|
|
11284
|
+
"Mono font",
|
|
11285
|
+
defaults.mono ?? "JetBrains Mono"
|
|
11286
|
+
);
|
|
11287
|
+
const lightPrimary = await ask(
|
|
11288
|
+
rl,
|
|
11289
|
+
"Light primary color",
|
|
11290
|
+
defaults.lightPrimary ?? "#E11D2A"
|
|
11291
|
+
);
|
|
11292
|
+
const darkPrimary = await ask(
|
|
11293
|
+
rl,
|
|
11294
|
+
"Dark primary color",
|
|
11295
|
+
defaults.darkPrimary ?? "#F23645"
|
|
11296
|
+
);
|
|
11297
|
+
let openApiPath;
|
|
11298
|
+
if (defaults.discoveredOpenApi) {
|
|
11299
|
+
const useDiscovered = await askYesNo(
|
|
11300
|
+
rl,
|
|
11301
|
+
`Wire OpenAPI at ${defaults.discoveredOpenApi}`,
|
|
11302
|
+
true
|
|
11303
|
+
);
|
|
11304
|
+
openApiPath = useDiscovered ? defaults.discoveredOpenApi : null;
|
|
11305
|
+
} else {
|
|
11306
|
+
const entered = await ask(
|
|
11307
|
+
rl,
|
|
11308
|
+
"OpenAPI file path (leave empty to skip)",
|
|
11309
|
+
""
|
|
11310
|
+
);
|
|
11311
|
+
openApiPath = entered || null;
|
|
11312
|
+
}
|
|
11313
|
+
const authentication = {
|
|
11314
|
+
enabled: authEnabled,
|
|
11315
|
+
type: "dev-portal"
|
|
11316
|
+
};
|
|
11317
|
+
if (apiBaseUrl) {
|
|
11318
|
+
authentication.apiBaseUrl = apiBaseUrl;
|
|
11319
|
+
}
|
|
11320
|
+
return {
|
|
11321
|
+
title,
|
|
11322
|
+
site: {
|
|
11323
|
+
title,
|
|
11324
|
+
logo: { alt: logoAlt }
|
|
11325
|
+
},
|
|
11326
|
+
theme: {
|
|
11327
|
+
fonts: { sans, mono },
|
|
11328
|
+
light: { primary: lightPrimary },
|
|
11329
|
+
dark: { primary: darkPrimary }
|
|
11330
|
+
},
|
|
11331
|
+
authentication,
|
|
11332
|
+
openApiPath
|
|
11333
|
+
};
|
|
11334
|
+
} finally {
|
|
11335
|
+
rl.close();
|
|
11336
|
+
}
|
|
11337
|
+
}
|
|
11338
|
+
|
|
11339
|
+
// src/cli/make-config/json-config.ts
|
|
11340
|
+
var APITOGO_JSON_FILENAME = "apitogo.config.json";
|
|
11341
|
+
var DOCUMENTATION_NAV_LABEL = "Documentation";
|
|
11342
|
+
var API_REFERENCE_PATH = "/api";
|
|
11343
|
+
var API_REFERENCE_LABEL = "API Reference";
|
|
11344
|
+
function isPlainObject2(value) {
|
|
11345
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11346
|
+
}
|
|
11347
|
+
function deepMerge(target, source) {
|
|
11348
|
+
const result = { ...target };
|
|
11349
|
+
for (const [key, value] of Object.entries(source)) {
|
|
11350
|
+
const existing = result[key];
|
|
11351
|
+
if (isPlainObject2(existing) && isPlainObject2(value)) {
|
|
11352
|
+
result[key] = deepMerge(existing, value);
|
|
11353
|
+
} else {
|
|
11354
|
+
result[key] = value;
|
|
11355
|
+
}
|
|
11356
|
+
}
|
|
11357
|
+
return result;
|
|
11358
|
+
}
|
|
11359
|
+
function parseJsonObjectFlag(raw, flagName) {
|
|
11360
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
11361
|
+
return void 0;
|
|
11362
|
+
}
|
|
11363
|
+
let parsed;
|
|
11364
|
+
try {
|
|
11365
|
+
parsed = JSON.parse(raw);
|
|
11366
|
+
} catch {
|
|
11367
|
+
throw new Error(`--${flagName} must be a JSON object.`);
|
|
11368
|
+
}
|
|
11369
|
+
if (!isPlainObject2(parsed)) {
|
|
11370
|
+
throw new Error(`--${flagName} must be a JSON object.`);
|
|
11371
|
+
}
|
|
11372
|
+
return parsed;
|
|
11373
|
+
}
|
|
11374
|
+
function titleFromDirectoryName(dirName) {
|
|
11375
|
+
const titled = dirName.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11376
|
+
return titled || "Developer Portal";
|
|
11377
|
+
}
|
|
11378
|
+
function createDefaultApitogoJsonConfig(options) {
|
|
11379
|
+
const filesGlob = `/${options.pagesDir}/**/*.{md,mdx}`;
|
|
11380
|
+
return {
|
|
11381
|
+
site: {
|
|
11382
|
+
title: options.title,
|
|
11383
|
+
logo: {
|
|
11384
|
+
src: { light: "/logo-light.png", dark: "/logo-dark.png" },
|
|
11385
|
+
alt: "APIToGo",
|
|
11386
|
+
width: "130px"
|
|
11387
|
+
}
|
|
11388
|
+
},
|
|
11389
|
+
authentication: {
|
|
11390
|
+
enabled: false,
|
|
11391
|
+
type: "dev-portal",
|
|
11392
|
+
redirectToAfterSignIn: "/account/dashboard",
|
|
11393
|
+
redirectToAfterSignUp: "/account/dashboard"
|
|
11394
|
+
},
|
|
11395
|
+
metadata: {
|
|
11396
|
+
favicon: "/Icon.png"
|
|
11397
|
+
},
|
|
11398
|
+
theme: {
|
|
11399
|
+
fonts: {
|
|
11400
|
+
sans: "IBM Plex Sans",
|
|
11401
|
+
mono: "JetBrains Mono"
|
|
11402
|
+
},
|
|
11403
|
+
light: {
|
|
11404
|
+
background: "#F5F6F9",
|
|
11405
|
+
foreground: "#0E1016",
|
|
11406
|
+
card: "#FFFFFF",
|
|
11407
|
+
cardForeground: "#0E1016",
|
|
11408
|
+
primary: "#E11D2A",
|
|
11409
|
+
primaryForeground: "#ffffff",
|
|
11410
|
+
secondary: "#FAFBFC",
|
|
11411
|
+
secondaryForeground: "#0E1016",
|
|
11412
|
+
muted: "#FAFBFC",
|
|
11413
|
+
mutedForeground: "#565E70",
|
|
11414
|
+
accent: "rgba(225, 29, 42, 0.08)",
|
|
11415
|
+
accentForeground: "#E11D2A",
|
|
11416
|
+
border: "#E6E8EF",
|
|
11417
|
+
input: "#D6DAE3",
|
|
11418
|
+
ring: "#E11D2A"
|
|
11419
|
+
},
|
|
11420
|
+
dark: {
|
|
11421
|
+
background: "#0B0D12",
|
|
11422
|
+
foreground: "#EDEFF5",
|
|
11423
|
+
card: "#14171F",
|
|
11424
|
+
cardForeground: "#EDEFF5",
|
|
11425
|
+
primary: "#F23645",
|
|
11426
|
+
primaryForeground: "#ffffff",
|
|
11427
|
+
secondary: "#191D27",
|
|
11428
|
+
secondaryForeground: "#EDEFF5",
|
|
11429
|
+
muted: "#191D27",
|
|
11430
|
+
mutedForeground: "#A6AEBF",
|
|
11431
|
+
accent: "rgba(242, 54, 69, 0.13)",
|
|
11432
|
+
accentForeground: "#F23645",
|
|
11433
|
+
border: "#252B37",
|
|
11434
|
+
input: "#333A48",
|
|
11435
|
+
ring: "#F23645"
|
|
11436
|
+
}
|
|
11437
|
+
},
|
|
11438
|
+
modules: {
|
|
11439
|
+
docs: {
|
|
11440
|
+
files: filesGlob
|
|
11441
|
+
},
|
|
11442
|
+
landing: {
|
|
11443
|
+
enabled: true,
|
|
11444
|
+
path: "/",
|
|
11445
|
+
layout: "landing",
|
|
11446
|
+
page: "./landing/LandingPage.tsx"
|
|
11447
|
+
},
|
|
11448
|
+
userPanel: {
|
|
11449
|
+
enabled: true,
|
|
11450
|
+
dashboard: { enabled: true },
|
|
11451
|
+
userManagement: { enabled: true },
|
|
11452
|
+
plans: {
|
|
11453
|
+
enabled: false,
|
|
11454
|
+
page: "./user-panel/AccountPlansPage.tsx"
|
|
11455
|
+
}
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
};
|
|
11459
|
+
}
|
|
11460
|
+
function applyConfigPatches(config2, patches) {
|
|
11461
|
+
let next = { ...config2 };
|
|
11462
|
+
if (patches.site) {
|
|
11463
|
+
next.site = deepMerge(
|
|
11464
|
+
isPlainObject2(next.site) ? next.site : {},
|
|
11465
|
+
patches.site
|
|
11466
|
+
);
|
|
11467
|
+
}
|
|
11468
|
+
if (patches.theme) {
|
|
11469
|
+
next.theme = deepMerge(
|
|
11470
|
+
isPlainObject2(next.theme) ? next.theme : {},
|
|
11471
|
+
patches.theme
|
|
11472
|
+
);
|
|
11473
|
+
}
|
|
11474
|
+
if (patches.authentication) {
|
|
11475
|
+
next.authentication = deepMerge(
|
|
11476
|
+
isPlainObject2(next.authentication) ? next.authentication : {},
|
|
11477
|
+
patches.authentication
|
|
11478
|
+
);
|
|
11479
|
+
}
|
|
11480
|
+
if (patches.title?.trim()) {
|
|
11481
|
+
const site = isPlainObject2(next.site) ? next.site : {};
|
|
11482
|
+
next.site = { ...site, title: patches.title.trim() };
|
|
11483
|
+
}
|
|
11484
|
+
return next;
|
|
11485
|
+
}
|
|
11486
|
+
var docSortRank = (docPath) => {
|
|
11487
|
+
const lastSegment = docPath.split("/").at(-1) ?? docPath;
|
|
11488
|
+
if (lastSegment === "introduction") return 0;
|
|
11489
|
+
if (lastSegment === "example") return 1;
|
|
11490
|
+
if (lastSegment === "getting-started") return 2;
|
|
11491
|
+
if (lastSegment === "index") return 3;
|
|
11492
|
+
return 4;
|
|
11493
|
+
};
|
|
11494
|
+
var sortDocPaths = (docPaths) => [...docPaths].sort((a, b) => {
|
|
11495
|
+
const rankDiff = docSortRank(a) - docSortRank(b);
|
|
11496
|
+
if (rankDiff !== 0) return rankDiff;
|
|
11497
|
+
return a.localeCompare(b);
|
|
11498
|
+
});
|
|
11499
|
+
var createTreeNode = () => ({ docs: [], children: /* @__PURE__ */ new Map() });
|
|
11500
|
+
var titleCase = (value) => value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11501
|
+
var buildTree = (routePaths) => {
|
|
11502
|
+
const root = createTreeNode();
|
|
11503
|
+
for (const routePath of routePaths) {
|
|
11504
|
+
const segments = routePath.replace(/^\//, "").split("/").filter(Boolean);
|
|
11505
|
+
const directorySegments = segments.slice(0, -1);
|
|
11506
|
+
let current = root;
|
|
11507
|
+
for (const segment of directorySegments) {
|
|
11508
|
+
let child = current.children.get(segment);
|
|
11509
|
+
if (!child) {
|
|
11510
|
+
child = createTreeNode();
|
|
11511
|
+
current.children.set(segment, child);
|
|
11512
|
+
}
|
|
11513
|
+
current = child;
|
|
11514
|
+
}
|
|
11515
|
+
current.docs.push(routePath);
|
|
11516
|
+
}
|
|
11517
|
+
return root;
|
|
11518
|
+
};
|
|
11519
|
+
var treeToNavigationItems = (node) => {
|
|
11520
|
+
const docs = sortDocPaths(node.docs);
|
|
11521
|
+
const categories = [...node.children.keys()].sort((a, b) => a.localeCompare(b)).map((directoryName) => {
|
|
11522
|
+
const childNode = node.children.get(directoryName);
|
|
11523
|
+
if (!childNode) {
|
|
11524
|
+
throw new Error(`Missing child node for directory '${directoryName}'.`);
|
|
11525
|
+
}
|
|
11526
|
+
return {
|
|
11527
|
+
type: "category",
|
|
11528
|
+
label: titleCase(directoryName),
|
|
11529
|
+
items: treeToNavigationItems(childNode)
|
|
11530
|
+
};
|
|
11531
|
+
});
|
|
11532
|
+
return [...docs, ...categories];
|
|
11533
|
+
};
|
|
11534
|
+
function buildDocumentationItems(routePaths) {
|
|
11535
|
+
return treeToNavigationItems(buildTree(sortDocPaths(routePaths)));
|
|
11536
|
+
}
|
|
11537
|
+
function navItemKey(item) {
|
|
11538
|
+
if (typeof item === "string") {
|
|
11539
|
+
return `path:${item}`;
|
|
11540
|
+
}
|
|
11541
|
+
if (typeof item.to === "string") {
|
|
11542
|
+
return `to:${item.to}`;
|
|
11543
|
+
}
|
|
11544
|
+
if (typeof item.path === "string") {
|
|
11545
|
+
return `path:${item.path}`;
|
|
11546
|
+
}
|
|
11547
|
+
if (item.type === "category" && typeof item.label === "string") {
|
|
11548
|
+
return `category:${item.label}`;
|
|
11549
|
+
}
|
|
11550
|
+
return JSON.stringify(item);
|
|
11551
|
+
}
|
|
11552
|
+
function mergeNavItems(existing, incoming) {
|
|
11553
|
+
const seen = new Set(existing.map(navItemKey));
|
|
11554
|
+
const merged = [...existing];
|
|
11555
|
+
for (const item of incoming) {
|
|
11556
|
+
const key = navItemKey(item);
|
|
11557
|
+
if (seen.has(key)) {
|
|
11558
|
+
continue;
|
|
11559
|
+
}
|
|
11560
|
+
seen.add(key);
|
|
11561
|
+
merged.push(item);
|
|
11562
|
+
}
|
|
11563
|
+
return merged;
|
|
11564
|
+
}
|
|
11565
|
+
function isDocumentationCategory(item) {
|
|
11566
|
+
return typeof item === "object" && item.type === "category" && item.label === DOCUMENTATION_NAV_LABEL;
|
|
11567
|
+
}
|
|
11568
|
+
function isApiReferenceLink(item) {
|
|
11569
|
+
return typeof item === "object" && item.type === "link" && item.to === API_REFERENCE_PATH;
|
|
11570
|
+
}
|
|
11571
|
+
function mergeDocumentationNavigation(navigation, docRoutePaths, options) {
|
|
11572
|
+
const existing = Array.isArray(navigation) ? navigation : [];
|
|
11573
|
+
const generated = buildDocumentationItems(docRoutePaths);
|
|
11574
|
+
const rest = existing.filter((item) => !isDocumentationCategory(item));
|
|
11575
|
+
const currentDocs = existing.find(isDocumentationCategory);
|
|
11576
|
+
const currentItems = Array.isArray(currentDocs?.items) ? currentDocs.items : [];
|
|
11577
|
+
const items = options.force ? generated : mergeNavItems(currentItems, generated);
|
|
11578
|
+
const documentation = {
|
|
11579
|
+
type: "category",
|
|
11580
|
+
label: DOCUMENTATION_NAV_LABEL,
|
|
11581
|
+
items
|
|
11582
|
+
};
|
|
11583
|
+
if (generated.length === 0 && currentItems.length === 0) {
|
|
11584
|
+
return existing;
|
|
11585
|
+
}
|
|
11586
|
+
if (currentDocs) {
|
|
11587
|
+
const index = existing.findIndex(isDocumentationCategory);
|
|
11588
|
+
const next = [...existing];
|
|
11589
|
+
next[index] = documentation;
|
|
11590
|
+
return next;
|
|
11591
|
+
}
|
|
11592
|
+
return [documentation, ...rest];
|
|
11593
|
+
}
|
|
11594
|
+
function applyOpenApiToJsonConfig(config2, input2) {
|
|
11595
|
+
const next = { ...config2 };
|
|
11596
|
+
next.apis = [
|
|
11597
|
+
{
|
|
11598
|
+
type: "file",
|
|
11599
|
+
input: input2,
|
|
11600
|
+
path: "api"
|
|
11601
|
+
}
|
|
11602
|
+
];
|
|
11603
|
+
const navigation = Array.isArray(next.navigation) ? [...next.navigation] : [];
|
|
11604
|
+
if (!navigation.some(isApiReferenceLink)) {
|
|
11605
|
+
navigation.push({
|
|
11606
|
+
type: "link",
|
|
11607
|
+
to: API_REFERENCE_PATH,
|
|
11608
|
+
label: API_REFERENCE_LABEL
|
|
11609
|
+
});
|
|
11610
|
+
}
|
|
11611
|
+
next.navigation = navigation;
|
|
11612
|
+
return next;
|
|
11613
|
+
}
|
|
11614
|
+
function openapiInputForConfig(specRelativePosix) {
|
|
11615
|
+
if (specRelativePosix.startsWith("./")) {
|
|
11616
|
+
return specRelativePosix;
|
|
11617
|
+
}
|
|
11618
|
+
return `./${specRelativePosix}`;
|
|
11619
|
+
}
|
|
11620
|
+
function toRoutePath(relativeFile, pagesDir) {
|
|
11621
|
+
const relativeNoExt = relativeFile.replace(/\.(mdx?|tsx|jsx)$/i, "");
|
|
11622
|
+
const escaped = pagesDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11623
|
+
const withoutPagesPrefix = relativeNoExt.replace(
|
|
11624
|
+
new RegExp(`^${escaped}/?`),
|
|
11625
|
+
""
|
|
11626
|
+
);
|
|
11627
|
+
const normalized = withoutPagesPrefix.replace(/\/index$/, "");
|
|
11628
|
+
return `/${normalized || "index"}`;
|
|
11629
|
+
}
|
|
11630
|
+
function pageComponentName(slug) {
|
|
11631
|
+
const pascal = slug.replace(/^\/+/, "").split(/[^a-zA-Z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
11632
|
+
const name = /^[A-Za-z]/.test(pascal) ? pascal : `Page${pascal}`;
|
|
11633
|
+
return name.endsWith("Page") ? name : `${name}Page`;
|
|
11634
|
+
}
|
|
11635
|
+
var THIN_WRAPPER_SOURCE = `import { devPortalBillingPlugin } from "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
11636
|
+
import { buildApitogoConfig } from "@lukoweb/apitogo/build-config";
|
|
11637
|
+
import siteConfig from "./apitogo.config.json";
|
|
11638
|
+
|
|
11639
|
+
export default buildApitogoConfig({
|
|
11640
|
+
...siteConfig,
|
|
11641
|
+
plugins: [devPortalBillingPlugin()],
|
|
11642
|
+
});
|
|
11643
|
+
`;
|
|
11644
|
+
function isThinConfigWrapper(source) {
|
|
11645
|
+
return /export\s+default\s+buildApitogoConfig\s*\(/.test(source);
|
|
11646
|
+
}
|
|
11647
|
+
function buildThinWrapperWithCustomPages(tsxRoutePaths) {
|
|
11648
|
+
if (tsxRoutePaths.length === 0) {
|
|
11649
|
+
return THIN_WRAPPER_SOURCE;
|
|
11650
|
+
}
|
|
11651
|
+
const imports = tsxRoutePaths.map((routePath) => {
|
|
11652
|
+
const slug = routePath.replace(/^\//, "");
|
|
11653
|
+
const name = pageComponentName(slug);
|
|
11654
|
+
return `import ${name} from "./pages/${slug}";`;
|
|
11655
|
+
}).join("\n");
|
|
11656
|
+
const customPages = tsxRoutePaths.map((routePath) => {
|
|
11657
|
+
const slug = routePath.replace(/^\//, "");
|
|
11658
|
+
const name = pageComponentName(slug);
|
|
11659
|
+
return ` {
|
|
11660
|
+
type: "custom-page",
|
|
11661
|
+
path: ${JSON.stringify(routePath)},
|
|
11662
|
+
element: <${name} />,
|
|
11663
|
+
},`;
|
|
11664
|
+
}).join("\n");
|
|
11665
|
+
return `${imports}
|
|
11666
|
+
import { devPortalBillingPlugin } from "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
11667
|
+
import { buildApitogoConfig } from "@lukoweb/apitogo/build-config";
|
|
11668
|
+
import siteConfig from "./apitogo.config.json";
|
|
11669
|
+
|
|
11670
|
+
const config = buildApitogoConfig({
|
|
11671
|
+
...siteConfig,
|
|
11672
|
+
plugins: [devPortalBillingPlugin()],
|
|
11673
|
+
});
|
|
11674
|
+
|
|
11675
|
+
export default {
|
|
11676
|
+
...config,
|
|
11677
|
+
navigation: [
|
|
11678
|
+
{
|
|
11679
|
+
type: "category",
|
|
11680
|
+
label: ${JSON.stringify(DOCUMENTATION_NAV_LABEL)},
|
|
11681
|
+
items: [
|
|
11682
|
+
${customPages}
|
|
11683
|
+
...(((config.navigation ?? []).find(
|
|
11684
|
+
(item) =>
|
|
11685
|
+
typeof item === "object" &&
|
|
11686
|
+
item !== null &&
|
|
11687
|
+
"label" in item &&
|
|
11688
|
+
item.label === ${JSON.stringify(DOCUMENTATION_NAV_LABEL)},
|
|
11689
|
+
) as { items?: unknown[] } | undefined)?.items ?? []),
|
|
11690
|
+
],
|
|
11691
|
+
},
|
|
11692
|
+
...(config.navigation ?? []).filter(
|
|
11693
|
+
(item) =>
|
|
11694
|
+
!(
|
|
11695
|
+
typeof item === "object" &&
|
|
11696
|
+
item !== null &&
|
|
11697
|
+
"label" in item &&
|
|
11698
|
+
item.label === ${JSON.stringify(DOCUMENTATION_NAV_LABEL)}
|
|
11699
|
+
),
|
|
11700
|
+
),
|
|
11701
|
+
],
|
|
11702
|
+
};
|
|
11703
|
+
`;
|
|
11704
|
+
}
|
|
11705
|
+
|
|
11247
11706
|
// src/cli/make-config/scaffold-project.ts
|
|
11248
11707
|
init_package_json();
|
|
11249
11708
|
import { constants as constants2 } from "node:fs";
|
|
11250
|
-
import { access as access4, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
|
|
11709
|
+
import { access as access4, mkdir as mkdir7, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
|
|
11251
11710
|
import path38 from "node:path";
|
|
11252
11711
|
import { glob as glob4 } from "glob";
|
|
11253
11712
|
var OPENAPI_GLOBS = [
|
|
@@ -11258,56 +11717,6 @@ var OPENAPI_GLOBS = [
|
|
|
11258
11717
|
"openapi.yaml",
|
|
11259
11718
|
"openapi.yml"
|
|
11260
11719
|
];
|
|
11261
|
-
var findMatchingCurly = (source, startIndex) => {
|
|
11262
|
-
let depth = 0;
|
|
11263
|
-
let inSingleQuote = false;
|
|
11264
|
-
let inDoubleQuote = false;
|
|
11265
|
-
let inTemplate = false;
|
|
11266
|
-
let inLineComment = false;
|
|
11267
|
-
let inBlockComment = false;
|
|
11268
|
-
for (let index = startIndex; index < source.length; index++) {
|
|
11269
|
-
const char = source[index];
|
|
11270
|
-
const next = source[index + 1];
|
|
11271
|
-
const prev = source[index - 1];
|
|
11272
|
-
if (inLineComment) {
|
|
11273
|
-
if (char === "\n") inLineComment = false;
|
|
11274
|
-
continue;
|
|
11275
|
-
}
|
|
11276
|
-
if (inBlockComment) {
|
|
11277
|
-
if (prev === "*" && char === "/") inBlockComment = false;
|
|
11278
|
-
continue;
|
|
11279
|
-
}
|
|
11280
|
-
if (!inSingleQuote && !inDoubleQuote && !inTemplate) {
|
|
11281
|
-
if (char === "/" && next === "/") {
|
|
11282
|
-
inLineComment = true;
|
|
11283
|
-
index++;
|
|
11284
|
-
continue;
|
|
11285
|
-
}
|
|
11286
|
-
if (char === "/" && next === "*") {
|
|
11287
|
-
inBlockComment = true;
|
|
11288
|
-
index++;
|
|
11289
|
-
continue;
|
|
11290
|
-
}
|
|
11291
|
-
}
|
|
11292
|
-
if (!inDoubleQuote && !inTemplate && char === "'" && prev !== "\\") {
|
|
11293
|
-
inSingleQuote = !inSingleQuote;
|
|
11294
|
-
continue;
|
|
11295
|
-
}
|
|
11296
|
-
if (!inSingleQuote && !inTemplate && char === '"' && prev !== "\\") {
|
|
11297
|
-
inDoubleQuote = !inDoubleQuote;
|
|
11298
|
-
continue;
|
|
11299
|
-
}
|
|
11300
|
-
if (!inSingleQuote && !inDoubleQuote && char === "`" && prev !== "\\") {
|
|
11301
|
-
inTemplate = !inTemplate;
|
|
11302
|
-
continue;
|
|
11303
|
-
}
|
|
11304
|
-
if (inSingleQuote || inDoubleQuote || inTemplate) continue;
|
|
11305
|
-
if (char === "{") depth++;
|
|
11306
|
-
if (char === "}") depth--;
|
|
11307
|
-
if (depth === 0) return index;
|
|
11308
|
-
}
|
|
11309
|
-
throw new Error("Could not find matching closing brace for object.");
|
|
11310
|
-
};
|
|
11311
11720
|
var toPosix = (p) => p.split(path38.sep).join("/");
|
|
11312
11721
|
var sanitizePackageName = (dir) => {
|
|
11313
11722
|
const base = path38.basename(path38.resolve(dir));
|
|
@@ -11340,7 +11749,11 @@ async function ensurePackageJson(dir) {
|
|
|
11340
11749
|
const baseDeps = {
|
|
11341
11750
|
react: ">=19.0.0",
|
|
11342
11751
|
"react-dom": ">=19.0.0",
|
|
11343
|
-
"@lukoweb/apitogo": apitogoVer
|
|
11752
|
+
"@lukoweb/apitogo": apitogoVer,
|
|
11753
|
+
"@lukoweb/apitogo-plugin-dev-portal-billing": apitogoVer,
|
|
11754
|
+
"@lukoweb/apitogo-module-landing": apitogoVer,
|
|
11755
|
+
"@lukoweb/apitogo-module-user-panel": apitogoVer,
|
|
11756
|
+
"lucide-react": "0.577.0"
|
|
11344
11757
|
};
|
|
11345
11758
|
const baseScripts = {
|
|
11346
11759
|
dev: "apitogo dev",
|
|
@@ -11423,7 +11836,9 @@ async function ensureTsconfigJson(dir) {
|
|
|
11423
11836
|
"*.config.jsx",
|
|
11424
11837
|
"*.config.js",
|
|
11425
11838
|
"*.config.mjs",
|
|
11426
|
-
"pages"
|
|
11839
|
+
"pages",
|
|
11840
|
+
"landing",
|
|
11841
|
+
"user-panel"
|
|
11427
11842
|
]
|
|
11428
11843
|
};
|
|
11429
11844
|
await writeFile7(tsPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
@@ -11431,298 +11846,90 @@ async function ensureTsconfigJson(dir) {
|
|
|
11431
11846
|
return true;
|
|
11432
11847
|
}
|
|
11433
11848
|
}
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
}
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
"zudoku.config.tsx",
|
|
11476
|
-
"zudoku.config.ts",
|
|
11477
|
-
"zudoku.config.jsx",
|
|
11478
|
-
"zudoku.config.js",
|
|
11479
|
-
"zudoku.config.mjs"
|
|
11480
|
-
];
|
|
11481
|
-
var DEFAULT_NEW_CONFIG_FILENAME = "apitogo.config.tsx";
|
|
11482
|
-
var createTreeNode = () => ({ docs: [], children: /* @__PURE__ */ new Map() });
|
|
11483
|
-
var toPosixPath2 = (value) => value.split(path39.sep).join(path39.posix.sep);
|
|
11484
|
-
var toRoutePath = (relativeFile, pagesDir) => {
|
|
11485
|
-
const relativeNoExt = relativeFile.replace(/\.mdx?$/, "");
|
|
11486
|
-
const withoutPagesPrefix = relativeNoExt.replace(
|
|
11487
|
-
new RegExp(`^${pagesDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/?`),
|
|
11488
|
-
""
|
|
11489
|
-
);
|
|
11490
|
-
const normalized = withoutPagesPrefix.replace(/\/index$/, "");
|
|
11491
|
-
return `/${normalized || "index"}`;
|
|
11492
|
-
};
|
|
11493
|
-
var titleCase = (value) => value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11494
|
-
var docSortRank = (docPath) => {
|
|
11495
|
-
const lastSegment = docPath.split("/").at(-1) ?? docPath;
|
|
11496
|
-
if (lastSegment === "introduction") return 0;
|
|
11497
|
-
if (lastSegment === "example") return 1;
|
|
11498
|
-
if (lastSegment === "getting-started") return 2;
|
|
11499
|
-
if (lastSegment === "index") return 3;
|
|
11500
|
-
return 4;
|
|
11501
|
-
};
|
|
11502
|
-
var sortDocPaths = (docPaths) => [...docPaths].sort((a, b) => {
|
|
11503
|
-
const rankDiff = docSortRank(a) - docSortRank(b);
|
|
11504
|
-
if (rankDiff !== 0) return rankDiff;
|
|
11505
|
-
return a.localeCompare(b);
|
|
11506
|
-
});
|
|
11507
|
-
var sortDirectoryNames = (names) => [...names].sort((a, b) => a.localeCompare(b));
|
|
11508
|
-
var buildTree = (routePaths) => {
|
|
11509
|
-
const root = createTreeNode();
|
|
11510
|
-
for (const routePath of routePaths) {
|
|
11511
|
-
const segments = routePath.replace(/^\//, "").split("/").filter(Boolean);
|
|
11512
|
-
const directorySegments = segments.slice(0, -1);
|
|
11513
|
-
let current = root;
|
|
11514
|
-
for (const segment of directorySegments) {
|
|
11515
|
-
let child = current.children.get(segment);
|
|
11516
|
-
if (!child) {
|
|
11517
|
-
child = createTreeNode();
|
|
11518
|
-
current.children.set(segment, child);
|
|
11519
|
-
}
|
|
11520
|
-
current = child;
|
|
11521
|
-
}
|
|
11522
|
-
current.docs.push(routePath);
|
|
11523
|
-
}
|
|
11524
|
-
return root;
|
|
11525
|
-
};
|
|
11526
|
-
var treeToNavigationItems = (node) => {
|
|
11527
|
-
const docs = sortDocPaths(node.docs);
|
|
11528
|
-
const categories = sortDirectoryNames([...node.children.keys()]).map(
|
|
11529
|
-
(directoryName) => {
|
|
11530
|
-
const childNode = node.children.get(directoryName);
|
|
11531
|
-
if (!childNode) {
|
|
11532
|
-
throw new Error(`Missing child node for directory '${directoryName}'.`);
|
|
11533
|
-
}
|
|
11534
|
-
return {
|
|
11535
|
-
type: "category",
|
|
11536
|
-
label: titleCase(directoryName),
|
|
11537
|
-
items: treeToNavigationItems(childNode)
|
|
11538
|
-
};
|
|
11539
|
-
}
|
|
11849
|
+
var LANDING_PAGE_SOURCE = `import {
|
|
11850
|
+
FeaturesSection,
|
|
11851
|
+
HeroSection,
|
|
11852
|
+
LandingShell,
|
|
11853
|
+
} from "@lukoweb/apitogo-module-landing";
|
|
11854
|
+
|
|
11855
|
+
export default function LandingPage() {
|
|
11856
|
+
return (
|
|
11857
|
+
<LandingShell
|
|
11858
|
+
title="Your API, ready for developers"
|
|
11859
|
+
description="Documentation, API reference, and a developer portal \u2014 all in one place."
|
|
11860
|
+
>
|
|
11861
|
+
<HeroSection
|
|
11862
|
+
hero={{
|
|
11863
|
+
title: "Your API, ready for developers",
|
|
11864
|
+
description:
|
|
11865
|
+
"Ship docs, interactive API reference, and a developer portal without building from scratch.",
|
|
11866
|
+
}}
|
|
11867
|
+
primaryAction={{ label: "Get started", href: "/introduction" }}
|
|
11868
|
+
secondaryAction={{ label: "API Reference", href: "/api" }}
|
|
11869
|
+
/>
|
|
11870
|
+
<FeaturesSection
|
|
11871
|
+
features={[
|
|
11872
|
+
{
|
|
11873
|
+
title: "Documentation",
|
|
11874
|
+
description: "Guides and references for every integration path.",
|
|
11875
|
+
icon: "book-open",
|
|
11876
|
+
},
|
|
11877
|
+
{
|
|
11878
|
+
title: "API Reference",
|
|
11879
|
+
description: "Interactive OpenAPI explorer with code samples.",
|
|
11880
|
+
icon: "code",
|
|
11881
|
+
},
|
|
11882
|
+
{
|
|
11883
|
+
title: "Developer Portal",
|
|
11884
|
+
description: "Auth, API keys, and subscription billing built in.",
|
|
11885
|
+
icon: "shield-check",
|
|
11886
|
+
},
|
|
11887
|
+
]}
|
|
11888
|
+
/>
|
|
11889
|
+
</LandingShell>
|
|
11540
11890
|
);
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
var
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11891
|
+
}
|
|
11892
|
+
`;
|
|
11893
|
+
var ACCOUNT_PLANS_PAGE_SOURCE = `export { AccountPlansPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
11894
|
+
`;
|
|
11895
|
+
var fileExists2 = async (filePath) => {
|
|
11896
|
+
try {
|
|
11897
|
+
await access4(filePath, constants2.F_OK);
|
|
11898
|
+
return true;
|
|
11899
|
+
} catch {
|
|
11900
|
+
return false;
|
|
11547
11901
|
}
|
|
11548
|
-
const items = item.items.map(
|
|
11549
|
-
(subItem) => formatNavigationItem(subItem, indentLevel + 2)
|
|
11550
|
-
);
|
|
11551
|
-
return [
|
|
11552
|
-
`${indent}{`,
|
|
11553
|
-
`${indent} type: "category",`,
|
|
11554
|
-
`${indent} label: "${item.label}",`,
|
|
11555
|
-
`${indent} items: [`,
|
|
11556
|
-
items.join(",\n"),
|
|
11557
|
-
`${indent} ],`,
|
|
11558
|
-
`${indent}}`
|
|
11559
|
-
].join("\n");
|
|
11560
11902
|
};
|
|
11561
|
-
|
|
11562
|
-
const
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
" {",
|
|
11567
|
-
' type: "category",',
|
|
11568
|
-
' label: "Useful Links",',
|
|
11569
|
-
" collapsible: false,",
|
|
11570
|
-
' icon: "link",',
|
|
11571
|
-
" items: [",
|
|
11572
|
-
" {",
|
|
11573
|
-
' type: "link",',
|
|
11574
|
-
' icon: "book",',
|
|
11575
|
-
' label: "APIToGo",',
|
|
11576
|
-
' to: "https://apitogo.com",',
|
|
11577
|
-
" },",
|
|
11578
|
-
" ],",
|
|
11579
|
-
" },"
|
|
11580
|
-
].join("\n")
|
|
11581
|
-
];
|
|
11582
|
-
return [
|
|
11583
|
-
"[",
|
|
11584
|
-
" {",
|
|
11585
|
-
' type: "category",',
|
|
11586
|
-
' label: "Documentation",',
|
|
11587
|
-
" items: [",
|
|
11588
|
-
documentationItems.join(",\n"),
|
|
11589
|
-
" ],",
|
|
11590
|
-
" },",
|
|
11591
|
-
" {",
|
|
11592
|
-
' type: "link",',
|
|
11593
|
-
' to: "/api",',
|
|
11594
|
-
' label: "API Reference",',
|
|
11595
|
-
" },",
|
|
11596
|
-
"]"
|
|
11597
|
-
].join("\n");
|
|
11598
|
-
};
|
|
11599
|
-
var buildRedirectsCode = (firstDocPath) => `[{ from: "/", to: "${firstDocPath}" }]`;
|
|
11600
|
-
var findMatchingBracket = (source, startIndex) => {
|
|
11601
|
-
let depth = 0;
|
|
11602
|
-
let inSingleQuote = false;
|
|
11603
|
-
let inDoubleQuote = false;
|
|
11604
|
-
let inTemplate = false;
|
|
11605
|
-
let inLineComment = false;
|
|
11606
|
-
let inBlockComment = false;
|
|
11607
|
-
for (let index = startIndex; index < source.length; index++) {
|
|
11608
|
-
const char = source[index];
|
|
11609
|
-
const next = source[index + 1];
|
|
11610
|
-
const prev = source[index - 1];
|
|
11611
|
-
if (inLineComment) {
|
|
11612
|
-
if (char === "\n") inLineComment = false;
|
|
11613
|
-
continue;
|
|
11614
|
-
}
|
|
11615
|
-
if (inBlockComment) {
|
|
11616
|
-
if (prev === "*" && char === "/") inBlockComment = false;
|
|
11617
|
-
continue;
|
|
11618
|
-
}
|
|
11619
|
-
if (!inSingleQuote && !inDoubleQuote && !inTemplate) {
|
|
11620
|
-
if (char === "/" && next === "/") {
|
|
11621
|
-
inLineComment = true;
|
|
11622
|
-
index++;
|
|
11623
|
-
continue;
|
|
11624
|
-
}
|
|
11625
|
-
if (char === "/" && next === "*") {
|
|
11626
|
-
inBlockComment = true;
|
|
11627
|
-
index++;
|
|
11628
|
-
continue;
|
|
11629
|
-
}
|
|
11630
|
-
}
|
|
11631
|
-
if (!inDoubleQuote && !inTemplate && char === "'" && prev !== "\\") {
|
|
11632
|
-
inSingleQuote = !inSingleQuote;
|
|
11633
|
-
continue;
|
|
11634
|
-
}
|
|
11635
|
-
if (!inSingleQuote && !inTemplate && char === '"' && prev !== "\\") {
|
|
11636
|
-
inDoubleQuote = !inDoubleQuote;
|
|
11637
|
-
continue;
|
|
11638
|
-
}
|
|
11639
|
-
if (!inSingleQuote && !inDoubleQuote && char === "`" && prev !== "\\") {
|
|
11640
|
-
inTemplate = !inTemplate;
|
|
11641
|
-
continue;
|
|
11642
|
-
}
|
|
11643
|
-
if (inSingleQuote || inDoubleQuote || inTemplate) continue;
|
|
11644
|
-
if (char === "[") depth++;
|
|
11645
|
-
if (char === "]") depth--;
|
|
11646
|
-
if (depth === 0) return index;
|
|
11903
|
+
async function ensurePortalStubFiles(dir) {
|
|
11904
|
+
const landingPath = path38.join(dir, "landing", "LandingPage.tsx");
|
|
11905
|
+
if (!await fileExists2(landingPath)) {
|
|
11906
|
+
await mkdir7(path38.join(dir, "landing"), { recursive: true });
|
|
11907
|
+
await writeFile7(landingPath, LANDING_PAGE_SOURCE, "utf8");
|
|
11647
11908
|
}
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
const match = source.match(re);
|
|
11653
|
-
if (!match || match.index === void 0) {
|
|
11654
|
-
throw new Error(`Could not find '${propertyName}' in config file.`);
|
|
11655
|
-
}
|
|
11656
|
-
const arrayStart = source.indexOf("[", match.index);
|
|
11657
|
-
if (arrayStart === -1) {
|
|
11658
|
-
throw new Error(
|
|
11659
|
-
`Could not find array value for '${propertyName}' (expected [...]).`
|
|
11660
|
-
);
|
|
11909
|
+
const plansPath = path38.join(dir, "user-panel", "AccountPlansPage.tsx");
|
|
11910
|
+
if (!await fileExists2(plansPath)) {
|
|
11911
|
+
await mkdir7(path38.join(dir, "user-panel"), { recursive: true });
|
|
11912
|
+
await writeFile7(plansPath, ACCOUNT_PLANS_PAGE_SOURCE, "utf8");
|
|
11661
11913
|
}
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
source,
|
|
11668
|
-
propertyName
|
|
11669
|
-
);
|
|
11670
|
-
return `${source.slice(0, arrayStart)}${replacement}${source.slice(arrayEnd + 1)}`;
|
|
11671
|
-
};
|
|
11672
|
-
var insertNavigationAndRedirectsKeys = (source) => {
|
|
11673
|
-
const m = source.match(/(const\s+config\s*:\s*ApitogoConfig\s*=\s*\{)/);
|
|
11674
|
-
if (!m || m.index === void 0) {
|
|
11675
|
-
throw new Error(
|
|
11676
|
-
"Could not find 'const config: ApitogoConfig = {' \u2014 add navigation and redirects arrays manually."
|
|
11677
|
-
);
|
|
11914
|
+
}
|
|
11915
|
+
async function ensureThinConfigTsx(dir) {
|
|
11916
|
+
const tsxPath = path38.join(dir, "apitogo.config.tsx");
|
|
11917
|
+
if (await fileExists2(tsxPath)) {
|
|
11918
|
+
return false;
|
|
11678
11919
|
}
|
|
11679
|
-
|
|
11680
|
-
return
|
|
11681
|
-
|
|
11682
|
-
redirects: [],${source.slice(insertAt)}`;
|
|
11683
|
-
};
|
|
11684
|
-
var insertRedirectsAfterNavigation = (source) => {
|
|
11685
|
-
if (/\bredirects\s*:/.test(source)) return source;
|
|
11686
|
-
const { arrayEnd } = findPropertyArrayBounds(source, "navigation");
|
|
11687
|
-
let i = arrayEnd + 1;
|
|
11688
|
-
while (i < source.length && /\s/.test(source[i])) i++;
|
|
11689
|
-
if (source[i] === ",") {
|
|
11690
|
-
return `${source.slice(0, i + 1)}
|
|
11691
|
-
redirects: [],${source.slice(i + 1)}`;
|
|
11692
|
-
}
|
|
11693
|
-
return `${source.slice(0, arrayEnd + 1)},
|
|
11694
|
-
redirects: [],${source.slice(arrayEnd + 1)}`;
|
|
11695
|
-
};
|
|
11696
|
-
var ensureNavigationAndRedirectsForUpdate = (source) => {
|
|
11697
|
-
const hasNav = /\bnavigation\s*:/.test(source);
|
|
11698
|
-
const hasRedirects = /\bredirects\s*:/.test(source);
|
|
11699
|
-
if (hasNav && hasRedirects) return source;
|
|
11700
|
-
if (!hasNav && !hasRedirects) return insertNavigationAndRedirectsKeys(source);
|
|
11701
|
-
if (hasNav && !hasRedirects) return insertRedirectsAfterNavigation(source);
|
|
11702
|
-
throw new Error(
|
|
11703
|
-
"Found 'redirects' but not 'navigation'. Add a navigation: [...] array manually."
|
|
11704
|
-
);
|
|
11705
|
-
};
|
|
11706
|
-
var buildNewConfigContents = (pagesDir, openApiSpecRelative) => {
|
|
11707
|
-
const filesGlob = `/${pagesDir}/**/*.{md,mdx}`;
|
|
11708
|
-
const apisSection = openApiSpecRelative == null ? "" : `
|
|
11709
|
-
apis: {
|
|
11710
|
-
type: "file",
|
|
11711
|
-
input: "${openapiInputForConfig(openApiSpecRelative)}",
|
|
11712
|
-
path: "api",
|
|
11713
|
-
},`;
|
|
11714
|
-
return `import type { ApitogoConfig } from "@lukoweb/apitogo";
|
|
11715
|
-
|
|
11716
|
-
const config: ApitogoConfig = {
|
|
11717
|
-
docs: {
|
|
11718
|
-
files: "${filesGlob}",
|
|
11719
|
-
},${apisSection}
|
|
11720
|
-
navigation: [],
|
|
11721
|
-
redirects: [],
|
|
11722
|
-
};
|
|
11920
|
+
await writeFile7(tsxPath, THIN_WRAPPER_SOURCE, "utf8");
|
|
11921
|
+
return true;
|
|
11922
|
+
}
|
|
11723
11923
|
|
|
11724
|
-
|
|
11725
|
-
|
|
11924
|
+
// src/cli/make-config/handler.ts
|
|
11925
|
+
var toPosixPath2 = (value) => value.split(path39.sep).join(path39.posix.sep);
|
|
11926
|
+
var fileExists3 = async (filePath) => {
|
|
11927
|
+
try {
|
|
11928
|
+
await access5(filePath, constants3.F_OK);
|
|
11929
|
+
return true;
|
|
11930
|
+
} catch {
|
|
11931
|
+
return false;
|
|
11932
|
+
}
|
|
11726
11933
|
};
|
|
11727
11934
|
var ensureApitogoDeployWorkflow = async (dir) => {
|
|
11728
11935
|
const workflowPath = path39.join(
|
|
@@ -11731,25 +11938,20 @@ var ensureApitogoDeployWorkflow = async (dir) => {
|
|
|
11731
11938
|
"workflows",
|
|
11732
11939
|
"apitogo-deploy.yml"
|
|
11733
11940
|
);
|
|
11734
|
-
|
|
11735
|
-
await access5(workflowPath, constants3.F_OK);
|
|
11941
|
+
if (await fileExists3(workflowPath)) {
|
|
11736
11942
|
return false;
|
|
11737
|
-
} catch {
|
|
11738
11943
|
}
|
|
11739
|
-
await
|
|
11944
|
+
await mkdir8(path39.join(dir, ".github", "workflows"), { recursive: true });
|
|
11740
11945
|
await writeFile8(workflowPath, APITOGO_DEPLOY_WORKFLOW_YAML, "utf8");
|
|
11741
11946
|
return true;
|
|
11742
11947
|
};
|
|
11743
|
-
var
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
return fullPath;
|
|
11749
|
-
} catch {
|
|
11750
|
-
}
|
|
11948
|
+
var readJsonConfig = async (filePath) => {
|
|
11949
|
+
const raw = await readFile6(filePath, "utf8");
|
|
11950
|
+
const parsed = JSON.parse(raw);
|
|
11951
|
+
if (!isPlainObject2(parsed)) {
|
|
11952
|
+
throw new Error(`${APITOGO_JSON_FILENAME} must be a JSON object.`);
|
|
11751
11953
|
}
|
|
11752
|
-
return
|
|
11954
|
+
return parsed;
|
|
11753
11955
|
};
|
|
11754
11956
|
async function makeConfig(argv) {
|
|
11755
11957
|
const dir = path39.resolve(process.cwd(), argv.dir);
|
|
@@ -11757,7 +11959,7 @@ async function makeConfig(argv) {
|
|
|
11757
11959
|
argv.pagesDir.replace(/^[./]+/, "").replace(/\/+$/, "")
|
|
11758
11960
|
);
|
|
11759
11961
|
try {
|
|
11760
|
-
const
|
|
11962
|
+
const discoveredOpenApi = await findOpenApiSpecPath(dir);
|
|
11761
11963
|
const createdPackageJson = await ensurePackageJson(dir);
|
|
11762
11964
|
const createdTsconfig = await ensureTsconfigJson(dir);
|
|
11763
11965
|
if (createdPackageJson) {
|
|
@@ -11768,51 +11970,98 @@ async function makeConfig(argv) {
|
|
|
11768
11970
|
if (createdTsconfig) {
|
|
11769
11971
|
printDiagnosticsToConsole("Created tsconfig.json.");
|
|
11770
11972
|
}
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11973
|
+
await ensurePortalStubFiles(dir);
|
|
11974
|
+
const jsonPath = path39.join(dir, APITOGO_JSON_FILENAME);
|
|
11975
|
+
let createdJson = false;
|
|
11976
|
+
let config2;
|
|
11977
|
+
if (await fileExists3(jsonPath)) {
|
|
11978
|
+
config2 = await readJsonConfig(jsonPath);
|
|
11979
|
+
} else {
|
|
11980
|
+
config2 = createDefaultApitogoJsonConfig({
|
|
11981
|
+
title: titleFromDirectoryName(path39.basename(dir)),
|
|
11982
|
+
pagesDir
|
|
11983
|
+
});
|
|
11984
|
+
createdJson = true;
|
|
11985
|
+
printDiagnosticsToConsole(`Created ${APITOGO_JSON_FILENAME}`);
|
|
11986
|
+
}
|
|
11987
|
+
const createdTsx = await ensureThinConfigTsx(dir);
|
|
11988
|
+
if (createdTsx) {
|
|
11989
|
+
printDiagnosticsToConsole("Created apitogo.config.tsx");
|
|
11990
|
+
}
|
|
11991
|
+
const flagPatches = {
|
|
11992
|
+
site: parseJsonObjectFlag(argv.site, "site"),
|
|
11993
|
+
theme: parseJsonObjectFlag(argv.theme, "theme"),
|
|
11994
|
+
authentication: parseJsonObjectFlag(argv.authentication, "authentication"),
|
|
11995
|
+
title: argv.title
|
|
11996
|
+
};
|
|
11997
|
+
let openApiOverride;
|
|
11998
|
+
if (argv.interactive) {
|
|
11999
|
+
const site = isPlainObject2(config2.site) ? config2.site : {};
|
|
12000
|
+
const logo = isPlainObject2(site.logo) ? site.logo : {};
|
|
12001
|
+
const theme = isPlainObject2(config2.theme) ? config2.theme : {};
|
|
12002
|
+
const fonts = isPlainObject2(theme.fonts) ? theme.fonts : {};
|
|
12003
|
+
const light = isPlainObject2(theme.light) ? theme.light : {};
|
|
12004
|
+
const dark = isPlainObject2(theme.dark) ? theme.dark : {};
|
|
12005
|
+
const authentication = isPlainObject2(config2.authentication) ? config2.authentication : {};
|
|
12006
|
+
const prompted = await promptMakeConfigInteractive({
|
|
12007
|
+
title: flagPatches.title ?? (typeof site.title === "string" ? site.title : titleFromDirectoryName(path39.basename(dir))),
|
|
12008
|
+
logoAlt: typeof logo.alt === "string" ? logo.alt : "APIToGo",
|
|
12009
|
+
authEnabled: authentication.enabled === true,
|
|
12010
|
+
apiBaseUrl: typeof authentication.apiBaseUrl === "string" ? authentication.apiBaseUrl : void 0,
|
|
12011
|
+
sans: typeof fonts.sans === "string" ? fonts.sans : void 0,
|
|
12012
|
+
mono: typeof fonts.mono === "string" ? fonts.mono : void 0,
|
|
12013
|
+
lightPrimary: typeof light.primary === "string" ? light.primary : void 0,
|
|
12014
|
+
darkPrimary: typeof dark.primary === "string" ? dark.primary : void 0,
|
|
12015
|
+
discoveredOpenApi
|
|
12016
|
+
});
|
|
12017
|
+
openApiOverride = prompted.openApiPath;
|
|
12018
|
+
config2 = applyConfigPatches(config2, prompted);
|
|
11782
12019
|
}
|
|
11783
|
-
|
|
12020
|
+
config2 = applyConfigPatches(config2, flagPatches);
|
|
12021
|
+
const pageFiles = await glob5(`${pagesDir}/**/*.{md,mdx,tsx,jsx}`, {
|
|
11784
12022
|
cwd: dir,
|
|
11785
12023
|
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
11786
12024
|
posix: true
|
|
11787
12025
|
});
|
|
11788
|
-
const
|
|
11789
|
-
|
|
12026
|
+
const markdownFiles = pageFiles.filter((file) => /\.mdx?$/i.test(file));
|
|
12027
|
+
const tsxFiles = pageFiles.filter((file) => /\.(tsx|jsx)$/i.test(file));
|
|
12028
|
+
const markdownRoutes = markdownFiles.map(
|
|
12029
|
+
(file) => toRoutePath(file, pagesDir)
|
|
11790
12030
|
);
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
if (!createdNew && openApiSpecPath) {
|
|
11796
|
-
originalConfig = insertApisIfMissing(
|
|
11797
|
-
originalConfig,
|
|
11798
|
-
openapiInputForConfig(openApiSpecPath)
|
|
12031
|
+
const tsxRoutes = tsxFiles.map((file) => toRoutePath(file, pagesDir));
|
|
12032
|
+
if (markdownRoutes.length === 0 && tsxRoutes.length === 0) {
|
|
12033
|
+
throw new Error(
|
|
12034
|
+
`No .md, .mdx, or .tsx files found under '${pagesDir}'.`
|
|
11799
12035
|
);
|
|
11800
12036
|
}
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
buildNavigationCode(treeToNavigationItems(buildTree(routePaths)))
|
|
12037
|
+
config2.navigation = mergeDocumentationNavigation(
|
|
12038
|
+
config2.navigation,
|
|
12039
|
+
markdownRoutes,
|
|
12040
|
+
{ force: argv.force === true }
|
|
11806
12041
|
);
|
|
11807
|
-
const
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
12042
|
+
const openApiRel = openApiOverride === void 0 ? discoveredOpenApi : openApiOverride;
|
|
12043
|
+
if (openApiRel) {
|
|
12044
|
+
config2 = applyOpenApiToJsonConfig(
|
|
12045
|
+
config2,
|
|
12046
|
+
openapiInputForConfig(openApiRel)
|
|
12047
|
+
);
|
|
12048
|
+
}
|
|
12049
|
+
await writeFile8(jsonPath, `${JSON.stringify(config2, null, 2)}
|
|
12050
|
+
`, "utf8");
|
|
12051
|
+
const tsxPath = path39.join(dir, "apitogo.config.tsx");
|
|
12052
|
+
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
12053
|
+
const tsxSource = await readFile6(tsxPath, "utf8");
|
|
12054
|
+
if (isThinConfigWrapper(tsxSource)) {
|
|
12055
|
+
await writeFile8(
|
|
12056
|
+
tsxPath,
|
|
12057
|
+
buildThinWrapperWithCustomPages(tsxRoutes),
|
|
12058
|
+
"utf8"
|
|
12059
|
+
);
|
|
12060
|
+
}
|
|
12061
|
+
}
|
|
12062
|
+
const action = createdJson ? "Initialized" : "Updated";
|
|
11814
12063
|
printResultToConsole(
|
|
11815
|
-
`${action} ${
|
|
12064
|
+
`${action} ${APITOGO_JSON_FILENAME} from ${pagesDir}/ (navigation, optional site/theme/auth; redirects unchanged).`
|
|
11816
12065
|
);
|
|
11817
12066
|
const createdWorkflow = await ensureApitogoDeployWorkflow(dir);
|
|
11818
12067
|
if (createdWorkflow) {
|
|
@@ -11834,7 +12083,7 @@ async function makeConfig(argv) {
|
|
|
11834
12083
|
|
|
11835
12084
|
// src/cli/cmds/make-config.ts
|
|
11836
12085
|
var make_config_default = {
|
|
11837
|
-
desc: "Create or update config
|
|
12086
|
+
desc: "Create or update apitogo.config.json from pages, OpenAPI, and optional site/theme/auth flags",
|
|
11838
12087
|
command: "make-config",
|
|
11839
12088
|
builder: (yargs2) => yargs2.option("dir", {
|
|
11840
12089
|
type: "string",
|
|
@@ -11844,8 +12093,29 @@ var make_config_default = {
|
|
|
11844
12093
|
hidden: true
|
|
11845
12094
|
}).option("pages-dir", {
|
|
11846
12095
|
type: "string",
|
|
11847
|
-
describe: "Directory to scan for md/mdx pages",
|
|
12096
|
+
describe: "Directory to scan for md/mdx/tsx pages",
|
|
11848
12097
|
default: "pages"
|
|
12098
|
+
}).option("interactive", {
|
|
12099
|
+
alias: "i",
|
|
12100
|
+
type: "boolean",
|
|
12101
|
+
describe: "Prompt for site, theme, authentication, and OpenAPI",
|
|
12102
|
+
default: false
|
|
12103
|
+
}).option("site", {
|
|
12104
|
+
type: "string",
|
|
12105
|
+
describe: "JSON object merged into site"
|
|
12106
|
+
}).option("theme", {
|
|
12107
|
+
type: "string",
|
|
12108
|
+
describe: "JSON object merged into theme"
|
|
12109
|
+
}).option("authentication", {
|
|
12110
|
+
type: "string",
|
|
12111
|
+
describe: "JSON object merged into authentication"
|
|
12112
|
+
}).option("title", {
|
|
12113
|
+
type: "string",
|
|
12114
|
+
describe: "Set site.title"
|
|
12115
|
+
}).option("force", {
|
|
12116
|
+
type: "boolean",
|
|
12117
|
+
describe: "Replace Documentation nav items from disk (keeps API Reference)",
|
|
12118
|
+
default: false
|
|
11849
12119
|
}),
|
|
11850
12120
|
handler: async (argv) => {
|
|
11851
12121
|
await captureEvent({ argv, event: "apitogo make-config" });
|