@lukoweb/apitogo 0.2.47 → 0.2.50

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 CHANGED
@@ -757,12 +757,12 @@ var init_transform_config = __esm({
757
757
 
758
758
  // src/config/apitogo-config-loader.node.ts
759
759
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
760
- import path17 from "node:path";
760
+ import path18 from "node:path";
761
761
  function loadApitogoConfig(rootDir = process.cwd()) {
762
- const configPath = path17.join(path17.resolve(rootDir), APITOGO_CONFIG_FILENAME);
762
+ const configPath = path18.join(path18.resolve(rootDir), APITOGO_CONFIG_FILENAME);
763
763
  if (!existsSync3(configPath)) {
764
764
  throw new Error(
765
- `Missing ${APITOGO_CONFIG_FILENAME} in ${path17.resolve(rootDir)}`
765
+ `Missing ${APITOGO_CONFIG_FILENAME} in ${path18.resolve(rootDir)}`
766
766
  );
767
767
  }
768
768
  const raw = readFileSync3(configPath, "utf8");
@@ -837,7 +837,7 @@ import { stat as stat4 } from "node:fs/promises";
837
837
  var fileExists;
838
838
  var init_file_exists = __esm({
839
839
  "src/config/file-exists.ts"() {
840
- fileExists = (path95) => stat4(path95).then(() => true).catch(() => false);
840
+ fileExists = (path97) => stat4(path97).then(() => true).catch(() => false);
841
841
  }
842
842
  });
843
843
 
@@ -1095,7 +1095,7 @@ var init_landing_manifest = __esm({
1095
1095
  });
1096
1096
 
1097
1097
  // src/config/local-manifest.ts
1098
- import path18 from "node:path";
1098
+ import path19 from "node:path";
1099
1099
  import { z as z7 } from "zod";
1100
1100
  function isPricingEnabled(manifest) {
1101
1101
  return manifest?.pricing?.enabled === true;
@@ -1148,8 +1148,8 @@ function resolveManifestEnv(viteMode) {
1148
1148
  return viteMode === "production" ? "production" : "development";
1149
1149
  }
1150
1150
  function manifestPathsForEnv(rootDir, _env) {
1151
- const resolvedRoot = path18.resolve(rootDir);
1152
- const configPath = path18.join(resolvedRoot, APITOGO_CONFIG_FILENAME);
1151
+ const resolvedRoot = path19.resolve(rootDir);
1152
+ const configPath = path19.join(resolvedRoot, APITOGO_CONFIG_FILENAME);
1153
1153
  return {
1154
1154
  basePath: configPath,
1155
1155
  overridePath: configPath,
@@ -1841,7 +1841,7 @@ var init_shadcn_registry = __esm({
1841
1841
 
1842
1842
  // src/vite/plugin-theme.ts
1843
1843
  import { createRequire } from "node:module";
1844
- import path19 from "node:path";
1844
+ import path20 from "node:path";
1845
1845
  import { normalizeTheme } from "shiki";
1846
1846
  var THEME_VARIABLES, uncamelize, processColorValue, generateCss, generateRegistryCss, processCustomCss, MAIN_REPLACE, DEFAULT_THEME_REPLACE, GOOGLE_FONTS, getGoogleFontUrl, processFont, processFontConfig, bundledFont, DEFAULT_FONT_SANS, DEFAULT_FONT_MONO, DEFAULT_FONT_HEADING, processFonts, MODULE_SOURCE_PACKAGES, resolveModuleSourceDirs, virtualModuleId, resolvedVirtualModuleId, viteThemePlugin;
1847
1847
  var init_plugin_theme = __esm({
@@ -1956,11 +1956,11 @@ var init_plugin_theme = __esm({
1956
1956
  };
1957
1957
  };
1958
1958
  bundledFont = (pkg) => {
1959
- const dir = path19.dirname(
1959
+ const dir = path20.dirname(
1960
1960
  createRequire(import.meta.url).resolve(`@fontsource/${pkg}/package.json`)
1961
1961
  );
1962
1962
  return [400, 500, 600, 700].map(
1963
- (weight) => path19.join(dir, `latin-${weight}.css`).split(path19.sep).join("/")
1963
+ (weight) => path20.join(dir, `latin-${weight}.css`).split(path20.sep).join("/")
1964
1964
  );
1965
1965
  };
1966
1966
  DEFAULT_FONT_SANS = '"IBM Plex Sans", system-ui, sans-serif';
@@ -2029,7 +2029,7 @@ var init_plugin_theme = __esm({
2029
2029
  resolveModuleSourceDirs = (config2) => {
2030
2030
  if (config2.__meta.mode === "internal") {
2031
2031
  return MODULE_SOURCE_PACKAGES.map(
2032
- (pkg) => path19.join(
2032
+ (pkg) => path20.join(
2033
2033
  config2.__meta.moduleDir,
2034
2034
  "..",
2035
2035
  pkg.replace("@lukoweb/apitogo-", ""),
@@ -2038,12 +2038,12 @@ var init_plugin_theme = __esm({
2038
2038
  );
2039
2039
  }
2040
2040
  const require2 = createRequire(
2041
- path19.join(config2.__meta.rootDir, "package.json")
2041
+ path20.join(config2.__meta.rootDir, "package.json")
2042
2042
  );
2043
2043
  const dirs = [];
2044
2044
  for (const pkg of MODULE_SOURCE_PACKAGES) {
2045
2045
  try {
2046
- dirs.push(path19.dirname(require2.resolve(pkg)));
2046
+ dirs.push(path20.dirname(require2.resolve(pkg)));
2047
2047
  } catch {
2048
2048
  }
2049
2049
  }
@@ -2170,7 +2170,7 @@ ${rootVars.join("\n")}
2170
2170
  ...config2.__meta.dependencies,
2171
2171
  ...config2.__pluginDirs ?? [],
2172
2172
  ...resolveModuleSourceDirs(config2)
2173
- ].map((file) => path19.relative(path19.dirname(id), file))
2173
+ ].map((file) => path20.relative(path20.dirname(id), file))
2174
2174
  );
2175
2175
  const code = [...files].map((file) => `@source "${file}";`);
2176
2176
  code.push("@theme inline {");
@@ -5012,7 +5012,7 @@ var init_ZudokuConfig = __esm({
5012
5012
 
5013
5013
  // src/config/loader.ts
5014
5014
  import { stat as stat5 } from "node:fs/promises";
5015
- import path20 from "node:path";
5015
+ import path21 from "node:path";
5016
5016
  import colors6 from "picocolors";
5017
5017
  import {
5018
5018
  runnerImport,
@@ -5039,7 +5039,7 @@ function mergeJsonConfigWithRuntimeOverrides(fileConfig, importedConfig) {
5039
5039
  }
5040
5040
  async function getConfigFilePath(rootDir) {
5041
5041
  for (const fileName of zudokuConfigFiles) {
5042
- const filepath = path20.join(rootDir, fileName);
5042
+ const filepath = path21.join(rootDir, fileName);
5043
5043
  if (await fileExists(filepath)) {
5044
5044
  return filepath;
5045
5045
  }
@@ -5268,7 +5268,7 @@ __export(llms_exports, {
5268
5268
  generateLlmsTxtFiles: () => generateLlmsTxtFiles
5269
5269
  });
5270
5270
  import { writeFile as writeFile12 } from "node:fs/promises";
5271
- import path44 from "node:path";
5271
+ import path45 from "node:path";
5272
5272
  import colors10 from "picocolors";
5273
5273
  async function generateLlmsTxtFiles({
5274
5274
  markdownFileInfos,
@@ -5303,7 +5303,7 @@ async function generateLlmsTxtFiles({
5303
5303
  }
5304
5304
  }
5305
5305
  const llmsTxt2 = llmsTxtParts.join("\n");
5306
- await writeFile12(path44.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5306
+ await writeFile12(path45.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5307
5307
  if (process.env.APITOGO_JSON_ONLY !== "1") {
5308
5308
  console.log(colors10.blue("\u2713 generated llms.txt"));
5309
5309
  }
@@ -5331,7 +5331,7 @@ ${info.content}
5331
5331
  }
5332
5332
  const llmsFull = llmsFullParts.join("\n");
5333
5333
  await writeFile12(
5334
- path44.join(baseOutputDir, "llms-full.txt"),
5334
+ path45.join(baseOutputDir, "llms-full.txt"),
5335
5335
  llmsFull,
5336
5336
  "utf-8"
5337
5337
  );
@@ -5834,7 +5834,37 @@ Run the command with --help to see its options.`;
5834
5834
  import yargs from "yargs/yargs";
5835
5835
 
5836
5836
  // src/cli/services/handler.ts
5837
- import path52 from "node:path";
5837
+ import path53 from "node:path";
5838
+
5839
+ // src/config/v2/billing.ts
5840
+ import { readFile as readFile2 } from "node:fs/promises";
5841
+ var DEFAULT_STRIPE_KEY_SECRET = "STRIPE_API_KEY";
5842
+ function isPaidPlan(plan2) {
5843
+ return plan2.pricingModel !== "custom" && typeof plan2.priceAmount === "number" && plan2.priceAmount > 0;
5844
+ }
5845
+ async function paidPlanSkus(resolved) {
5846
+ const file = resolved.plans?.file;
5847
+ if (!file) return [];
5848
+ try {
5849
+ const raw = JSON.parse(await readFile2(file, "utf8"));
5850
+ if (!Array.isArray(raw.plans)) return [];
5851
+ return raw.plans.filter(
5852
+ (plan2) => typeof plan2 === "object" && plan2 !== null && isPaidPlan(plan2)
5853
+ ).map((plan2) => String(plan2.sku ?? "?"));
5854
+ } catch {
5855
+ return [];
5856
+ }
5857
+ }
5858
+ async function billingNeeded(resolved) {
5859
+ const plans = resolved.plans;
5860
+ if (!plans?.enabled || plans.billing.provider !== "stripe") return false;
5861
+ return (await paidPlanSkus(resolved)).length > 0;
5862
+ }
5863
+ function stripeKeySecretName(resolved) {
5864
+ const ref = resolved.plans?.billing.apiKey;
5865
+ const match = ref ? /^secret:(.+)$/.exec(ref) : null;
5866
+ return match?.[1] ?? DEFAULT_STRIPE_KEY_SECRET;
5867
+ }
5838
5868
 
5839
5869
  // src/config/v2/errors.ts
5840
5870
  init_invariant();
@@ -6043,8 +6073,8 @@ var ConfigV2Schema = z.strictObject({
6043
6073
  var REMOVED_KEYS = {
6044
6074
  "plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
6045
6075
  };
6046
- function pathToString(path95) {
6047
- return path95.reduce(
6076
+ function pathToString(path97) {
6077
+ return path97.reduce(
6048
6078
  (acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
6049
6079
  ""
6050
6080
  );
@@ -6064,9 +6094,9 @@ function step(schema2, segment) {
6064
6094
  }
6065
6095
  return void 0;
6066
6096
  }
6067
- function knownKeysAt(path95) {
6097
+ function knownKeysAt(path97) {
6068
6098
  let current = ConfigV2Schema;
6069
- for (const segment of path95) {
6099
+ for (const segment of path97) {
6070
6100
  if (!current) return [];
6071
6101
  current = step(current, segment);
6072
6102
  }
@@ -6102,7 +6132,7 @@ function unknownKeyHint(fullPath, key, known) {
6102
6132
  return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
6103
6133
  }
6104
6134
  function mapIssue(issue4) {
6105
- const path95 = pathToString(issue4.path);
6135
+ const path97 = pathToString(issue4.path);
6106
6136
  switch (issue4.code) {
6107
6137
  case "unrecognized_keys":
6108
6138
  return issue4.keys.map((key) => {
@@ -6118,20 +6148,20 @@ function mapIssue(issue4) {
6118
6148
  return [
6119
6149
  {
6120
6150
  code: "CONFIG_INVALID_VALUE",
6121
- path: path95,
6151
+ path: path97,
6122
6152
  message: `must be one of: ${issue4.values.map((v) => JSON.stringify(v)).join(", ")}.`
6123
6153
  }
6124
6154
  ];
6125
6155
  case "custom": {
6126
6156
  const code = typeof issue4.params?.code === "string" ? issue4.params.code : "CONFIG_INVALID_VALUE";
6127
- return [{ code, path: path95, message: issue4.message ?? "is invalid." }];
6157
+ return [{ code, path: path97, message: issue4.message ?? "is invalid." }];
6128
6158
  }
6129
6159
  case "invalid_type":
6130
6160
  return [
6131
- /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path95, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path95, message: issue4.message }
6161
+ /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path97, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path97, message: issue4.message }
6132
6162
  ];
6133
6163
  default:
6134
- return [{ code: "CONFIG_INVALID_VALUE", path: path95, message: issue4.message }];
6164
+ return [{ code: "CONFIG_INVALID_VALUE", path: path97, message: issue4.message }];
6135
6165
  }
6136
6166
  }
6137
6167
  function isRecord(value) {
@@ -6453,7 +6483,7 @@ function resolveConfigV2(config2, configPath) {
6453
6483
  docs: {
6454
6484
  enabled: portal.docs?.enabled ?? true,
6455
6485
  provider,
6456
- content: provider === "external" ? [] : portal.docs?.content ? portal.docs.content.map((glob8) => fromConfig(configDir, glob8)) : [toGlobPath(path7.join(dir, "pages", "**", "*.{md,mdx}"))],
6486
+ content: provider === "external" ? [] : portal.docs?.content ? portal.docs.content.map((glob9) => fromConfig(configDir, glob9)) : [toGlobPath(path7.join(dir, "pages", "**", "*.{md,mdx}"))],
6457
6487
  url: portal.docs?.url
6458
6488
  },
6459
6489
  console: { enabled: portal.console?.enabled ?? true },
@@ -6495,7 +6525,8 @@ function resolveConfigV2(config2, configPath) {
6495
6525
  file: plans.file ? path7.resolve(configDir, plans.file) : path7.join(root, "plans.json"),
6496
6526
  billing: {
6497
6527
  provider: plans.billing?.provider ?? "stripe",
6498
- apiKey: plans.billing?.apiKey,
6528
+ // Stripe is the only provider. Its key lives in the STRIPE_API_KEY secret unless the config names another.
6529
+ apiKey: plans.billing?.apiKey ?? ((plans.billing?.provider ?? "stripe") === "stripe" ? "secret:STRIPE_API_KEY" : void 0),
6499
6530
  webhookSecret: plans.billing?.webhookSecret,
6500
6531
  successUrl: plans.billing?.successUrl,
6501
6532
  cancelUrl: plans.billing?.cancelUrl
@@ -6575,10 +6606,141 @@ function loadConfigV2(pathOrDir) {
6575
6606
  }
6576
6607
 
6577
6608
  // src/config/v2/rules.ts
6578
- import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
6609
+ import { readdir as readdir2, readFile as readFile4, stat as stat2 } from "node:fs/promises";
6610
+ import path10 from "node:path";
6611
+ import { glob as glob2 } from "glob";
6612
+ import { parse as parseYaml } from "yaml";
6613
+
6614
+ // src/config/v2/tsx-pages.ts
6615
+ import { readFile as readFile3 } from "node:fs/promises";
6579
6616
  import path9 from "node:path";
6580
6617
  import { glob } from "glob";
6581
- import { parse as parseYaml } from "yaml";
6618
+ import globParent from "glob-parent";
6619
+
6620
+ // src/config/v2/docs-navigation.ts
6621
+ var DOCS_NAVIGATION_LABEL = "Documentation";
6622
+ var emptyBranch = () => ({ pages: /* @__PURE__ */ new Map(), folders: /* @__PURE__ */ new Map() });
6623
+ function folderLabel(name) {
6624
+ const words = name.replace(/[-_]+/g, " ").trim();
6625
+ return words.charAt(0).toUpperCase() + words.slice(1);
6626
+ }
6627
+ var byName = (a, b) => a.localeCompare(b);
6628
+ var toItems = (branch, prefix) => {
6629
+ const names = [...branch.pages.keys()].sort(
6630
+ (a, b) => a === "index" ? -1 : b === "index" ? 1 : byName(a, b)
6631
+ );
6632
+ const folders = [...branch.folders.keys()].sort(byName);
6633
+ const folderPath = prefix === "" ? "/" : `/${prefix.slice(0, -1)}`;
6634
+ return [
6635
+ ...names.map((name) => {
6636
+ const page = branch.pages.get(name);
6637
+ if (page.kind === "custom") {
6638
+ return {
6639
+ type: "custom-page",
6640
+ // The index page of a folder is the folder's own address.
6641
+ path: name === "index" ? folderPath : page.route,
6642
+ label: page.label,
6643
+ element: page.element
6644
+ };
6645
+ }
6646
+ return name === "index" ? { type: "doc", file: `${prefix}${name}`, path: folderPath } : `${prefix}${name}`;
6647
+ }),
6648
+ ...folders.map(
6649
+ (folder) => ({
6650
+ type: "category",
6651
+ label: folderLabel(folder),
6652
+ collapsible: true,
6653
+ collapsed: false,
6654
+ items: toItems(
6655
+ branch.folders.get(folder) ?? emptyBranch(),
6656
+ `${prefix}${folder}/`
6657
+ )
6658
+ })
6659
+ )
6660
+ ];
6661
+ };
6662
+ var branchOf = (root, route) => {
6663
+ const parts = route.split("/").filter(Boolean);
6664
+ const page = parts.pop();
6665
+ const branch = parts.reduce((current, folder) => {
6666
+ const next = current.folders.get(folder) ?? emptyBranch();
6667
+ current.folders.set(folder, next);
6668
+ return next;
6669
+ }, root);
6670
+ return { branch, page };
6671
+ };
6672
+ function docsNavigation(routes, customPages = []) {
6673
+ const root = emptyBranch();
6674
+ for (const route of routes) {
6675
+ const { branch, page } = branchOf(root, route);
6676
+ if (page) branch.pages.set(page, { kind: "doc" });
6677
+ }
6678
+ for (const custom of customPages) {
6679
+ const { branch, page } = branchOf(root, custom.route);
6680
+ if (page && !branch.pages.has(page))
6681
+ branch.pages.set(page, { kind: "custom", ...custom });
6682
+ }
6683
+ const items = toItems(root, "");
6684
+ return items.length === 0 ? [] : [{ type: "category", label: DOCS_NAVIGATION_LABEL, items }];
6685
+ }
6686
+
6687
+ // src/config/v2/tsx-pages.ts
6688
+ var IGNORED = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
6689
+ function isTsxHelper(relative) {
6690
+ return relative.split("/").some((part) => part.startsWith("_")) || /\.(test|spec|stories)\.tsx$/.test(relative) || relative.split("/").includes("node_modules");
6691
+ }
6692
+ function tsxPagePatterns(contentGlobs) {
6693
+ const globs = contentGlobs.map(toGlobPath);
6694
+ const included = globs.filter((pattern) => !pattern.startsWith("!"));
6695
+ const excluded = globs.filter((pattern) => pattern.startsWith("!"));
6696
+ return [
6697
+ ...new Set(
6698
+ included.map(
6699
+ (pattern) => path9.posix.join(globParent(pattern), "**", "*.tsx")
6700
+ )
6701
+ ),
6702
+ ...excluded
6703
+ ];
6704
+ }
6705
+ var TITLE = /export\s+const\s+title\s*(?::\s*string\s*)?=\s*(["'`])((?:\\.|(?!\1).)*)\1/;
6706
+ function tsxPageLabel(source, file) {
6707
+ const match = TITLE.exec(source);
6708
+ if (match?.[2]) return match[2].replace(/\\(.)/g, "$1");
6709
+ const name = path9.basename(file, ".tsx");
6710
+ return folderLabel(
6711
+ name === "index" ? path9.basename(path9.dirname(file)) : name
6712
+ );
6713
+ }
6714
+ async function findTsxPages(contentGlobs) {
6715
+ const patterns = tsxPagePatterns(contentGlobs);
6716
+ const ignore2 = [
6717
+ ...IGNORED,
6718
+ ...patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1))
6719
+ ];
6720
+ const found = /* @__PURE__ */ new Map();
6721
+ for (const pattern of patterns.filter((p) => !p.startsWith("!"))) {
6722
+ const parent = globParent(pattern);
6723
+ const files = await glob(pattern, {
6724
+ absolute: true,
6725
+ posix: true,
6726
+ nodir: true,
6727
+ ignore: ignore2
6728
+ });
6729
+ for (const raw of files.sort()) {
6730
+ const file = raw.replace(/^\/\/\?\//, "");
6731
+ const relative = path9.posix.relative(parent, file);
6732
+ if (isTsxHelper(relative)) continue;
6733
+ const route = `/${relative.replace(/\.tsx$/, "")}`;
6734
+ if (found.has(route)) continue;
6735
+ const source = await readFile3(file, "utf8").catch(() => "");
6736
+ found.set(route, { route, file, label: tsxPageLabel(source, file) });
6737
+ }
6738
+ }
6739
+ return [...found.values()].sort((a, b) => a.route.localeCompare(b.route));
6740
+ }
6741
+ var TSX_PAGE_ELEMENT_PREFIX = "apitogo:tsx-page:";
6742
+
6743
+ // src/config/v2/rules.ts
6582
6744
  var SPEC_MAX_BYTES = 5 * 1024 * 1024;
6583
6745
  var SECRET_REFERENCE2 = /^secret:([A-Za-z_][A-Za-z0-9_]*)$/;
6584
6746
  async function isFile(target) {
@@ -6727,13 +6889,13 @@ async function portalRules(config2, resolved, out) {
6727
6889
  }
6728
6890
  if (await isFile(portal.theme)) {
6729
6891
  try {
6730
- JSON.parse(await readFile2(portal.theme, "utf8"));
6892
+ JSON.parse(await readFile4(portal.theme, "utf8"));
6731
6893
  } catch (error) {
6732
6894
  out.error({
6733
6895
  code: "PORTAL_THEME_INVALID",
6734
6896
  path: "portal.theme",
6735
6897
  message: `the theme file is not valid JSON (${error instanceof Error ? error.message : String(error)}).`,
6736
- hint: path9.relative(resolved.configDir, portal.theme)
6898
+ hint: path10.relative(resolved.configDir, portal.theme)
6737
6899
  });
6738
6900
  }
6739
6901
  }
@@ -6742,16 +6904,17 @@ async function docsContentRule(config2, patterns, resolved, out) {
6742
6904
  const globs = patterns.map(toGlobPath);
6743
6905
  const include = globs.filter((pattern) => !pattern.startsWith("!"));
6744
6906
  const ignore2 = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
6745
- const matches = include.length > 0 ? await glob(include, { nodir: true, ignore: ignore2, absolute: true }) : [];
6907
+ const matches = include.length > 0 ? await glob2(include, { nodir: true, ignore: ignore2, absolute: true }) : [];
6746
6908
  if (matches.length > 0) return;
6909
+ if ((await findTsxPages(patterns)).length > 0) return;
6747
6910
  const shown = config2.portal?.docs?.content ?? [
6748
- path9.relative(resolved.configDir, patterns[0] ?? "")
6911
+ path10.relative(resolved.configDir, patterns[0] ?? "")
6749
6912
  ];
6750
6913
  out.warn({
6751
6914
  code: "PORTAL_DOCS_EMPTY",
6752
6915
  path: "portal.docs",
6753
6916
  message: `portal.docs matched 0 files (${shown.join(", ")}). The site will only contain the API reference.`,
6754
- hint: "Add .md or .mdx files, or point docs.content at your existing docs."
6917
+ hint: "Add .md, .mdx or .tsx pages, or point docs.content at your existing docs."
6755
6918
  });
6756
6919
  }
6757
6920
  function isIpLiteral(hostname) {
@@ -6818,8 +6981,8 @@ async function gatewayRules(resolved, out) {
6818
6981
  }
6819
6982
  }
6820
6983
  if (origin.source) {
6821
- const inside = path9.relative(resolved.configDir, origin.source);
6822
- if (inside.startsWith("..") || path9.isAbsolute(inside)) {
6984
+ const inside = path10.relative(resolved.configDir, origin.source);
6985
+ if (inside.startsWith("..") || path10.isAbsolute(inside)) {
6823
6986
  out.error({
6824
6987
  code: "GATEWAY_ORIGIN_INVALID",
6825
6988
  path: "gateway.origin.source",
@@ -6904,7 +7067,7 @@ async function gatewayRules(resolved, out) {
6904
7067
  }
6905
7068
  async function specRule(spec, index, resolved, out) {
6906
7069
  const at = `gateway.specs[${index}]`;
6907
- const shown = path9.relative(resolved.configDir, spec);
7070
+ const shown = path10.relative(resolved.configDir, spec);
6908
7071
  const info = await stat2(spec).catch(() => void 0);
6909
7072
  if (!info?.isFile()) {
6910
7073
  out.error({
@@ -6924,7 +7087,7 @@ async function specRule(spec, index, resolved, out) {
6924
7087
  }
6925
7088
  let document;
6926
7089
  try {
6927
- const text = await readFile2(spec, "utf8");
7090
+ const text = await readFile4(spec, "utf8");
6928
7091
  document = spec.toLowerCase().endsWith(".json") ? JSON.parse(text) : parseYaml(text);
6929
7092
  } catch (error) {
6930
7093
  out.error({
@@ -7066,19 +7229,19 @@ function identityRules(config2, resolved, out) {
7066
7229
  }
7067
7230
  }
7068
7231
  }
7069
- async function plansRules(config2, resolved, out) {
7232
+ async function plansRules(resolved, out, paid) {
7070
7233
  const plans = resolved.plans;
7071
7234
  if (!plans?.enabled) return;
7072
7235
  if (!await isFile(plans.file)) {
7073
7236
  out.error({
7074
7237
  code: "PLANS_FILE_INVALID",
7075
7238
  path: "plans.file",
7076
- message: `the plan catalog was not found: ${path9.relative(resolved.configDir, plans.file)}.`,
7239
+ message: `the plan catalog was not found: ${path10.relative(resolved.configDir, plans.file)}.`,
7077
7240
  hint: "Create it with `apitogo add plans`."
7078
7241
  });
7079
7242
  } else {
7080
7243
  try {
7081
- JSON.parse(await readFile2(plans.file, "utf8"));
7244
+ JSON.parse(await readFile4(plans.file, "utf8"));
7082
7245
  } catch (error) {
7083
7246
  out.error({
7084
7247
  code: "PLANS_FILE_INVALID",
@@ -7087,15 +7250,7 @@ async function plansRules(config2, resolved, out) {
7087
7250
  });
7088
7251
  }
7089
7252
  }
7090
- if (plans.billing.provider === "stripe") {
7091
- if (!config2.plans?.billing?.apiKey) {
7092
- out.error({
7093
- code: "PLANS_BILLING_INCOMPLETE",
7094
- path: "plans.billing.apiKey",
7095
- message: "plans.billing.apiKey is required when billing.provider is stripe.",
7096
- hint: 'Set it to a secret reference, for example "secret:STRIPE_API_KEY".'
7097
- });
7098
- }
7253
+ if (paid) {
7099
7254
  const headless = !(resolved.portal?.enabled === true);
7100
7255
  for (const field of ["successUrl", "cancelUrl"]) {
7101
7256
  if (headless && !plans.billing[field]) {
@@ -7109,8 +7264,12 @@ async function plansRules(config2, resolved, out) {
7109
7264
  }
7110
7265
  }
7111
7266
  }
7112
- function secretRules(config2, context, out, skipped) {
7113
- const refs = collectSecretRefs(config2);
7267
+ function secretRules(config2, resolved, context, out, skipped, paid) {
7268
+ const stripeKeyPath = "plans.billing.apiKey";
7269
+ const named = collectSecretRefs(config2).filter(
7270
+ (ref) => paid || !ref.path.startsWith("plans.billing")
7271
+ );
7272
+ const refs = paid && !named.some((ref) => ref.path === stripeKeyPath) ? [...named, { name: stripeKeySecretName(resolved), path: stripeKeyPath }] : named;
7114
7273
  if (refs.length === 0) return;
7115
7274
  if (!context.knownSecrets) {
7116
7275
  skipped.push(
@@ -7119,7 +7278,14 @@ function secretRules(config2, context, out, skipped) {
7119
7278
  return;
7120
7279
  }
7121
7280
  for (const ref of refs) {
7122
- if (!context.knownSecrets.has(ref.name)) {
7281
+ if (ref.path === stripeKeyPath && !context.knownSecrets.has(ref.name)) {
7282
+ out.warn({
7283
+ code: "SECRET_NOT_SET",
7284
+ path: ref.path,
7285
+ message: `a plan has a price, and the Stripe key (secret ${ref.name}) is not set yet.`,
7286
+ hint: `apitogo deploy asks for it. Or run: apitogo secrets set ${ref.name}`
7287
+ });
7288
+ } else if (!context.knownSecrets.has(ref.name)) {
7123
7289
  out.error({
7124
7290
  code: "SECRET_NOT_SET",
7125
7291
  path: ref.path,
@@ -7139,12 +7305,13 @@ async function checkConfigV2(config2, resolved, context = {}) {
7139
7305
  error: (issue4) => errors.push(issue4),
7140
7306
  warn: (issue4) => warnings.push(issue4)
7141
7307
  };
7308
+ const paid = await billingNeeded(resolved);
7142
7309
  dependencyRules(config2, resolved, out);
7143
- secretRules(config2, context, out, skipped);
7310
+ secretRules(config2, resolved, context, out, skipped, paid);
7144
7311
  await portalRules(config2, resolved, out);
7145
7312
  await gatewayRules(resolved, out);
7146
7313
  identityRules(config2, resolved, out);
7147
- await plansRules(config2, resolved, out);
7314
+ await plansRules(resolved, out, paid);
7148
7315
  return { errors, warnings, skipped };
7149
7316
  }
7150
7317
 
@@ -7190,8 +7357,8 @@ function portalProfile(resolved) {
7190
7357
  }
7191
7358
 
7192
7359
  // src/config/v2/manifest.ts
7193
- import { mkdir as mkdir3, readFile as readFile3, rename, rm, writeFile as writeFile4 } from "node:fs/promises";
7194
- import path10 from "node:path";
7360
+ import { mkdir as mkdir3, readFile as readFile5, rename, rm, writeFile as writeFile4 } from "node:fs/promises";
7361
+ import path11 from "node:path";
7195
7362
  import { z as z3 } from "zod";
7196
7363
  var SUPPORTED_MANIFEST_SCHEMA = 1;
7197
7364
  var Capabilities = z3.record(z3.string(), z3.boolean()).default({});
@@ -7240,7 +7407,7 @@ var ServiceManifestSchema = z3.looseObject({
7240
7407
  }).optional()
7241
7408
  }).default({})
7242
7409
  });
7243
- var issue = (code, path95, message, hint) => ({ code, path: path95, message, hint });
7410
+ var issue = (code, path97, message, hint) => ({ code, path: path97, message, hint });
7244
7411
  function parseManifest(raw, file = "services.json") {
7245
7412
  const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
7246
7413
  if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
@@ -7273,7 +7440,7 @@ async function readManifest(state) {
7273
7440
  const file = manifestPathFor(state);
7274
7441
  let text;
7275
7442
  try {
7276
- text = await readFile3(file, "utf8");
7443
+ text = await readFile5(file, "utf8");
7277
7444
  } catch (error) {
7278
7445
  if (error.code === "ENOENT") return void 0;
7279
7446
  throw error;
@@ -7457,7 +7624,7 @@ function withPlansEntry(existing, info) {
7457
7624
  }
7458
7625
  async function writeManifest(state, manifest) {
7459
7626
  const file = manifestPathFor(state);
7460
- await mkdir3(path10.dirname(file), { recursive: true });
7627
+ await mkdir3(path11.dirname(file), { recursive: true });
7461
7628
  const temporary = `${file}.${process.pid}.tmp`;
7462
7629
  try {
7463
7630
  await writeFile4(
@@ -7488,7 +7655,7 @@ async function removeServicesFromManifest(state, names) {
7488
7655
  }
7489
7656
 
7490
7657
  // src/config/v2/gateway-reference.ts
7491
- import { readFile as readFile4 } from "node:fs/promises";
7658
+ import { readFile as readFile6 } from "node:fs/promises";
7492
7659
  import { parse } from "yaml";
7493
7660
  var DEFAULT_KEY_HEADER = "Ocp-Apim-Subscription-Key";
7494
7661
  var METHODS = [
@@ -7502,8 +7669,8 @@ var METHODS = [
7502
7669
  "trace"
7503
7670
  ];
7504
7671
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7505
- function actionKey(method, path95) {
7506
- const trimmed = path95.trim();
7672
+ function actionKey(method, path97) {
7673
+ const trimmed = path97.trim();
7507
7674
  const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
7508
7675
  const normalized = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") || "/" : withSlash;
7509
7676
  return `${method.trim().toLowerCase()}:${normalized}`;
@@ -7540,8 +7707,8 @@ var ReferenceSpecError = class extends Error {
7540
7707
  };
7541
7708
  function operationsOf(paths) {
7542
7709
  return Object.entries(paths).flatMap(
7543
- ([path95, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7544
- (method) => [path95, item, method]
7710
+ ([path97, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7711
+ (method) => [path97, item, method]
7545
7712
  ) : []
7546
7713
  );
7547
7714
  }
@@ -7556,14 +7723,14 @@ function buildReferenceSpec(specText, options) {
7556
7723
  throw new ReferenceSpecError("The OpenAPI file could not be read.");
7557
7724
  const doc = structuredClone(root);
7558
7725
  const paths = isObject(doc.paths) ? doc.paths : {};
7559
- for (const [path95, item, method] of operationsOf(paths))
7560
- if (options.deniedOperations.has(actionKey(method, path95)))
7726
+ for (const [path97, item, method] of operationsOf(paths))
7727
+ if (options.deniedOperations.has(actionKey(method, path97)))
7561
7728
  delete item[method];
7562
- for (const [path95, item] of Object.entries(paths))
7729
+ for (const [path97, item] of Object.entries(paths))
7563
7730
  if (!isObject(item) || !METHODS.some((method) => method in item))
7564
- delete paths[path95];
7565
- for (const [path95, item, method] of operationsOf(paths))
7566
- if (options.publicOperations.has(actionKey(method, path95)))
7731
+ delete paths[path97];
7732
+ for (const [path97, item, method] of operationsOf(paths))
7733
+ if (options.publicOperations.has(actionKey(method, path97)))
7567
7734
  item[method].security = [];
7568
7735
  doc.servers = [{ url: options.gatewayBaseUrl.replace(/\/+$/, "") }];
7569
7736
  for (const item of Object.values(paths)) {
@@ -7626,7 +7793,7 @@ async function renderReferenceSpec(gateway, gatewayBaseUrl, keyHeaderName) {
7626
7793
  throw new ReferenceSpecError("gateway.specs lists no OpenAPI file.");
7627
7794
  let text;
7628
7795
  try {
7629
- text = await readFile4(file, "utf8");
7796
+ text = await readFile6(file, "utf8");
7630
7797
  } catch {
7631
7798
  throw new ReferenceSpecError(`Cannot read the OpenAPI file ${file}.`);
7632
7799
  }
@@ -7697,69 +7864,16 @@ function stableJson(value) {
7697
7864
  }
7698
7865
 
7699
7866
  // src/config/v2/wrapper.ts
7700
- import { mkdir as mkdir4, readFile as readFile6, rename as rename2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
7701
- import path12 from "node:path";
7702
-
7703
- // src/config/v2/docs-navigation.ts
7704
- var DOCS_NAVIGATION_LABEL = "Documentation";
7705
- var emptyBranch = () => ({ pages: /* @__PURE__ */ new Set(), folders: /* @__PURE__ */ new Map() });
7706
- function folderLabel(name) {
7707
- const words = name.replace(/[-_]+/g, " ").trim();
7708
- return words.charAt(0).toUpperCase() + words.slice(1);
7709
- }
7710
- var byName = (a, b) => a.localeCompare(b);
7711
- var toItems = (branch, prefix) => {
7712
- const pages = [...branch.pages].sort(
7713
- (a, b) => a === "index" ? -1 : b === "index" ? 1 : byName(a, b)
7714
- );
7715
- const folders = [...branch.folders.keys()].sort(byName);
7716
- return [
7717
- // The index page of a folder is the folder's own address, so the sidebar also shows on `/`.
7718
- ...pages.map(
7719
- (page) => page === "index" ? {
7720
- type: "doc",
7721
- file: `${prefix}${page}`,
7722
- path: prefix === "" ? "/" : `/${prefix.slice(0, -1)}`
7723
- } : `${prefix}${page}`
7724
- ),
7725
- ...folders.map(
7726
- (folder) => ({
7727
- type: "category",
7728
- label: folderLabel(folder),
7729
- collapsible: true,
7730
- collapsed: false,
7731
- items: toItems(
7732
- branch.folders.get(folder) ?? emptyBranch(),
7733
- `${prefix}${folder}/`
7734
- )
7735
- })
7736
- )
7737
- ];
7738
- };
7739
- function docsNavigation(routes) {
7740
- const root = emptyBranch();
7741
- for (const route of routes) {
7742
- const parts = route.split("/").filter(Boolean);
7743
- const page = parts.pop();
7744
- if (!page) continue;
7745
- const branch = parts.reduce((current, folder) => {
7746
- const next = current.folders.get(folder) ?? emptyBranch();
7747
- current.folders.set(folder, next);
7748
- return next;
7749
- }, root);
7750
- branch.pages.add(page);
7751
- }
7752
- const items = toItems(root, "");
7753
- return items.length === 0 ? [] : [{ type: "category", label: DOCS_NAVIGATION_LABEL, items }];
7754
- }
7867
+ import { mkdir as mkdir4, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
7868
+ import path13 from "node:path";
7755
7869
 
7756
7870
  // src/config/v2/home-redirect.ts
7757
- import path11 from "node:path";
7758
- import { glob as glob2 } from "glob";
7759
- import globParent from "glob-parent";
7871
+ import path12 from "node:path";
7872
+ import { glob as glob3 } from "glob";
7873
+ import globParent2 from "glob-parent";
7760
7874
 
7761
7875
  // src/lib/util/readFrontmatter.ts
7762
- import { readFile as readFile5 } from "node:fs/promises";
7876
+ import { readFile as readFile7 } from "node:fs/promises";
7763
7877
  import matter from "gray-matter";
7764
7878
  import { parse as parse2, stringify } from "yaml";
7765
7879
  var yaml = {
@@ -7767,25 +7881,25 @@ var yaml = {
7767
7881
  stringify: (obj) => stringify(obj)
7768
7882
  };
7769
7883
  var readFrontmatter = async (filePath) => {
7770
- const content = await readFile5(filePath, "utf-8");
7884
+ const content = await readFile7(filePath, "utf-8");
7771
7885
  const normalizedContent = content.replace(/\r\n/g, "\n");
7772
7886
  return matter(normalizedContent, { engines: { yaml } });
7773
7887
  };
7774
7888
 
7775
7889
  // src/config/v2/home-redirect.ts
7776
- var IGNORED = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
7890
+ var IGNORED2 = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
7777
7891
  async function docRoutes(patterns, options = {}) {
7778
7892
  const globs = patterns.map(toGlobPath);
7779
7893
  const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
7780
7894
  const included = globs.filter((pattern) => !pattern.startsWith("!"));
7781
7895
  const routes = await Promise.all(
7782
7896
  included.map(async (pattern) => {
7783
- const files = await glob2(pattern, {
7897
+ const files = await glob3(pattern, {
7784
7898
  absolute: true,
7785
7899
  posix: true,
7786
- ignore: [...IGNORED, ...excluded]
7900
+ ignore: [...IGNORED2, ...excluded]
7787
7901
  });
7788
- const parent = globParent(pattern);
7902
+ const parent = globParent2(pattern);
7789
7903
  const published = options.includeDrafts ? files : (await Promise.all(
7790
7904
  files.map(async (file) => ({
7791
7905
  file,
@@ -7793,7 +7907,7 @@ async function docRoutes(patterns, options = {}) {
7793
7907
  }))
7794
7908
  )).filter(({ draft }) => !draft).map(({ file }) => file);
7795
7909
  return published.map(
7796
- (file) => `/${path11.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
7910
+ (file) => `/${path12.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
7797
7911
  );
7798
7912
  })
7799
7913
  );
@@ -7842,10 +7956,10 @@ function protectedRoutesFor(input5) {
7842
7956
  // src/config/v2/wrapper.ts
7843
7957
  var WRAPPER_FILE = "apitogo.config.tsx";
7844
7958
  var CONTENT_LINK = "content";
7845
- function contentGlob(configDir, glob8) {
7846
- const negated = glob8.startsWith("!");
7847
- const absolute = negated ? glob8.slice(1) : glob8;
7848
- const relative = path12.relative(configDir, absolute).split(path12.sep).join("/");
7959
+ function contentGlob(configDir, glob9) {
7960
+ const negated = glob9.startsWith("!");
7961
+ const absolute = negated ? glob9.slice(1) : glob9;
7962
+ const relative = path13.relative(configDir, absolute).split(path13.sep).join("/");
7849
7963
  return `${negated ? "!" : ""}/${CONTENT_LINK}/${relative}`;
7850
7964
  }
7851
7965
  var PLANS_CONSOLE_CAPABILITIES = [
@@ -8014,7 +8128,7 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8014
8128
  enabled: true,
8015
8129
  path: "/",
8016
8130
  layout: "landing",
8017
- page: portal.landing.split(path12.sep).join("/")
8131
+ page: portal.landing.split(path13.sep).join("/")
8018
8132
  }
8019
8133
  } : {},
8020
8134
  // The plans pages (pricing, choosing a plan) exist only when billing is on, and the plans service is what makes it so.
@@ -8031,7 +8145,7 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8031
8145
  ...plan2.docs ? {
8032
8146
  docs: {
8033
8147
  files: portal?.docs.content.map(
8034
- (glob8) => contentGlob(resolved.configDir, glob8)
8148
+ (glob9) => contentGlob(resolved.configDir, glob9)
8035
8149
  )
8036
8150
  }
8037
8151
  } : {},
@@ -8077,33 +8191,72 @@ function authenticationFor(plan2) {
8077
8191
  return { type: "openid", ...signIn };
8078
8192
  }
8079
8193
  var PREVIEW_BANNER_MESSAGE = "Preview mode: sign in with demo@apitogo.local / password to try the console. Run `apitogo deploy` for the real services.";
8080
- function renderWrapper(plan2) {
8194
+ function renderWrapper(plan2, pages = []) {
8081
8195
  const imports = [
8082
8196
  'import { buildApitogoConfig } from "@lukoweb/apitogo/build-config";',
8083
8197
  ...plan2.plansPlugin ? [
8084
8198
  'import { devPortalBillingPlugin } from "@lukoweb/apitogo-plugin-dev-portal-billing";'
8085
8199
  ] : [],
8200
+ ...pages.length > 0 ? ['import { createElement } from "react";'] : [],
8201
+ ...pages.map(
8202
+ (page, index) => `import TsxPage${index} from ${JSON.stringify(page.importPath)};`
8203
+ ),
8086
8204
  `import siteConfig from "./${SITE_CONFIG_FILE}";`
8087
8205
  ];
8088
8206
  const plugins = plan2.plansPlugin ? `[devPortalBillingPlugin(${JSON.stringify(plan2.plansPlugin)})]` : "[]";
8207
+ const pageLines = pages.length === 0 ? [] : [
8208
+ "",
8209
+ "// React docs pages: each sidebar placeholder becomes the page's component.",
8210
+ "const tsxPages: Record<string, any> = {",
8211
+ ...pages.map(
8212
+ (page, index) => ` ${JSON.stringify(page.id)}: TsxPage${index},`
8213
+ ),
8214
+ "};",
8215
+ "const withTsxPages = (items: any[]): any[] =>",
8216
+ " items.map((item) => {",
8217
+ ' if (item === null || typeof item !== "object") return item;',
8218
+ " const page =",
8219
+ ' typeof item.element === "string" ? tsxPages[item.element] : undefined;',
8220
+ " return {",
8221
+ " ...item,",
8222
+ " ...(page ? { element: createElement(page) } : {}),",
8223
+ " ...(Array.isArray(item.items) ? { items: withTsxPages(item.items) } : {}),",
8224
+ " };",
8225
+ " });"
8226
+ ];
8089
8227
  return [
8090
8228
  "// Generated by apitogo from apitogo.config.json and services.json. Do not edit: it is rewritten on every build.",
8091
8229
  ...plan2.preview ? [
8092
8230
  "// PREVIEW: built from a stand-in manifest for local runs. A production build refuses this file."
8093
8231
  ] : [],
8094
8232
  ...imports,
8233
+ ...pageLines,
8095
8234
  "",
8096
- "export default buildApitogoConfig({",
8097
- " ...siteConfig,",
8098
- ` plugins: ${plugins},`,
8099
- "});",
8235
+ // The components go in after buildApitogoConfig: it copies the config with structuredClone, which cannot copy a
8236
+ // React element.
8237
+ ...pages.length > 0 ? [
8238
+ "const config = buildApitogoConfig({",
8239
+ " ...siteConfig,",
8240
+ ` plugins: ${plugins},`,
8241
+ "});",
8242
+ "",
8243
+ "export default {",
8244
+ " ...config,",
8245
+ " navigation: withTsxPages((config as any).navigation ?? []),",
8246
+ "};"
8247
+ ] : [
8248
+ "export default buildApitogoConfig({",
8249
+ " ...siteConfig,",
8250
+ ` plugins: ${plugins},`,
8251
+ "});"
8252
+ ],
8100
8253
  ""
8101
8254
  ].join("\n");
8102
8255
  }
8103
8256
  async function readDemoPlans(file) {
8104
8257
  if (!file) return void 0;
8105
8258
  try {
8106
- const parsed = JSON.parse(await readFile6(file, "utf8"));
8259
+ const parsed = JSON.parse(await readFile8(file, "utf8"));
8107
8260
  return Array.isArray(parsed.plans) ? parsed.plans : void 0;
8108
8261
  } catch {
8109
8262
  return void 0;
@@ -8113,7 +8266,7 @@ async function readTheme(resolved) {
8113
8266
  const file = resolved.portal?.theme;
8114
8267
  if (!file) return void 0;
8115
8268
  try {
8116
- return JSON.parse(await readFile6(file, "utf8"));
8269
+ return JSON.parse(await readFile8(file, "utf8"));
8117
8270
  } catch {
8118
8271
  return void 0;
8119
8272
  }
@@ -8171,9 +8324,19 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8171
8324
  });
8172
8325
  }
8173
8326
  }
8174
- const hostedDocRoutes = plan2.docs ? await docRoutes(profile.docs.content, {
8327
+ const markdownRoutes = plan2.docs ? await docRoutes(profile.docs.content, {
8175
8328
  includeDrafts: mode === "development"
8176
8329
  }) : [];
8330
+ const tsxPages = plan2.docs ? (await findTsxPages(profile.docs.content)).filter(
8331
+ (page) => !markdownRoutes.includes(page.route)
8332
+ ) : [];
8333
+ const wrapperPages = tsxPages.map((page, index) => ({
8334
+ id: `${TSX_PAGE_ELEMENT_PREFIX}${index}`,
8335
+ importPath: `./${CONTENT_LINK}/${path13.relative(resolved.configDir, page.file).split(path13.sep).join("/")}`
8336
+ }));
8337
+ const hostedDocRoutes = [
8338
+ .../* @__PURE__ */ new Set([...markdownRoutes, ...tsxPages.map((page) => page.route)])
8339
+ ].sort();
8177
8340
  if (plan2.plansPlugin && plan2.docs) {
8178
8341
  const landing = hostedDocRoutes.some(
8179
8342
  (route) => route === "/" || route === "/index"
@@ -8195,7 +8358,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8195
8358
  await readTheme(resolved),
8196
8359
  {
8197
8360
  redirects: home ? [home] : [],
8198
- docsNavigation: docsNavigation(hostedDocRoutes),
8361
+ docsNavigation: docsNavigation(
8362
+ markdownRoutes,
8363
+ tsxPages.map((page, index) => ({
8364
+ route: page.route,
8365
+ label: page.label,
8366
+ element: wrapperPages[index]?.id ?? ""
8367
+ }))
8368
+ ),
8199
8369
  docsPath: pickLandingRoute(hostedDocRoutes),
8200
8370
  demoPlans: plan2.demo && plan2.plansPlugin ? await readDemoPlans(resolved.plans?.file) : void 0,
8201
8371
  protectedRoutes: protectedRoutesFor({
@@ -8206,21 +8376,21 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8206
8376
  })
8207
8377
  }
8208
8378
  );
8209
- const wrapper = renderWrapper(plan2);
8210
- const wrapperPath = path12.join(resolved.state.generatedDir, WRAPPER_FILE);
8379
+ const wrapper = renderWrapper(plan2, wrapperPages);
8380
+ const wrapperPath = path13.join(resolved.state.generatedDir, WRAPPER_FILE);
8211
8381
  if (options.write !== false) {
8212
- await mkdir4(path12.dirname(wrapperPath), { recursive: true });
8382
+ await mkdir4(path13.dirname(wrapperPath), { recursive: true });
8213
8383
  await writeFile5(
8214
- path12.join(path12.dirname(wrapperPath), SITE_CONFIG_FILE),
8384
+ path13.join(path13.dirname(wrapperPath), SITE_CONFIG_FILE),
8215
8385
  `${JSON.stringify(siteConfig, null, 2)}
8216
8386
  `
8217
8387
  );
8218
- const referenceFile = path12.join(
8219
- path12.dirname(wrapperPath),
8388
+ const referenceFile = path13.join(
8389
+ path13.dirname(wrapperPath),
8220
8390
  REFERENCE_SPEC_FILE
8221
8391
  );
8222
8392
  if (referenceSpec !== void 0) {
8223
- await mkdir4(path12.dirname(referenceFile), { recursive: true });
8393
+ await mkdir4(path13.dirname(referenceFile), { recursive: true });
8224
8394
  await writeFile5(referenceFile, referenceSpec);
8225
8395
  } else {
8226
8396
  await rm2(referenceFile, { force: true });
@@ -8249,9 +8419,9 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8249
8419
 
8250
8420
  // src/config/v2/workspace.ts
8251
8421
  import { createHash as createHash2 } from "node:crypto";
8252
- import { access as access2, lstat, readFile as readFile7, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
8253
- import path13 from "node:path";
8254
- import { glob as glob3 } from "glob";
8422
+ import { access as access2, lstat, readFile as readFile9, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
8423
+ import path14 from "node:path";
8424
+ import { glob as glob4 } from "glob";
8255
8425
 
8256
8426
  // src/config/v2/link-directory.ts
8257
8427
  import { symlink } from "node:fs/promises";
@@ -8284,18 +8454,18 @@ async function relink(target, link) {
8284
8454
  }
8285
8455
  var GENERATED_PACKAGE_JSON = '{\n "private": true,\n "type": "module"\n}\n';
8286
8456
  async function writeGeneratedPackageJson(root) {
8287
- const file = path13.join(root, "package.json");
8288
- const current = await readFile7(file, "utf8").catch(() => void 0);
8457
+ const file = path14.join(root, "package.json");
8458
+ const current = await readFile9(file, "utf8").catch(() => void 0);
8289
8459
  if (current !== GENERATED_PACKAGE_JSON)
8290
8460
  await writeFile6(file, GENERATED_PACKAGE_JSON, "utf8");
8291
8461
  }
8292
8462
  async function materializeWorkspace(resolved, prepared) {
8293
- const root = path13.dirname(prepared.wrapperPath);
8463
+ const root = path14.dirname(prepared.wrapperPath);
8294
8464
  await ensureStateDir(resolved.state);
8295
8465
  await writeGeneratedPackageJson(root);
8296
- await relink(resolved.configDir, path13.join(root, CONTENT_LINK));
8297
- const publicDir = resolved.portal && path13.join(resolved.portal.dir, "public");
8298
- const publicLink = path13.join(root, "public");
8466
+ await relink(resolved.configDir, path14.join(root, CONTENT_LINK));
8467
+ const publicDir = resolved.portal && path14.join(resolved.portal.dir, "public");
8468
+ const publicLink = path14.join(root, "public");
8299
8469
  if (publicDir && await exists2(publicDir)) {
8300
8470
  await relink(publicDir, publicLink);
8301
8471
  } else {
@@ -8304,7 +8474,7 @@ async function materializeWorkspace(resolved, prepared) {
8304
8474
  return root;
8305
8475
  }
8306
8476
  async function configFingerprint(configPath) {
8307
- const text = await readFile7(configPath, "utf8");
8477
+ const text = await readFile9(configPath, "utf8");
8308
8478
  try {
8309
8479
  const raw = JSON.parse(text);
8310
8480
  if (raw.project) {
@@ -8320,8 +8490,10 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8320
8490
  const portal = resolved.portal;
8321
8491
  const patterns = [
8322
8492
  ...portal?.docs.content ?? [],
8493
+ // React docs pages and the helpers next to them are imported by the wrapper, so their contents decide the build too.
8494
+ ...portal?.docs.provider === "apitogo" ? tsxPagePatterns(portal.docs.content) : [],
8323
8495
  ...resolved.gateway?.specs ?? [],
8324
- ...portal ? [portal.theme, path13.join(portal.dir, "public", "**", "*")] : []
8496
+ ...portal ? [portal.theme, path14.join(portal.dir, "public", "**", "*")] : []
8325
8497
  ];
8326
8498
  const positive = patterns.filter((p) => !p.startsWith("!"));
8327
8499
  const ignore2 = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
@@ -8329,7 +8501,7 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8329
8501
  ...new Set(
8330
8502
  (await Promise.all(
8331
8503
  positive.map(
8332
- (pattern) => glob3(pattern.split(path13.sep).join("/"), {
8504
+ (pattern) => glob4(pattern.split(path14.sep).join("/"), {
8333
8505
  absolute: true,
8334
8506
  nodir: true,
8335
8507
  ignore: [...ignore2, "**/node_modules/**"]
@@ -8351,9 +8523,9 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8351
8523
  `);
8352
8524
  }
8353
8525
  for (const file of files) {
8354
- hash2.update(`file:${path13.relative(resolved.configDir, file)}
8526
+ hash2.update(`file:${path14.relative(resolved.configDir, file)}
8355
8527
  `);
8356
- hash2.update(await readFile7(file));
8528
+ hash2.update(await readFile9(file));
8357
8529
  }
8358
8530
  return hash2.digest("hex");
8359
8531
  }
@@ -8369,7 +8541,7 @@ import {
8369
8541
  stat as stat3,
8370
8542
  writeFile as writeFile7
8371
8543
  } from "node:fs/promises";
8372
- import path14 from "node:path";
8544
+ import path15 from "node:path";
8373
8545
 
8374
8546
  // src/config/v2/full-server.ts
8375
8547
  var FULL_SERVER_SOURCE = "server";
@@ -8735,7 +8907,7 @@ var exists3 = (target) => access3(target).then(
8735
8907
  () => true,
8736
8908
  () => false
8737
8909
  );
8738
- var rel = (dir, target) => path14.relative(dir, target).split(path14.sep).join("/") || ".";
8910
+ var rel = (dir, target) => path15.relative(dir, target).split(path15.sep).join("/") || ".";
8739
8911
  var BLANK_SPEC = (name) => `openapi: 3.1.0
8740
8912
  info:
8741
8913
  title: ${JSON.stringify(name)}
@@ -8834,14 +9006,14 @@ function pair(value, flag, unit) {
8834
9006
  return [Number(match[1]), Number(match[2])];
8835
9007
  }
8836
9008
  function insideProject(dir, requested, what) {
8837
- const absolute = path14.resolve(dir, requested);
8838
- const relative = path14.relative(dir, absolute);
8839
- if (relative === "" || relative.startsWith("..") || path14.isAbsolute(relative)) {
9009
+ const absolute = path15.resolve(dir, requested);
9010
+ const relative = path15.relative(dir, absolute);
9011
+ if (relative === "" || relative.startsWith("..") || path15.isAbsolute(relative)) {
8840
9012
  throw new ServiceEditError(
8841
9013
  `${what} "${requested}" is outside the project directory.`
8842
9014
  );
8843
9015
  }
8844
- return relative.split(path14.sep).join("/");
9016
+ return relative.split(path15.sep).join("/");
8845
9017
  }
8846
9018
  async function addFile(context, target, content) {
8847
9019
  if (await exists3(target)) context.keep.push(target);
@@ -8893,16 +9065,16 @@ async function buildPortal(context) {
8893
9065
  { value: "starter", label: "A starter page I can edit" },
8894
9066
  { value: "blank", label: "An empty pages folder" }
8895
9067
  ]);
8896
- const pages = path14.join(resolved.root, "portal", "pages");
9068
+ const pages = path15.join(resolved.root, "portal", "pages");
8897
9069
  if (template === "blank")
8898
- await addFile(context, path14.join(pages, ".gitkeep"), "");
9070
+ await addFile(context, path15.join(pages, ".gitkeep"), "");
8899
9071
  else if (isFilled(options.template)) {
8900
9072
  await addFile(
8901
9073
  context,
8902
- path14.join(pages, "introduction.mdx"),
9074
+ path15.join(pages, "introduction.mdx"),
8903
9075
  starterPage(name)
8904
9076
  );
8905
- const landingFile = path14.join(
9077
+ const landingFile = path15.join(
8906
9078
  resolved.root,
8907
9079
  "portal",
8908
9080
  "landing",
@@ -8920,7 +9092,7 @@ async function buildPortal(context) {
8920
9092
  } else
8921
9093
  await addFile(
8922
9094
  context,
8923
- path14.join(pages, "index.mdx"),
9095
+ path15.join(pages, "index.mdx"),
8924
9096
  starterPage(name)
8925
9097
  );
8926
9098
  }
@@ -8936,18 +9108,18 @@ async function buildPortal(context) {
8936
9108
  };
8937
9109
  }
8938
9110
  async function createSourceFolder(context, sourcePath, runtime, writeSample) {
8939
- const folder = path14.join(context.dir, sourcePath);
9111
+ const folder = path15.join(context.dir, sourcePath);
8940
9112
  const entries = await readdir3(folder).catch(() => []);
8941
9113
  if (entries.some((name) => name !== ".gitkeep")) return false;
8942
9114
  if (writeSample && runtime === FULL_SERVER_RUNTIME) {
8943
9115
  for (const [file, content] of Object.entries(
8944
9116
  fullServerFiles(context.resolved.project.name)
8945
9117
  ))
8946
- await addFile(context, path14.join(folder, file), content);
9118
+ await addFile(context, path15.join(folder, file), content);
8947
9119
  return true;
8948
9120
  }
8949
9121
  if (entries.length === 0)
8950
- await addFile(context, path14.join(folder, ".gitkeep"), "");
9122
+ await addFile(context, path15.join(folder, ".gitkeep"), "");
8951
9123
  context.notes.push(
8952
9124
  `The folder ${sourcePath}/ is empty: put your backend code there before you deploy.`
8953
9125
  );
@@ -9059,7 +9231,7 @@ async function buildGateway(context) {
9059
9231
  specs = [];
9060
9232
  for (const requested of requestedSpecs) {
9061
9233
  const relative = insideProject(dir, requested, "OpenAPI file");
9062
- if (!await stat3(path14.join(dir, relative)).then(
9234
+ if (!await stat3(path15.join(dir, relative)).then(
9063
9235
  (s) => s.isFile(),
9064
9236
  () => false
9065
9237
  )) {
@@ -9070,7 +9242,7 @@ async function buildGateway(context) {
9070
9242
  specs.push(relative);
9071
9243
  }
9072
9244
  } else if (createSpec) {
9073
- const target = path14.join(resolved.apisDir, "openapi.yaml");
9245
+ const target = path15.join(resolved.apisDir, "openapi.yaml");
9074
9246
  await addFile(
9075
9247
  context,
9076
9248
  target,
@@ -9163,35 +9335,22 @@ async function buildPlans(context) {
9163
9335
  const { options, dir, resolved } = context;
9164
9336
  const o = options.plans ?? {};
9165
9337
  const template = o.template ?? (options.template === "blank" ? "none" : isFilled(options.template) ? "starter" : "free");
9166
- const billing = o.billing ?? (template === "tiered" ? "stripe" : "none");
9167
- if (template === "tiered" && billing === "none") {
9168
- throw new ServiceEditError(
9169
- "The tiered template has paid plans, which need a billing provider.",
9170
- "Use --billing stripe, or --template free."
9171
- );
9172
- }
9173
9338
  const file = o.file ? insideProject(dir, o.file, "Plans file") : void 0;
9174
- const target = file ? path14.join(dir, file) : resolved.plans?.file ?? path14.join(resolved.root, "plans.json");
9339
+ const target = file ? path15.join(dir, file) : resolved.plans?.file ?? path15.join(resolved.root, "plans.json");
9175
9340
  await addFile(
9176
9341
  context,
9177
9342
  target,
9178
9343
  `${JSON.stringify({ version: 1, plans: PLAN_TEMPLATES[template] }, null, 2)}
9179
9344
  `
9180
9345
  );
9181
- if (billing === "stripe") {
9346
+ if (PLAN_TEMPLATES[template].some((plan2) => isPaidPlan(plan2))) {
9182
9347
  context.notes.push(
9183
- "Set the billing key: apitogo secrets set STRIPE_API_KEY"
9348
+ "A plan has a price, so payments go through Stripe: apitogo deploy asks for your Stripe secret key."
9184
9349
  );
9185
9350
  }
9186
9351
  return {
9187
9352
  ...options.disabled ? { enabled: false } : {},
9188
- ...file ? { file } : {},
9189
- ...billing === "stripe" ? {
9190
- billing: {
9191
- provider: "stripe",
9192
- apiKey: "secret:STRIPE_API_KEY"
9193
- }
9194
- } : { billing: { provider: "none" } }
9353
+ ...file ? { file } : {}
9195
9354
  };
9196
9355
  }
9197
9356
  function reparse(configPath, raw) {
@@ -9210,11 +9369,11 @@ function dependencyProblems(config2, configPath) {
9210
9369
  return checkDependencies(config2, resolveConfigV2(config2, configPath));
9211
9370
  }
9212
9371
  async function planAddService(service, options) {
9213
- const dir = path14.resolve(options.dir);
9372
+ const dir = path15.resolve(options.dir);
9214
9373
  const { config: config2, resolved } = loadConfigV2(dir);
9215
9374
  if (config2[service] !== void 0) {
9216
9375
  throw new ServiceEditError(
9217
- `${service} is already in ${path14.basename(resolved.configPath)}.`,
9376
+ `${service} is already in ${path15.basename(resolved.configPath)}.`,
9218
9377
  `Edit the file, or run \`apitogo remove ${service}\` first. Its files are kept.`
9219
9378
  );
9220
9379
  }
@@ -9259,11 +9418,11 @@ async function planAddService(service, options) {
9259
9418
  };
9260
9419
  }
9261
9420
  async function planRemoveService(service, options) {
9262
- const dir = path14.resolve(options.dir);
9421
+ const dir = path15.resolve(options.dir);
9263
9422
  const { config: config2, resolved } = loadConfigV2(dir);
9264
9423
  if (config2[service] === void 0) {
9265
9424
  throw new ServiceEditError(
9266
- `${service} is not in ${path14.basename(resolved.configPath)}.`
9425
+ `${service} is not in ${path15.basename(resolved.configPath)}.`
9267
9426
  );
9268
9427
  }
9269
9428
  const { [service]: removed, ...rest } = config2;
@@ -9287,8 +9446,8 @@ async function planRemoveService(service, options) {
9287
9446
  if (service === "gateway" && r.gateway) owned.push(...r.gateway.specs);
9288
9447
  void removed;
9289
9448
  const inside = (target) => {
9290
- const relative = path14.relative(resolved.root, target);
9291
- return relative !== "" && !relative.startsWith("..") && !path14.isAbsolute(relative);
9449
+ const relative = path15.relative(resolved.root, target);
9450
+ return relative !== "" && !relative.startsWith("..") && !path15.isAbsolute(relative);
9292
9451
  };
9293
9452
  const deletes = [];
9294
9453
  const left = [];
@@ -9327,7 +9486,7 @@ async function writeConfig(configPath, config2) {
9327
9486
  async function applyServiceEdit(edit) {
9328
9487
  const created = [];
9329
9488
  for (const file of edit.files) {
9330
- await mkdir5(path14.dirname(file.path), { recursive: true });
9489
+ await mkdir5(path15.dirname(file.path), { recursive: true });
9331
9490
  try {
9332
9491
  await writeFile7(file.path, file.content, { flag: "wx" });
9333
9492
  created.push(file.path);
@@ -9343,7 +9502,7 @@ async function applyServiceEdit(edit) {
9343
9502
  function formatServiceEdit(edit) {
9344
9503
  const verb = edit.action === "add" ? "Adds" : "Removes";
9345
9504
  return [
9346
- `${verb} the ${edit.service} section in ${path14.basename(edit.configPath)}.`,
9505
+ `${verb} the ${edit.service} section in ${path15.basename(edit.configPath)}.`,
9347
9506
  ...edit.files.length > 0 ? [
9348
9507
  "Will create:",
9349
9508
  ...edit.files.map((file) => ` ${rel(edit.dir, file.path)}`)
@@ -9383,8 +9542,8 @@ async function clearProjectId(dir, projectId) {
9383
9542
 
9384
9543
  // src/config/v2/ledger.ts
9385
9544
  init_invariant();
9386
- import { mkdir as mkdir6, readFile as readFile8, rename as rename4, rm as rm5, writeFile as writeFile8 } from "node:fs/promises";
9387
- import path15 from "node:path";
9545
+ import { mkdir as mkdir6, readFile as readFile10, rename as rename4, rm as rm5, writeFile as writeFile8 } from "node:fs/promises";
9546
+ import path16 from "node:path";
9388
9547
  var LEDGER_VERSION = 1;
9389
9548
  var LedgerError = class extends ApitogoError {
9390
9549
  constructor(code, message, developerHint) {
@@ -9400,7 +9559,7 @@ var emptyLedger = () => ({
9400
9559
  async function readLedger(state) {
9401
9560
  let text;
9402
9561
  try {
9403
- text = await readFile8(state.file, "utf8");
9562
+ text = await readFile10(state.file, "utf8");
9404
9563
  } catch (error) {
9405
9564
  if (error.code === "ENOENT")
9406
9565
  return emptyLedger();
@@ -9439,7 +9598,7 @@ function corrupt(file, why) {
9439
9598
  );
9440
9599
  }
9441
9600
  async function writeLedger(state, ledger) {
9442
- await mkdir6(path15.dirname(state.file), { recursive: true });
9601
+ await mkdir6(path16.dirname(state.file), { recursive: true });
9443
9602
  const temporary = `${state.file}.${process.pid}.tmp`;
9444
9603
  try {
9445
9604
  await writeFile8(temporary, `${JSON.stringify(ledger, null, 2)}
@@ -9535,8 +9694,8 @@ init_invariant();
9535
9694
 
9536
9695
  // src/cli/check/v2.ts
9537
9696
  init_apitogo_config_core();
9538
- import { readFile as readFile15 } from "node:fs/promises";
9539
- import path49 from "node:path";
9697
+ import { readFile as readFile17 } from "node:fs/promises";
9698
+ import path50 from "node:path";
9540
9699
 
9541
9700
  // src/cli/platform/client/platform-api.ts
9542
9701
  import { basename } from "node:path";
@@ -9649,8 +9808,8 @@ var PlatformApiClient = class {
9649
9808
  constructor(config2) {
9650
9809
  this.config = config2;
9651
9810
  }
9652
- url(path95) {
9653
- return `${this.config.apiUrl}${path95.startsWith("/") ? path95 : `/${path95}`}`;
9811
+ url(path97) {
9812
+ return `${this.config.apiUrl}${path97.startsWith("/") ? path97 : `/${path97}`}`;
9654
9813
  }
9655
9814
  headers(extra) {
9656
9815
  return {
@@ -9660,9 +9819,9 @@ var PlatformApiClient = class {
9660
9819
  ...extra
9661
9820
  };
9662
9821
  }
9663
- async request(method, path95, init3) {
9822
+ async request(method, path97, init3) {
9664
9823
  const response = await fetchWithRetry(
9665
- this.url(path95),
9824
+ this.url(path97),
9666
9825
  {
9667
9826
  method,
9668
9827
  ...init3,
@@ -10108,15 +10267,15 @@ var PlatformApiClient = class {
10108
10267
  }
10109
10268
  );
10110
10269
  }
10111
- deleteDevPortalPage(projectId, path95) {
10112
- const query = new URLSearchParams({ path: path95 });
10270
+ deleteDevPortalPage(projectId, path97) {
10271
+ const query = new URLSearchParams({ path: path97 });
10113
10272
  return this.request(
10114
10273
  "DELETE",
10115
10274
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
10116
10275
  );
10117
10276
  }
10118
- renameDevPortalPage(projectId, path95, newName) {
10119
- const query = new URLSearchParams({ path: path95 });
10277
+ renameDevPortalPage(projectId, path97, newName) {
10278
+ const query = new URLSearchParams({ path: path97 });
10120
10279
  return this.request(
10121
10280
  "POST",
10122
10281
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -10232,8 +10391,8 @@ var PlatformApiClient = class {
10232
10391
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
10233
10392
  */
10234
10393
  validateGatewayPlans(plans, options = {}) {
10235
- const path95 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10236
- return this.request("POST", path95, {
10394
+ const path97 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10395
+ return this.request("POST", path97, {
10237
10396
  headers: { "Content-Type": "application/json" },
10238
10397
  body: JSON.stringify({ plans, planTier: options.planTier })
10239
10398
  });
@@ -10358,8 +10517,8 @@ var PlatformApiClient = class {
10358
10517
  }
10359
10518
  };
10360
10519
  async function readFileBytes(filePath) {
10361
- const { readFile: readFile26 } = await import("node:fs/promises");
10362
- const buffer = await readFile26(filePath);
10520
+ const { readFile: readFile28 } = await import("node:fs/promises");
10521
+ const buffer = await readFile28(filePath);
10363
10522
  const arrayBuffer = new ArrayBuffer(buffer.byteLength);
10364
10523
  new Uint8Array(arrayBuffer).set(buffer);
10365
10524
  return arrayBuffer;
@@ -10370,8 +10529,8 @@ function isNotFoundError(error) {
10370
10529
 
10371
10530
  // src/cli/platform/credentials-store.ts
10372
10531
  import fs from "node:fs";
10373
- import path16 from "node:path";
10374
- var CREDENTIALS_FILE = path16.join(
10532
+ import path17 from "node:path";
10533
+ var CREDENTIALS_FILE = path17.join(
10375
10534
  ZUDOKU_XDG_CONFIG_HOME,
10376
10535
  "credentials.json"
10377
10536
  );
@@ -10392,7 +10551,7 @@ function readStoredCredentials() {
10392
10551
  }
10393
10552
  }
10394
10553
  function writeStoredCredentials(creds) {
10395
- fs.mkdirSync(path16.dirname(CREDENTIALS_FILE), { recursive: true });
10554
+ fs.mkdirSync(path17.dirname(CREDENTIALS_FILE), { recursive: true });
10396
10555
  fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), {
10397
10556
  mode: 384
10398
10557
  });
@@ -10456,16 +10615,16 @@ function resolveProjectId(explicit, config2) {
10456
10615
  import {
10457
10616
  access as access4,
10458
10617
  mkdir as mkdir12,
10459
- readFile as readFile12,
10618
+ readFile as readFile14,
10460
10619
  rename as rename6,
10461
10620
  rm as rm8,
10462
10621
  writeFile as writeFile15
10463
10622
  } from "node:fs/promises";
10464
- import path48 from "node:path";
10623
+ import path49 from "node:path";
10465
10624
 
10466
10625
  // src/vite/build.ts
10467
- import { mkdir as mkdir11, readFile as readFile10, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
10468
- import path46 from "node:path";
10626
+ import { mkdir as mkdir11, readFile as readFile12, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
10627
+ import path47 from "node:path";
10469
10628
  import { build as esbuild } from "esbuild";
10470
10629
  import { build as viteBuild, mergeConfig as mergeConfig3 } from "vite";
10471
10630
 
@@ -10601,7 +10760,7 @@ init_invariant();
10601
10760
  init_joinUrl();
10602
10761
 
10603
10762
  // src/vite/config.ts
10604
- import path41 from "node:path";
10763
+ import path42 from "node:path";
10605
10764
  import dotenv from "dotenv";
10606
10765
  import colors8 from "picocolors";
10607
10766
  import {
@@ -10612,7 +10771,7 @@ import {
10612
10771
  // package.json
10613
10772
  var package_default = {
10614
10773
  name: "@lukoweb/apitogo",
10615
- version: "0.2.47",
10774
+ version: "0.2.50",
10616
10775
  type: "module",
10617
10776
  sideEffects: [
10618
10777
  "**/*.css",
@@ -11049,24 +11208,24 @@ async function loadDevPortalViteDefines(rootDir, mode) {
11049
11208
 
11050
11209
  // src/vite/package-root.ts
11051
11210
  init_file_exists();
11052
- import path21 from "node:path";
11211
+ import path22 from "node:path";
11053
11212
  var findPackageRoot = async (startDir) => {
11054
11213
  let dir = startDir;
11055
- while (dir !== path21.dirname(dir)) {
11056
- if (await fileExists(path21.join(dir, "package.json"))) return dir;
11057
- dir = path21.dirname(dir);
11214
+ while (dir !== path22.dirname(dir)) {
11215
+ if (await fileExists(path22.join(dir, "package.json"))) return dir;
11216
+ dir = path22.dirname(dir);
11058
11217
  }
11059
11218
  };
11060
11219
 
11061
11220
  // src/vite/pagefind-dev-stub.ts
11062
11221
  import fs2 from "node:fs/promises";
11063
- import path22 from "node:path";
11222
+ import path23 from "node:path";
11064
11223
  var NOT_BUILT_YET = 'throw new Error("NOT_BUILT_YET");';
11065
11224
  async function ensurePagefindDevStub(publicDir) {
11066
- const pagefindPath = path22.join(publicDir, "pagefind/pagefind.js");
11225
+ const pagefindPath = path23.join(publicDir, "pagefind/pagefind.js");
11067
11226
  const exists6 = await fs2.stat(pagefindPath).catch(() => false);
11068
11227
  if (!exists6) {
11069
- await fs2.mkdir(path22.dirname(pagefindPath), { recursive: true });
11228
+ await fs2.mkdir(path23.dirname(pagefindPath), { recursive: true });
11070
11229
  await fs2.writeFile(pagefindPath, NOT_BUILT_YET);
11071
11230
  }
11072
11231
  }
@@ -11078,7 +11237,7 @@ import react from "@vitejs/plugin-react";
11078
11237
  // src/vite/css/plugin.ts
11079
11238
  init_loader();
11080
11239
  init_plugin_theme();
11081
- import path23 from "node:path";
11240
+ import path24 from "node:path";
11082
11241
  import { isCSSRequest as isCSSRequest2 } from "vite";
11083
11242
 
11084
11243
  // src/vite/css/collect.ts
@@ -11113,7 +11272,7 @@ var VIRTUAL_ENTRY = "virtual:ssr-css.css";
11113
11272
  function vitePluginSsrCss(pluginOpts) {
11114
11273
  let server;
11115
11274
  const config2 = getCurrentConfig();
11116
- const virtualHref = path23.join(
11275
+ const virtualHref = path24.join(
11117
11276
  config2.basePath ?? "",
11118
11277
  `/@id/__x00__${VIRTUAL_ENTRY}?direct`
11119
11278
  );
@@ -11250,7 +11409,7 @@ var plugin_api_keys_default = viteApiKeysPlugin;
11250
11409
 
11251
11410
  // src/vite/plugin-api.ts
11252
11411
  import fs4 from "node:fs/promises";
11253
- import path29 from "node:path";
11412
+ import path30 from "node:path";
11254
11413
  import { deepEqual as deepEqual2 } from "fast-equals";
11255
11414
  import { runnerImport as runnerImport3 } from "vite";
11256
11415
  init_package_json();
@@ -11259,7 +11418,7 @@ init_loader();
11259
11418
  // src/config/validators/BuildSchema.ts
11260
11419
  init_file_exists();
11261
11420
  init_loader();
11262
- import path24 from "node:path";
11421
+ import path25 from "node:path";
11263
11422
  import { runnerImport as runnerImport2 } from "vite";
11264
11423
  import { z as z13 } from "zod";
11265
11424
  var BuildProcessorSchema = z13.custom((val) => typeof val === "function");
@@ -11288,7 +11447,7 @@ var buildConfigFiles = [
11288
11447
  ];
11289
11448
  async function getBuildConfigFilePath(rootDir) {
11290
11449
  for (const fileName of buildConfigFiles) {
11291
- const filepath = path24.join(rootDir, fileName);
11450
+ const filepath = path25.join(rootDir, fileName);
11292
11451
  if (await fileExists(filepath)) {
11293
11452
  return filepath;
11294
11453
  }
@@ -11397,14 +11556,14 @@ var flattenAllOf = (schema2) => {
11397
11556
  };
11398
11557
 
11399
11558
  // src/lib/util/traverse.ts
11400
- var traverse = (specification, transform, path95 = []) => {
11401
- const transformed = transform(specification, path95);
11559
+ var traverse = (specification, transform, path97 = []) => {
11560
+ const transformed = transform(specification, path97);
11402
11561
  if (typeof transformed !== "object" || transformed === null) {
11403
11562
  return transformed;
11404
11563
  }
11405
11564
  const result = Array.isArray(transformed) ? [] : {};
11406
11565
  for (const [key, value] of Object.entries(transformed)) {
11407
- const currentPath = [...path95, key];
11566
+ const currentPath = [...path97, key];
11408
11567
  if (Array.isArray(value)) {
11409
11568
  result[key] = value.map(
11410
11569
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -11432,9 +11591,9 @@ var resolveLocalRef = (schema2, ref) => {
11432
11591
  if (schemaCache?.has(ref)) {
11433
11592
  return schemaCache.get(ref);
11434
11593
  }
11435
- const path95 = ref.split("/").slice(1);
11594
+ const path97 = ref.split("/").slice(1);
11436
11595
  let current = schema2;
11437
- for (const segment of path95) {
11596
+ for (const segment of path97) {
11438
11597
  if (!current || typeof current !== "object") {
11439
11598
  current = null;
11440
11599
  }
@@ -11453,7 +11612,7 @@ var dereference = async (schema2, resolvers = []) => {
11453
11612
  }
11454
11613
  const cloned = structuredClone(schema2);
11455
11614
  const visited = /* @__PURE__ */ new Set();
11456
- const resolve = async (current, path95) => {
11615
+ const resolve = async (current, path97) => {
11457
11616
  if (isIndexableObject(current)) {
11458
11617
  if (visited.has(current)) {
11459
11618
  return CIRCULAR_REF;
@@ -11461,7 +11620,7 @@ var dereference = async (schema2, resolvers = []) => {
11461
11620
  visited.add(current);
11462
11621
  if (Array.isArray(current)) {
11463
11622
  for (let index = 0; index < current.length; index++) {
11464
- current[index] = await resolve(current[index], `${path95}/${index}`);
11623
+ current[index] = await resolve(current[index], `${path97}/${index}`);
11465
11624
  }
11466
11625
  } else {
11467
11626
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -11472,13 +11631,13 @@ var dereference = async (schema2, resolvers = []) => {
11472
11631
  for (const resolver of resolvers) {
11473
11632
  const resolved = await resolver($ref);
11474
11633
  if (resolved) {
11475
- result2 = await resolve(resolved, path95);
11634
+ result2 = await resolve(resolved, path97);
11476
11635
  break;
11477
11636
  }
11478
11637
  }
11479
11638
  if (result2 === void 0) {
11480
11639
  const resolved = await resolveLocalRef(cloned, $ref);
11481
- result2 = await resolve(resolved, path95);
11640
+ result2 = await resolve(resolved, path97);
11482
11641
  }
11483
11642
  if (hasSiblings) {
11484
11643
  if (result2 === CIRCULAR_REF) {
@@ -11491,7 +11650,7 @@ var dereference = async (schema2, resolvers = []) => {
11491
11650
  return result2;
11492
11651
  }
11493
11652
  for (const key in current) {
11494
- current[key] = await resolve(current[key], `${path95}/${key}`);
11653
+ current[key] = await resolve(current[key], `${path97}/${key}`);
11495
11654
  }
11496
11655
  }
11497
11656
  visited.delete(current);
@@ -11530,9 +11689,9 @@ var upgradeSchema = (schema2) => {
11530
11689
  }
11531
11690
  return sub;
11532
11691
  });
11533
- schema2 = traverse(schema2, (sub, path95) => {
11692
+ schema2 = traverse(schema2, (sub, path97) => {
11534
11693
  if (sub.example !== void 0) {
11535
- if (isSchemaPath(path95 ?? [])) {
11694
+ if (isSchemaPath(path97 ?? [])) {
11536
11695
  sub.examples = [sub.example];
11537
11696
  } else {
11538
11697
  sub.examples = {
@@ -11545,11 +11704,11 @@ var upgradeSchema = (schema2) => {
11545
11704
  }
11546
11705
  return sub;
11547
11706
  });
11548
- schema2 = traverse(schema2, (schema3, path95) => {
11707
+ schema2 = traverse(schema2, (schema3, path97) => {
11549
11708
  if (schema3.type === "object" && schema3.properties !== void 0) {
11550
- const parentPath = path95?.slice(0, -1);
11709
+ const parentPath = path97?.slice(0, -1);
11551
11710
  const isMultipart = parentPath?.some((segment, index) => {
11552
- return segment === "content" && path95?.[index + 1] === "multipart/form-data";
11711
+ return segment === "content" && path97?.[index + 1] === "multipart/form-data";
11553
11712
  });
11554
11713
  if (isMultipart) {
11555
11714
  const entries = Object.entries(schema3.properties);
@@ -11563,8 +11722,8 @@ var upgradeSchema = (schema2) => {
11563
11722
  }
11564
11723
  return schema3;
11565
11724
  });
11566
- schema2 = traverse(schema2, (schema3, path95) => {
11567
- if (path95?.includes("content") && path95.includes("application/octet-stream")) {
11725
+ schema2 = traverse(schema2, (schema3, path97) => {
11726
+ if (path97?.includes("content") && path97.includes("application/octet-stream")) {
11568
11727
  return {};
11569
11728
  }
11570
11729
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -11590,11 +11749,11 @@ var upgradeSchema = (schema2) => {
11590
11749
  }
11591
11750
  return sub;
11592
11751
  });
11593
- schema2 = traverse(schema2, (schema3, path95) => {
11752
+ schema2 = traverse(schema2, (schema3, path97) => {
11594
11753
  if (schema3.type === "string" && schema3.format === "byte") {
11595
- const parentPath = path95?.slice(0, -1);
11754
+ const parentPath = path97?.slice(0, -1);
11596
11755
  const contentMediaType = parentPath?.find(
11597
- (_, index) => path95?.[index - 1] === "content"
11756
+ (_, index) => path97?.[index - 1] === "content"
11598
11757
  );
11599
11758
  return {
11600
11759
  type: "string",
@@ -11606,7 +11765,7 @@ var upgradeSchema = (schema2) => {
11606
11765
  });
11607
11766
  return schema2;
11608
11767
  };
11609
- function isSchemaPath(path95) {
11768
+ function isSchemaPath(path97) {
11610
11769
  const schemaLocations = [
11611
11770
  ["components", "schemas"],
11612
11771
  "properties",
@@ -11619,10 +11778,10 @@ function isSchemaPath(path95) {
11619
11778
  ];
11620
11779
  return schemaLocations.some((location) => {
11621
11780
  if (Array.isArray(location)) {
11622
- return location.every((segment, index) => path95[index] === segment);
11781
+ return location.every((segment, index) => path97[index] === segment);
11623
11782
  }
11624
- return path95.includes(location);
11625
- }) || path95.includes("schema") || path95.some((segment) => segment.endsWith("Schema"));
11783
+ return path97.includes(location);
11784
+ }) || path97.includes("schema") || path97.some((segment) => segment.endsWith("Schema"));
11626
11785
  }
11627
11786
 
11628
11787
  // src/lib/oas/parser/index.ts
@@ -11704,13 +11863,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
11704
11863
  "anyOf",
11705
11864
  "oneOf"
11706
11865
  ]);
11707
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path95 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
11866
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path97 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
11708
11867
  if (obj === null || typeof obj !== "object") return obj;
11709
11868
  const refPath = obj.__$ref;
11710
11869
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
11711
11870
  if (isCircular) {
11712
11871
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
11713
- const circularProp = path95.find((p) => !OPENAPI_PROPS.has(p)) || path95[0];
11872
+ const circularProp = path97.find((p) => !OPENAPI_PROPS.has(p)) || path97[0];
11714
11873
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
11715
11874
  }
11716
11875
  if (refs.has(obj)) return refs.get(obj);
@@ -11720,7 +11879,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
11720
11879
  value,
11721
11880
  currentPath,
11722
11881
  refs,
11723
- [...path95, key],
11882
+ [...path97, key],
11724
11883
  currentRefPaths
11725
11884
  );
11726
11885
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -11755,7 +11914,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
11755
11914
  var getAllTags = (schema2) => {
11756
11915
  const rootTags = schema2.tags ?? [];
11757
11916
  const operations = Object.values(schema2.paths ?? {}).flatMap(
11758
- (path95) => HttpMethods.map((k) => path95?.[k]).filter((op) => op != null)
11917
+ (path97) => HttpMethods.map((k) => path97?.[k]).filter((op) => op != null)
11759
11918
  );
11760
11919
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
11761
11920
  const hasUntaggedOperations = operations.some(
@@ -11810,7 +11969,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
11810
11969
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
11811
11970
  var getAllOperations = (paths) => {
11812
11971
  const operations = Object.entries(paths ?? {}).flatMap(
11813
- ([path95, value]) => HttpMethods.flatMap((method) => {
11972
+ ([path97, value]) => HttpMethods.flatMap((method) => {
11814
11973
  if (!value?.[method]) return [];
11815
11974
  const operation = value[method];
11816
11975
  const pathParameters = value.parameters ?? [];
@@ -11830,7 +11989,7 @@ var getAllOperations = (paths) => {
11830
11989
  return {
11831
11990
  ...operation,
11832
11991
  method,
11833
- path: path95,
11992
+ path: path97,
11834
11993
  parameters,
11835
11994
  servers,
11836
11995
  tags: operation.tags ?? []
@@ -12188,8 +12347,8 @@ var Schema = builder.objectRef("Schema").implement({
12188
12347
  }),
12189
12348
  paths: t.field({
12190
12349
  type: [PathItem],
12191
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path95, value]) => ({
12192
- path: path95,
12350
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path97, value]) => ({
12351
+ path: path97,
12193
12352
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
12194
12353
  methods: Object.keys(value)
12195
12354
  }))
@@ -12302,7 +12461,7 @@ var ensureArray = (value) => Array.isArray(value) ? value : [value];
12302
12461
 
12303
12462
  // src/vite/api/SchemaManager.ts
12304
12463
  import fs3 from "node:fs/promises";
12305
- import path25 from "node:path";
12464
+ import path26 from "node:path";
12306
12465
  import {
12307
12466
  $RefParser as $RefParser2
12308
12467
  } from "@apidevtools/json-schema-ref-parser";
@@ -12354,7 +12513,7 @@ init_joinUrl();
12354
12513
 
12355
12514
  // src/vite/api/schema-codegen.ts
12356
12515
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
12357
- var getSegmentsFromPath = (path95) => path95.split("/").slice(1).map(unescapeJsonPointer);
12516
+ var getSegmentsFromPath = (path97) => path97.split("/").slice(1).map(unescapeJsonPointer);
12358
12517
  var createLocalRefMap = (obj) => {
12359
12518
  const refMap = /* @__PURE__ */ new Map();
12360
12519
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -12385,16 +12544,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
12385
12544
  /"__refMap\+Siblings:(.*?)"/g,
12386
12545
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
12387
12546
  );
12388
- var lookup = (schema2, path95, filePath) => {
12389
- const parts = getSegmentsFromPath(path95);
12547
+ var lookup = (schema2, path97, filePath) => {
12548
+ const parts = getSegmentsFromPath(path97);
12390
12549
  let val = schema2;
12391
12550
  for (const part of parts) {
12392
12551
  while (val.$ref?.startsWith("#/")) {
12393
- val = val.$ref === path95 ? val : lookup(schema2, val.$ref, filePath);
12552
+ val = val.$ref === path97 ? val : lookup(schema2, val.$ref, filePath);
12394
12553
  }
12395
12554
  if (val[part] === void 0) {
12396
12555
  throw new Error(
12397
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path95}`
12556
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path97}`
12398
12557
  );
12399
12558
  }
12400
12559
  val = val[part];
@@ -12492,18 +12651,18 @@ var SchemaManager = class {
12492
12651
  ];
12493
12652
  }
12494
12653
  getPathForFile = (input5, params) => {
12495
- const filePath = path25.resolve(this.config.__meta.rootDir, input5);
12654
+ const filePath = path26.resolve(this.config.__meta.rootDir, input5);
12496
12655
  const apis = ensureArray(this.config.apis ?? []);
12497
12656
  for (const apiConfig of apis) {
12498
12657
  if (!apiConfig || apiConfig.type !== "file" || !apiConfig.path) continue;
12499
12658
  const match = normalizeInputs(apiConfig.input).some(
12500
- (i) => path25.resolve(this.config.__meta.rootDir, i.input) === filePath && deepEqual(i.params, params)
12659
+ (i) => path26.resolve(this.config.__meta.rootDir, i.input) === filePath && deepEqual(i.params, params)
12501
12660
  );
12502
12661
  if (match) return apiConfig.path;
12503
12662
  }
12504
12663
  };
12505
12664
  processSchema = async (input5) => {
12506
- const filePath = path25.resolve(this.config.__meta.rootDir, input5.input);
12665
+ const filePath = path26.resolve(this.config.__meta.rootDir, input5.input);
12507
12666
  const params = input5.params;
12508
12667
  const configuredPath = this.getPathForFile(input5.input, params);
12509
12668
  if (!configuredPath) {
@@ -12539,9 +12698,9 @@ var SchemaManager = class {
12539
12698
  const processedTime = Date.now();
12540
12699
  const code = generateCode(processedSchema, filePath);
12541
12700
  const prefixPath = slugify(configuredPath);
12542
- const processedFilePath = path25.posix.join(
12701
+ const processedFilePath = path26.posix.join(
12543
12702
  this.storeDir,
12544
- `${prefixPath}-${path25.basename(filePath)}${paramsSuffix(params)}.js`
12703
+ `${prefixPath}-${path26.basename(filePath)}${paramsSuffix(params)}.js`
12545
12704
  );
12546
12705
  const importKey = processedFilePath;
12547
12706
  await fs3.writeFile(processedFilePath, code);
@@ -12590,7 +12749,7 @@ var SchemaManager = class {
12590
12749
  };
12591
12750
  getAllTrackedFiles = () => Array.from(this.referencedBy.keys());
12592
12751
  getFilesToReprocess = (changedFile) => {
12593
- const resolvedPath = path25.resolve(this.config.__meta.rootDir, changedFile);
12752
+ const resolvedPath = path26.resolve(this.config.__meta.rootDir, changedFile);
12594
12753
  const referencedBy = this.referencedBy.get(resolvedPath);
12595
12754
  if (!referencedBy) return [];
12596
12755
  const filesToProcess = referencedBy.size === 0 ? [resolvedPath] : Array.from(referencedBy);
@@ -12619,7 +12778,7 @@ var SchemaManager = class {
12619
12778
  version: "",
12620
12779
  path: input5.path ?? "",
12621
12780
  label: input5.label,
12622
- inputPath: path25.resolve(this.config.__meta.rootDir, input5.input),
12781
+ inputPath: path26.resolve(this.config.__meta.rootDir, input5.input),
12623
12782
  params: input5.params,
12624
12783
  importKey: "",
12625
12784
  downloadUrl: "",
@@ -12639,8 +12798,8 @@ var SchemaManager = class {
12639
12798
  }
12640
12799
  }
12641
12800
  };
12642
- getLatestSchema = (path95) => this.processedSchemas[path95]?.at(0);
12643
- getSchemasForPath = (path95) => this.processedSchemas[path95];
12801
+ getLatestSchema = (path97) => this.processedSchemas[path97]?.at(0);
12802
+ getSchemasForPath = (path97) => this.processedSchemas[path97];
12644
12803
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
12645
12804
  getUrlToFilePathMap = () => {
12646
12805
  const map = /* @__PURE__ */ new Map();
@@ -12662,7 +12821,7 @@ var SchemaManager = class {
12662
12821
  };
12663
12822
  createSchemaPath = (inputPath, versionPath, apiPath, params) => {
12664
12823
  const suffix = paramsSuffix(params);
12665
- const extension = suffix ? ".json" : path25.extname(inputPath);
12824
+ const extension = suffix ? ".json" : path26.extname(inputPath);
12666
12825
  return joinUrl(
12667
12826
  this.config.basePath,
12668
12827
  apiPath,
@@ -12685,7 +12844,7 @@ var SchemaManager = class {
12685
12844
  // src/vite/plugin-config-reload.ts
12686
12845
  init_logger();
12687
12846
  init_loader();
12688
- import path28 from "node:path";
12847
+ import path29 from "node:path";
12689
12848
  import colors7 from "picocolors";
12690
12849
 
12691
12850
  // src/vite/plugin-navigation.ts
@@ -12695,8 +12854,8 @@ import { stringify as stringify3 } from "javascript-stringify";
12695
12854
  import { isElement } from "react-is";
12696
12855
 
12697
12856
  // src/config/validators/NavigationSchema.ts
12698
- import path26 from "node:path";
12699
- import { glob as glob4 } from "glob";
12857
+ import path27 from "node:path";
12858
+ import { glob as glob5 } from "glob";
12700
12859
  import { fromMarkdown } from "mdast-util-from-markdown";
12701
12860
  import { mdxFromMarkdown } from "mdast-util-mdx";
12702
12861
  import { mdxjs } from "micromark-extension-mdxjs";
@@ -12730,7 +12889,7 @@ var extractRichH1 = (content) => {
12730
12889
  }
12731
12890
  };
12732
12891
  var isNavigationItem = (item) => item !== void 0;
12733
- var toPosixPath = (filePath) => filePath.split(path26.win32.sep).join(path26.posix.sep);
12892
+ var toPosixPath = (filePath) => filePath.split(path27.win32.sep).join(path27.posix.sep);
12734
12893
  var NavigationResolver = class {
12735
12894
  rootDir;
12736
12895
  globPatterns;
@@ -12744,7 +12903,7 @@ var NavigationResolver = class {
12744
12903
  }
12745
12904
  async initialize() {
12746
12905
  if (this.globFiles.length > 0) return;
12747
- this.globFiles = await glob4(this.globPatterns, {
12906
+ this.globFiles = await glob5(this.globPatterns, {
12748
12907
  root: this.rootDir,
12749
12908
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"]
12750
12909
  }).then((files) => files.map(toPosixPath));
@@ -12863,13 +13022,13 @@ init_invariant();
12863
13022
 
12864
13023
  // src/vite/debug.ts
12865
13024
  import { mkdir as mkdir7, writeFile as writeFile9 } from "node:fs/promises";
12866
- import path27 from "node:path";
13025
+ import path28 from "node:path";
12867
13026
  async function writePluginDebugCode(rootDir, pluginName, code, extension = "js") {
12868
13027
  if (process.env.ZUDOKU_BUILD_DEBUG) {
12869
- const debugDir = path27.join(rootDir, "dist", "debug");
13028
+ const debugDir = path28.join(rootDir, "dist", "debug");
12870
13029
  await mkdir7(debugDir, { recursive: true });
12871
13030
  await writeFile9(
12872
- path27.join(debugDir, `${pluginName}.${extension}`),
13031
+ path28.join(debugDir, `${pluginName}.${extension}`),
12873
13032
  typeof code === "string" ? code : code.join("\n")
12874
13033
  );
12875
13034
  }
@@ -12978,7 +13137,7 @@ var viteConfigReloadPlugin = () => ({
12978
13137
  reload(server);
12979
13138
  logger.info(
12980
13139
  colors7.blue(
12981
- `Config ${path28.basename(currentConfig.__meta.configPath)} changed. Reloading...`
13140
+ `Config ${path29.basename(currentConfig.__meta.configPath)} changed. Reloading...`
12982
13141
  ),
12983
13142
  { timestamp: true }
12984
13143
  );
@@ -12992,11 +13151,11 @@ var viteApiPlugin = async () => {
12992
13151
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
12993
13152
  const initialConfig = getCurrentConfig();
12994
13153
  const zuploProcessors = ZuploEnv.isZuplo ? await runnerImport3(
12995
- path29.resolve(getZudokuRootDir(), "src/zuplo/with-zuplo-processors.ts")
13154
+ path30.resolve(getZudokuRootDir(), "src/zuplo/with-zuplo-processors.ts")
12996
13155
  ).then((m) => m.module.default(initialConfig.__meta.rootDir)) : [];
12997
13156
  const buildConfig = await getBuildConfig();
12998
13157
  const buildProcessors = buildConfig?.processors ?? [];
12999
- const tmpStoreDir = path29.posix.join(
13158
+ const tmpStoreDir = path30.posix.join(
13000
13159
  initialConfig.__meta.rootDir,
13001
13160
  "node_modules/.apitogo/processed"
13002
13161
  );
@@ -13028,7 +13187,7 @@ var viteApiPlugin = async () => {
13028
13187
  const inputPath = pathMap.get(req.url);
13029
13188
  if (!inputPath) return next();
13030
13189
  const content = await fs4.readFile(inputPath, "utf-8");
13031
- const mimeType = path29.extname(inputPath).toLowerCase() === ".json" ? "application/json" : "application/x-yaml";
13190
+ const mimeType = path30.extname(inputPath).toLowerCase() === ".json" ? "application/json" : "application/x-yaml";
13032
13191
  res.setHeader("Content-Type", `${mimeType}; charset=utf-8`);
13033
13192
  return res.end(content);
13034
13193
  });
@@ -13216,8 +13375,8 @@ var viteApiPlugin = async () => {
13216
13375
  if (process.env.NODE_ENV !== "production") return;
13217
13376
  for (const [urlPath, inputPath] of pathMap) {
13218
13377
  const content = await fs4.readFile(inputPath, "utf-8");
13219
- const outputPath = path29.join(config2.__meta.rootDir, "dist", urlPath);
13220
- await fs4.mkdir(path29.dirname(outputPath), { recursive: true });
13378
+ const outputPath = path30.join(config2.__meta.rootDir, "dist", urlPath);
13379
+ await fs4.mkdir(path30.dirname(outputPath), { recursive: true });
13221
13380
  await fs4.writeFile(outputPath, content, "utf-8");
13222
13381
  }
13223
13382
  }
@@ -13260,7 +13419,7 @@ var plugin_auth_default = viteAuthPlugin;
13260
13419
 
13261
13420
  // src/vite/plugin-component.ts
13262
13421
  init_loader();
13263
- import path30 from "node:path";
13422
+ import path31 from "node:path";
13264
13423
  var viteAliasPlugin = () => {
13265
13424
  return {
13266
13425
  name: "zudoku-component-plugin",
@@ -13288,7 +13447,7 @@ var viteAliasPlugin = () => {
13288
13447
  ];
13289
13448
  const aliases = replacements.map(([find, replacement]) => ({
13290
13449
  find,
13291
- replacement: path30.posix.join(config2.__meta.moduleDir, replacement)
13450
+ replacement: path31.posix.join(config2.__meta.moduleDir, replacement)
13292
13451
  }));
13293
13452
  return config2.__meta.mode === "internal" || config2.__meta.mode === "standalone" ? { resolve: { alias: aliases } } : void 0;
13294
13453
  }
@@ -13299,11 +13458,11 @@ var plugin_component_default = viteAliasPlugin;
13299
13458
  // src/vite/plugin-config.ts
13300
13459
  init_package_json();
13301
13460
  init_loader();
13302
- import path31 from "node:path";
13461
+ import path32 from "node:path";
13303
13462
  import { normalizePath } from "vite";
13304
13463
  var virtualModuleId3 = "virtual:zudoku-config";
13305
13464
  var resolvedVirtualModuleId3 = `\0${virtualModuleId3}`;
13306
- var browserConfigLoaderPath = path31.join(
13465
+ var browserConfigLoaderPath = path32.join(
13307
13466
  getZudokuRootDir(),
13308
13467
  "src/config/apitogo-config-loader.browser.ts"
13309
13468
  );
@@ -13437,7 +13596,7 @@ var plugin_custom_pages_default = viteCustomPagesPlugin;
13437
13596
 
13438
13597
  // src/vite/plugin-doc-metadata.ts
13439
13598
  init_loader();
13440
- import { glob as glob5 } from "glob";
13599
+ import { glob as glob6 } from "glob";
13441
13600
  var serializeMetadata = async (filePath) => {
13442
13601
  const fm = await readFrontmatter(filePath);
13443
13602
  const h1 = fm.content.match(/^#\s+(.*)$/m)?.[1];
@@ -13448,7 +13607,7 @@ var viteDocMetadataPlugin = () => ({
13448
13607
  name: "zudoku-doc-metadata-plugin",
13449
13608
  configureServer: async (server) => {
13450
13609
  const config2 = getCurrentConfig();
13451
- const files = await glob5("**/*.{md,mdx}", {
13610
+ const files = await glob6("**/*.{md,mdx}", {
13452
13611
  cwd: config2.__meta.rootDir,
13453
13612
  ignore: ["node_modules", "dist"],
13454
13613
  absolute: true
@@ -13474,9 +13633,9 @@ var viteDocMetadataPlugin = () => ({
13474
13633
 
13475
13634
  // src/vite/plugin-docs.ts
13476
13635
  init_loader();
13477
- import path32 from "node:path";
13478
- import { glob as glob6 } from "glob";
13479
- import globParent2 from "glob-parent";
13636
+ import path33 from "node:path";
13637
+ import { glob as glob7 } from "glob";
13638
+ import globParent3 from "glob-parent";
13480
13639
  init_ZudokuConfig();
13481
13640
 
13482
13641
  // src/lib/components/navigation/utils.ts
@@ -13531,19 +13690,19 @@ var globMarkdownFiles = async (config2, options = { absolute: false }) => {
13531
13690
  const docsConfig = DocsConfigSchema.parse(config2.docs ?? {});
13532
13691
  const fileMapping = {};
13533
13692
  for (const globPattern of docsConfig.files) {
13534
- const globbedFiles = await glob6(globPattern, {
13693
+ const globbedFiles = await glob7(globPattern, {
13535
13694
  root: config2.__meta.rootDir,
13536
13695
  ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
13537
13696
  // Always glob with relative paths to avoid issues on different OS
13538
13697
  absolute: false,
13539
13698
  posix: true
13540
13699
  });
13541
- const parent = globParent2(globPattern).replace(/^\.?\//, "");
13700
+ const parent = globParent3(globPattern).replace(/^\.?\//, "");
13542
13701
  let draftFiles = /* @__PURE__ */ new Set();
13543
13702
  if (process.env.NODE_ENV !== "development") {
13544
13703
  const draftStatuses = await Promise.all(
13545
13704
  globbedFiles.map(async (file) => {
13546
- const absolutePath = path32.resolve(config2.__meta.rootDir, file);
13705
+ const absolutePath = path33.resolve(config2.__meta.rootDir, file);
13547
13706
  const { data } = await readFrontmatter(absolutePath);
13548
13707
  return { file, isDraft: data.draft === true };
13549
13708
  })
@@ -13556,9 +13715,9 @@ var globMarkdownFiles = async (config2, options = { absolute: false }) => {
13556
13715
  if (draftFiles.has(file)) {
13557
13716
  continue;
13558
13717
  }
13559
- const relativePath = path32.posix.relative(parent, file);
13718
+ const relativePath = path33.posix.relative(parent, file);
13560
13719
  const routePath = ensureLeadingSlash(relativePath.replace(/\.mdx?$/, ""));
13561
- const filePath = options.absolute ? path32.resolve(config2.__meta.rootDir, file) : file;
13720
+ const filePath = options.absolute ? path33.resolve(config2.__meta.rootDir, file) : file;
13562
13721
  fileMapping[routePath] = filePath;
13563
13722
  }
13564
13723
  }
@@ -13622,7 +13781,7 @@ var viteDocsPlugin = () => {
13622
13781
  const globbedDocuments = {};
13623
13782
  for (const [routePath, file] of Object.entries(fileMapping)) {
13624
13783
  const importPath = ensureLeadingSlash(
13625
- path32.posix.join(globImportBasePath, file)
13784
+ path33.posix.join(globImportBasePath, file)
13626
13785
  );
13627
13786
  globbedDocuments[routePath] = importPath;
13628
13787
  }
@@ -13648,7 +13807,7 @@ var plugin_docs_default = viteDocsPlugin;
13648
13807
  // src/vite/plugin-local-manifest.ts
13649
13808
  init_apitogo_config_loader();
13650
13809
  init_local_manifest();
13651
- import path33 from "node:path";
13810
+ import path34 from "node:path";
13652
13811
  var APITOGO_LOCAL_MANIFEST_VIRTUAL_ID = "virtual:apitogo-local-manifest";
13653
13812
  var resolvedVirtualModuleId4 = `\0${APITOGO_LOCAL_MANIFEST_VIRTUAL_ID}`;
13654
13813
  var viteLocalManifestPlugin = () => {
@@ -13698,8 +13857,8 @@ var viteLocalManifestPlugin = () => {
13698
13857
  server.watcher.add(watchPath);
13699
13858
  }
13700
13859
  server.watcher.on("change", async (changedPath) => {
13701
- const resolvedChanged = path33.resolve(changedPath);
13702
- if (!watchPaths.some((p) => path33.resolve(p) === resolvedChanged)) {
13860
+ const resolvedChanged = path34.resolve(changedPath);
13861
+ if (!watchPaths.some((p) => path34.resolve(p) === resolvedChanged)) {
13703
13862
  return;
13704
13863
  }
13705
13864
  const module = server.moduleGraph.getModuleById(
@@ -13726,7 +13885,7 @@ init_loader();
13726
13885
  init_ProtectedRoutesSchema();
13727
13886
  init_joinUrl();
13728
13887
  import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
13729
- import path34 from "node:path";
13888
+ import path35 from "node:path";
13730
13889
  import { matchPath } from "react-router";
13731
13890
  var processMarkdownFile = async (filePath) => {
13732
13891
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
@@ -13814,7 +13973,7 @@ var viteMarkdownExportPlugin = () => {
13814
13973
  if (process.env.NODE_ENV !== "production" || Object.keys(markdownFiles).length === 0 || !needsMdFiles) {
13815
13974
  return;
13816
13975
  }
13817
- const distDir = path34.join(
13976
+ const distDir = path35.join(
13818
13977
  config2.__meta.rootDir,
13819
13978
  "dist",
13820
13979
  config2.basePath ?? ""
@@ -13835,15 +13994,15 @@ var viteMarkdownExportPlugin = () => {
13835
13994
  content: finalMarkdown
13836
13995
  });
13837
13996
  const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
13838
- const outputPath = `${path34.join(distDir, ...segments)}.md`;
13839
- await mkdir8(path34.dirname(outputPath), { recursive: true });
13997
+ const outputPath = `${path35.join(distDir, ...segments)}.md`;
13998
+ await mkdir8(path35.dirname(outputPath), { recursive: true });
13840
13999
  await writeFile10(outputPath, finalMarkdown, "utf-8");
13841
14000
  } catch (error) {
13842
14001
  console.warn(`Failed to export markdown for ${routePath}:`, error);
13843
14002
  }
13844
14003
  }
13845
14004
  if (config2.docs?.llms?.llmsTxt || config2.docs?.llms?.llmsTxtFull) {
13846
- const markdownInfoPath = path34.join(
14005
+ const markdownInfoPath = path35.join(
13847
14006
  config2.__meta.rootDir,
13848
14007
  "node_modules/.apitogo/markdown-info.json"
13849
14008
  );
@@ -13995,9 +14154,9 @@ var remarkCodeTabs = () => (tree) => {
13995
14154
  };
13996
14155
 
13997
14156
  // src/vite/mdx/remark-inject-filepath.ts
13998
- import path35 from "node:path";
14157
+ import path36 from "node:path";
13999
14158
  var remarkInjectFilepath = (rootDir) => (tree, vfile) => {
14000
- const relativePath = path35.relative(rootDir, vfile.path).split(path35.sep).join(path35.posix.sep);
14159
+ const relativePath = path36.relative(rootDir, vfile.path).split(path36.sep).join(path36.posix.sep);
14001
14160
  tree.children.unshift(exportMdxjsConst("__filepath", relativePath));
14002
14161
  };
14003
14162
 
@@ -14084,18 +14243,18 @@ var remarkLastModified = () => {
14084
14243
  };
14085
14244
 
14086
14245
  // src/vite/mdx/remark-link-rewrite.ts
14087
- import path36 from "node:path";
14246
+ import path37 from "node:path";
14088
14247
  import { visit as visit5 } from "unist-util-visit";
14089
14248
  var remarkLinkRewrite = (basePath = "") => (tree) => {
14090
14249
  visit5(tree, "link", (node) => {
14091
14250
  if (!node.url) return;
14092
14251
  if (node.url.startsWith("http") || node.url.startsWith("mailto:")) return;
14093
14252
  node.url = node.url.replace(/\\/g, "/");
14094
- const base = path36.posix.join(basePath);
14253
+ const base = path37.posix.join(basePath);
14095
14254
  if (basePath && node.url.startsWith(base)) {
14096
14255
  node.url = node.url.slice(base.length);
14097
14256
  } else if (!node.url.startsWith("/") && !node.url.startsWith("#")) {
14098
- node.url = path36.posix.join("..", node.url);
14257
+ node.url = path37.posix.join("..", node.url);
14099
14258
  }
14100
14259
  node.url = node.url.replace(/\.mdx?(#.*)?$/, "$1");
14101
14260
  });
@@ -14315,14 +14474,14 @@ var plugin_mdx_default = viteMdxPlugin;
14315
14474
 
14316
14475
  // src/vite/plugin-modules.ts
14317
14476
  init_loader();
14318
- import path37 from "node:path";
14477
+ import path38 from "node:path";
14319
14478
  import { normalizePath as normalizePath2 } from "vite";
14320
- var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path37.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14321
- var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path37.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
14479
+ var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path38.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14480
+ var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path38.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
14322
14481
  var getBillingPluginImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(
14323
- path37.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14482
+ path38.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14324
14483
  ) : "@lukoweb/apitogo-plugin-dev-portal-billing";
14325
- var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path37.resolve(rootDir, pagePath));
14484
+ var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path38.resolve(rootDir, pagePath));
14326
14485
  var viteModulesPlugin = () => {
14327
14486
  const virtualModuleId4 = "virtual:zudoku-modules-plugin";
14328
14487
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -14464,17 +14623,17 @@ var plugin_modules_default = viteModulesPlugin;
14464
14623
 
14465
14624
  // src/vite/plugin-offroot-resolve.ts
14466
14625
  init_loader();
14467
- import path38 from "node:path";
14626
+ import path39 from "node:path";
14468
14627
  function stripQuery(id) {
14469
14628
  const query = id.indexOf("?");
14470
14629
  return query === -1 ? id : id.slice(0, query);
14471
14630
  }
14472
14631
  function isInside(parent, child) {
14473
- const relative = path38.relative(path38.resolve(parent), path38.resolve(child));
14474
- return relative === "" || !relative.startsWith("..") && !path38.isAbsolute(relative);
14632
+ const relative = path39.relative(path39.resolve(parent), path39.resolve(child));
14633
+ return relative === "" || !relative.startsWith("..") && !path39.isAbsolute(relative);
14475
14634
  }
14476
14635
  function isBareSpecifier(source) {
14477
- return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path38.isAbsolute(source);
14636
+ return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path39.isAbsolute(source);
14478
14637
  }
14479
14638
  function isInsideNodeModules(filePath) {
14480
14639
  return filePath.split(/[\\/]/).includes("node_modules");
@@ -14498,7 +14657,7 @@ function viteOffrootResolvePlugin() {
14498
14657
  if (!shouldResolveFromSiteRoot(source, importer, rootDir)) {
14499
14658
  return;
14500
14659
  }
14501
- return this.resolve(source, path38.join(rootDir, "package.json"), {
14660
+ return this.resolve(source, path39.join(rootDir, "package.json"), {
14502
14661
  ...options,
14503
14662
  skipSelf: true
14504
14663
  });
@@ -14508,7 +14667,7 @@ function viteOffrootResolvePlugin() {
14508
14667
 
14509
14668
  // src/vite/plugin-search.ts
14510
14669
  init_loader();
14511
- import path39 from "node:path";
14670
+ import path40 from "node:path";
14512
14671
  var viteSearchPlugin = () => {
14513
14672
  const virtualModuleId4 = "virtual:zudoku-search-plugin";
14514
14673
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -14526,7 +14685,7 @@ var viteSearchPlugin = () => {
14526
14685
  return resolvedVirtualModuleId5;
14527
14686
  }
14528
14687
  if (id === "/pagefind/pagefind.js" && resolvedViteConfig?.publicDir) {
14529
- return path39.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
14688
+ return path40.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
14530
14689
  }
14531
14690
  },
14532
14691
  async load(id) {
@@ -14667,27 +14826,27 @@ function vitePlugin() {
14667
14826
 
14668
14827
  // src/vite/watch-ignore.ts
14669
14828
  import { lstatSync, realpathSync } from "node:fs";
14670
- import path40 from "node:path";
14829
+ import path41 from "node:path";
14671
14830
  var STATE_INSIDE_CONTENT = /[\\/]content[\\/](?:\.apitogo|apitogo[\\/]\.state)(?:[\\/]|$)/;
14672
14831
  var within = (base, file) => {
14673
- const resolvedBase = path40.resolve(base);
14674
- const resolvedFile = path40.resolve(file);
14675
- return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path40.sep);
14832
+ const resolvedBase = path41.resolve(base);
14833
+ const resolvedFile = path41.resolve(file);
14834
+ return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path41.sep);
14676
14835
  };
14677
14836
  var unique = (paths) => [...new Set(paths)];
14678
14837
  function isWorkspace(root) {
14679
14838
  try {
14680
- return path40.basename(root) === "generated" && lstatSync(path40.join(root, "content")).isSymbolicLink();
14839
+ return path41.basename(root) === "generated" && lstatSync(path41.join(root, "content")).isSymbolicLink();
14681
14840
  } catch {
14682
14841
  return false;
14683
14842
  }
14684
14843
  }
14685
14844
  function tenantDir(root) {
14686
- return isWorkspace(root) ? realpathSync(path40.join(root, "content")) : void 0;
14845
+ return isWorkspace(root) ? realpathSync(path41.join(root, "content")) : void 0;
14687
14846
  }
14688
14847
  function stateIgnore(root) {
14689
14848
  if (!isWorkspace(root)) return void 0;
14690
- const state = path40.dirname(root);
14849
+ const state = path41.dirname(root);
14691
14850
  const states = unique([state, realpathSync(state)]);
14692
14851
  const roots = unique([root, realpathSync(root)]);
14693
14852
  return (file) => {
@@ -14698,17 +14857,17 @@ function stateIgnore(root) {
14698
14857
  }
14699
14858
  function devServerAllow(root, moduleDir) {
14700
14859
  const tenant = tenantDir(root);
14701
- return [root, path40.dirname(root), moduleDir, ...tenant ? [tenant] : []];
14860
+ return [root, path41.dirname(root), moduleDir, ...tenant ? [tenant] : []];
14702
14861
  }
14703
14862
 
14704
14863
  // src/vite/config.ts
14705
14864
  var resolveMergedPublicDir = (rootDir, merged) => {
14706
14865
  if (merged.publicDir === false) return void 0;
14707
14866
  const rel2 = typeof merged.publicDir === "string" ? merged.publicDir : "public";
14708
- return path41.resolve(rootDir, rel2);
14867
+ return path42.resolve(rootDir, rel2);
14709
14868
  };
14710
- var getAppClientEntryPath = () => path41.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
14711
- var getAppServerEntryPath = () => path41.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
14869
+ var getAppClientEntryPath = () => path42.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
14870
+ var getAppServerEntryPath = () => path42.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
14712
14871
  var hasLoggedCdnInfo = false;
14713
14872
  var MEDIA_REGEX = /\.(a?png|jpe?g|gif|bmp|svg|webp|tiff|ico|webm|ogg|mp3|wav|m4a|avif|mp4)/i;
14714
14873
  var defineEnvVars = (vars) => Object.fromEntries(
@@ -14739,7 +14898,7 @@ async function getViteConfig(dir, configEnv) {
14739
14898
  const ignoreState = stateIgnore(dir);
14740
14899
  if (ZuploEnv.isZuplo) {
14741
14900
  dotenv.config({
14742
- path: path41.resolve(config2.__meta.rootDir, "../.env.zuplo"),
14901
+ path: path42.resolve(config2.__meta.rootDir, "../.env.zuplo"),
14743
14902
  quiet: true
14744
14903
  });
14745
14904
  }
@@ -14802,8 +14961,8 @@ async function getViteConfig(dir, configEnv) {
14802
14961
  ssr: configEnv.isSsrBuild,
14803
14962
  sourcemap: true,
14804
14963
  target: "es2022",
14805
- outDir: path41.resolve(
14806
- path41.join(
14964
+ outDir: path42.resolve(
14965
+ path42.join(
14807
14966
  dir,
14808
14967
  "dist",
14809
14968
  config2.basePath ?? "",
@@ -14822,7 +14981,7 @@ async function getViteConfig(dir, configEnv) {
14822
14981
  },
14823
14982
  experimental: {
14824
14983
  renderBuiltUrl(filename) {
14825
- if (cdnUrl?.base && [".js", ".css"].includes(path41.extname(filename))) {
14984
+ if (cdnUrl?.base && [".js", ".css"].includes(path42.extname(filename))) {
14826
14985
  return joinUrl(cdnUrl.base, filename);
14827
14986
  }
14828
14987
  if (cdnUrl?.media && MEDIA_REGEX.test(filename)) {
@@ -14835,7 +14994,7 @@ async function getViteConfig(dir, configEnv) {
14835
14994
  esbuildOptions: {
14836
14995
  target: "es2022"
14837
14996
  },
14838
- entries: [path41.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
14997
+ entries: [path42.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
14839
14998
  exclude: [
14840
14999
  "@lukoweb/apitogo",
14841
15000
  "@lukoweb/apitogo-plugin-dev-portal-billing"
@@ -14939,7 +15098,7 @@ init_package_json();
14939
15098
  init_joinUrl();
14940
15099
  import assert from "node:assert";
14941
15100
  import { cp, mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
14942
- import path42 from "node:path";
15101
+ import path43 from "node:path";
14943
15102
  var pkgJson = getZudokuPackageJson();
14944
15103
  function generateOutput({
14945
15104
  config: config2,
@@ -14996,9 +15155,9 @@ async function writeOutput(dir, {
14996
15155
  rewrites
14997
15156
  }) {
14998
15157
  const output4 = generateOutput({ config: config2, redirects, rewrites });
14999
- const outputDir = process.env.VERCEL ? path42.join(dir, ".vercel/output") : path42.join(dir, "dist/.output");
15158
+ const outputDir = process.env.VERCEL ? path43.join(dir, ".vercel/output") : path43.join(dir, "dist/.output");
15000
15159
  await mkdir9(outputDir, { recursive: true });
15001
- const outputFile = path42.join(outputDir, "config.json");
15160
+ const outputFile = path43.join(outputDir, "config.json");
15002
15161
  await writeFile11(outputFile, JSON.stringify(output4, null, 2), "utf-8");
15003
15162
  if (process.env.VERCEL) {
15004
15163
  console.log("Wrote Vercel output to", outputDir);
@@ -15008,9 +15167,9 @@ async function writeOutput(dir, {
15008
15167
  // src/vite/prerender/prerender.ts
15009
15168
  init_logger();
15010
15169
  init_file_exists();
15011
- import { readFile as readFile9, rm as rm6 } from "node:fs/promises";
15170
+ import { readFile as readFile11, rm as rm6 } from "node:fs/promises";
15012
15171
  import os from "node:os";
15013
- import path45 from "node:path";
15172
+ import path46 from "node:path";
15014
15173
  import { pathToFileURL } from "node:url";
15015
15174
  import { createIndex } from "pagefind";
15016
15175
  import colors11 from "picocolors";
@@ -15052,7 +15211,7 @@ function throttle(fn) {
15052
15211
  init_joinUrl();
15053
15212
  import { createWriteStream, existsSync as existsSync4 } from "node:fs";
15054
15213
  import { mkdir as mkdir10 } from "node:fs/promises";
15055
- import path43 from "node:path";
15214
+ import path44 from "node:path";
15056
15215
  import colors9 from "picocolors";
15057
15216
  import { SitemapStream } from "sitemap";
15058
15217
  async function generateSitemap({
@@ -15066,11 +15225,11 @@ async function generateSitemap({
15066
15225
  return;
15067
15226
  }
15068
15227
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
15069
- const outputDir = path43.resolve(baseOutputDir, config2.outDir ?? "");
15228
+ const outputDir = path44.resolve(baseOutputDir, config2.outDir ?? "");
15070
15229
  if (!existsSync4(outputDir)) {
15071
15230
  await mkdir10(outputDir, { recursive: true });
15072
15231
  }
15073
- const sitemapOutputPath = path43.join(outputDir, "sitemap.xml");
15232
+ const sitemapOutputPath = path44.join(outputDir, "sitemap.xml");
15074
15233
  const writeStream = createWriteStream(sitemapOutputPath);
15075
15234
  sitemap.pipe(writeStream);
15076
15235
  let lastmod;
@@ -15100,14 +15259,14 @@ async function generateSitemap({
15100
15259
 
15101
15260
  // src/vite/prerender/utils.ts
15102
15261
  init_joinUrl();
15103
- var resolveRoutePath = (path95) => {
15104
- const segments = path95.split("/");
15262
+ var resolveRoutePath = (path97) => {
15263
+ const segments = path97.split("/");
15105
15264
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
15106
15265
  return void 0;
15107
15266
  }
15108
15267
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
15109
15268
  };
15110
- var isSkipped = (path95) => path95.includes("*") || /^\d+$/.test(path95);
15269
+ var isSkipped = (path97) => path97.includes("*") || /^\d+$/.test(path97);
15111
15270
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
15112
15271
  if (route.path && isSkipped(route.path)) return [];
15113
15272
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -15156,12 +15315,12 @@ var prerender = async ({
15156
15315
  serverConfigFilename,
15157
15316
  writeRedirects = true
15158
15317
  }) => {
15159
- const distDir = path45.join(dir, "dist", basePath);
15318
+ const distDir = path46.join(dir, "dist", basePath);
15160
15319
  const serverConfigPath = pathToFileURL(
15161
- path45.join(distDir, "server", serverConfigFilename)
15320
+ path46.join(distDir, "server", serverConfigFilename)
15162
15321
  ).href;
15163
15322
  const entryServerPath = pathToFileURL(
15164
- path45.join(distDir, "server/entry.server.js")
15323
+ path46.join(distDir, "server/entry.server.js")
15165
15324
  ).href;
15166
15325
  const rawConfig = await import(serverConfigPath).then(
15167
15326
  (m) => m.default
@@ -15246,7 +15405,7 @@ var prerender = async ({
15246
15405
  })
15247
15406
  );
15248
15407
  const pagefindWriteResult = await pagefindIndex?.writeFiles({
15249
- outputPath: path45.join(distDir, "pagefind")
15408
+ outputPath: path46.join(distDir, "pagefind")
15250
15409
  });
15251
15410
  const seconds = ((performance.now() - start) / 1e3).toFixed(1);
15252
15411
  const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
@@ -15278,13 +15437,13 @@ var prerender = async ({
15278
15437
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
15279
15438
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
15280
15439
  const llmsConfig = docsConfig.llms ?? {};
15281
- const markdownInfoPath = path45.join(
15440
+ const markdownInfoPath = path46.join(
15282
15441
  dir,
15283
15442
  "node_modules/.apitogo/markdown-info.json"
15284
15443
  );
15285
15444
  let markdownFileInfos = [];
15286
15445
  if (await fileExists(markdownInfoPath)) {
15287
- const markdownInfoContent = await readFile9(markdownInfoPath, "utf-8");
15446
+ const markdownInfoContent = await readFile11(markdownInfoPath, "utf-8");
15288
15447
  markdownFileInfos = JSON.parse(markdownInfoContent);
15289
15448
  }
15290
15449
  if (llmsConfig.llmsTxt || llmsConfig.llmsTxtFull) {
@@ -15369,7 +15528,7 @@ async function runBuild(options) {
15369
15528
  html,
15370
15529
  basePath: config2.basePath
15371
15530
  });
15372
- await rm7(path46.join(clientOutDir, "index.html"), { force: true });
15531
+ await rm7(path47.join(clientOutDir, "index.html"), { force: true });
15373
15532
  } else {
15374
15533
  await runPrerender({
15375
15534
  dir,
@@ -15393,7 +15552,7 @@ var runPrerender = async (options) => {
15393
15552
  serverConfigFilename,
15394
15553
  writeRedirects: process.env.VERCEL === void 0
15395
15554
  });
15396
- const indexHtml = path46.join(clientOutDir, "index.html");
15555
+ const indexHtml = path47.join(clientOutDir, "index.html");
15397
15556
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
15398
15557
  await writeFile13(indexHtml, html, "utf-8");
15399
15558
  }
@@ -15403,15 +15562,15 @@ var runPrerender = async (options) => {
15403
15562
  for (const statusPage of statusPages) {
15404
15563
  await rename5(
15405
15564
  statusPage,
15406
- path46.join(dir, DIST_DIR, path46.basename(statusPage))
15565
+ path47.join(dir, DIST_DIR, path47.basename(statusPage))
15407
15566
  );
15408
15567
  }
15409
15568
  await rm7(serverOutDir, { recursive: true, force: true });
15410
15569
  if (process.env.VERCEL) {
15411
- await mkdir11(path46.join(dir, ".vercel/output/static"), { recursive: true });
15570
+ await mkdir11(path47.join(dir, ".vercel/output/static"), { recursive: true });
15412
15571
  await rename5(
15413
- path46.join(dir, DIST_DIR),
15414
- path46.join(dir, ".vercel/output/static")
15572
+ path47.join(dir, DIST_DIR),
15573
+ path47.join(dir, ".vercel/output/static")
15415
15574
  );
15416
15575
  }
15417
15576
  await writeOutput(dir, {
@@ -15421,7 +15580,7 @@ var runPrerender = async (options) => {
15421
15580
  });
15422
15581
  if (ZuploEnv.isZuplo && issuer) {
15423
15582
  await writeFile13(
15424
- path46.join(dir, DIST_DIR, ".output/zuplo.json"),
15583
+ path47.join(dir, DIST_DIR, ".output/zuplo.json"),
15425
15584
  JSON.stringify({ issuer }, null, 2),
15426
15585
  "utf-8"
15427
15586
  );
@@ -15433,10 +15592,10 @@ var runPrerender = async (options) => {
15433
15592
  };
15434
15593
  var bundleSSREntry = async (options) => {
15435
15594
  const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } = options;
15436
- const tempEntryPath = path46.join(dir, "__ssr-entry.ts");
15595
+ const tempEntryPath = path47.join(dir, "__ssr-entry.ts");
15437
15596
  const packageRoot = getZudokuRootDir();
15438
- const templateContent = await readFile10(
15439
- path46.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
15597
+ const templateContent = await readFile12(
15598
+ path47.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
15440
15599
  "utf-8"
15441
15600
  );
15442
15601
  const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`)).replace(
@@ -15451,9 +15610,9 @@ var bundleSSREntry = async (options) => {
15451
15610
  platform: adapter === "node" ? "node" : "neutral",
15452
15611
  target: "es2022",
15453
15612
  format: "esm",
15454
- outfile: path46.join(serverOutDir, "entry.js"),
15613
+ outfile: path47.join(serverOutDir, "entry.js"),
15455
15614
  external: ["./entry.server.js", `./${serverConfigFilename}`],
15456
- nodePaths: [path46.join(packageRoot, "node_modules")],
15615
+ nodePaths: [path47.join(packageRoot, "node_modules")],
15457
15616
  banner: { js: "// Bundled SSR entry" }
15458
15617
  });
15459
15618
  } finally {
@@ -15466,8 +15625,8 @@ init_package_json();
15466
15625
 
15467
15626
  // src/cli/init/v2-packages.ts
15468
15627
  import { existsSync as existsSync5 } from "node:fs";
15469
- import { readFile as readFile11, writeFile as writeFile14 } from "node:fs/promises";
15470
- import path47 from "node:path";
15628
+ import { readFile as readFile13, writeFile as writeFile14 } from "node:fs/promises";
15629
+ import path48 from "node:path";
15471
15630
  var CORE_PACKAGES = [
15472
15631
  "@lukoweb/apitogo",
15473
15632
  "react",
@@ -15484,7 +15643,7 @@ function packageNameFrom(name) {
15484
15643
  return slug || "portal";
15485
15644
  }
15486
15645
  async function ensurePackageJson(dir, name) {
15487
- const file = path47.join(dir, "package.json");
15646
+ const file = path48.join(dir, "package.json");
15488
15647
  if (existsSync5(file)) return false;
15489
15648
  await writeFile14(
15490
15649
  file,
@@ -15503,11 +15662,11 @@ async function ensurePackageJson(dir, name) {
15503
15662
  return true;
15504
15663
  }
15505
15664
  function isPackageInstalled(startDir, name) {
15506
- let dir = path47.resolve(startDir);
15665
+ let dir = path48.resolve(startDir);
15507
15666
  for (; ; ) {
15508
- if (existsSync5(path47.join(dir, "node_modules", name, "package.json")))
15667
+ if (existsSync5(path48.join(dir, "node_modules", name, "package.json")))
15509
15668
  return true;
15510
- const parent = path47.dirname(dir);
15669
+ const parent = path48.dirname(dir);
15511
15670
  if (parent === dir) return false;
15512
15671
  dir = parent;
15513
15672
  }
@@ -15563,7 +15722,7 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
15563
15722
  return {
15564
15723
  name: "build",
15565
15724
  hash: () => hash2,
15566
- stillValid: () => access4(path48.join(buildDir, "index.html")).then(
15725
+ stillValid: () => access4(path49.join(buildDir, "index.html")).then(
15567
15726
  () => true,
15568
15727
  () => false
15569
15728
  ),
@@ -15580,11 +15739,11 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
15580
15739
  else process.env.NODE_ENV = originalNodeEnv;
15581
15740
  }
15582
15741
  await rm8(buildDir, { recursive: true, force: true });
15583
- await mkdir12(path48.dirname(buildDir), { recursive: true });
15584
- await rename6(path48.join(root, "dist"), buildDir);
15742
+ await mkdir12(path49.dirname(buildDir), { recursive: true });
15743
+ await rename6(path49.join(root, "dist"), buildDir);
15585
15744
  if (prepared.publishedManifest) {
15586
15745
  await writeFile15(
15587
- path48.join(buildDir, MANIFEST_FILE),
15746
+ path49.join(buildDir, MANIFEST_FILE),
15588
15747
  `${JSON.stringify(prepared.publishedManifest, null, 2)}
15589
15748
  `
15590
15749
  );
@@ -15613,17 +15772,17 @@ async function buildV2(options) {
15613
15772
  }
15614
15773
 
15615
15774
  // src/cli/check/gateway-online.ts
15616
- import { readFile as readFile13 } from "node:fs/promises";
15775
+ import { readFile as readFile15 } from "node:fs/promises";
15617
15776
  var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
15618
- var issue2 = (code, path95, message, hint) => ({ code, path: path95, message, hint });
15777
+ var issue2 = (code, path97, message, hint) => ({ code, path: path97, message, hint });
15619
15778
  function capabilityIssues(gateway, answer) {
15620
15779
  const can = answer.capabilities;
15621
15780
  const plan2 = `your ${answer.tier} plan`;
15622
15781
  const found = [];
15623
- const unsupported = (path95, what, hint) => found.push(
15782
+ const unsupported = (path97, what, hint) => found.push(
15624
15783
  issue2(
15625
15784
  "GATEWAY_CAPABILITY_UNSUPPORTED",
15626
- path95,
15785
+ path97,
15627
15786
  `${what} is not available on ${plan2}.`,
15628
15787
  hint
15629
15788
  )
@@ -15677,7 +15836,7 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
15677
15836
  const specFile = gateway.specs[0];
15678
15837
  if (specFile) {
15679
15838
  try {
15680
- const text = await readFile13(specFile, "utf8");
15839
+ const text = await readFile15(specFile, "utf8");
15681
15840
  if (text.length <= SPEC_SEND_LIMIT) spec = text;
15682
15841
  } catch {
15683
15842
  }
@@ -15704,9 +15863,9 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
15704
15863
  }
15705
15864
 
15706
15865
  // src/cli/deploy/plans-file.ts
15707
- init_invariant();
15708
- import { readFile as readFile14 } from "node:fs/promises";
15866
+ import { readFile as readFile16 } from "node:fs/promises";
15709
15867
  import { z as z14 } from "zod";
15868
+ init_invariant();
15710
15869
  var PlansFileError = class extends ApitogoError {
15711
15870
  constructor(code, message, developerHint) {
15712
15871
  super(message, { developerHint, title: "Cannot read the plans file" });
@@ -15749,7 +15908,7 @@ var issuesOf = (error) => error.issues.map(
15749
15908
  async function readPlansFile(file) {
15750
15909
  let text;
15751
15910
  try {
15752
- text = await readFile14(file, "utf8");
15911
+ text = await readFile16(file, "utf8");
15753
15912
  } catch {
15754
15913
  throw new PlansFileError(
15755
15914
  "PLANS_FILE_UNREADABLE",
@@ -15790,12 +15949,12 @@ async function readPlansFile(file) {
15790
15949
  var BILLING_PERIOD = { month: "monthly", year: "annual" };
15791
15950
  function toPlatformPlans(plans, options = {}) {
15792
15951
  return plans.map((plan2, index) => {
15793
- const paid = plan2.pricingModel !== "custom" && (plan2.priceAmount ?? 0) > 0;
15952
+ const paid = isPaidPlan(plan2);
15794
15953
  if (paid && !options.billing)
15795
15954
  throw new PlansFileError(
15796
15955
  "PLANS_PAID_UNSUPPORTED",
15797
- `The plan "${plan2.sku}" has a price, and plans.billing.provider is not stripe, so nobody could pay for it.`,
15798
- "Set plans.billing.provider to stripe (with plans.billing.apiKey), or set priceAmount to 0 (or pricingModel to custom for a contact-us plan)."
15956
+ `The plan "${plan2.sku}" has a price, but plans.billing.provider is "none", so nobody could pay for it.`,
15957
+ "Remove plans.billing.provider (Stripe is the provider; deploy asks for its key), or set priceAmount to 0 (or pricingModel to custom for a contact-us plan)."
15799
15958
  );
15800
15959
  const limits = plan2.limits ? {
15801
15960
  ...plan2.limits.rateLimit ? {
@@ -15827,9 +15986,9 @@ function toPlatformPlans(plans, options = {}) {
15827
15986
  }
15828
15987
 
15829
15988
  // src/cli/check/services-online.ts
15830
- var issue3 = (code, path95, message, hint) => ({
15989
+ var issue3 = (code, path97, message, hint) => ({
15831
15990
  code,
15832
- path: path95,
15991
+ path: path97,
15833
15992
  message,
15834
15993
  hint
15835
15994
  });
@@ -15940,7 +16099,7 @@ async function onlinePlansRules(resolved, projectId, api) {
15940
16099
  async function isConfigV2(dir) {
15941
16100
  try {
15942
16101
  return /"version"\s*:\s*2\b/.test(
15943
- await readFile15(path49.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16102
+ await readFile17(path50.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
15944
16103
  );
15945
16104
  } catch {
15946
16105
  return false;
@@ -16087,7 +16246,7 @@ async function validatePortalBuild(dir) {
16087
16246
  }
16088
16247
  }
16089
16248
  async function runCheckV2(dir, args = {}, deps = {}) {
16090
- const configPath = path49.join(dir, APITOGO_CONFIG_FILENAME);
16249
+ const configPath = path50.join(dir, APITOGO_CONFIG_FILENAME);
16091
16250
  let report;
16092
16251
  try {
16093
16252
  const { config: config2, resolved } = loadConfigV2(dir);
@@ -16197,7 +16356,7 @@ async function checkV2(dir, args, deps = platformDependencies(args)) {
16197
16356
  init_package_json();
16198
16357
 
16199
16358
  // src/cli/init/v2-handler.ts
16200
- import path50 from "node:path";
16359
+ import path51 from "node:path";
16201
16360
  import { createInterface } from "node:readline/promises";
16202
16361
  function parseDocsFlag(value) {
16203
16362
  if (value === void 0) return void 0;
@@ -16219,15 +16378,15 @@ async function ask(question) {
16219
16378
  }
16220
16379
  function describePlan(plan2) {
16221
16380
  return {
16222
- willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path50.relative(plan2.dir, entry.path)),
16223
- willKeep: plan2.keep.map((target) => path50.relative(plan2.dir, target)),
16381
+ willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path51.relative(plan2.dir, entry.path)),
16382
+ willKeep: plan2.keep.map((target) => path51.relative(plan2.dir, target)),
16224
16383
  replacesConfig: plan2.replacesConfig
16225
16384
  };
16226
16385
  }
16227
16386
  async function initV2(argv) {
16228
16387
  const json = argv.json === true;
16229
16388
  try {
16230
- const dir = path50.resolve(process.cwd(), argv.dir ?? ".");
16389
+ const dir = path51.resolve(process.cwd(), argv.dir ?? ".");
16231
16390
  const plan2 = await planInit({
16232
16391
  dir,
16233
16392
  name: argv.name,
@@ -16268,8 +16427,8 @@ async function initV2(argv) {
16268
16427
  status: "created",
16269
16428
  dir,
16270
16429
  ...describePlan(plan2),
16271
- created: result.created.map((p) => path50.relative(dir, p)),
16272
- kept: result.kept.map((p) => path50.relative(dir, p))
16430
+ created: result.created.map((p) => path51.relative(dir, p)),
16431
+ kept: result.kept.map((p) => path51.relative(dir, p))
16273
16432
  },
16274
16433
  null,
16275
16434
  2
@@ -16278,7 +16437,7 @@ async function initV2(argv) {
16278
16437
  return;
16279
16438
  }
16280
16439
  printResultToConsole(
16281
- `Created ${path50.basename(plan2.configPath)} (config version 2) in ${dir}.`
16440
+ `Created ${path51.basename(plan2.configPath)} (config version 2) in ${dir}.`
16282
16441
  );
16283
16442
  printHintToConsole(
16284
16443
  "Next: add a service with `apitogo add <portal|gateway|identity|plans>`, or run `apitogo create` for a guided setup."
@@ -16306,7 +16465,7 @@ async function initV2(argv) {
16306
16465
  // src/cli/services/packages.ts
16307
16466
  import { spawn } from "node:child_process";
16308
16467
  import { existsSync as existsSync6 } from "node:fs";
16309
- import path51 from "node:path";
16468
+ import path52 from "node:path";
16310
16469
  var USER_PANEL_PACKAGE = "@lukoweb/apitogo-module-user-panel";
16311
16470
  var BILLING_PLUGIN_PACKAGE = "@lukoweb/apitogo-plugin-dev-portal-billing";
16312
16471
  var LANDING_PACKAGE = "@lukoweb/apitogo-module-landing";
@@ -16324,18 +16483,18 @@ function packagesForConfig(config2) {
16324
16483
  ];
16325
16484
  }
16326
16485
  function isInstalled(dir, name) {
16327
- for (let current = path51.resolve(dir); ; current = path51.dirname(current)) {
16328
- if (existsSync6(path51.join(current, "node_modules", name, "package.json")))
16486
+ for (let current = path52.resolve(dir); ; current = path52.dirname(current)) {
16487
+ if (existsSync6(path52.join(current, "node_modules", name, "package.json")))
16329
16488
  return true;
16330
- if (path51.dirname(current) === current) return false;
16489
+ if (path52.dirname(current) === current) return false;
16331
16490
  }
16332
16491
  }
16333
16492
  function missingPackages(dir, names) {
16334
16493
  return names.filter((name) => !isInstalled(dir, name));
16335
16494
  }
16336
16495
  function detectPackageManager(dir) {
16337
- if (existsSync6(path51.join(dir, "pnpm-lock.yaml"))) return "pnpm";
16338
- if (existsSync6(path51.join(dir, "yarn.lock"))) return "yarn";
16496
+ if (existsSync6(path52.join(dir, "pnpm-lock.yaml"))) return "pnpm";
16497
+ if (existsSync6(path52.join(dir, "yarn.lock"))) return "yarn";
16339
16498
  return "npm";
16340
16499
  }
16341
16500
  function installSpecsCommand(manager, specs) {
@@ -16394,7 +16553,7 @@ async function installMissingPackages(dir, config2, version, options) {
16394
16553
  reason: "--no-install was given",
16395
16554
  command: shown
16396
16555
  };
16397
- await ensurePackageJson(dir, config2.project?.name ?? path51.basename(dir));
16556
+ await ensurePackageJson(dir, config2.project?.name ?? path52.basename(dir));
16398
16557
  await (options.installer ?? runInstaller)(dir, command2);
16399
16558
  return { status: "installed", packages: missing, command: shown };
16400
16559
  }
@@ -16457,7 +16616,7 @@ var list = (value) => {
16457
16616
  return items.length > 0 ? items : void 0;
16458
16617
  };
16459
16618
  function addOptionsFromArguments(service, argv) {
16460
- const dir = path52.resolve(process.cwd(), argv.dir ?? ".");
16619
+ const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
16461
16620
  const issuers = list(argv.issuer);
16462
16621
  return {
16463
16622
  dir,
@@ -16502,8 +16661,7 @@ function addOptionsFromArguments(service, argv) {
16502
16661
  } : void 0,
16503
16662
  plans: service === "plans" ? {
16504
16663
  file: argv.file,
16505
- template: argv.template === "free" || argv.template === "tiered" ? argv.template : void 0,
16506
- billing: argv.billing
16664
+ template: argv.template === "free" || argv.template === "tiered" ? argv.template : void 0
16507
16665
  } : void 0
16508
16666
  };
16509
16667
  }
@@ -16512,8 +16670,8 @@ function describe(edit, dir) {
16512
16670
  return {
16513
16671
  service: edit.service,
16514
16672
  action: edit.action,
16515
- willCreate: edit.files.map((file) => path52.relative(dir, file.path)),
16516
- keep: edit.keep.map((file) => path52.relative(dir, file)),
16673
+ willCreate: edit.files.map((file) => path53.relative(dir, file.path)),
16674
+ keep: edit.keep.map((file) => path53.relative(dir, file)),
16517
16675
  notes: edit.notes
16518
16676
  };
16519
16677
  }
@@ -16527,7 +16685,7 @@ async function run(action, argv, plan2, deps = {}) {
16527
16685
  `Choose one of ${SERVICES.join(", ")}.`
16528
16686
  );
16529
16687
  }
16530
- const dir = path52.resolve(process.cwd(), argv.dir ?? ".");
16688
+ const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
16531
16689
  if (!await isConfigV2(dir)) {
16532
16690
  throw new ServiceEditError(
16533
16691
  `${action} works with a config version 2 project, and there is none in ${dir}.`,
@@ -16595,7 +16753,7 @@ async function run(action, argv, plan2, deps = {}) {
16595
16753
  {
16596
16754
  status: action === "add" ? "added" : "removed",
16597
16755
  ...describe(edit, dir),
16598
- created: created.map((file) => path52.relative(dir, file)),
16756
+ created: created.map((file) => path53.relative(dir, file)),
16599
16757
  ...install ? { packages: install } : {}
16600
16758
  },
16601
16759
  null,
@@ -16605,7 +16763,7 @@ async function run(action, argv, plan2, deps = {}) {
16605
16763
  return;
16606
16764
  }
16607
16765
  printResultToConsole(
16608
- `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path52.basename(edit.configPath)}.`
16766
+ `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path53.basename(edit.configPath)}.`
16609
16767
  );
16610
16768
  } catch (error) {
16611
16769
  const message = error instanceof Error ? error.message : String(error);
@@ -16666,7 +16824,7 @@ var removeService = (argv) => run(
16666
16824
  "remove",
16667
16825
  argv,
16668
16826
  (service) => planRemoveService(service, {
16669
- dir: path52.resolve(process.cwd(), argv.dir ?? "."),
16827
+ dir: path53.resolve(process.cwd(), argv.dir ?? "."),
16670
16828
  deleteFiles: argv.deleteFiles === true
16671
16829
  })
16672
16830
  );
@@ -16810,10 +16968,6 @@ var add_default = {
16810
16968
  "none"
16811
16969
  ],
16812
16970
  describe: "Content to create: blank (empty shell), starter (filled example) or full (starter plus a Node.js backend in server/). Plans also accept free, tiered and none; none is blank"
16813
- }).option("billing", {
16814
- type: "string",
16815
- choices: ["stripe", "none"],
16816
- describe: "plans: billing provider"
16817
16971
  }),
16818
16972
  handler: async (argv) => {
16819
16973
  await captureEvent({ argv, event: "apitogo add" });
@@ -16822,16 +16976,16 @@ var add_default = {
16822
16976
  };
16823
16977
 
16824
16978
  // src/cli/build/handler.ts
16825
- import path54 from "node:path";
16979
+ import path55 from "node:path";
16826
16980
  init_logger();
16827
16981
  init_package_json();
16828
16982
 
16829
16983
  // src/cli/preview/handler.ts
16830
- import path53 from "node:path";
16984
+ import path54 from "node:path";
16831
16985
  import { preview as vitePreview } from "vite";
16832
16986
  var DEFAULT_PREVIEW_PORT = 4e3;
16833
16987
  async function preview(argv) {
16834
- const dir = path53.resolve(process.cwd(), argv.dir);
16988
+ const dir = path54.resolve(process.cwd(), argv.dir);
16835
16989
  const viteConfig = await getViteConfig(dir, {
16836
16990
  command: "serve",
16837
16991
  mode: "production",
@@ -16874,7 +17028,7 @@ async function build(argv) {
16874
17028
  printDiagnosticsToConsole("");
16875
17029
  printDiagnosticsToConsole("");
16876
17030
  }
16877
- const dir = path54.resolve(process.cwd(), argv.dir);
17031
+ const dir = path55.resolve(process.cwd(), argv.dir);
16878
17032
  try {
16879
17033
  if (await isConfigV2(dir)) {
16880
17034
  const result = await buildV2({ dir, jsonOnly });
@@ -16939,10 +17093,10 @@ var build_default = {
16939
17093
  };
16940
17094
 
16941
17095
  // src/cli/check/handler.ts
16942
- import path60 from "node:path";
17096
+ import path61 from "node:path";
16943
17097
 
16944
17098
  // src/cli/platform/tools/check-publish-readiness.ts
16945
- import path59 from "node:path";
17099
+ import path60 from "node:path";
16946
17100
 
16947
17101
  // src/cli/platform/helpers/dev-portal-backend.ts
16948
17102
  function deriveProvisioningState(backend) {
@@ -17026,8 +17180,8 @@ function formatDevPortalBackendResponse(projectId, backend) {
17026
17180
  // src/cli/platform/helpers/dev-portal-manifest.ts
17027
17181
  init_gateway_limits();
17028
17182
  import { existsSync as existsSync7 } from "node:fs";
17029
- import { access as access5, mkdir as mkdir13, readFile as readFile16, writeFile as writeFile16 } from "node:fs/promises";
17030
- import path55 from "node:path";
17183
+ import { access as access5, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile16 } from "node:fs/promises";
17184
+ import path56 from "node:path";
17031
17185
 
17032
17186
  // src/cli/make-config/header-pricing-nav.ts
17033
17187
  var HEADER_PRICING_PATH = "/pricing";
@@ -17149,7 +17303,7 @@ function isPlanFree(plan2) {
17149
17303
  return (plan2.priceAmount ?? 0) <= 0;
17150
17304
  }
17151
17305
  function manifestFilePath(siteDirectory) {
17152
- return path55.join(path55.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17306
+ return path56.join(path56.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17153
17307
  }
17154
17308
  var STORED_MANIFEST_KEYS = [
17155
17309
  "authentication",
@@ -17573,23 +17727,23 @@ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
17573
17727
  if (!trimmed) {
17574
17728
  return void 0;
17575
17729
  }
17576
- return path55.resolve(siteDirectory, trimmed);
17730
+ return path56.resolve(siteDirectory, trimmed);
17577
17731
  }
17578
17732
  function resolveBackendOpenApiPath(openApiPath, options) {
17579
17733
  const trimmed = openApiPath?.trim();
17580
17734
  if (!trimmed) {
17581
17735
  return void 0;
17582
17736
  }
17583
- if (path55.isAbsolute(trimmed)) {
17737
+ if (path56.isAbsolute(trimmed)) {
17584
17738
  return trimmed;
17585
17739
  }
17586
- const siteResolved = path55.resolve(options.siteDirectory, trimmed);
17740
+ const siteResolved = path56.resolve(options.siteDirectory, trimmed);
17587
17741
  if (existsSync7(siteResolved)) {
17588
17742
  return siteResolved;
17589
17743
  }
17590
17744
  const backendDir = options.backendSourceDirectory?.trim();
17591
17745
  if (backendDir) {
17592
- const backendResolved = path55.resolve(backendDir, trimmed);
17746
+ const backendResolved = path56.resolve(backendDir, trimmed);
17593
17747
  if (existsSync7(backendResolved)) {
17594
17748
  return backendResolved;
17595
17749
  }
@@ -17606,7 +17760,7 @@ function mergePublishPrerequisitesWithPlatformValidation(prerequisites, manifest
17606
17760
  async function readManifest2(siteDirectory) {
17607
17761
  const filePath = manifestFilePath(siteDirectory);
17608
17762
  if (await pathExists(filePath)) {
17609
- const raw = await readFile16(filePath, "utf8");
17763
+ const raw = await readFile18(filePath, "utf8");
17610
17764
  const config2 = JSON.parse(raw);
17611
17765
  return extractManifestFromConfig(config2);
17612
17766
  }
@@ -17614,10 +17768,10 @@ async function readManifest2(siteDirectory) {
17614
17768
  }
17615
17769
  async function writeManifest2(siteDirectory, manifest) {
17616
17770
  const filePath = manifestFilePath(siteDirectory);
17617
- await mkdir13(path55.dirname(filePath), { recursive: true });
17771
+ await mkdir13(path56.dirname(filePath), { recursive: true });
17618
17772
  let config2 = {};
17619
17773
  if (await pathExists(filePath)) {
17620
- const raw = await readFile16(filePath, "utf8");
17774
+ const raw = await readFile18(filePath, "utf8");
17621
17775
  config2 = JSON.parse(raw);
17622
17776
  }
17623
17777
  const {
@@ -17636,11 +17790,11 @@ import {
17636
17790
  access as access6,
17637
17791
  appendFile,
17638
17792
  mkdir as mkdir14,
17639
- readFile as readFile17,
17793
+ readFile as readFile19,
17640
17794
  unlink,
17641
17795
  writeFile as writeFile17
17642
17796
  } from "node:fs/promises";
17643
- import path56 from "node:path";
17797
+ import path57 from "node:path";
17644
17798
  var ENV_FILENAME = ".env";
17645
17799
  var COLLAPSED_UPPERCASE_ALIASES2 = {
17646
17800
  SECRETKEY: "secretKey",
@@ -17742,7 +17896,7 @@ function applyEnvOverrides2(config2, env) {
17742
17896
  }
17743
17897
  async function readEnvFile(filePath) {
17744
17898
  try {
17745
- const raw = await readFile17(filePath, "utf8");
17899
+ const raw = await readFile19(filePath, "utf8");
17746
17900
  const env = {};
17747
17901
  for (const line2 of raw.split("\n")) {
17748
17902
  const trimmed = line2.trim();
@@ -17759,12 +17913,12 @@ async function readEnvFile(filePath) {
17759
17913
  }
17760
17914
  }
17761
17915
  async function loadSiteEnv(siteDirectory) {
17762
- const resolvedDirectory = path56.resolve(siteDirectory);
17916
+ const resolvedDirectory = path57.resolve(siteDirectory);
17763
17917
  const merged = { ...process.env };
17764
17918
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
17765
17919
  Object.assign(
17766
17920
  merged,
17767
- await readEnvFile(path56.join(resolvedDirectory, fileName))
17921
+ await readEnvFile(path57.join(resolvedDirectory, fileName))
17768
17922
  );
17769
17923
  }
17770
17924
  return merged;
@@ -17801,7 +17955,7 @@ function buildAuthSecretsJson(secrets2, options) {
17801
17955
  return payload;
17802
17956
  }
17803
17957
  function envFilePath(siteDirectory) {
17804
- return path56.join(path56.resolve(siteDirectory), ENV_FILENAME);
17958
+ return path57.join(path57.resolve(siteDirectory), ENV_FILENAME);
17805
17959
  }
17806
17960
  function getSecretsStatus(siteDirectory, manifest, secrets2, envExists) {
17807
17961
  const providerSecretStatus = {};
@@ -17837,7 +17991,7 @@ function validateSecretsForPublish(manifest, secrets2, emailOptions) {
17837
17991
  }
17838
17992
  async function siteEnvFileHasEntries(siteDirectory) {
17839
17993
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
17840
- const loaded = await readEnvFile(path56.join(siteDirectory, fileName));
17994
+ const loaded = await readEnvFile(path57.join(siteDirectory, fileName));
17841
17995
  if (Object.keys(loaded).length > 0) {
17842
17996
  return true;
17843
17997
  }
@@ -17845,7 +17999,7 @@ async function siteEnvFileHasEntries(siteDirectory) {
17845
17999
  return false;
17846
18000
  }
17847
18001
  async function resolvePublishConfig(siteDirectory, emailOptions) {
17848
- const resolvedDirectory = path56.resolve(siteDirectory);
18002
+ const resolvedDirectory = path57.resolve(siteDirectory);
17849
18003
  const manifestRaw = await readManifest2(resolvedDirectory);
17850
18004
  if (!manifestRaw) {
17851
18005
  throw new Error("apitogo.config.json not found in sourceDirectory.");
@@ -17875,13 +18029,13 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
17875
18029
 
17876
18030
  // src/cli/platform/helpers/dev-portal-site-validation.ts
17877
18031
  init_gateway_limits();
17878
- import { readFile as readFile18 } from "node:fs/promises";
18032
+ import { readFile as readFile20 } from "node:fs/promises";
17879
18033
  import { createRequire as createRequire2 } from "node:module";
17880
- import path57 from "node:path";
18034
+ import path58 from "node:path";
17881
18035
  var cachedFallbackTs;
17882
18036
  async function loadTypeScriptForSite(sourceDirectory) {
17883
18037
  const require2 = createRequire2(
17884
- path57.join(path57.resolve(sourceDirectory), "package.json")
18038
+ path58.join(path58.resolve(sourceDirectory), "package.json")
17885
18039
  );
17886
18040
  try {
17887
18041
  return require2("typescript");
@@ -17928,7 +18082,7 @@ async function referencedFileExists(sourceDirectory, reference) {
17928
18082
  return true;
17929
18083
  }
17930
18084
  return pathExists(
17931
- path57.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18085
+ path58.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
17932
18086
  );
17933
18087
  }
17934
18088
  async function validateSiteConfigStructure(config2, sourceDirectory) {
@@ -18043,17 +18197,17 @@ var DEFAULT_TSX_CANDIDATES = [
18043
18197
  "landing/LandingPage.tsx"
18044
18198
  ];
18045
18199
  async function validateDevPortalSiteConfig(sourceDirectory, options) {
18046
- const resolvedDir = path57.resolve(sourceDirectory);
18200
+ const resolvedDir = path58.resolve(sourceDirectory);
18047
18201
  const issues = [];
18048
18202
  const checkedFiles = [];
18049
18203
  const skippedChecks = [];
18050
- const jsonPath = path57.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18204
+ const jsonPath = path58.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18051
18205
  if (await pathExists(jsonPath)) {
18052
18206
  checkedFiles.push(APITOGO_CONFIG_FILENAME2);
18053
18207
  let parsed;
18054
18208
  let parseFailed = false;
18055
18209
  try {
18056
- parsed = JSON.parse(await readFile18(jsonPath, "utf8"));
18210
+ parsed = JSON.parse(await readFile20(jsonPath, "utf8"));
18057
18211
  } catch (error) {
18058
18212
  parseFailed = true;
18059
18213
  issues.push({
@@ -18079,7 +18233,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18079
18233
  );
18080
18234
  const existingTsxFiles = [];
18081
18235
  for (const relativeFile of tsxFiles) {
18082
- if (await pathExists(path57.join(resolvedDir, relativeFile))) {
18236
+ if (await pathExists(path58.join(resolvedDir, relativeFile))) {
18083
18237
  existingTsxFiles.push(relativeFile);
18084
18238
  }
18085
18239
  }
@@ -18092,8 +18246,8 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18092
18246
  } else {
18093
18247
  for (const relativeFile of existingTsxFiles) {
18094
18248
  checkedFiles.push(relativeFile);
18095
- const content = await readFile18(
18096
- path57.join(resolvedDir, relativeFile),
18249
+ const content = await readFile20(
18250
+ path58.join(resolvedDir, relativeFile),
18097
18251
  "utf8"
18098
18252
  );
18099
18253
  issues.push(
@@ -18115,8 +18269,8 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18115
18269
 
18116
18270
  // src/cli/platform/helpers/npm-build.ts
18117
18271
  import { spawn as spawn2 } from "node:child_process";
18118
- import { access as access7, readFile as readFile19 } from "node:fs/promises";
18119
- import path58 from "node:path";
18272
+ import { access as access7, readFile as readFile21 } from "node:fs/promises";
18273
+ import path59 from "node:path";
18120
18274
  var BUILD_LOG_TAIL_BYTES = 8192;
18121
18275
  var MIN_NODE_MAJOR = 20;
18122
18276
  var NpmBuildError = class extends Error {
@@ -18183,13 +18337,13 @@ function runCommand(command2, args, cwd, env) {
18183
18337
  }
18184
18338
  async function runNpmInstall(sourceDirectory, options) {
18185
18339
  assertNodeVersion(options?.nodeVersion);
18186
- const cwd = path58.resolve(sourceDirectory);
18187
- const packageJsonPath = path58.join(cwd, "package.json");
18340
+ const cwd = path59.resolve(sourceDirectory);
18341
+ const packageJsonPath = path59.join(cwd, "package.json");
18188
18342
  if (!await pathExists2(packageJsonPath)) {
18189
18343
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18190
18344
  }
18191
18345
  const startedAt = Date.now();
18192
- const lockPath = path58.join(cwd, "package-lock.json");
18346
+ const lockPath = path59.join(cwd, "package-lock.json");
18193
18347
  const installArgs = await pathExists2(lockPath) ? ["ci"] : ["install"];
18194
18348
  const install = await runCommand("npm", installArgs, cwd);
18195
18349
  const installLog = install.output;
@@ -18207,8 +18361,8 @@ async function runNpmInstall(sourceDirectory, options) {
18207
18361
  }
18208
18362
  async function runNpmBuild(sourceDirectory, options) {
18209
18363
  assertNodeVersion(options?.nodeVersion);
18210
- const cwd = path58.resolve(sourceDirectory);
18211
- const packageJsonPath = path58.join(cwd, "package.json");
18364
+ const cwd = path59.resolve(sourceDirectory);
18365
+ const packageJsonPath = path59.join(cwd, "package.json");
18212
18366
  if (!await pathExists2(packageJsonPath)) {
18213
18367
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18214
18368
  }
@@ -18225,8 +18379,8 @@ async function runNpmBuild(sourceDirectory, options) {
18225
18379
  build2.exitCode
18226
18380
  );
18227
18381
  }
18228
- const distPath = path58.join(cwd, "dist");
18229
- const indexPath = path58.join(distPath, "index.html");
18382
+ const distPath = path59.join(cwd, "dist");
18383
+ const indexPath = path59.join(distPath, "index.html");
18230
18384
  if (!await pathExists2(indexPath)) {
18231
18385
  throw new NpmBuildError(
18232
18386
  `Build completed but dist/index.html was not found at ${indexPath}`,
@@ -18610,7 +18764,7 @@ function validateManifestForPlatformPublish(manifest) {
18610
18764
 
18611
18765
  // src/cli/platform/tools/check-publish-readiness.ts
18612
18766
  async function checkPublishReadiness(client, _config, args) {
18613
- const sourceDirectory = path59.resolve(args.sourceDirectory);
18767
+ const sourceDirectory = path60.resolve(args.sourceDirectory);
18614
18768
  const manifest = await readManifest2(sourceDirectory) ?? createScaffoldManifest();
18615
18769
  const emailOptions = await resolveEmailOptionsForPublish(
18616
18770
  client,
@@ -18688,7 +18842,7 @@ async function checkPublishReadiness(client, _config, args) {
18688
18842
 
18689
18843
  // src/cli/check/handler.ts
18690
18844
  async function check(argv) {
18691
- const sourceDirectory = path60.resolve(process.cwd(), argv.dir);
18845
+ const sourceDirectory = path61.resolve(process.cwd(), argv.dir);
18692
18846
  if (await isConfigV2(sourceDirectory)) {
18693
18847
  await checkV2(sourceDirectory, argv);
18694
18848
  return;
@@ -18752,7 +18906,7 @@ var check_default = {
18752
18906
 
18753
18907
  // src/cli/make-config/handler.ts
18754
18908
  init_invariant();
18755
- import path62 from "node:path";
18909
+ import path63 from "node:path";
18756
18910
  import { stdin as input2 } from "node:process";
18757
18911
 
18758
18912
  // src/cli/make-config/interactive.ts
@@ -18761,9 +18915,9 @@ import { createInterface as createInterface3 } from "node:readline/promises";
18761
18915
 
18762
18916
  // src/cli/make-config/v2-edits.ts
18763
18917
  init_apitogo_config_core();
18764
- import { access as access8, mkdir as mkdir15, readFile as readFile20, writeFile as writeFile18 } from "node:fs/promises";
18765
- import path61 from "node:path";
18766
- import { glob as glob7 } from "glob";
18918
+ import { access as access8, mkdir as mkdir15, readFile as readFile22, writeFile as writeFile18 } from "node:fs/promises";
18919
+ import path62 from "node:path";
18920
+ import { glob as glob8 } from "glob";
18767
18921
  init_invariant();
18768
18922
  var ConfigEditError = class extends ApitogoError {
18769
18923
  constructor(message, developerHint) {
@@ -18775,10 +18929,10 @@ var exists4 = (target) => access8(target).then(
18775
18929
  () => true,
18776
18930
  () => false
18777
18931
  );
18778
- var toPosix2 = (value) => value.split(path61.sep).join("/");
18779
- var relativeToConfig = (project, target) => toPosix2(path61.relative(project.resolved.configDir, target)) || ".";
18932
+ var toPosix2 = (value) => value.split(path62.sep).join("/");
18933
+ var relativeToConfig = (project, target) => toPosix2(path62.relative(project.resolved.configDir, target)) || ".";
18780
18934
  async function openConfigProject(dir) {
18781
- const configPath = path61.join(dir, APITOGO_CONFIG_FILENAME);
18935
+ const configPath = path62.join(dir, APITOGO_CONFIG_FILENAME);
18782
18936
  if (!await exists4(configPath)) {
18783
18937
  throw new ConfigEditError(
18784
18938
  `There is no ${APITOGO_CONFIG_FILENAME} in ${dir}.`,
@@ -18843,7 +18997,7 @@ function setPortalName(project, name) {
18843
18997
  }
18844
18998
  async function addGatewaySpec(project, specPath) {
18845
18999
  const gateway = requireGateway(project);
18846
- const absolute = path61.resolve(project.dir, specPath);
19000
+ const absolute = path62.resolve(project.dir, specPath);
18847
19001
  if (!await exists4(absolute)) {
18848
19002
  throw new ConfigEditError(`The OpenAPI file ${specPath} does not exist.`);
18849
19003
  }
@@ -18887,7 +19041,7 @@ function parseBackendInput(input5) {
18887
19041
  }
18888
19042
  async function setGatewayOrigin(project, backend) {
18889
19043
  const gateway = requireGateway(project);
18890
- const source = path61.resolve(project.dir, backend.source);
19044
+ const source = path62.resolve(project.dir, backend.source);
18891
19045
  if (!await exists4(source)) {
18892
19046
  throw new ConfigEditError(
18893
19047
  `The API source folder ${backend.source} does not exist.`
@@ -18925,6 +19079,33 @@ title: ${JSON.stringify(title)}
18925
19079
  ${body}
18926
19080
  `;
18927
19081
  }
19082
+ function pageComponentName(slug) {
19083
+ const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
19084
+ return `${/^[0-9]/.test(name) ? "Page" : ""}${name || "Docs"}Page`;
19085
+ }
19086
+ function buildTsxDocsPage(slug, title, file, content) {
19087
+ const text = content?.trim() ? `{${JSON.stringify(content.trim())}}` : "This page is a React component. Change it like any other component: add state, fetch data, or use the portal's components.";
19088
+ return `import { Callout, Heading, Typography } from "@lukoweb/apitogo/components";
19089
+
19090
+ /** The page's label in the sidebar. */
19091
+ export const title = ${JSON.stringify(title)};
19092
+
19093
+ export default function ${pageComponentName(slug)}() {
19094
+ return (
19095
+ <section className="mx-auto max-w-screen-lg px-4 py-12 pt-(--padding-content-top)">
19096
+ <Typography>
19097
+ <Heading level={1}>{title}</Heading>
19098
+ <p>${text}</p>
19099
+ <Callout type="info" title="Where this page lives">
19100
+ Edit <code>${file}</code>. Files whose name starts with <code>_</code> are not pages, so you can keep shared
19101
+ components next to your pages.
19102
+ </Callout>
19103
+ </Typography>
19104
+ </section>
19105
+ );
19106
+ }
19107
+ `;
19108
+ }
18928
19109
  async function writeDocsPage(project, input5) {
18929
19110
  requirePortal(project);
18930
19111
  if (/^[A-Za-z]:[\\/]/.test(input5.slug.trim())) {
@@ -18942,29 +19123,36 @@ async function writeDocsPage(project, input5) {
18942
19123
  }
18943
19124
  const slug = normalizeDocsSlug(input5.slug);
18944
19125
  if (!slug) throw new ConfigEditError("The page path must not be empty.");
18945
- const base = path61.join(portal.dir, "pages", ...slug.split("/"));
19126
+ const base = path62.join(portal.dir, "pages", ...slug.split("/"));
18946
19127
  const existing = (await Promise.all(
18947
- [`${base}.mdx`, `${base}.md`].map(
19128
+ [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
18948
19129
  async (file2) => await exists4(file2) ? file2 : null
18949
19130
  )
18950
19131
  )).find((file2) => file2 !== null);
19132
+ const relative = (file2) => toPosix2(path62.relative(project.dir, file2));
19133
+ const write = (file2, title) => writeFile18(
19134
+ file2,
19135
+ file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content) : buildDocsPage(title, input5.content),
19136
+ "utf8"
19137
+ );
18951
19138
  let file;
18952
19139
  let action;
18953
19140
  if (input5.update) {
18954
19141
  if (!existing) {
18955
19142
  throw new ConfigEditError(
18956
- `There is no page at ${toPosix2(path61.relative(project.dir, `${base}.mdx`))}.`,
19143
+ `There is no page at ${relative(`${base}.${input5.format ?? "tsx"}`)}.`,
18957
19144
  "Leave out --page-update to add it."
18958
19145
  );
18959
19146
  }
18960
- const title = input5.title?.trim() || titleOf(await readFile20(existing, "utf8"));
19147
+ const source = await readFile22(existing, "utf8");
19148
+ const title = input5.title?.trim() || (existing.endsWith(".tsx") ? tsxPageLabel(source, existing) : titleOf(source));
18961
19149
  file = existing;
18962
19150
  action = "updated";
18963
- await writeFile18(file, buildDocsPage(title ?? slug, input5.content), "utf8");
19151
+ await write(file, title ?? slug);
18964
19152
  } else {
18965
19153
  if (existing) {
18966
19154
  throw new ConfigEditError(
18967
- `A page already exists at ${toPosix2(path61.relative(project.dir, existing))}.`,
19155
+ `A page already exists at ${relative(existing)}.`,
18968
19156
  "Pass --page-update to change it."
18969
19157
  );
18970
19158
  }
@@ -18972,20 +19160,19 @@ async function writeDocsPage(project, input5) {
18972
19160
  if (!title) {
18973
19161
  throw new ConfigEditError("A new page needs a title (--page-title).");
18974
19162
  }
18975
- file = `${base}.mdx`;
19163
+ file = `${base}.${input5.format ?? "tsx"}`;
18976
19164
  action = "added";
18977
- await mkdir15(path61.dirname(file), { recursive: true });
18978
- await writeFile18(file, buildDocsPage(title, input5.content), "utf8");
19165
+ await mkdir15(path62.dirname(file), { recursive: true });
19166
+ await write(file, title);
18979
19167
  }
18980
- const included = await glob7(portal.docs.content, { absolute: true });
18981
- const inPortal = included.some(
18982
- (match) => path61.resolve(match) === path61.resolve(file)
18983
- );
19168
+ const same = (match) => path62.resolve(match) === path62.resolve(file);
19169
+ const inPortal = file.endsWith(".tsx") ? (await findTsxPages(portal.docs.content)).some((page) => same(page.file)) : (await glob8(portal.docs.content, { absolute: true })).some(same);
18984
19170
  return {
18985
19171
  action,
18986
19172
  slug,
18987
19173
  route: `/${slug}`,
18988
- file: toPosix2(path61.relative(project.dir, file)),
19174
+ file: relative(file),
19175
+ format: path62.extname(file).slice(1),
18989
19176
  inPortal
18990
19177
  };
18991
19178
  }
@@ -19046,7 +19233,7 @@ async function upsertPlan(project, plan2) {
19046
19233
  if (await exists4(plans.file)) {
19047
19234
  let raw;
19048
19235
  try {
19049
- raw = JSON.parse(await readFile20(plans.file, "utf8"));
19236
+ raw = JSON.parse(await readFile22(plans.file, "utf8"));
19050
19237
  } catch (error) {
19051
19238
  throw new ConfigEditError(
19052
19239
  `${relativeToConfig(project, plans.file)} is not valid JSON (${error.message}).`
@@ -19066,18 +19253,18 @@ async function upsertPlan(project, plan2) {
19066
19253
  const next = [...catalog.plans];
19067
19254
  if (index === -1) next.push(plan2);
19068
19255
  else next[index] = plan2;
19069
- await mkdir15(path61.dirname(plans.file), { recursive: true });
19256
+ await mkdir15(path62.dirname(plans.file), { recursive: true });
19070
19257
  await writeFile18(
19071
19258
  plans.file,
19072
19259
  `${JSON.stringify({ ...catalog, plans: next }, null, 2)}
19073
19260
  `,
19074
19261
  "utf8"
19075
19262
  );
19076
- const paid = plan2.pricingModel !== "custom" && (plan2.priceAmount ?? 0) > 0;
19263
+ const paid = isPaidPlan(plan2);
19077
19264
  return {
19078
19265
  action: index === -1 ? "added" : "updated",
19079
19266
  sku: plan2.sku,
19080
- file: toPosix2(path61.relative(project.dir, plans.file)),
19267
+ file: toPosix2(path62.relative(project.dir, plans.file)),
19081
19268
  paid,
19082
19269
  needsBilling: paid && plans.billing.provider !== "stripe"
19083
19270
  };
@@ -19105,7 +19292,7 @@ function printPageResult(page) {
19105
19292
  );
19106
19293
  if (page.inPortal) {
19107
19294
  printHintToConsole(
19108
- "The sidebar is built from the pages folder, so the config does not change. Edit the file to change the content."
19295
+ page.format === "tsx" ? "The page is a React component: edit it like any component. The sidebar is built from the pages folder, so the config does not change." : "The sidebar is built from the pages folder, so the config does not change. Edit the file to change the content."
19109
19296
  );
19110
19297
  } else {
19111
19298
  printWarningToConsole(
@@ -19117,7 +19304,12 @@ function printPlanResult(plan2) {
19117
19304
  printSuccessToConsole(`Plan "${plan2.sku}" ${plan2.action} in ${plan2.file}.`);
19118
19305
  if (plan2.needsBilling) {
19119
19306
  printWarningToConsole(
19120
- "This plan has a price and plans.billing.provider is not stripe, so deploy will refuse it. Set plans.billing.provider to stripe, or make the plan free or custom."
19307
+ 'This plan has a price, but plans.billing.provider is "none", so deploy will refuse it. Remove plans.billing.provider (Stripe is the provider), or make the plan free or custom.'
19308
+ );
19309
+ }
19310
+ if (plan2.paid && !plan2.needsBilling) {
19311
+ printHintToConsole(
19312
+ "Payments go through Stripe: apitogo deploy asks for your Stripe secret key if the project has none yet."
19121
19313
  );
19122
19314
  }
19123
19315
  if (plan2.paid) printPaidPlanPlatformWarning();
@@ -19346,7 +19538,7 @@ async function applyConfigFlags(project, argv, changes) {
19346
19538
  return { project: next, openApi };
19347
19539
  }
19348
19540
  async function makeConfig(argv) {
19349
- const dir = path62.resolve(process.cwd(), argv.dir);
19541
+ const dir = path63.resolve(process.cwd(), argv.dir);
19350
19542
  const interactive = shouldRunInteractive(argv);
19351
19543
  const json = argv.json === true;
19352
19544
  const changes = [];
@@ -19393,7 +19585,8 @@ async function makeConfig(argv) {
19393
19585
  slug: argv.pageSlug,
19394
19586
  title: argv.pageTitle,
19395
19587
  content: argv.pageContent,
19396
- update: argv.pageUpdate === true
19588
+ update: argv.pageUpdate === true,
19589
+ format: argv.pageFormat
19397
19590
  });
19398
19591
  changes.push(page.action === "added" ? "page_add" : "page_update");
19399
19592
  }
@@ -19480,13 +19673,17 @@ function addConfigOptions(yargs2) {
19480
19673
  describe: "Runtime version of --source, for example NODE_22_LTS"
19481
19674
  }).option("page-slug", {
19482
19675
  type: "string",
19483
- describe: "Add or update a docs page at <portal>/pages/<slug>.mdx (for example guides/quickstart)"
19676
+ describe: "Add or update a docs page at <portal>/pages/<slug>.tsx (for example guides/quickstart)"
19484
19677
  }).option("page-title", {
19485
19678
  type: "string",
19486
19679
  describe: "Title of --page-slug (required when adding)"
19487
19680
  }).option("page-content", {
19488
19681
  type: "string",
19489
- describe: "Markdown body of --page-slug"
19682
+ describe: "Text of --page-slug (Markdown for an .mdx page)"
19683
+ }).option("page-format", {
19684
+ type: "string",
19685
+ choices: ["tsx", "mdx"],
19686
+ describe: "Format of a new page: tsx (a React component, the default) or mdx (Markdown)"
19490
19687
  }).option("page-update", {
19491
19688
  type: "boolean",
19492
19689
  default: false,
@@ -19512,15 +19709,15 @@ var config_default = {
19512
19709
  // src/cli/create/handler.ts
19513
19710
  init_apitogo_config_core();
19514
19711
  import { execFile } from "node:child_process";
19515
- import { readFile as readFile21 } from "node:fs/promises";
19516
- import path63 from "node:path";
19712
+ import { readFile as readFile23 } from "node:fs/promises";
19713
+ import path64 from "node:path";
19517
19714
  import { promisify } from "node:util";
19518
19715
  var execFileAsync = promisify(execFile);
19519
19716
  function nextStepHint(dir) {
19520
19717
  const linked = loadConfigV2(dir).resolved.project.id;
19521
- const cd = path63.resolve(dir) === path63.resolve(process.cwd());
19718
+ const cd = path64.resolve(dir) === path64.resolve(process.cwd());
19522
19719
  const steps = linked ? "apitogo deploy" : "apitogo project create, then apitogo deploy";
19523
- return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path63.relative(process.cwd(), dir))}, then ${steps}`;
19720
+ return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path64.relative(process.cwd(), dir))}, then ${steps}`;
19524
19721
  }
19525
19722
  function projectFolderName(name) {
19526
19723
  return name.trim().replace(/[<>:"/\\|?*\s]+/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "apitogo-project";
@@ -19546,13 +19743,13 @@ async function gitHasUncommittedChanges(dir) {
19546
19743
  async function defaultName(dir) {
19547
19744
  try {
19548
19745
  const parsed = JSON.parse(
19549
- await readFile21(path63.join(dir, "package.json"), "utf8")
19746
+ await readFile23(path64.join(dir, "package.json"), "utf8")
19550
19747
  );
19551
19748
  if (typeof parsed.name === "string" && parsed.name.trim())
19552
19749
  return parsed.name.replace(/^@[^/]+\//, "");
19553
19750
  } catch {
19554
19751
  }
19555
- return path63.basename(dir);
19752
+ return path64.basename(dir);
19556
19753
  }
19557
19754
  function parseServicesFlag(value) {
19558
19755
  const names = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
@@ -19578,7 +19775,7 @@ async function createProject(argv, deps = {}) {
19578
19775
  if (!json) printDiagnosticsToConsole(line2);
19579
19776
  };
19580
19777
  try {
19581
- const base = path63.resolve(process.cwd(), argv.dir ?? ".");
19778
+ const base = path64.resolve(process.cwd(), argv.dir ?? ".");
19582
19779
  let dir = base;
19583
19780
  let requested = parseServicesFlag(argv.services);
19584
19781
  let template = argv.template;
@@ -19601,8 +19798,8 @@ async function createProject(argv, deps = {}) {
19601
19798
  default: await defaultName(base)
19602
19799
  }) : await defaultName(base));
19603
19800
  const folder = projectFolderName(name);
19604
- if (argv.dir === void 0 && folder !== path63.basename(base))
19605
- dir = path63.join(base, folder);
19801
+ if (argv.dir === void 0 && folder !== path64.basename(base))
19802
+ dir = path64.join(base, folder);
19606
19803
  }
19607
19804
  if (await (deps.hasUncommittedChanges ?? gitHasUncommittedChanges)(dir))
19608
19805
  say(
@@ -19629,7 +19826,7 @@ async function createProject(argv, deps = {}) {
19629
19826
  }
19630
19827
  await applyInitPlan(plan2);
19631
19828
  say(
19632
- dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path63.relative(base, dir)}${path63.sep}${APITOGO_CONFIG_FILENAME}.`
19829
+ dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path64.relative(base, dir)}${path64.sep}${APITOGO_CONFIG_FILENAME}.`
19633
19830
  );
19634
19831
  } else {
19635
19832
  say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
@@ -19772,10 +19969,10 @@ var create_default = {
19772
19969
  };
19773
19970
 
19774
19971
  // src/cli/cmds/deploy.ts
19775
- import path80 from "node:path";
19972
+ import path82 from "node:path";
19776
19973
 
19777
19974
  // src/cli/apply/handler.ts
19778
- import path75 from "node:path";
19975
+ import path76 from "node:path";
19779
19976
 
19780
19977
  // src/cli/platform/helpers/gateway-plan-validation.ts
19781
19978
  init_gateway_limits();
@@ -19984,11 +20181,11 @@ init_gateway_limits();
19984
20181
  // src/cli/platform/helpers/deploy-artifact-hash.ts
19985
20182
  import { createHash as createHash3 } from "node:crypto";
19986
20183
  import { createReadStream, promises as fs6 } from "node:fs";
19987
- import path65 from "node:path";
20184
+ import path66 from "node:path";
19988
20185
 
19989
20186
  // src/cli/platform/helpers/package-files.ts
19990
20187
  import { promises as fs5 } from "node:fs";
19991
- import path64 from "node:path";
20188
+ import path65 from "node:path";
19992
20189
  import ignore from "ignore";
19993
20190
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
19994
20191
  "node_modules",
@@ -20002,7 +20199,7 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20002
20199
  const ig = ignore();
20003
20200
  ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
20004
20201
  if (extraExcludes.length > 0) ig.add([...extraExcludes]);
20005
- const gitignorePath = path64.join(rootDir, ".gitignore");
20202
+ const gitignorePath = path65.join(rootDir, ".gitignore");
20006
20203
  try {
20007
20204
  const content = await fs5.readFile(gitignorePath, "utf8");
20008
20205
  ig.add(content);
@@ -20011,11 +20208,11 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20011
20208
  return ig;
20012
20209
  }
20013
20210
  async function collectFiles(rootDir, relativeDir, ig, files) {
20014
- const absDir = path64.join(rootDir, relativeDir);
20211
+ const absDir = path65.join(rootDir, relativeDir);
20015
20212
  const entries = await fs5.readdir(absDir, { withFileTypes: true });
20016
20213
  for (const entry of entries) {
20017
- const rel2 = relativeDir ? path64.join(relativeDir, entry.name) : entry.name;
20018
- const relPosix = rel2.split(path64.sep).join("/");
20214
+ const rel2 = relativeDir ? path65.join(relativeDir, entry.name) : entry.name;
20215
+ const relPosix = rel2.split(path65.sep).join("/");
20019
20216
  if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
20020
20217
  continue;
20021
20218
  }
@@ -20030,7 +20227,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
20030
20227
  }
20031
20228
  }
20032
20229
  async function collectPackageableFiles(sourceDirectory, extraExcludes = []) {
20033
- const resolved = path64.resolve(sourceDirectory);
20230
+ const resolved = path65.resolve(sourceDirectory);
20034
20231
  const stat7 = await fs5.stat(resolved).catch(() => null);
20035
20232
  if (!stat7?.isDirectory()) {
20036
20233
  throw new Error(`sourceDirectory is not a directory: ${resolved}`);
@@ -20056,7 +20253,7 @@ async function sha256File(filePath) {
20056
20253
  return hash2.digest("hex");
20057
20254
  }
20058
20255
  async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20059
- const resolved = path65.resolve(rootDirectory);
20256
+ const resolved = path66.resolve(rootDirectory);
20060
20257
  const files = (await collectPackageableFiles(resolved, extraExcludes)).sort(
20061
20258
  (a, b) => a.localeCompare(b)
20062
20259
  );
@@ -20064,13 +20261,13 @@ async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20064
20261
  for (const rel2 of files) {
20065
20262
  hash2.update(rel2);
20066
20263
  hash2.update("\0");
20067
- hash2.update(await fs6.readFile(path65.join(resolved, rel2)));
20264
+ hash2.update(await fs6.readFile(path66.join(resolved, rel2)));
20068
20265
  hash2.update("\0");
20069
20266
  }
20070
20267
  return hash2.digest("hex");
20071
20268
  }
20072
20269
  async function hashDevPortalSiteSources(siteDirectory, options) {
20073
- const resolved = path65.resolve(siteDirectory);
20270
+ const resolved = path66.resolve(siteDirectory);
20074
20271
  const hash2 = createHash3("sha256");
20075
20272
  const candidates = [
20076
20273
  "apitogo.config.json",
@@ -20080,7 +20277,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20080
20277
  "zudoku.config.tsx"
20081
20278
  ];
20082
20279
  for (const name of candidates) {
20083
- const full = path65.join(resolved, name);
20280
+ const full = path66.join(resolved, name);
20084
20281
  try {
20085
20282
  hash2.update(name);
20086
20283
  hash2.update("\0");
@@ -20090,7 +20287,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20090
20287
  }
20091
20288
  }
20092
20289
  for (const dir of ["pages", "landing", "src"]) {
20093
- const full = path65.join(resolved, dir);
20290
+ const full = path66.join(resolved, dir);
20094
20291
  try {
20095
20292
  const st = await fs6.stat(full);
20096
20293
  if (!st.isDirectory()) continue;
@@ -20319,8 +20516,8 @@ function normalizeActionKey(actionKey2) {
20319
20516
  const trimmed = actionKey2.trim();
20320
20517
  if (trimmed.includes(":")) {
20321
20518
  const [method2, ...pathParts] = trimmed.split(":");
20322
- const path96 = pathParts.join(":").trim();
20323
- return `${method2?.toLowerCase()}:${path96.startsWith("/") ? path96 : `/${path96}`}`;
20519
+ const path98 = pathParts.join(":").trim();
20520
+ return `${method2?.toLowerCase()}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
20324
20521
  }
20325
20522
  const spaceIndex = trimmed.indexOf(" ");
20326
20523
  if (spaceIndex <= 0) {
@@ -20329,8 +20526,8 @@ function normalizeActionKey(actionKey2) {
20329
20526
  );
20330
20527
  }
20331
20528
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
20332
- const path95 = trimmed.slice(spaceIndex + 1).trim();
20333
- return `${method}:${path95.startsWith("/") ? path95 : `/${path95}`}`;
20529
+ const path97 = trimmed.slice(spaceIndex + 1).trim();
20530
+ return `${method}:${path97.startsWith("/") ? path97 : `/${path97}`}`;
20334
20531
  }
20335
20532
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
20336
20533
  const applied = [];
@@ -20808,8 +21005,8 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
20808
21005
  }
20809
21006
 
20810
21007
  // src/cli/platform/helpers/openapi-gateway-servers.ts
20811
- import { mkdir as mkdir16, readFile as readFile22, writeFile as writeFile19 } from "node:fs/promises";
20812
- import path66 from "node:path";
21008
+ import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile19 } from "node:fs/promises";
21009
+ import path67 from "node:path";
20813
21010
  import YAML from "yaml";
20814
21011
  var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
20815
21012
  var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
@@ -20840,8 +21037,8 @@ async function resolveProjectGatewayUrl(client, projectId) {
20840
21037
  return null;
20841
21038
  }
20842
21039
  async function readOpenApiDocument(filePath) {
20843
- const content = await readFile22(filePath, "utf8");
20844
- const ext = path66.extname(filePath).toLowerCase();
21040
+ const content = await readFile24(filePath, "utf8");
21041
+ const ext = path67.extname(filePath).toLowerCase();
20845
21042
  const parsed = ext === ".json" ? JSON.parse(content) : YAML.parse(content);
20846
21043
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
20847
21044
  throw new Error(`OpenAPI document must be an object: ${filePath}`);
@@ -20849,13 +21046,13 @@ async function readOpenApiDocument(filePath) {
20849
21046
  return parsed;
20850
21047
  }
20851
21048
  async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
20852
- const site = path66.resolve(siteDirectory);
20853
- const source = path66.resolve(sourceOpenApiPath);
21049
+ const site = path67.resolve(siteDirectory);
21050
+ const source = path67.resolve(sourceOpenApiPath);
20854
21051
  const relativeInput = PUBLISH_OPENAPI_RELATIVE;
20855
- const outputPath = path66.join(site, ...relativeInput.split("/"));
21052
+ const outputPath = path67.join(site, ...relativeInput.split("/"));
20856
21053
  const doc = await readOpenApiDocument(source);
20857
21054
  const rewritten = withGatewayServers(doc, gatewayUrl);
20858
- await mkdir16(path66.dirname(outputPath), { recursive: true });
21055
+ await mkdir16(path67.dirname(outputPath), { recursive: true });
20859
21056
  await writeFile19(
20860
21057
  outputPath,
20861
21058
  `${JSON.stringify(rewritten, null, 2)}
@@ -20865,11 +21062,11 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
20865
21062
  return { outputPath, relativeInput };
20866
21063
  }
20867
21064
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
20868
- const configPath = path66.join(
20869
- path66.resolve(siteDirectory),
21065
+ const configPath = path67.join(
21066
+ path67.resolve(siteDirectory),
20870
21067
  APITOGO_CONFIG_FILENAME2
20871
21068
  );
20872
- const raw = await readFile22(configPath, "utf8");
21069
+ const raw = await readFile24(configPath, "utf8");
20873
21070
  const config2 = JSON.parse(raw);
20874
21071
  const previousApis = config2.apis;
20875
21072
  const previousBackend = config2.backend;
@@ -20886,7 +21083,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
20886
21083
  `, "utf8");
20887
21084
  return {
20888
21085
  restore: async () => {
20889
- const currentRaw = await readFile22(configPath, "utf8");
21086
+ const currentRaw = await readFile24(configPath, "utf8");
20890
21087
  const current = JSON.parse(currentRaw);
20891
21088
  if (previousApis === void 0) {
20892
21089
  delete current.apis;
@@ -20908,7 +21105,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
20908
21105
  };
20909
21106
  }
20910
21107
  async function prepareOpenApiServersForGatewayBuild(options) {
20911
- const siteDirectory = path66.resolve(options.siteDirectory);
21108
+ const siteDirectory = path67.resolve(options.siteDirectory);
20912
21109
  let openApiPath = resolveBackendOpenApiPath(
20913
21110
  options.manifest?.backend?.openApiPath,
20914
21111
  {
@@ -20921,8 +21118,8 @@ async function prepareOpenApiServersForGatewayBuild(options) {
20921
21118
  );
20922
21119
  if (!openApiPath) {
20923
21120
  try {
20924
- const raw = await readFile22(
20925
- path66.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21121
+ const raw = await readFile24(
21122
+ path67.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
20926
21123
  "utf8"
20927
21124
  );
20928
21125
  const config2 = JSON.parse(raw);
@@ -20951,8 +21148,8 @@ function normalizeActionKeyFormat(actionKey2) {
20951
21148
  const trimmed = actionKey2.trim();
20952
21149
  if (trimmed.includes(":")) {
20953
21150
  const [method2, ...pathParts] = trimmed.split(":");
20954
- const path96 = pathParts.join(":").trim();
20955
- return `${method2.toLowerCase()}:${path96.startsWith("/") ? path96 : `/${path96}`}`;
21151
+ const path98 = pathParts.join(":").trim();
21152
+ return `${method2.toLowerCase()}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
20956
21153
  }
20957
21154
  const spaceIndex = trimmed.indexOf(" ");
20958
21155
  if (spaceIndex <= 0) {
@@ -20961,8 +21158,8 @@ function normalizeActionKeyFormat(actionKey2) {
20961
21158
  );
20962
21159
  }
20963
21160
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
20964
- const path95 = trimmed.slice(spaceIndex + 1).trim();
20965
- return `${method}:${path95.startsWith("/") ? path95 : `/${path95}`}`;
21161
+ const path97 = trimmed.slice(spaceIndex + 1).trim();
21162
+ return `${method}:${path97.startsWith("/") ? path97 : `/${path97}`}`;
20966
21163
  }
20967
21164
  function normalizePublicRoute(route) {
20968
21165
  const trimmed = route.trim();
@@ -20995,16 +21192,16 @@ function collectManifestPublicActionKeys(gateway) {
20995
21192
  }
20996
21193
 
20997
21194
  // src/cli/platform/helpers/publish-ledger.ts
20998
- import { mkdir as mkdir17, readFile as readFile23, writeFile as writeFile20 } from "node:fs/promises";
20999
- import path67 from "node:path";
21195
+ import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile20 } from "node:fs/promises";
21196
+ import path68 from "node:path";
21000
21197
  var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
21001
21198
  function publishLedgerPath(siteDirectory) {
21002
- return path67.join(path67.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21199
+ return path68.join(path68.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21003
21200
  }
21004
21201
  async function readPublishLedger(siteDirectory) {
21005
21202
  const filePath = publishLedgerPath(siteDirectory);
21006
21203
  try {
21007
- const raw = await readFile23(filePath, "utf8");
21204
+ const raw = await readFile25(filePath, "utf8");
21008
21205
  const parsed = JSON.parse(raw);
21009
21206
  if (parsed?.version !== 1 || !Array.isArray(parsed.steps)) {
21010
21207
  return null;
@@ -21016,7 +21213,7 @@ async function readPublishLedger(siteDirectory) {
21016
21213
  }
21017
21214
  async function writePublishLedger(siteDirectory, ledger) {
21018
21215
  const filePath = publishLedgerPath(siteDirectory);
21019
- await mkdir17(path67.dirname(filePath), { recursive: true });
21216
+ await mkdir17(path68.dirname(filePath), { recursive: true });
21020
21217
  const next = {
21021
21218
  ...ledger,
21022
21219
  version: 1,
@@ -21538,7 +21735,7 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
21538
21735
  import { spawn as spawn3 } from "node:child_process";
21539
21736
  import { mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
21540
21737
  import { tmpdir } from "node:os";
21541
- import path68 from "node:path";
21738
+ import path69 from "node:path";
21542
21739
  async function directoryHasRuntimeConfig(dir) {
21543
21740
  if (!await pathExists(dir)) {
21544
21741
  return false;
@@ -21581,20 +21778,20 @@ function runDotnetPublish(projectDir, outputDir) {
21581
21778
  async function findCsproj(projectDir) {
21582
21779
  const entries = await readdir4(projectDir);
21583
21780
  const csproj = entries.find((name) => name.endsWith(".csproj"));
21584
- return csproj ? path68.join(projectDir, csproj) : null;
21781
+ return csproj ? path69.join(projectDir, csproj) : null;
21585
21782
  }
21586
21783
  async function prepareBackendPackagingDirectory(args) {
21587
- const sourceDirectory = path68.resolve(args.sourceDirectory);
21784
+ const sourceDirectory = path69.resolve(args.sourceDirectory);
21588
21785
  if (args.publishDirectory?.trim()) {
21589
21786
  return {
21590
- directory: path68.resolve(args.publishDirectory),
21787
+ directory: path69.resolve(args.publishDirectory),
21591
21788
  packagingMode: "published"
21592
21789
  };
21593
21790
  }
21594
21791
  if (await directoryHasRuntimeConfig(sourceDirectory)) {
21595
21792
  return { directory: sourceDirectory, packagingMode: "published" };
21596
21793
  }
21597
- const publishSubdir = path68.join(sourceDirectory, "publish");
21794
+ const publishSubdir = path69.join(sourceDirectory, "publish");
21598
21795
  if (await directoryHasRuntimeConfig(publishSubdir)) {
21599
21796
  return { directory: publishSubdir, packagingMode: "published" };
21600
21797
  }
@@ -21606,9 +21803,9 @@ async function prepareBackendPackagingDirectory(args) {
21606
21803
  return { directory: sourceDirectory, packagingMode: "source" };
21607
21804
  }
21608
21805
  const tempRoot = await mkdtemp(
21609
- path68.join(tmpdir(), "apitogo-dotnet-publish-")
21806
+ path69.join(tmpdir(), "apitogo-dotnet-publish-")
21610
21807
  );
21611
- const outputDir = path68.join(tempRoot, "publish");
21808
+ const outputDir = path69.join(tempRoot, "publish");
21612
21809
  const result = await runDotnetPublish(sourceDirectory, outputDir);
21613
21810
  if (result.exitCode !== 0) {
21614
21811
  await rm9(tempRoot, { recursive: true, force: true });
@@ -21653,13 +21850,13 @@ function formatJobStatusResponse(options) {
21653
21850
 
21654
21851
  // src/cli/platform/helpers/openapi.ts
21655
21852
  import { promises as fs7 } from "node:fs";
21656
- import path69 from "node:path";
21853
+ import path70 from "node:path";
21657
21854
  import SwaggerParser from "@apidevtools/swagger-parser";
21658
21855
  import YAML2 from "yaml";
21659
21856
  async function validateOpenApiFile(filePath) {
21660
- const resolved = path69.resolve(filePath);
21857
+ const resolved = path70.resolve(filePath);
21661
21858
  const content = await fs7.readFile(resolved, "utf8");
21662
- const ext = path69.extname(resolved).toLowerCase();
21859
+ const ext = path70.extname(resolved).toLowerCase();
21663
21860
  let parsed;
21664
21861
  try {
21665
21862
  parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
@@ -21703,12 +21900,12 @@ async function validateOpenApiFile(filePath) {
21703
21900
  // src/cli/platform/helpers/zip.ts
21704
21901
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs8 } from "node:fs";
21705
21902
  import { tmpdir as tmpdir2 } from "node:os";
21706
- import path70 from "node:path";
21903
+ import path71 from "node:path";
21707
21904
  import { ZipArchive } from "archiver";
21708
21905
  async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
21709
- const resolved = path70.resolve(sourceDirectory);
21906
+ const resolved = path71.resolve(sourceDirectory);
21710
21907
  const files = await collectPackageableFiles(resolved, extraExcludes);
21711
- const zipPath = path70.join(
21908
+ const zipPath = path71.join(
21712
21909
  tmpdir2(),
21713
21910
  `apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
21714
21911
  );
@@ -21720,8 +21917,8 @@ async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
21720
21917
  output4.on("error", reject);
21721
21918
  archive.pipe(output4);
21722
21919
  for (const rel2 of files) {
21723
- archive.append(createReadStream2(path70.join(resolved, rel2)), {
21724
- name: rel2.split(path70.sep).join("/")
21920
+ archive.append(createReadStream2(path71.join(resolved, rel2)), {
21921
+ name: rel2.split(path71.sep).join("/")
21725
21922
  });
21726
21923
  }
21727
21924
  void archive.finalize();
@@ -23272,11 +23469,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
23272
23469
  }
23273
23470
 
23274
23471
  // src/cli/platform/tools/dev-portal-site-build.ts
23275
- import path74 from "node:path";
23472
+ import path75 from "node:path";
23276
23473
 
23277
23474
  // src/cli/platform/helpers/dev-portal-site-api-url.ts
23278
- import { readdir as readdir5, readFile as readFile24, writeFile as writeFile21 } from "node:fs/promises";
23279
- import path71 from "node:path";
23475
+ import { readdir as readdir5, readFile as readFile26, writeFile as writeFile21 } from "node:fs/promises";
23476
+ import path72 from "node:path";
23280
23477
  var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
23281
23478
  var PLACEHOLDER_HOST_MARKERS = [
23282
23479
  "dp-example.azurecontainerapps.io",
@@ -23297,10 +23494,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
23297
23494
  return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
23298
23495
  }
23299
23496
  async function readSiteApiBaseUrl(sourceDirectory) {
23300
- const resolved = path71.resolve(sourceDirectory);
23497
+ const resolved = path72.resolve(sourceDirectory);
23301
23498
  for (const envFile of [".env", ".env.production"]) {
23302
23499
  try {
23303
- const content = await readFile24(path71.join(resolved, envFile), "utf8");
23500
+ const content = await readFile26(path72.join(resolved, envFile), "utf8");
23304
23501
  const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
23305
23502
  if (lines.length === 0) {
23306
23503
  continue;
@@ -23325,13 +23522,13 @@ async function readSiteApiBaseUrl(sourceDirectory) {
23325
23522
  return null;
23326
23523
  }
23327
23524
  async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
23328
- const resolved = path71.resolve(sourceDirectory);
23525
+ const resolved = path72.resolve(sourceDirectory);
23329
23526
  const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
23330
23527
  let patchedExisting = false;
23331
23528
  for (const envFile of [".env.production", ".env"]) {
23332
- const envPath = path71.join(resolved, envFile);
23529
+ const envPath = path72.join(resolved, envFile);
23333
23530
  try {
23334
- const content = await readFile24(envPath, "utf8");
23531
+ const content = await readFile26(envPath, "utf8");
23335
23532
  const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
23336
23533
  const patched = withoutAssignments ? `${withoutAssignments}
23337
23534
  ${line2}
@@ -23343,7 +23540,7 @@ ${line2}
23343
23540
  }
23344
23541
  }
23345
23542
  if (!patchedExisting) {
23346
- await writeFile21(path71.join(resolved, ".env"), `${line2}
23543
+ await writeFile21(path72.join(resolved, ".env"), `${line2}
23347
23544
  `);
23348
23545
  }
23349
23546
  }
@@ -23351,7 +23548,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
23351
23548
  return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
23352
23549
  }
23353
23550
  async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
23354
- const sourceDirectory = path71.resolve(args.sourceDirectory);
23551
+ const sourceDirectory = path72.resolve(args.sourceDirectory);
23355
23552
  const explicit = args.apiBaseUrl?.trim();
23356
23553
  if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
23357
23554
  await patchSiteApiBaseUrl(sourceDirectory, explicit);
@@ -23387,7 +23584,7 @@ async function* walkFiles(root) {
23387
23584
  return;
23388
23585
  }
23389
23586
  for (const entry of entries) {
23390
- const full = path71.join(root, entry.name);
23587
+ const full = path72.join(root, entry.name);
23391
23588
  if (entry.isDirectory()) {
23392
23589
  if (entry.name === "node_modules" || entry.name === ".git") {
23393
23590
  continue;
@@ -23399,8 +23596,8 @@ async function* walkFiles(root) {
23399
23596
  }
23400
23597
  }
23401
23598
  async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
23402
- const dist = path71.resolve(distDirectory);
23403
- const siteRoot = path71.dirname(dist);
23599
+ const dist = path72.resolve(distDirectory);
23600
+ const siteRoot = path72.dirname(dist);
23404
23601
  const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
23405
23602
  if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
23406
23603
  throw new Error(
@@ -23411,7 +23608,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
23411
23608
  let found = false;
23412
23609
  let foundPlaceholder = false;
23413
23610
  for await (const file of walkFiles(dist)) {
23414
- const content = await readFile24(file, "utf8");
23611
+ const content = await readFile26(file, "utf8");
23415
23612
  if (content.includes(needle)) {
23416
23613
  found = true;
23417
23614
  }
@@ -23436,7 +23633,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
23436
23633
 
23437
23634
  // src/cli/platform/tools/dev-portal.ts
23438
23635
  import { promises as fs10 } from "node:fs";
23439
- import path73 from "node:path";
23636
+ import path74 from "node:path";
23440
23637
 
23441
23638
  // src/cli/platform/helpers/dev-portal-backend-job.ts
23442
23639
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
@@ -23546,7 +23743,7 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
23546
23743
  // src/cli/platform/helpers/tarball.ts
23547
23744
  import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs9 } from "node:fs";
23548
23745
  import { tmpdir as tmpdir3 } from "node:os";
23549
- import path72 from "node:path";
23746
+ import path73 from "node:path";
23550
23747
  import { TarArchive } from "archiver";
23551
23748
  var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
23552
23749
  function normalizeArchiveRoot(archiveRoot) {
@@ -23557,10 +23754,10 @@ function normalizeArchiveRoot(archiveRoot) {
23557
23754
  return `${trimmed}/`;
23558
23755
  }
23559
23756
  async function packageDirectoryAsTarGz(sourceDirectory, options) {
23560
- const resolved = path72.resolve(sourceDirectory);
23757
+ const resolved = path73.resolve(sourceDirectory);
23561
23758
  const files = await collectPackageableFiles(resolved);
23562
23759
  const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
23563
- const archivePath = path72.join(
23760
+ const archivePath = path73.join(
23564
23761
  tmpdir3(),
23565
23762
  `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
23566
23763
  );
@@ -23572,8 +23769,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
23572
23769
  output4.on("error", reject);
23573
23770
  archive.pipe(output4);
23574
23771
  for (const rel2 of files) {
23575
- const entryName = entryPrefix + rel2.split(path72.sep).join("/");
23576
- archive.append(createReadStream3(path72.join(resolved, rel2)), {
23772
+ const entryName = entryPrefix + rel2.split(path73.sep).join("/");
23773
+ archive.append(createReadStream3(path73.join(resolved, rel2)), {
23577
23774
  name: entryName
23578
23775
  });
23579
23776
  }
@@ -23815,7 +24012,7 @@ function validateDeployArchiveInput(args) {
23815
24012
  async function resolveDevPortalArchive(args) {
23816
24013
  validateDeployArchiveInput(args);
23817
24014
  if (args.archivePath?.trim()) {
23818
- const archivePath = path73.resolve(args.archivePath.trim());
24015
+ const archivePath = path74.resolve(args.archivePath.trim());
23819
24016
  const stat7 = await fs10.stat(archivePath).catch(() => null);
23820
24017
  if (!stat7?.isFile()) {
23821
24018
  throw new Error(`archivePath is not a file: ${archivePath}`);
@@ -23880,7 +24077,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
23880
24077
  // src/cli/platform/tools/dev-portal-site-build.ts
23881
24078
  async function handleBuildDevPortalSite(client, config2, args, progress) {
23882
24079
  const buildProgress = progress?.child("Build dev portal site");
23883
- const sourceDirectory = path74.resolve(args.sourceDirectory);
24080
+ const sourceDirectory = path75.resolve(args.sourceDirectory);
23884
24081
  await buildProgress?.onStep("Ensuring live portal API URL");
23885
24082
  const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
23886
24083
  client,
@@ -25386,7 +25583,7 @@ function resultLooksFailed(payload) {
25386
25583
  return parsed.status === "failed" || parsed.status === "blocked";
25387
25584
  }
25388
25585
  async function apply(argv) {
25389
- const sourceDirectory = path75.resolve(process.cwd(), argv.dir);
25586
+ const sourceDirectory = path76.resolve(process.cwd(), argv.dir);
25390
25587
  let config2;
25391
25588
  try {
25392
25589
  config2 = loadConfig();
@@ -25456,7 +25653,7 @@ async function apply(argv) {
25456
25653
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
25457
25654
  const resolution = await resolveProjectNotFoundInteractively(
25458
25655
  parsed,
25459
- path75.basename(sourceDirectory)
25656
+ path76.basename(sourceDirectory)
25460
25657
  );
25461
25658
  if (resolution) {
25462
25659
  effectiveProjectId = void 0;
@@ -25476,7 +25673,7 @@ async function apply(argv) {
25476
25673
  while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
25477
25674
  const resolution = await resolveProjectNameExistsInteractively(
25478
25675
  parsed,
25479
- effectiveProjectName ?? path75.basename(sourceDirectory)
25676
+ effectiveProjectName ?? path76.basename(sourceDirectory)
25480
25677
  );
25481
25678
  if (!resolution) {
25482
25679
  break;
@@ -25547,11 +25744,11 @@ async function apply(argv) {
25547
25744
  }
25548
25745
 
25549
25746
  // src/cli/deploy/command.ts
25550
- import path79 from "node:path";
25747
+ import path81 from "node:path";
25551
25748
  init_invariant();
25552
25749
 
25553
25750
  // src/cli/project/create-handler.ts
25554
- import path76 from "node:path";
25751
+ import path77 from "node:path";
25555
25752
  import open2 from "open";
25556
25753
  init_invariant();
25557
25754
 
@@ -25855,7 +26052,7 @@ async function createLinkedProject(argv, dir, options = {}) {
25855
26052
  }
25856
26053
  }
25857
26054
  async function createProject2(argv) {
25858
- const dir = path76.resolve(process.cwd(), argv.dir ?? ".");
26055
+ const dir = path77.resolve(process.cwd(), argv.dir ?? ".");
25859
26056
  if (await isConfigV2(dir)) {
25860
26057
  await createLinkedProject(argv, dir);
25861
26058
  return;
@@ -25914,17 +26111,129 @@ async function createProject2(argv) {
25914
26111
  }
25915
26112
  }
25916
26113
 
26114
+ // src/cli/secrets/input.ts
26115
+ import { createInterface as createInterface6 } from "node:readline/promises";
26116
+ var CTRL_C = "";
26117
+ var CTRL_D = "";
26118
+ function readHidden(prompt, input5 = process.stdin, output4 = process.stdout) {
26119
+ return new Promise((resolve, reject) => {
26120
+ if (!input5.isTTY || !input5.setRawMode) {
26121
+ reject(new Error("A hidden prompt needs an interactive terminal."));
26122
+ return;
26123
+ }
26124
+ let value = "";
26125
+ output4.write(prompt);
26126
+ input5.setRawMode(true);
26127
+ input5.resume();
26128
+ const finish = (action) => {
26129
+ input5.removeListener("data", onData);
26130
+ input5.setRawMode?.(false);
26131
+ input5.pause();
26132
+ output4.write("\n");
26133
+ action();
26134
+ };
26135
+ const onData = (chunk) => {
26136
+ for (const char of chunk.toString("utf8")) {
26137
+ if (char === CTRL_C)
26138
+ return finish(() => reject(new Error("Cancelled.")));
26139
+ if (char === "\r" || char === "\n" || char === CTRL_D)
26140
+ return finish(() => resolve(value));
26141
+ if (char === "\x7F" || char === "\b") value = value.slice(0, -1);
26142
+ else value += char;
26143
+ }
26144
+ };
26145
+ input5.on("data", onData);
26146
+ });
26147
+ }
26148
+ async function readStdin(input5 = process.stdin) {
26149
+ const chunks = [];
26150
+ for await (const chunk of input5) {
26151
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
26152
+ }
26153
+ return Buffer.concat(chunks).toString("utf8").replace(/\r?\n$/, "");
26154
+ }
26155
+ async function confirm(question) {
26156
+ const rl = createInterface6({ input: process.stdin, output: process.stdout });
26157
+ try {
26158
+ const answer = (await rl.question(question)).trim().toLowerCase();
26159
+ return answer === "y" || answer === "yes";
26160
+ } finally {
26161
+ rl.close();
26162
+ }
26163
+ }
26164
+
26165
+ // src/cli/deploy/stripe-key.ts
26166
+ import path78 from "node:path";
26167
+ init_invariant();
26168
+ var StripeKeyError = class extends ApitogoError {
26169
+ constructor(message, developerHint) {
26170
+ super(message, { developerHint, title: "Stripe key needed" });
26171
+ this.name = "StripeKeyError";
26172
+ }
26173
+ };
26174
+ var STRIPE_KEY = /^(sk|rk)_(test|live)_[A-Za-z0-9]+$/;
26175
+ var wantsPlans = (only) => {
26176
+ if (only === void 0) return true;
26177
+ const names = (Array.isArray(only) ? only : only.split(",")).map((name) => name.trim()).filter(Boolean);
26178
+ return names.length === 0 || names.includes("plans");
26179
+ };
26180
+ async function ensureStripeKey(argv, deps) {
26181
+ if (!wantsPlans(argv.only)) return { status: "not-needed" };
26182
+ let resolved;
26183
+ try {
26184
+ resolved = loadConfigV2(path78.resolve(process.cwd(), argv.dir)).resolved;
26185
+ } catch {
26186
+ return { status: "not-needed" };
26187
+ }
26188
+ if (!resolved.plans?.enabled || resolved.plans.billing.provider !== "stripe")
26189
+ return { status: "not-needed" };
26190
+ const paid = await paidPlanSkus(resolved);
26191
+ if (paid.length === 0) return { status: "not-needed" };
26192
+ const projectId = argv.projectId?.trim() || resolved.project.id || deps.envProjectId?.trim();
26193
+ if (!projectId) return { status: "not-needed" };
26194
+ const name = stripeKeySecretName(resolved);
26195
+ const client = deps.client();
26196
+ const { secrets: secrets2 } = await client.listSecrets(projectId);
26197
+ if (secrets2.some((secret) => secret.name === name))
26198
+ return { status: "present", name };
26199
+ const reason = `The plan${paid.length > 1 ? "s" : ""} ${paid.join(", ")} ${paid.length > 1 ? "have" : "has"} a price, so payments go through Stripe, and this project has no Stripe key yet.`;
26200
+ if (!deps.interactive) {
26201
+ throw new StripeKeyError(
26202
+ reason,
26203
+ `Store it with \`apitogo secrets set ${name}\`, then deploy again. Or make the plans free.`
26204
+ );
26205
+ }
26206
+ deps.print(reason);
26207
+ deps.print(
26208
+ "Paste your Stripe secret key (starts with sk_ or rk_; use an sk_test_ key to try it out). It is stored as a project secret, not in your files."
26209
+ );
26210
+ const key = (await deps.askHidden("Stripe secret key (hidden): ")).trim();
26211
+ if (!key)
26212
+ throw new StripeKeyError(
26213
+ "No key was entered. Nothing was deployed.",
26214
+ `Run \`apitogo secrets set ${name}\` when you have it, then deploy again.`
26215
+ );
26216
+ if (!STRIPE_KEY.test(key))
26217
+ throw new StripeKeyError(
26218
+ "That is not a Stripe secret key. Nothing was deployed.",
26219
+ "A Stripe secret key starts with sk_test_, sk_live_, rk_test_ or rk_live_. Find it in Stripe under Developers > API keys."
26220
+ );
26221
+ await client.setSecret(projectId, name, key);
26222
+ deps.print(`Stripe key saved as the project secret ${name}.`);
26223
+ return { status: "saved", name };
26224
+ }
26225
+
25917
26226
  // src/cli/deploy/v2.ts
25918
26227
  import { rm as rm10 } from "node:fs/promises";
25919
26228
  init_invariant();
25920
26229
 
25921
26230
  // src/cli/portal/report.ts
25922
26231
  import { access as access9 } from "node:fs/promises";
25923
- import path77 from "node:path";
26232
+ import path79 from "node:path";
25924
26233
 
25925
26234
  // src/cli/deploy/gateway.ts
25926
26235
  import { createHash as createHash4 } from "node:crypto";
25927
- import { readFile as readFile25 } from "node:fs/promises";
26236
+ import { readFile as readFile27 } from "node:fs/promises";
25928
26237
  init_invariant();
25929
26238
  var GatewayDeployError = class extends ApitogoError {
25930
26239
  constructor(code, message, developerHint) {
@@ -25962,7 +26271,7 @@ async function readSpec(gateway) {
25962
26271
  "Add one to gateway.specs in the config."
25963
26272
  );
25964
26273
  try {
25965
- return await readFile25(file);
26274
+ return await readFile27(file);
25966
26275
  } catch {
25967
26276
  throw new GatewayDeployError(
25968
26277
  "GATEWAY_SPEC_UNREADABLE",
@@ -26722,12 +27031,13 @@ async function plansInputs(resolved, projectId) {
26722
27031
  const plans = resolved.plans;
26723
27032
  if (!plans) throw new Error("plans is not in the config.");
26724
27033
  const billing = plans.billing;
26725
- const wanted = toPlatformPlans(await readPlansFile(plans.file), {
27034
+ const filePlans = await readPlansFile(plans.file);
27035
+ const wanted = toPlatformPlans(filePlans, {
26726
27036
  billing: billing.provider === "stripe"
26727
27037
  });
26728
27038
  const manifest = await readManifest(resolved.state);
26729
27039
  const ledger = await readLedger(resolved.state);
26730
- const billingOn = billing.provider === "stripe";
27040
+ const billingOn = billing.provider === "stripe" && filePlans.some((plan2) => isPaidPlan(plan2));
26731
27041
  const billingStep = billingOn || stepOf(ledger, "plans", "billing") !== void 0;
26732
27042
  const identity = manifest?.services.identity;
26733
27043
  const ownIdentity = Boolean(
@@ -26739,11 +27049,12 @@ async function plansInputs(resolved, projectId) {
26739
27049
  ownIdentity,
26740
27050
  catalogHash: stableJson({ projectId, plans: wanted }),
26741
27051
  backendHash: stableJson({ projectId }),
27052
+ billingOn,
26742
27053
  billingStep,
26743
27054
  // Only the references to the project's secrets: a rotated value is applied with --force.
26744
27055
  billingHash: stableJson({
26745
27056
  projectId,
26746
- provider: billing.provider,
27057
+ provider: billingOn ? "stripe" : "none",
26747
27058
  apiKey: billingOn ? billing.apiKey : void 0,
26748
27059
  webhookSecret: billingOn ? billing.webhookSecret : void 0,
26749
27060
  successUrl: billingOn ? billing.successUrl : void 0,
@@ -26809,6 +27120,7 @@ async function deployPlans(resolved, projectId, deps, options = {}) {
26809
27120
  wanted,
26810
27121
  manifest,
26811
27122
  ownIdentity,
27123
+ billingOn,
26812
27124
  billingStep,
26813
27125
  billingHash,
26814
27126
  catalogHash,
@@ -26968,7 +27280,7 @@ async function deployPlans(resolved, projectId, deps, options = {}) {
26968
27280
  name: "billing",
26969
27281
  hash: () => billingHash,
26970
27282
  run: async () => {
26971
- const body = plans.billing.provider === "stripe" ? {
27283
+ const body = billingOn ? {
26972
27284
  provider: "stripe",
26973
27285
  apiKey: plans.billing.apiKey,
26974
27286
  webhookSecret: plans.billing.webhookSecret,
@@ -26985,8 +27297,7 @@ async function deployPlans(resolved, projectId, deps, options = {}) {
26985
27297
  code: "PLANS_BILLING",
26986
27298
  text: "the billing settings"
26987
27299
  });
26988
- if (plans.billing.provider === "stripe")
26989
- billingNote = billingNotes(queued);
27300
+ if (billingOn) billingNote = billingNotes(queued);
26990
27301
  return { ...done, mode: queued.mode };
26991
27302
  }
26992
27303
  };
@@ -27029,9 +27340,9 @@ async function deployPlans(resolved, projectId, deps, options = {}) {
27029
27340
  projectId,
27030
27341
  projectName: resolved.project.name,
27031
27342
  apiBaseUrl,
27032
- billing: plans.billing.provider,
27343
+ billing: billingOn ? "stripe" : "none",
27033
27344
  catalogRevision: catalog && !catalog.skipped ? previous + 1 : Math.max(previous, 1),
27034
- capabilities: plans.billing.provider === "stripe" ? { ...PLANS_CAPABILITIES, checkout: true } : PLANS_CAPABILITIES
27345
+ capabilities: billingOn ? { ...PLANS_CAPABILITIES, checkout: true } : PLANS_CAPABILITIES
27035
27346
  })
27036
27347
  );
27037
27348
  }
@@ -27190,7 +27501,7 @@ async function planPlansService(resolved, ledger, plansRemote2) {
27190
27501
  ),
27191
27502
  inputs.ownIdentity ? "sign-in: the project's identity will be applied to the plans backend" : "sign-in: no identity of the platform deployed, so nothing is applied to the plans backend",
27192
27503
  ...inputs.billingStep ? [
27193
- resolved.plans?.billing.provider === "stripe" ? "billing: the Stripe key and webhook secret of the project's secrets will be applied to the plans backend" : "billing: switched off, so the stored Stripe settings will be removed from the plans backend"
27504
+ inputs.billingOn ? "billing: a plan has a price, so the Stripe key and webhook secret of the project's secrets will be applied to the plans backend" : "billing: no plan has a price, so the stored Stripe settings will be removed from the plans backend"
27194
27505
  ] : [],
27195
27506
  ...steps.map(
27196
27507
  (name) => `step ${name}: ${stepState(stepOf(ledger, "plans", name), hashes[name])}`
@@ -27384,7 +27695,7 @@ async function planPortalService(resolved, ledger) {
27384
27695
  const hash2 = await portalBuildHash(resolved, prepared);
27385
27696
  const previous = stepOf(ledger, "portal", "build");
27386
27697
  const built = await exists5(
27387
- path77.join(resolved.state.buildDir, "index.html")
27698
+ path79.join(resolved.state.buildDir, "index.html")
27388
27699
  );
27389
27700
  if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
27390
27701
  return {
@@ -27743,7 +28054,7 @@ async function statusV2(dir, only, options = {}) {
27743
28054
  if (service === "portal") {
27744
28055
  const build2 = stepOf(ledger, "portal", "build");
27745
28056
  const built = await exists5(
27746
- path77.join(resolved.state.buildDir, "index.html")
28057
+ path79.join(resolved.state.buildDir, "index.html")
27747
28058
  );
27748
28059
  const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
27749
28060
  services.push({
@@ -27754,7 +28065,7 @@ async function statusV2(dir, only, options = {}) {
27754
28065
  ...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
27755
28066
  ...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
27756
28067
  ...state === "built" ? [
27757
- `output: ${path77.relative(resolved.configDir, resolved.state.buildDir)}`
28068
+ `output: ${path79.relative(resolved.configDir, resolved.state.buildDir)}`
27758
28069
  ] : [],
27759
28070
  ...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"],
27760
28071
  ...await hostingLine(resolved, manifest, options.hostingRemote)
@@ -27821,7 +28132,7 @@ function formatStatus(output4) {
27821
28132
 
27822
28133
  // src/cli/deploy/static-web-config.ts
27823
28134
  import { access as access10, writeFile as writeFile22 } from "node:fs/promises";
27824
- import path78 from "node:path";
28135
+ import path80 from "node:path";
27825
28136
  var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
27826
28137
  var STATIC_WEB_CONFIG_VERSION = 3;
27827
28138
  var CLIENT_ONLY_ROUTES = [
@@ -27839,7 +28150,7 @@ var DEFAULT_STATIC_WEB_CONFIG = {
27839
28150
  }
27840
28151
  };
27841
28152
  async function ensureStaticWebConfig(buildDir) {
27842
- const file = path78.join(buildDir, STATIC_WEB_CONFIG_FILE);
28153
+ const file = path80.join(buildDir, STATIC_WEB_CONFIG_FILE);
27843
28154
  try {
27844
28155
  await access10(file);
27845
28156
  return false;
@@ -28190,7 +28501,7 @@ function formatDeploy(output4) {
28190
28501
  // src/cli/deploy/command.ts
28191
28502
  async function ensureLinkedProject(argv, deps) {
28192
28503
  if (argv.projectId?.trim() || deps.envProjectId?.trim()) return true;
28193
- const dir = path79.resolve(process.cwd(), argv.dir);
28504
+ const dir = path81.resolve(process.cwd(), argv.dir);
28194
28505
  let linked;
28195
28506
  try {
28196
28507
  linked = Boolean(loadConfigV2(dir).resolved.project.id);
@@ -28220,6 +28531,18 @@ async function deployProject(argv) {
28220
28531
  createProject: (dir) => createLinkedProject({ dir }, dir, { continuingDeploy: true })
28221
28532
  });
28222
28533
  if (!ready) return;
28534
+ try {
28535
+ await ensureStripeKey(argv, {
28536
+ client: () => new PlatformApiClient(loadConfig()),
28537
+ envProjectId: process.env.APITOGO_DEFAULT_PROJECT_ID,
28538
+ interactive,
28539
+ askHidden: (prompt) => readHidden(prompt),
28540
+ print: printDiagnosticsToConsole
28541
+ });
28542
+ } catch (error) {
28543
+ await reportFailure(error, json);
28544
+ return;
28545
+ }
28223
28546
  const quiet = json || argv.verbose !== true;
28224
28547
  if (quiet) applyJsonOnlyDeployQuietMode();
28225
28548
  if (!json && quiet)
@@ -28230,7 +28553,7 @@ async function deployProject(argv) {
28230
28553
  const config2 = loadConfig();
28231
28554
  const output4 = await deployV2(
28232
28555
  {
28233
- dir: path79.resolve(process.cwd(), argv.dir),
28556
+ dir: path81.resolve(process.cwd(), argv.dir),
28234
28557
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
28235
28558
  projectId: argv.projectId,
28236
28559
  env: argv.env,
@@ -28250,23 +28573,26 @@ async function deployProject(argv) {
28250
28573
  for (const line2 of formatDeploy(output4)) printDiagnosticsToConsole(line2);
28251
28574
  printResultToConsole("Deploy finished.");
28252
28575
  } catch (error) {
28253
- const message = error instanceof Error ? error.message : String(error);
28254
- const hint = error instanceof ApitogoError ? error.developerHint : void 0;
28255
- if (json) {
28256
- console.log(
28257
- JSON.stringify({
28258
- status: "failed",
28259
- error: message,
28260
- hint: hint ?? null
28261
- })
28262
- );
28263
- process.exitCode = 1;
28264
- return;
28265
- }
28266
- await printCriticalFailureToConsoleAndExit(
28267
- hint ? `${message} ${hint}` : message
28576
+ await reportFailure(error, json);
28577
+ }
28578
+ }
28579
+ async function reportFailure(error, json) {
28580
+ const message = error instanceof Error ? error.message : String(error);
28581
+ const hint = error instanceof ApitogoError ? error.developerHint : void 0;
28582
+ if (json) {
28583
+ console.log(
28584
+ JSON.stringify({
28585
+ status: "failed",
28586
+ error: message,
28587
+ hint: hint ?? null
28588
+ })
28268
28589
  );
28590
+ process.exitCode = 1;
28591
+ return;
28269
28592
  }
28593
+ await printCriticalFailureToConsoleAndExit(
28594
+ hint ? `${message} ${hint}` : message
28595
+ );
28270
28596
  }
28271
28597
 
28272
28598
  // src/cli/cmds/deploy.ts
@@ -28321,7 +28647,7 @@ var deploy_default = {
28321
28647
  }),
28322
28648
  handler: async (argv) => {
28323
28649
  await captureEvent({ argv, event: "apitogo deploy" });
28324
- if (await isConfigV2(path80.resolve(process.cwd(), argv.dir))) {
28650
+ if (await isConfigV2(path82.resolve(process.cwd(), argv.dir))) {
28325
28651
  await deployProject(argv);
28326
28652
  return;
28327
28653
  }
@@ -28331,7 +28657,7 @@ var deploy_default = {
28331
28657
 
28332
28658
  // src/cli/export/handler.ts
28333
28659
  import { mkdir as mkdir18, writeFile as writeFile23 } from "node:fs/promises";
28334
- import path81 from "node:path";
28660
+ import path83 from "node:path";
28335
28661
  init_invariant();
28336
28662
  var ExportError = class extends ApitogoError {
28337
28663
  constructor(message, developerHint) {
@@ -28345,7 +28671,7 @@ async function exportOpenApi(args, deps) {
28345
28671
  `Cannot export "${args.target}".`,
28346
28672
  "Only `apitogo export openapi` exists."
28347
28673
  );
28348
- const dir = path81.resolve(process.cwd(), args.dir ?? ".");
28674
+ const dir = path83.resolve(process.cwd(), args.dir ?? ".");
28349
28675
  if (!await isConfigV2(dir))
28350
28676
  throw new ExportError(
28351
28677
  "This command needs a config version 2 project.",
@@ -28370,8 +28696,8 @@ async function exportOpenApi(args, deps) {
28370
28696
  throw error;
28371
28697
  }
28372
28698
  if (!args.out) return { text };
28373
- const file = path81.resolve(process.cwd(), args.out);
28374
- await mkdir18(path81.dirname(file), { recursive: true });
28699
+ const file = path83.resolve(process.cwd(), args.out);
28700
+ await mkdir18(path83.dirname(file), { recursive: true });
28375
28701
  await writeFile23(file, text);
28376
28702
  return { text, file };
28377
28703
  }
@@ -28464,8 +28790,8 @@ import open3 from "open";
28464
28790
  import colors14 from "picocolors";
28465
28791
 
28466
28792
  // src/cli/platform/device-auth.ts
28467
- function url(apiUrl, path95) {
28468
- return `${apiUrl}${path95.startsWith("/") ? path95 : `/${path95}`}`;
28793
+ function url(apiUrl, path97) {
28794
+ return `${apiUrl}${path97.startsWith("/") ? path97 : `/${path97}`}`;
28469
28795
  }
28470
28796
  async function requestDeviceCode(apiUrl) {
28471
28797
  const response = await fetchWithRetry(
@@ -28647,7 +28973,7 @@ var logout_default = {
28647
28973
  };
28648
28974
 
28649
28975
  // src/cli/portal/commands.ts
28650
- import path82 from "node:path";
28976
+ import path84 from "node:path";
28651
28977
  init_invariant();
28652
28978
  function parseOnly(value) {
28653
28979
  if (value === void 0) return void 0;
@@ -28664,7 +28990,7 @@ function parseOnly(value) {
28664
28990
  async function run2(command2, argv, compute, format) {
28665
28991
  const json = argv.json === true;
28666
28992
  try {
28667
- const dir = path82.resolve(process.cwd(), argv.dir ?? ".");
28993
+ const dir = path84.resolve(process.cwd(), argv.dir ?? ".");
28668
28994
  if (!await isConfigV2(dir)) {
28669
28995
  throw new ServiceEditError(
28670
28996
  `${command2} works with a config version 2 project, and there is none in ${dir}.`,
@@ -28880,14 +29206,14 @@ var remove_default = {
28880
29206
 
28881
29207
  // src/cli/dev/handler.ts
28882
29208
  init_joinUrl();
28883
- import path85 from "node:path";
29209
+ import path87 from "node:path";
28884
29210
 
28885
29211
  // src/vite/dev-server.ts
28886
29212
  init_logger();
28887
29213
  import fs11 from "node:fs/promises";
28888
29214
  import http from "node:http";
28889
29215
  import https from "node:https";
28890
- import path84 from "node:path";
29216
+ import path86 from "node:path";
28891
29217
  import { createHttpTerminator } from "http-terminator";
28892
29218
  import {
28893
29219
  createServer as createViteServer,
@@ -28920,7 +29246,7 @@ init_loader();
28920
29246
  // src/vite/pagefind-dev-index.ts
28921
29247
  init_invariant();
28922
29248
  init_joinUrl();
28923
- import path83 from "node:path";
29249
+ import path85 from "node:path";
28924
29250
  import { createIndex as createIndex2 } from "pagefind";
28925
29251
  import { isRunnableDevEnvironment } from "vite";
28926
29252
  async function* buildPagefindDevIndex(vite, config2) {
@@ -28973,7 +29299,7 @@ async function* buildPagefindDevIndex(vite, config2) {
28973
29299
  path: urlPath
28974
29300
  };
28975
29301
  }
28976
- const outputPath = path83.join(vite.config.publicDir, "pagefind");
29302
+ const outputPath = path85.join(vite.config.publicDir, "pagefind");
28977
29303
  await pagefindIndex.writeFiles({ outputPath });
28978
29304
  yield { type: "complete", success: true, indexed };
28979
29305
  }
@@ -28992,9 +29318,9 @@ var DevServer = class {
28992
29318
  this.protocol = "https";
28993
29319
  const { dir } = this.options;
28994
29320
  const [key, cert, ca] = await Promise.all([
28995
- fs11.readFile(path84.resolve(dir, config2.https.key)),
28996
- fs11.readFile(path84.resolve(dir, config2.https.cert)),
28997
- config2.https.ca ? fs11.readFile(path84.resolve(dir, config2.https.ca)) : void 0
29321
+ fs11.readFile(path86.resolve(dir, config2.https.key)),
29322
+ fs11.readFile(path86.resolve(dir, config2.https.cert)),
29323
+ config2.https.ca ? fs11.readFile(path86.resolve(dir, config2.https.ca)) : void 0
28998
29324
  ]);
28999
29325
  return https.createServer({ key, cert, ca });
29000
29326
  }
@@ -29011,7 +29337,7 @@ var DevServer = class {
29011
29337
  );
29012
29338
  const server = await this.createNodeServer(config2);
29013
29339
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
29014
- const publicRoot = path84.resolve(
29340
+ const publicRoot = path86.resolve(
29015
29341
  this.options.dir,
29016
29342
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
29017
29343
  );
@@ -29028,7 +29354,7 @@ var DevServer = class {
29028
29354
  // built-in transform middleware which would treat the path as a static asset.
29029
29355
  name: "apitogo:entry-client",
29030
29356
  configureServer(server2) {
29031
- const entryPath = path84.posix.join(
29357
+ const entryPath = path86.posix.join(
29032
29358
  server2.config.base,
29033
29359
  "/__z/entry.client.tsx"
29034
29360
  );
@@ -29193,7 +29519,7 @@ init_package_json();
29193
29519
  async function dev(argv) {
29194
29520
  const packageJson3 = getZudokuPackageJson();
29195
29521
  process.env.NODE_ENV = "development";
29196
- let dir = path85.resolve(process.cwd(), argv.dir);
29522
+ let dir = path87.resolve(process.cwd(), argv.dir);
29197
29523
  if (await isConfigV2(dir)) {
29198
29524
  const { root, prepared } = await preparePortalWorkspace(dir, "development");
29199
29525
  dir = root;
@@ -29292,61 +29618,8 @@ var run_default = {
29292
29618
  };
29293
29619
 
29294
29620
  // src/cli/secrets/handler.ts
29295
- import path86 from "node:path";
29621
+ import path88 from "node:path";
29296
29622
  init_invariant();
29297
-
29298
- // src/cli/secrets/input.ts
29299
- import { createInterface as createInterface6 } from "node:readline/promises";
29300
- var CTRL_C = "";
29301
- var CTRL_D = "";
29302
- function readHidden(prompt, input5 = process.stdin, output4 = process.stdout) {
29303
- return new Promise((resolve, reject) => {
29304
- if (!input5.isTTY || !input5.setRawMode) {
29305
- reject(new Error("A hidden prompt needs an interactive terminal."));
29306
- return;
29307
- }
29308
- let value = "";
29309
- output4.write(prompt);
29310
- input5.setRawMode(true);
29311
- input5.resume();
29312
- const finish = (action) => {
29313
- input5.removeListener("data", onData);
29314
- input5.setRawMode?.(false);
29315
- input5.pause();
29316
- output4.write("\n");
29317
- action();
29318
- };
29319
- const onData = (chunk) => {
29320
- for (const char of chunk.toString("utf8")) {
29321
- if (char === CTRL_C)
29322
- return finish(() => reject(new Error("Cancelled.")));
29323
- if (char === "\r" || char === "\n" || char === CTRL_D)
29324
- return finish(() => resolve(value));
29325
- if (char === "\x7F" || char === "\b") value = value.slice(0, -1);
29326
- else value += char;
29327
- }
29328
- };
29329
- input5.on("data", onData);
29330
- });
29331
- }
29332
- async function readStdin(input5 = process.stdin) {
29333
- const chunks = [];
29334
- for await (const chunk of input5) {
29335
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
29336
- }
29337
- return Buffer.concat(chunks).toString("utf8").replace(/\r?\n$/, "");
29338
- }
29339
- async function confirm(question) {
29340
- const rl = createInterface6({ input: process.stdin, output: process.stdout });
29341
- try {
29342
- const answer = (await rl.question(question)).trim().toLowerCase();
29343
- return answer === "y" || answer === "yes";
29344
- } finally {
29345
- rl.close();
29346
- }
29347
- }
29348
-
29349
- // src/cli/secrets/handler.ts
29350
29623
  var SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
29351
29624
  var SecretsError = class extends ApitogoError {
29352
29625
  constructor(message, developerHint) {
@@ -29357,7 +29630,7 @@ var SecretsError = class extends ApitogoError {
29357
29630
  async function resolveProjectId2(argv, deps) {
29358
29631
  const explicit = argv.projectId?.trim();
29359
29632
  if (explicit) return explicit;
29360
- const dir = path86.resolve(process.cwd(), argv.dir ?? ".");
29633
+ const dir = path88.resolve(process.cwd(), argv.dir ?? ".");
29361
29634
  if (await isConfigV2(dir)) {
29362
29635
  const id = loadConfigV2(dir).resolved.project.id;
29363
29636
  if (id) return id;
@@ -29517,12 +29790,12 @@ var secrets_default = {
29517
29790
  };
29518
29791
 
29519
29792
  // src/cli/cmds/status.ts
29520
- import path88 from "node:path";
29793
+ import path90 from "node:path";
29521
29794
 
29522
29795
  // src/cli/status/handler.ts
29523
- import path87 from "node:path";
29796
+ import path89 from "node:path";
29524
29797
  async function status(argv) {
29525
- const sourceDirectory = path87.resolve(process.cwd(), argv.dir);
29798
+ const sourceDirectory = path89.resolve(process.cwd(), argv.dir);
29526
29799
  let config2;
29527
29800
  try {
29528
29801
  config2 = loadConfig();
@@ -29620,7 +29893,7 @@ var status_default = {
29620
29893
  }),
29621
29894
  handler: async (argv) => {
29622
29895
  await captureEvent({ argv, event: "apitogo status" });
29623
- if (await isConfigV2(path88.resolve(process.cwd(), argv.dir))) {
29896
+ if (await isConfigV2(path90.resolve(process.cwd(), argv.dir))) {
29624
29897
  await statusOfProject(argv);
29625
29898
  return;
29626
29899
  }
@@ -29629,11 +29902,11 @@ var status_default = {
29629
29902
  };
29630
29903
 
29631
29904
  // src/cli/cmds/teardown.ts
29632
- import path91 from "node:path";
29905
+ import path93 from "node:path";
29633
29906
 
29634
29907
  // src/cli/teardown/command.ts
29635
29908
  init_invariant();
29636
- import path89 from "node:path";
29909
+ import path91 from "node:path";
29637
29910
  import { createInterface as createInterface7 } from "node:readline/promises";
29638
29911
 
29639
29912
  // src/cli/teardown/v2.ts
@@ -29994,7 +30267,7 @@ async function teardownProject(argv) {
29994
30267
  const progress = createConsoleProgressReporter();
29995
30268
  const result = await teardownV2(
29996
30269
  {
29997
- dir: path89.resolve(process.cwd(), argv.dir),
30270
+ dir: path91.resolve(process.cwd(), argv.dir),
29998
30271
  projectId: argv.projectId,
29999
30272
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
30000
30273
  yes: argv.yes === true,
@@ -30053,7 +30326,7 @@ async function teardownProject(argv) {
30053
30326
  }
30054
30327
 
30055
30328
  // src/cli/teardown/handler.ts
30056
- import path90 from "node:path";
30329
+ import path92 from "node:path";
30057
30330
  function resultLooksFailed3(payload) {
30058
30331
  try {
30059
30332
  const parsed = JSON.parse(payload);
@@ -30069,7 +30342,7 @@ async function teardown(argv) {
30069
30342
  );
30070
30343
  return;
30071
30344
  }
30072
- const sourceDirectory = path90.resolve(process.cwd(), argv.dir);
30345
+ const sourceDirectory = path92.resolve(process.cwd(), argv.dir);
30073
30346
  let config2;
30074
30347
  try {
30075
30348
  config2 = loadConfig();
@@ -30147,7 +30420,7 @@ var teardown_default = {
30147
30420
  }),
30148
30421
  handler: async (argv) => {
30149
30422
  await captureEvent({ argv, event: "apitogo teardown" });
30150
- if (await isConfigV2(path91.resolve(process.cwd(), argv.dir))) {
30423
+ if (await isConfigV2(path93.resolve(process.cwd(), argv.dir))) {
30151
30424
  await teardownProject(argv);
30152
30425
  return;
30153
30426
  }
@@ -30157,7 +30430,7 @@ var teardown_default = {
30157
30430
 
30158
30431
  // src/cli/teardown/reset-command.ts
30159
30432
  init_invariant();
30160
- import path92 from "node:path";
30433
+ import path94 from "node:path";
30161
30434
 
30162
30435
  // src/cli/teardown/reset.ts
30163
30436
  init_invariant();
@@ -30280,7 +30553,7 @@ async function resetProject(argv) {
30280
30553
  const config2 = loadConfig();
30281
30554
  const result = await resetProjectV2(
30282
30555
  {
30283
- dir: path92.resolve(process.cwd(), argv.dir),
30556
+ dir: path94.resolve(process.cwd(), argv.dir),
30284
30557
  projectId: argv.projectId,
30285
30558
  yes: argv.yes === true
30286
30559
  },
@@ -30344,7 +30617,7 @@ var test_reset_default = {
30344
30617
  };
30345
30618
 
30346
30619
  // src/cli/test-sign-in/handler.ts
30347
- import path93 from "node:path";
30620
+ import path95 from "node:path";
30348
30621
  init_invariant();
30349
30622
  var TestSignInError = class extends ApitogoError {
30350
30623
  constructor(message, developerHint) {
@@ -30355,7 +30628,7 @@ var TestSignInError = class extends ApitogoError {
30355
30628
  async function resolveProjectId3(argv) {
30356
30629
  const explicit = argv.projectId?.trim();
30357
30630
  if (explicit) return explicit;
30358
- const dir = path93.resolve(process.cwd(), argv.dir ?? ".");
30631
+ const dir = path95.resolve(process.cwd(), argv.dir ?? ".");
30359
30632
  if (await isConfigV2(dir)) {
30360
30633
  const id = loadConfigV2(dir).resolved.project.id;
30361
30634
  if (id) return id;
@@ -30495,36 +30768,36 @@ function createApitogoCli(argv) {
30495
30768
  // src/cli/local-handoff.ts
30496
30769
  import { spawn as spawn4 } from "node:child_process";
30497
30770
  import { existsSync as existsSync8, realpathSync as realpathSync2 } from "node:fs";
30498
- import path94 from "node:path";
30771
+ import path96 from "node:path";
30499
30772
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
30500
30773
  var PACKAGE_NAME = "@lukoweb/apitogo";
30501
30774
  var resolveProjectDir = (argv, cwd) => {
30502
30775
  for (let i = 0; i < argv.length; i++) {
30503
30776
  const arg = argv[i];
30504
30777
  const next = argv[i + 1];
30505
- if (arg === "--dir" && next) return path94.resolve(cwd, next);
30778
+ if (arg === "--dir" && next) return path96.resolve(cwd, next);
30506
30779
  if (arg?.startsWith("--dir=")) {
30507
- return path94.resolve(cwd, arg.slice("--dir=".length));
30780
+ return path96.resolve(cwd, arg.slice("--dir=".length));
30508
30781
  }
30509
30782
  }
30510
30783
  return cwd;
30511
30784
  };
30512
30785
  var findLocalCli = (projectDir, runningRoot) => {
30513
- let dir = path94.resolve(projectDir);
30786
+ let dir = path96.resolve(projectDir);
30514
30787
  let localPackageRoot;
30515
30788
  while (!localPackageRoot) {
30516
- const candidate = path94.join(dir, "node_modules", PACKAGE_NAME);
30517
- if (existsSync8(path94.join(candidate, "package.json"))) {
30789
+ const candidate = path96.join(dir, "node_modules", PACKAGE_NAME);
30790
+ if (existsSync8(path96.join(candidate, "package.json"))) {
30518
30791
  localPackageRoot = candidate;
30519
- } else if (dir === path94.dirname(dir)) {
30792
+ } else if (dir === path96.dirname(dir)) {
30520
30793
  return void 0;
30521
30794
  } else {
30522
- dir = path94.dirname(dir);
30795
+ dir = path96.dirname(dir);
30523
30796
  }
30524
30797
  }
30525
30798
  const localRoot = realpathSync2(localPackageRoot);
30526
30799
  if (localRoot === realpathSync2(runningRoot)) return void 0;
30527
- return path94.join(localRoot, "cli.js");
30800
+ return path96.join(localRoot, "cli.js");
30528
30801
  };
30529
30802
  var handoffToLocalCli = async (argv, runningRoot) => {
30530
30803
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -30536,7 +30809,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
30536
30809
  );
30537
30810
  if (!localCli) return void 0;
30538
30811
  printDiagnosticsToConsole(
30539
- `Using the project's local ${PACKAGE_NAME} (${path94.dirname(localCli)})`
30812
+ `Using the project's local ${PACKAGE_NAME} (${path96.dirname(localCli)})`
30540
30813
  );
30541
30814
  const child = spawn4(process.execPath, [localCli, ...argv], {
30542
30815
  stdio: "inherit",