@lukoweb/apitogo 0.2.60 → 0.2.61

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.
Files changed (43) hide show
  1. package/dist/cli/cli.js +893 -527
  2. package/docs/cli/commands.md +6 -6
  3. package/docs/cli/go-live.md +12 -12
  4. package/docs/cli/overview.md +26 -27
  5. package/docs/cli/setup.md +38 -46
  6. package/docs/cli/shape.md +32 -35
  7. package/docs/cli/walkthrough.md +19 -19
  8. package/docs/components/slot.mdx +116 -116
  9. package/docs/configuration/api-reference.md +1 -1
  10. package/docs/configuration/authentication.md +3 -3
  11. package/docs/configuration/footer.mdx +89 -89
  12. package/docs/configuration/gateway.md +8 -8
  13. package/docs/configuration/make-config.mdx +20 -10
  14. package/docs/configuration/overview.md +3 -3
  15. package/docs/configuration/plans.md +5 -5
  16. package/docs/configuration/portal.md +36 -0
  17. package/docs/customization/colors-theme.mdx +255 -255
  18. package/docs/customization/fonts.md +105 -105
  19. package/docs/customization/landing-page.md +1 -1
  20. package/docs/deploy/apitogo.md +3 -3
  21. package/docs/deployment.md +3 -3
  22. package/docs/guides/custom-pages.md +3 -3
  23. package/docs/guides/customer-account.md +1 -1
  24. package/docs/guides/stripe-setup.mdx +9 -9
  25. package/docs/markdown/frontmatter.md +2 -2
  26. package/docs/mcp/go-live.md +7 -7
  27. package/docs/mcp/overview.md +58 -58
  28. package/docs/mcp/setup.md +6 -6
  29. package/docs/mcp/shape.md +2 -2
  30. package/docs/mcp/tools.md +57 -57
  31. package/docs/quickstart.md +6 -4
  32. package/docs/writing.mdx +68 -68
  33. package/package.json +1 -1
  34. package/src/config/apitogo-config-core.ts +11 -1
  35. package/src/config/v2/resolve.ts +19 -0
  36. package/src/config/v2/rules.ts +88 -0
  37. package/src/config/v2/schema.ts +28 -0
  38. package/src/config/v2/wrapper.ts +76 -13
  39. package/src/vite/config.ts +278 -273
  40. package/src/vite/markdown-output-path.ts +48 -0
  41. package/src/vite/plugin-markdown-export.ts +210 -214
  42. package/src/vite/prerender/prerender.ts +4 -4
  43. package/src/vite/watch-ignore.ts +19 -0
package/dist/cli/cli.js CHANGED
@@ -457,8 +457,12 @@ function syncAuthentication(normalized) {
457
457
  if (!userPanelEnabled && !authentication) {
458
458
  return;
459
459
  }
460
+ if (!userPanelEnabled && authentication?.enabled === false) {
461
+ delete normalized.authentication;
462
+ return;
463
+ }
460
464
  const auth = authentication ?? {};
461
- auth.enabled = userPanelEnabled;
465
+ if (userPanelEnabled) auth.enabled = true;
462
466
  const keepsOpenId = auth.type === "openid";
463
467
  if (!keepsOpenId) auth.type = "dev-portal";
464
468
  if (userPanelEnabled && !keepsOpenId) {
@@ -837,7 +841,7 @@ import { stat as stat4 } from "node:fs/promises";
837
841
  var fileExists;
838
842
  var init_file_exists = __esm({
839
843
  "src/config/file-exists.ts"() {
840
- fileExists = (path99) => stat4(path99).then(() => true).catch(() => false);
844
+ fileExists = (path100) => stat4(path100).then(() => true).catch(() => false);
841
845
  }
842
846
  });
843
847
 
@@ -5270,7 +5274,7 @@ __export(llms_exports, {
5270
5274
  generateLlmsTxtFiles: () => generateLlmsTxtFiles
5271
5275
  });
5272
5276
  import { writeFile as writeFile12 } from "node:fs/promises";
5273
- import path46 from "node:path";
5277
+ import path47 from "node:path";
5274
5278
  import colors10 from "picocolors";
5275
5279
  async function generateLlmsTxtFiles({
5276
5280
  markdownFileInfos,
@@ -5305,7 +5309,7 @@ async function generateLlmsTxtFiles({
5305
5309
  }
5306
5310
  }
5307
5311
  const llmsTxt2 = llmsTxtParts.join("\n");
5308
- await writeFile12(path46.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5312
+ await writeFile12(path47.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5309
5313
  if (process.env.APITOGO_JSON_ONLY !== "1") {
5310
5314
  console.log(colors10.blue("\u2713 generated llms.txt"));
5311
5315
  }
@@ -5333,7 +5337,7 @@ ${info.content}
5333
5337
  }
5334
5338
  const llmsFull = llmsFullParts.join("\n");
5335
5339
  await writeFile12(
5336
- path46.join(baseOutputDir, "llms-full.txt"),
5340
+ path47.join(baseOutputDir, "llms-full.txt"),
5337
5341
  llmsFull,
5338
5342
  "utf-8"
5339
5343
  );
@@ -5836,7 +5840,7 @@ Run the command with --help to see its options.`;
5836
5840
  import yargs from "yargs/yargs";
5837
5841
 
5838
5842
  // src/cli/services/handler.ts
5839
- import path54 from "node:path";
5843
+ import path55 from "node:path";
5840
5844
 
5841
5845
  // src/config/v2/billing.ts
5842
5846
  import { readFile as readFile2 } from "node:fs/promises";
@@ -5915,6 +5919,21 @@ var nonEmpty = z.string().min(1, { error: "must not be empty" });
5915
5919
  var path3 = nonEmpty;
5916
5920
  var pathList = z.array(nonEmpty);
5917
5921
  var enabled = z.boolean().optional();
5922
+ var headerLabel = z.string().trim().min(1, { error: "must not be empty" }).max(40, { error: "must be 40 characters or fewer" });
5923
+ var HeaderLinkSchema = z.union(
5924
+ [
5925
+ z.strictObject({ label: headerLabel, page: path3 }),
5926
+ z.strictObject({
5927
+ label: headerLabel,
5928
+ to: z.string().regex(/^(\/(?!\/)|https:\/\/)/, {
5929
+ error: 'must start with "/" or "https://"'
5930
+ })
5931
+ })
5932
+ ],
5933
+ {
5934
+ error: 'must be { "label": "...", "page": "path/to/page.tsx" } or { "label": "...", "to": "/path or https://..." }'
5935
+ }
5936
+ );
5918
5937
  var PortalSchema = z.strictObject({
5919
5938
  enabled,
5920
5939
  dir: path3.optional(),
@@ -5931,6 +5950,8 @@ var PortalSchema = z.strictObject({
5931
5950
  landing: path3.optional(),
5932
5951
  /** The footer: a React component file shown under every page except the console's (API-129). */
5933
5952
  footer: path3.optional(),
5953
+ /** Links of the header's top menu, after the built-in ones (Documentation, API Reference; the plans add Pricing). */
5954
+ header: z.strictObject({ links: z.array(HeaderLinkSchema) }).optional(),
5934
5955
  /**
5935
5956
  * The logo in the portal header: one image, or one per color scheme (API-129). `dark` is optional; without it the
5936
5957
  * light image is used in both.
@@ -6094,8 +6115,8 @@ var ConfigV2Schema = z.strictObject({
6094
6115
  var REMOVED_KEYS = {
6095
6116
  "plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
6096
6117
  };
6097
- function pathToString(path99) {
6098
- return path99.reduce(
6118
+ function pathToString(path100) {
6119
+ return path100.reduce(
6099
6120
  (acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
6100
6121
  ""
6101
6122
  );
@@ -6115,9 +6136,9 @@ function step(schema2, segment) {
6115
6136
  }
6116
6137
  return void 0;
6117
6138
  }
6118
- function knownKeysAt(path99) {
6139
+ function knownKeysAt(path100) {
6119
6140
  let current = ConfigV2Schema;
6120
- for (const segment of path99) {
6141
+ for (const segment of path100) {
6121
6142
  if (!current) return [];
6122
6143
  current = step(current, segment);
6123
6144
  }
@@ -6153,7 +6174,7 @@ function unknownKeyHint(fullPath, key, known) {
6153
6174
  return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
6154
6175
  }
6155
6176
  function mapIssue(issue4) {
6156
- const path99 = pathToString(issue4.path);
6177
+ const path100 = pathToString(issue4.path);
6157
6178
  switch (issue4.code) {
6158
6179
  case "unrecognized_keys":
6159
6180
  return issue4.keys.map((key) => {
@@ -6169,20 +6190,20 @@ function mapIssue(issue4) {
6169
6190
  return [
6170
6191
  {
6171
6192
  code: "CONFIG_INVALID_VALUE",
6172
- path: path99,
6193
+ path: path100,
6173
6194
  message: `must be one of: ${issue4.values.map((v) => JSON.stringify(v)).join(", ")}.`
6174
6195
  }
6175
6196
  ];
6176
6197
  case "custom": {
6177
6198
  const code = typeof issue4.params?.code === "string" ? issue4.params.code : "CONFIG_INVALID_VALUE";
6178
- return [{ code, path: path99, message: issue4.message ?? "is invalid." }];
6199
+ return [{ code, path: path100, message: issue4.message ?? "is invalid." }];
6179
6200
  }
6180
6201
  case "invalid_type":
6181
6202
  return [
6182
- /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path99, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path99, message: issue4.message }
6203
+ /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path100, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path100, message: issue4.message }
6183
6204
  ];
6184
6205
  default:
6185
- return [{ code: "CONFIG_INVALID_VALUE", path: path99, message: issue4.message }];
6206
+ return [{ code: "CONFIG_INVALID_VALUE", path: path100, message: issue4.message }];
6186
6207
  }
6187
6208
  }
6188
6209
  function isRecord(value) {
@@ -6478,6 +6499,7 @@ function toGlobPath(value) {
6478
6499
  function fromConfig(configDir, value) {
6479
6500
  return value.startsWith("!") ? `!${toGlobPath(path7.resolve(configDir, value.slice(1)))}` : toGlobPath(path7.resolve(configDir, value));
6480
6501
  }
6502
+ var headerPageRoute = (file) => `/${path7.basename(file).replace(/\.(mdx?|tsx)$/i, "")}`;
6481
6503
  var isLogoUrl = (value) => value.toLowerCase().startsWith("https://");
6482
6504
  var logoSource = (configDir, value) => isLogoUrl(value) ? value : path7.resolve(configDir, value);
6483
6505
  function resolveConfigV2(config2, configPath) {
@@ -6513,6 +6535,13 @@ function resolveConfigV2(config2, configPath) {
6513
6535
  theme: portal.theme ? path7.resolve(configDir, portal.theme) : path7.join(dir, "theme.json"),
6514
6536
  ...portal.landing ? { landing: path7.resolve(configDir, portal.landing) } : {},
6515
6537
  ...portal.footer ? { footer: path7.resolve(configDir, portal.footer) } : {},
6538
+ headerLinks: (portal.header?.links ?? []).map(
6539
+ (link) => {
6540
+ if ("to" in link) return { label: link.label, to: link.to };
6541
+ const page = path7.resolve(configDir, link.page);
6542
+ return { label: link.label, to: headerPageRoute(page), page };
6543
+ }
6544
+ ),
6516
6545
  ...portal.logo ? {
6517
6546
  logo: typeof portal.logo === "string" ? {
6518
6547
  light: logoSource(configDir, portal.logo),
@@ -6644,17 +6673,30 @@ function loadConfigV2(pathOrDir) {
6644
6673
  }
6645
6674
 
6646
6675
  // src/config/v2/rules.ts
6647
- import { readdir as readdir2, readFile as readFile4, stat as stat2 } from "node:fs/promises";
6648
- import path10 from "node:path";
6649
- import { glob as glob2 } from "glob";
6676
+ import { readdir as readdir2, readFile as readFile5, stat as stat2 } from "node:fs/promises";
6677
+ import path11 from "node:path";
6678
+ import { glob as glob3 } from "glob";
6650
6679
  import { parse as parseYaml } from "yaml";
6651
6680
 
6652
- // src/config/v2/tsx-pages.ts
6653
- import { readFile as readFile3 } from "node:fs/promises";
6681
+ // src/config/v2/home-redirect.ts
6654
6682
  import path9 from "node:path";
6655
6683
  import { glob } from "glob";
6656
6684
  import globParent from "glob-parent";
6657
6685
 
6686
+ // src/lib/util/readFrontmatter.ts
6687
+ import { readFile as readFile3 } from "node:fs/promises";
6688
+ import matter from "gray-matter";
6689
+ import { parse, stringify } from "yaml";
6690
+ var yaml = {
6691
+ parse: (input5) => parse(input5) ?? {},
6692
+ stringify: (obj) => stringify(obj)
6693
+ };
6694
+ var readFrontmatter = async (filePath) => {
6695
+ const content = await readFile3(filePath, "utf-8");
6696
+ const normalizedContent = content.replace(/\r\n/g, "\n");
6697
+ return matter(normalizedContent, { engines: { yaml } });
6698
+ };
6699
+
6658
6700
  // src/config/v2/page-order.ts
6659
6701
  var FIRST_PAGE_NAMES = [
6660
6702
  "introduction",
@@ -6696,6 +6738,79 @@ function tsxPageOrder(source) {
6696
6738
  return match?.[1] ? Number(match[1]) : void 0;
6697
6739
  }
6698
6740
 
6741
+ // src/config/v2/home-redirect.ts
6742
+ var IGNORED = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
6743
+ async function docRoutes(patterns, options = {}) {
6744
+ const globs = patterns.map(toGlobPath);
6745
+ const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
6746
+ const included = globs.filter((pattern) => !pattern.startsWith("!"));
6747
+ const routes = await Promise.all(
6748
+ included.map(async (pattern) => {
6749
+ const files = await glob(pattern, {
6750
+ absolute: true,
6751
+ posix: true,
6752
+ ignore: [...IGNORED, ...excluded]
6753
+ });
6754
+ const parent = globParent(pattern);
6755
+ const published = options.includeDrafts ? files : (await Promise.all(
6756
+ files.map(async (file) => ({
6757
+ file,
6758
+ draft: (await readFrontmatter(file)).data.draft === true
6759
+ }))
6760
+ )).filter(({ draft }) => !draft).map(({ file }) => file);
6761
+ return published.map(
6762
+ (file) => `/${path9.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
6763
+ );
6764
+ })
6765
+ );
6766
+ return [...new Set(routes.flat())].sort();
6767
+ }
6768
+ function pickLandingRoute(routes, orders) {
6769
+ return firstPage(routes, orders);
6770
+ }
6771
+ async function docPageOrders(patterns) {
6772
+ const globs = patterns.map(toGlobPath);
6773
+ const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
6774
+ const orders = /* @__PURE__ */ new Map();
6775
+ for (const pattern of globs.filter((p) => !p.startsWith("!"))) {
6776
+ const parent = globParent(pattern);
6777
+ const files = await glob(pattern, {
6778
+ absolute: true,
6779
+ posix: true,
6780
+ ignore: [...IGNORED, ...excluded]
6781
+ });
6782
+ for (const raw of files) {
6783
+ const order = orderValue((await readFrontmatter(raw)).data.order);
6784
+ if (order === void 0) continue;
6785
+ const route = `/${path9.posix.relative(parent, raw.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`;
6786
+ if (!orders.has(route)) orders.set(route, order);
6787
+ }
6788
+ }
6789
+ return orders;
6790
+ }
6791
+ function homeRedirect(input5) {
6792
+ if (input5.hostsDocs) {
6793
+ if (input5.docRoutes.some((route) => route === "/" || route === "/index"))
6794
+ return void 0;
6795
+ const landing = pickLandingRoute(input5.docRoutes, input5.pageOrders);
6796
+ if (landing) return { from: "/", to: landing };
6797
+ return input5.apiReferencePath ? { from: "/", to: input5.apiReferencePath } : void 0;
6798
+ }
6799
+ if (input5.externalDocsUrl && !input5.userPanel) {
6800
+ return { from: "/", to: input5.externalDocsUrl };
6801
+ }
6802
+ if (input5.userPanel) {
6803
+ return { from: "/", to: input5.pricingPath ?? "/account" };
6804
+ }
6805
+ return void 0;
6806
+ }
6807
+
6808
+ // src/config/v2/tsx-pages.ts
6809
+ import { readFile as readFile4 } from "node:fs/promises";
6810
+ import path10 from "node:path";
6811
+ import { glob as glob2 } from "glob";
6812
+ import globParent2 from "glob-parent";
6813
+
6699
6814
  // src/config/v2/docs-navigation.ts
6700
6815
  var DOCS_NAVIGATION_LABEL = "Documentation";
6701
6816
  var emptyBranch = () => ({ pages: /* @__PURE__ */ new Map(), folders: /* @__PURE__ */ new Map() });
@@ -6766,7 +6881,7 @@ function docsNavigation(routes, customPages = [], orders = /* @__PURE__ */ new M
6766
6881
  }
6767
6882
 
6768
6883
  // src/config/v2/tsx-pages.ts
6769
- var IGNORED = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
6884
+ var IGNORED2 = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
6770
6885
  function isTsxHelper(relative) {
6771
6886
  return relative.split("/").some((part) => part.startsWith("_")) || /\.(test|spec|stories)\.tsx$/.test(relative) || relative.split("/").includes("node_modules");
6772
6887
  }
@@ -6777,7 +6892,7 @@ function tsxPagePatterns(contentGlobs) {
6777
6892
  return [
6778
6893
  ...new Set(
6779
6894
  included.map(
6780
- (pattern) => path9.posix.join(globParent(pattern), "**", "*.tsx")
6895
+ (pattern) => path10.posix.join(globParent2(pattern), "**", "*.tsx")
6781
6896
  )
6782
6897
  ),
6783
6898
  ...excluded
@@ -6787,21 +6902,21 @@ var TITLE = /export\s+const\s+title\s*(?::\s*string\s*)?=\s*(["'`])((?:\\.|(?!\1
6787
6902
  function tsxPageLabel(source, file) {
6788
6903
  const match = TITLE.exec(source);
6789
6904
  if (match?.[2]) return match[2].replace(/\\(.)/g, "$1");
6790
- const name = path9.basename(file, ".tsx");
6905
+ const name = path10.basename(file, ".tsx");
6791
6906
  return folderLabel(
6792
- name === "index" ? path9.basename(path9.dirname(file)) : name
6907
+ name === "index" ? path10.basename(path10.dirname(file)) : name
6793
6908
  );
6794
6909
  }
6795
6910
  async function findTsxPages(contentGlobs) {
6796
6911
  const patterns = tsxPagePatterns(contentGlobs);
6797
6912
  const ignore2 = [
6798
- ...IGNORED,
6913
+ ...IGNORED2,
6799
6914
  ...patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1))
6800
6915
  ];
6801
6916
  const found = /* @__PURE__ */ new Map();
6802
6917
  for (const pattern of patterns.filter((p) => !p.startsWith("!"))) {
6803
- const parent = globParent(pattern);
6804
- const files = await glob(pattern, {
6918
+ const parent = globParent2(pattern);
6919
+ const files = await glob2(pattern, {
6805
6920
  absolute: true,
6806
6921
  posix: true,
6807
6922
  nodir: true,
@@ -6809,11 +6924,11 @@ async function findTsxPages(contentGlobs) {
6809
6924
  });
6810
6925
  for (const raw of files.sort()) {
6811
6926
  const file = raw.replace(/^\/\/\?\//, "");
6812
- const relative = path9.posix.relative(parent, file);
6927
+ const relative = path10.posix.relative(parent, file);
6813
6928
  if (isTsxHelper(relative)) continue;
6814
6929
  const route = `/${relative.replace(/\.tsx$/, "")}`;
6815
6930
  if (found.has(route)) continue;
6816
- const source = await readFile3(file, "utf8").catch(() => "");
6931
+ const source = await readFile4(file, "utf8").catch(() => "");
6817
6932
  const order = tsxPageOrder(source);
6818
6933
  found.set(route, {
6819
6934
  route,
@@ -6978,40 +7093,41 @@ async function portalRules(config2, resolved, out) {
6978
7093
  out.error({
6979
7094
  code: "PORTAL_FILE_MISSING",
6980
7095
  path: "portal.footer",
6981
- message: `the file ${path10.relative(resolved.configDir, portal.footer)} does not exist.`,
7096
+ message: `the file ${path11.relative(resolved.configDir, portal.footer)} does not exist.`,
6982
7097
  hint: "Create it, or remove portal.footer."
6983
7098
  });
6984
- const publicDir = path10.join(portal.dir, "public");
7099
+ await headerLinksRule(portal, resolved, out);
7100
+ const publicDir = path11.join(portal.dir, "public");
6985
7101
  for (const file of new Set(
6986
7102
  [portal.logo?.light, portal.logo?.dark].filter(
6987
7103
  (value) => !!value && !isLogoUrl(value)
6988
7104
  )
6989
7105
  )) {
6990
- const inside = path10.relative(publicDir, file);
6991
- if (inside.startsWith("..") || path10.isAbsolute(inside))
7106
+ const inside = path11.relative(publicDir, file);
7107
+ if (inside.startsWith("..") || path11.isAbsolute(inside))
6992
7108
  out.error({
6993
7109
  code: "PORTAL_LOGO_OUTSIDE_PUBLIC",
6994
7110
  path: "portal.logo",
6995
- message: `the logo ${path10.relative(resolved.configDir, file)} is not in the portal's public folder, which is what the site serves.`,
6996
- hint: `Move it into ${path10.relative(resolved.configDir, publicDir)}/, or use an https:// address.`
7111
+ message: `the logo ${path11.relative(resolved.configDir, file)} is not in the portal's public folder, which is what the site serves.`,
7112
+ hint: `Move it into ${path11.relative(resolved.configDir, publicDir)}/, or use an https:// address.`
6997
7113
  });
6998
7114
  else if (!await isFile(file))
6999
7115
  out.error({
7000
7116
  code: "PORTAL_FILE_MISSING",
7001
7117
  path: "portal.logo",
7002
- message: `the file ${path10.relative(resolved.configDir, file)} does not exist.`,
7118
+ message: `the file ${path11.relative(resolved.configDir, file)} does not exist.`,
7003
7119
  hint: "Create it, or remove portal.logo."
7004
7120
  });
7005
7121
  }
7006
7122
  if (await isFile(portal.theme)) {
7007
7123
  try {
7008
- JSON.parse(await readFile4(portal.theme, "utf8"));
7124
+ JSON.parse(await readFile5(portal.theme, "utf8"));
7009
7125
  } catch (error) {
7010
7126
  out.error({
7011
7127
  code: "PORTAL_THEME_INVALID",
7012
7128
  path: "portal.theme",
7013
7129
  message: `the theme file is not valid JSON (${error instanceof Error ? error.message : String(error)}).`,
7014
- hint: path10.relative(resolved.configDir, portal.theme)
7130
+ hint: path11.relative(resolved.configDir, portal.theme)
7015
7131
  });
7016
7132
  }
7017
7133
  }
@@ -7020,11 +7136,11 @@ async function docsContentRule(config2, patterns, resolved, out) {
7020
7136
  const globs = patterns.map(toGlobPath);
7021
7137
  const include = globs.filter((pattern) => !pattern.startsWith("!"));
7022
7138
  const ignore2 = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
7023
- const matches = include.length > 0 ? await glob2(include, { nodir: true, ignore: ignore2, absolute: true }) : [];
7139
+ const matches = include.length > 0 ? await glob3(include, { nodir: true, ignore: ignore2, absolute: true }) : [];
7024
7140
  if (matches.length > 0) return;
7025
7141
  if ((await findTsxPages(patterns)).length > 0) return;
7026
7142
  const shown = config2.portal?.docs?.content ?? [
7027
- path10.relative(resolved.configDir, patterns[0] ?? "")
7143
+ path11.relative(resolved.configDir, patterns[0] ?? "")
7028
7144
  ];
7029
7145
  out.warn({
7030
7146
  code: "PORTAL_DOCS_EMPTY",
@@ -7033,6 +7149,77 @@ async function docsContentRule(config2, patterns, resolved, out) {
7033
7149
  hint: "Add .md, .mdx or .tsx pages, or point docs.content at your existing docs."
7034
7150
  });
7035
7151
  }
7152
+ var RESERVED_HEADER_ROUTES = [
7153
+ "/",
7154
+ "/index",
7155
+ "/api",
7156
+ "/pricing",
7157
+ "/account",
7158
+ "/login",
7159
+ "/signup"
7160
+ ];
7161
+ async function headerLinksRule(portal, resolved, out) {
7162
+ const pages = portal.headerLinks.map((link, index) => ({ link, index })).filter(({ link }) => link.page !== void 0);
7163
+ if (pages.length === 0) return;
7164
+ const hostsDocs = portal.docs.enabled && portal.docs.provider === "apitogo";
7165
+ const docs = hostsDocs ? [
7166
+ ...await docRoutes(portal.docs.content, { includeDrafts: true }),
7167
+ ...(await findTsxPages(portal.docs.content)).map((page) => page.route)
7168
+ ] : [];
7169
+ const seen = /* @__PURE__ */ new Set();
7170
+ for (const { link, index } of pages) {
7171
+ const file = link.page;
7172
+ const at = `portal.header.links[${index}].page`;
7173
+ const shown = path11.relative(resolved.configDir, file);
7174
+ if (!hostsDocs) {
7175
+ out.error({
7176
+ code: "PORTAL_HEADER_PAGE_NO_DOCS",
7177
+ path: at,
7178
+ message: `the header page ${shown} needs hosted docs, which this portal does not have.`,
7179
+ hint: 'Header pages are served with the docs pages: set portal.docs.provider to "apitogo", or link an address with "to" instead.'
7180
+ });
7181
+ continue;
7182
+ }
7183
+ if (!/\.(mdx?|tsx)$/i.test(file)) {
7184
+ out.error({
7185
+ code: "PORTAL_HEADER_PAGE_INVALID",
7186
+ path: at,
7187
+ message: `the header page ${shown} is neither a React page nor a Markdown page.`,
7188
+ hint: "Use a .tsx file (its default export is the page) or a .md / .mdx file."
7189
+ });
7190
+ continue;
7191
+ }
7192
+ if (!await isFile(file)) {
7193
+ out.error({
7194
+ code: "PORTAL_FILE_MISSING",
7195
+ path: at,
7196
+ message: `the file ${shown} does not exist.`,
7197
+ hint: "Create it (`apitogo config --page-slug <name> --page-title <title> --page-header`), or remove the link."
7198
+ });
7199
+ continue;
7200
+ }
7201
+ const route = link.to;
7202
+ if (RESERVED_HEADER_ROUTES.includes(route.toLowerCase())) {
7203
+ out.error({
7204
+ code: "PORTAL_HEADER_PAGE_ROUTE_TAKEN",
7205
+ path: at,
7206
+ message: `the header page ${shown} would be served at ${route}, which the portal uses itself.`,
7207
+ hint: "Rename the file: its name is the page's address."
7208
+ });
7209
+ continue;
7210
+ }
7211
+ if (docs.includes(route) || seen.has(route)) {
7212
+ out.error({
7213
+ code: "PORTAL_HEADER_PAGE_ROUTE_TAKEN",
7214
+ path: at,
7215
+ message: `the header page ${shown} would be served at ${route}, which another page already has.`,
7216
+ hint: `Rename the file: its name is the page's address. A docs page that should be in the header too can be linked with "to" instead.`
7217
+ });
7218
+ continue;
7219
+ }
7220
+ seen.add(route);
7221
+ }
7222
+ }
7036
7223
  function isIpLiteral(hostname) {
7037
7224
  return /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) || hostname.startsWith("[");
7038
7225
  }
@@ -7097,8 +7284,8 @@ async function gatewayRules(resolved, out) {
7097
7284
  }
7098
7285
  }
7099
7286
  if (origin.source) {
7100
- const inside = path10.relative(resolved.configDir, origin.source);
7101
- if (inside.startsWith("..") || path10.isAbsolute(inside)) {
7287
+ const inside = path11.relative(resolved.configDir, origin.source);
7288
+ if (inside.startsWith("..") || path11.isAbsolute(inside)) {
7102
7289
  out.error({
7103
7290
  code: "GATEWAY_ORIGIN_INVALID",
7104
7291
  path: "gateway.origin.source",
@@ -7183,7 +7370,7 @@ async function gatewayRules(resolved, out) {
7183
7370
  }
7184
7371
  async function specRule(spec, index, resolved, out) {
7185
7372
  const at = `gateway.specs[${index}]`;
7186
- const shown = path10.relative(resolved.configDir, spec);
7373
+ const shown = path11.relative(resolved.configDir, spec);
7187
7374
  const info = await stat2(spec).catch(() => void 0);
7188
7375
  if (!info?.isFile()) {
7189
7376
  out.error({
@@ -7203,7 +7390,7 @@ async function specRule(spec, index, resolved, out) {
7203
7390
  }
7204
7391
  let document;
7205
7392
  try {
7206
- const text2 = await readFile4(spec, "utf8");
7393
+ const text2 = await readFile5(spec, "utf8");
7207
7394
  document = spec.toLowerCase().endsWith(".json") ? JSON.parse(text2) : parseYaml(text2);
7208
7395
  } catch (error) {
7209
7396
  out.error({
@@ -7352,12 +7539,12 @@ async function plansRules(resolved, out, paid) {
7352
7539
  out.error({
7353
7540
  code: "PLANS_FILE_INVALID",
7354
7541
  path: "plans.file",
7355
- message: `the plan catalog was not found: ${path10.relative(resolved.configDir, plans.file)}.`,
7542
+ message: `the plan catalog was not found: ${path11.relative(resolved.configDir, plans.file)}.`,
7356
7543
  hint: "Create it with `apitogo add plans`."
7357
7544
  });
7358
7545
  } else {
7359
7546
  try {
7360
- JSON.parse(await readFile4(plans.file, "utf8"));
7547
+ JSON.parse(await readFile5(plans.file, "utf8"));
7361
7548
  } catch (error) {
7362
7549
  out.error({
7363
7550
  code: "PLANS_FILE_INVALID",
@@ -7473,8 +7660,8 @@ function portalProfile(resolved) {
7473
7660
  }
7474
7661
 
7475
7662
  // src/config/v2/manifest.ts
7476
- import { mkdir as mkdir3, readFile as readFile5, rename, rm, writeFile as writeFile4 } from "node:fs/promises";
7477
- import path11 from "node:path";
7663
+ import { mkdir as mkdir3, readFile as readFile6, rename, rm, writeFile as writeFile4 } from "node:fs/promises";
7664
+ import path12 from "node:path";
7478
7665
  import { z as z3 } from "zod";
7479
7666
  var SUPPORTED_MANIFEST_SCHEMA = 1;
7480
7667
  var Capabilities = z3.record(z3.string(), z3.boolean()).default({});
@@ -7525,7 +7712,7 @@ var ServiceManifestSchema = z3.looseObject({
7525
7712
  }).optional()
7526
7713
  }).default({})
7527
7714
  });
7528
- var issue = (code, path99, message, hint) => ({ code, path: path99, message, hint });
7715
+ var issue = (code, path100, message, hint) => ({ code, path: path100, message, hint });
7529
7716
  function parseManifest(raw, file = "services.json") {
7530
7717
  const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
7531
7718
  if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
@@ -7558,7 +7745,7 @@ async function readManifest(state) {
7558
7745
  const file = manifestPathFor(state);
7559
7746
  let text2;
7560
7747
  try {
7561
- text2 = await readFile5(file, "utf8");
7748
+ text2 = await readFile6(file, "utf8");
7562
7749
  } catch (error) {
7563
7750
  if (error.code === "ENOENT") return void 0;
7564
7751
  throw error;
@@ -7756,7 +7943,7 @@ function withPlansEntry(existing, info) {
7756
7943
  }
7757
7944
  async function writeManifest(state, manifest) {
7758
7945
  const file = manifestPathFor(state);
7759
- await mkdir3(path11.dirname(file), { recursive: true });
7946
+ await mkdir3(path12.dirname(file), { recursive: true });
7760
7947
  const temporary = `${file}.${process.pid}.tmp`;
7761
7948
  try {
7762
7949
  await writeFile4(
@@ -7787,8 +7974,8 @@ async function removeServicesFromManifest(state, names) {
7787
7974
  }
7788
7975
 
7789
7976
  // src/config/v2/gateway-reference.ts
7790
- import { readFile as readFile6 } from "node:fs/promises";
7791
- import { parse } from "yaml";
7977
+ import { readFile as readFile7 } from "node:fs/promises";
7978
+ import { parse as parse2 } from "yaml";
7792
7979
  var DEFAULT_KEY_HEADER = "X-API-Key";
7793
7980
  var KEY_HEADER_EXTENSION = "x-apitogo-key-header";
7794
7981
  var METHODS = [
@@ -7802,8 +7989,8 @@ var METHODS = [
7802
7989
  "trace"
7803
7990
  ];
7804
7991
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7805
- function actionKey(method, path99) {
7806
- const trimmed = path99.trim();
7992
+ function actionKey(method, path100) {
7993
+ const trimmed = path100.trim();
7807
7994
  const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
7808
7995
  const normalized = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") || "/" : withSlash;
7809
7996
  return `${method.trim().toLowerCase()}:${normalized}`;
@@ -7840,15 +8027,15 @@ var ReferenceSpecError = class extends Error {
7840
8027
  };
7841
8028
  function operationsOf(paths) {
7842
8029
  return Object.entries(paths).flatMap(
7843
- ([path99, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7844
- (method) => [path99, item, method]
8030
+ ([path100, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
8031
+ (method) => [path100, item, method]
7845
8032
  ) : []
7846
8033
  );
7847
8034
  }
7848
8035
  function buildReferenceSpec(specText, options) {
7849
8036
  let root;
7850
8037
  try {
7851
- root = parse(specText);
8038
+ root = parse2(specText);
7852
8039
  } catch {
7853
8040
  throw new ReferenceSpecError("The OpenAPI file could not be read.");
7854
8041
  }
@@ -7856,14 +8043,14 @@ function buildReferenceSpec(specText, options) {
7856
8043
  throw new ReferenceSpecError("The OpenAPI file could not be read.");
7857
8044
  const doc = structuredClone(root);
7858
8045
  const paths = isObject(doc.paths) ? doc.paths : {};
7859
- for (const [path99, item, method] of operationsOf(paths))
7860
- if (options.deniedOperations.has(actionKey(method, path99)))
8046
+ for (const [path100, item, method] of operationsOf(paths))
8047
+ if (options.deniedOperations.has(actionKey(method, path100)))
7861
8048
  delete item[method];
7862
- for (const [path99, item] of Object.entries(paths))
8049
+ for (const [path100, item] of Object.entries(paths))
7863
8050
  if (!isObject(item) || !METHODS.some((method) => method in item))
7864
- delete paths[path99];
7865
- for (const [path99, item, method] of operationsOf(paths))
7866
- if (options.publicOperations.has(actionKey(method, path99)))
8051
+ delete paths[path100];
8052
+ for (const [path100, item, method] of operationsOf(paths))
8053
+ if (options.publicOperations.has(actionKey(method, path100)))
7867
8054
  item[method].security = [];
7868
8055
  doc.servers = [{ url: options.gatewayBaseUrl.replace(/\/+$/, "") }];
7869
8056
  for (const item of Object.values(paths)) {
@@ -7903,11 +8090,11 @@ function buildReferenceSpec(specText, options) {
7903
8090
  if (Object.keys(components).length > 0) doc.components = components;
7904
8091
  else delete doc.components;
7905
8092
  doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
7906
- for (const [path99, item, method] of operationsOf(paths)) {
8093
+ for (const [path100, item, method] of operationsOf(paths)) {
7907
8094
  const operation = item[method];
7908
8095
  if (Array.isArray(operation.security) && operation.security.length > 0)
7909
8096
  delete operation.security;
7910
- if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path99)))
8097
+ if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path100)))
7911
8098
  operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
7912
8099
  else delete operation[KEY_HEADER_EXTENSION];
7913
8100
  }
@@ -7929,7 +8116,7 @@ async function renderReferenceSpec(gateway, gatewayBaseUrl, keyHeaderName) {
7929
8116
  throw new ReferenceSpecError("gateway.specs lists no OpenAPI file.");
7930
8117
  let text2;
7931
8118
  try {
7932
- text2 = await readFile6(file, "utf8");
8119
+ text2 = await readFile7(file, "utf8");
7933
8120
  } catch {
7934
8121
  throw new ReferenceSpecError(`Cannot read the OpenAPI file ${file}.`);
7935
8122
  }
@@ -8006,7 +8193,7 @@ import path14 from "node:path";
8006
8193
 
8007
8194
  // src/cli/platform/helpers/openapi-action-keys.ts
8008
8195
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
8009
- import path12 from "node:path";
8196
+ import path13 from "node:path";
8010
8197
  import YAML from "yaml";
8011
8198
  var HTTP_METHODS = [
8012
8199
  "get",
@@ -8075,7 +8262,7 @@ function extractGatewayActionKeysFromOpenApiDocument(document) {
8075
8262
  function extractGatewayActionKeysFromOpenApiFile(filePath) {
8076
8263
  try {
8077
8264
  const raw = readFileSync3(filePath, "utf8");
8078
- const ext = path12.extname(filePath).toLowerCase();
8265
+ const ext = path13.extname(filePath).toLowerCase();
8079
8266
  const parsed = ext === ".json" ? JSON.parse(raw) : YAML.parse(raw);
8080
8267
  return extractGatewayActionKeysFromOpenApiDocument(parsed);
8081
8268
  } catch {
@@ -8083,92 +8270,6 @@ function extractGatewayActionKeysFromOpenApiFile(filePath) {
8083
8270
  }
8084
8271
  }
8085
8272
 
8086
- // src/config/v2/home-redirect.ts
8087
- import path13 from "node:path";
8088
- import { glob as glob3 } from "glob";
8089
- import globParent2 from "glob-parent";
8090
-
8091
- // src/lib/util/readFrontmatter.ts
8092
- import { readFile as readFile7 } from "node:fs/promises";
8093
- import matter from "gray-matter";
8094
- import { parse as parse2, stringify } from "yaml";
8095
- var yaml = {
8096
- parse: (input5) => parse2(input5) ?? {},
8097
- stringify: (obj) => stringify(obj)
8098
- };
8099
- var readFrontmatter = async (filePath) => {
8100
- const content = await readFile7(filePath, "utf-8");
8101
- const normalizedContent = content.replace(/\r\n/g, "\n");
8102
- return matter(normalizedContent, { engines: { yaml } });
8103
- };
8104
-
8105
- // src/config/v2/home-redirect.ts
8106
- var IGNORED2 = ["**/node_modules/**", "**/dist/**", "**/.git/**"];
8107
- async function docRoutes(patterns, options = {}) {
8108
- const globs = patterns.map(toGlobPath);
8109
- const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
8110
- const included = globs.filter((pattern) => !pattern.startsWith("!"));
8111
- const routes = await Promise.all(
8112
- included.map(async (pattern) => {
8113
- const files = await glob3(pattern, {
8114
- absolute: true,
8115
- posix: true,
8116
- ignore: [...IGNORED2, ...excluded]
8117
- });
8118
- const parent = globParent2(pattern);
8119
- const published = options.includeDrafts ? files : (await Promise.all(
8120
- files.map(async (file) => ({
8121
- file,
8122
- draft: (await readFrontmatter(file)).data.draft === true
8123
- }))
8124
- )).filter(({ draft }) => !draft).map(({ file }) => file);
8125
- return published.map(
8126
- (file) => `/${path13.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
8127
- );
8128
- })
8129
- );
8130
- return [...new Set(routes.flat())].sort();
8131
- }
8132
- function pickLandingRoute(routes, orders) {
8133
- return firstPage(routes, orders);
8134
- }
8135
- async function docPageOrders(patterns) {
8136
- const globs = patterns.map(toGlobPath);
8137
- const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
8138
- const orders = /* @__PURE__ */ new Map();
8139
- for (const pattern of globs.filter((p) => !p.startsWith("!"))) {
8140
- const parent = globParent2(pattern);
8141
- const files = await glob3(pattern, {
8142
- absolute: true,
8143
- posix: true,
8144
- ignore: [...IGNORED2, ...excluded]
8145
- });
8146
- for (const raw of files) {
8147
- const order = orderValue((await readFrontmatter(raw)).data.order);
8148
- if (order === void 0) continue;
8149
- const route = `/${path13.posix.relative(parent, raw.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`;
8150
- if (!orders.has(route)) orders.set(route, order);
8151
- }
8152
- }
8153
- return orders;
8154
- }
8155
- function homeRedirect(input5) {
8156
- if (input5.hostsDocs) {
8157
- if (input5.docRoutes.some((route) => route === "/" || route === "/index"))
8158
- return void 0;
8159
- const landing = pickLandingRoute(input5.docRoutes, input5.pageOrders);
8160
- if (landing) return { from: "/", to: landing };
8161
- return input5.apiReferencePath ? { from: "/", to: input5.apiReferencePath } : void 0;
8162
- }
8163
- if (input5.externalDocsUrl && !input5.userPanel) {
8164
- return { from: "/", to: input5.externalDocsUrl };
8165
- }
8166
- if (input5.userPanel) {
8167
- return { from: "/", to: input5.pricingPath ?? "/account" };
8168
- }
8169
- return void 0;
8170
- }
8171
-
8172
8273
  // src/config/v2/protected-routes.ts
8173
8274
  function protectedRoutesFor(input5) {
8174
8275
  const { requireAuth } = input5;
@@ -8346,11 +8447,38 @@ function planPortal(profile, manifest) {
8346
8447
  var SITE_CONFIG_FILE = "apitogo.config.json";
8347
8448
  var REFERENCE_SPEC_FILE = "openapi/gateway.json";
8348
8449
  var API_REFERENCE_PATH = "api";
8450
+ var MARKDOWN_PAGE = /\.mdx?$/i;
8451
+ var HEADER_PAGE_ELEMENT_PREFIX = "apitogo:header-page:";
8452
+ function headerTsxPages(resolved, plan2) {
8453
+ if (!plan2.docs) return [];
8454
+ return (resolved.portal?.headerLinks ?? []).flatMap(
8455
+ (link) => link.page && /\.tsx$/i.test(link.page) ? [{ route: link.to, label: link.label, file: link.page }] : []
8456
+ ).map((page, index) => ({
8457
+ ...page,
8458
+ id: `${HEADER_PAGE_ELEMENT_PREFIX}${index}`
8459
+ }));
8460
+ }
8349
8461
  function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8350
8462
  const portal = resolved.portal;
8351
8463
  const redirects = extras.redirects ?? [];
8464
+ const headerLinks = portal?.headerLinks ?? [];
8465
+ const headerPages = plan2.docs ? headerLinks.flatMap(
8466
+ (link) => link.page && MARKDOWN_PAGE.test(link.page) ? [link.page] : []
8467
+ ) : [];
8468
+ const shownHeaderLinks = headerLinks.filter((link) => !link.page || plan2.docs).map((link) => ({
8469
+ label: link.label,
8470
+ to: link.to,
8471
+ ...link.to.startsWith("https://") ? { target: "_blank" } : {}
8472
+ }));
8352
8473
  const navigation = [
8353
8474
  ...extras.docsNavigation ?? [],
8475
+ ...headerTsxPages(resolved, plan2).map((page) => ({
8476
+ type: "custom-page",
8477
+ path: page.route,
8478
+ label: page.label,
8479
+ element: page.id,
8480
+ display: "hide"
8481
+ })),
8354
8482
  ...plan2.apiReference ? [
8355
8483
  {
8356
8484
  type: "link",
@@ -8389,9 +8517,10 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8389
8517
  },
8390
8518
  ...plan2.docs ? {
8391
8519
  docs: {
8392
- files: portal?.docs.content.map(
8393
- (glob9) => contentGlob(resolved.configDir, glob9)
8394
- )
8520
+ files: [
8521
+ ...portal?.docs.content ?? [],
8522
+ ...headerPages.map(toGlobPath)
8523
+ ].map((glob9) => contentGlob(resolved.configDir, glob9))
8395
8524
  }
8396
8525
  } : {},
8397
8526
  ...plan2.search ? { search: { type: plan2.search } } : {},
@@ -8406,12 +8535,13 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8406
8535
  } : {},
8407
8536
  ...navigation.length > 0 ? { navigation } : {},
8408
8537
  // The sidebar link above is only seen inside the docs; the header is what every page shows. A header navigation
8409
- // replaces the docs tab, so the docs get a header link of their own.
8410
- ...plan2.apiReference ? {
8538
+ // replaces the docs tab, so the docs get a header link of their own. The tenant's own links (portal.header) follow.
8539
+ ...plan2.apiReference || shownHeaderLinks.length > 0 ? {
8411
8540
  header: {
8412
8541
  navigation: [
8413
8542
  ...plan2.docs && extras.docsPath ? [{ label: "Documentation", to: extras.docsPath }] : [],
8414
- { label: "API Reference", to: `/${API_REFERENCE_PATH}` }
8543
+ ...plan2.apiReference ? [{ label: "API Reference", to: `/${API_REFERENCE_PATH}` }] : [],
8544
+ ...shownHeaderLinks
8415
8545
  ]
8416
8546
  }
8417
8547
  } : {},
@@ -8617,10 +8747,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8617
8747
  const tsxPages = plan2.docs ? (await findTsxPages(profile.docs.content)).filter(
8618
8748
  (page) => !markdownRoutes.includes(page.route)
8619
8749
  ) : [];
8750
+ const importPathOf = (file) => `./${CONTENT_LINK}/${path14.relative(resolved.configDir, file).split(path14.sep).join("/")}`;
8620
8751
  const wrapperPages = tsxPages.map((page, index) => ({
8621
8752
  id: `${TSX_PAGE_ELEMENT_PREFIX}${index}`,
8622
- importPath: `./${CONTENT_LINK}/${path14.relative(resolved.configDir, page.file).split(path14.sep).join("/")}`
8753
+ importPath: importPathOf(page.file)
8623
8754
  }));
8755
+ const headerWrapperPages = headerTsxPages(resolved, plan2).map(
8756
+ (page) => ({ id: page.id, importPath: importPathOf(page.file) })
8757
+ );
8624
8758
  const hostedDocRoutes = [
8625
8759
  .../* @__PURE__ */ new Set([...markdownRoutes, ...tsxPages.map((page) => page.route)])
8626
8760
  ].sort();
@@ -8691,14 +8825,20 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8691
8825
  protectedRoutes: protectedRoutesFor({
8692
8826
  requireAuth: resolved.portal?.requireAuth,
8693
8827
  hasAuth: !!plan2.auth,
8694
- docRoutes: hostedDocRoutes,
8828
+ // A header page is a docs page too, so a portal behind login keeps it behind login.
8829
+ docRoutes: plan2.docs ? [
8830
+ ...hostedDocRoutes,
8831
+ ...(resolved.portal?.headerLinks ?? []).flatMap(
8832
+ (link) => link.page ? [link.to] : []
8833
+ )
8834
+ ] : hostedDocRoutes,
8695
8835
  apiReferencePath: plan2.apiReference ? `/${API_REFERENCE_PATH}` : void 0
8696
8836
  })
8697
8837
  }
8698
8838
  );
8699
8839
  const wrapper = renderWrapper(
8700
8840
  plan2,
8701
- wrapperPages,
8841
+ [...wrapperPages, ...headerWrapperPages],
8702
8842
  resolved.portal?.footer ? wrapperImport(contentFile(resolved.configDir, resolved.portal.footer)) : void 0
8703
8843
  );
8704
8844
  const wrapperPath = path14.join(resolved.state.generatedDir, WRAPPER_FILE);
@@ -10105,7 +10245,7 @@ init_invariant();
10105
10245
  // src/cli/check/v2.ts
10106
10246
  init_apitogo_config_core();
10107
10247
  import { readFile as readFile18 } from "node:fs/promises";
10108
- import path51 from "node:path";
10248
+ import path52 from "node:path";
10109
10249
 
10110
10250
  // src/cli/platform/client/platform-api.ts
10111
10251
  import { basename } from "node:path";
@@ -10218,8 +10358,8 @@ var PlatformApiClient = class {
10218
10358
  constructor(config2) {
10219
10359
  this.config = config2;
10220
10360
  }
10221
- url(path99) {
10222
- return `${this.config.apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
10361
+ url(path100) {
10362
+ return `${this.config.apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
10223
10363
  }
10224
10364
  headers(extra) {
10225
10365
  return {
@@ -10229,9 +10369,9 @@ var PlatformApiClient = class {
10229
10369
  ...extra
10230
10370
  };
10231
10371
  }
10232
- async request(method, path99, init3) {
10372
+ async request(method, path100, init3) {
10233
10373
  const response = await fetchWithRetry(
10234
- this.url(path99),
10374
+ this.url(path100),
10235
10375
  {
10236
10376
  method,
10237
10377
  ...init3,
@@ -10699,15 +10839,15 @@ var PlatformApiClient = class {
10699
10839
  }
10700
10840
  );
10701
10841
  }
10702
- deleteDevPortalPage(projectId, path99) {
10703
- const query = new URLSearchParams({ path: path99 });
10842
+ deleteDevPortalPage(projectId, path100) {
10843
+ const query = new URLSearchParams({ path: path100 });
10704
10844
  return this.request(
10705
10845
  "DELETE",
10706
10846
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
10707
10847
  );
10708
10848
  }
10709
- renameDevPortalPage(projectId, path99, newName) {
10710
- const query = new URLSearchParams({ path: path99 });
10849
+ renameDevPortalPage(projectId, path100, newName) {
10850
+ const query = new URLSearchParams({ path: path100 });
10711
10851
  return this.request(
10712
10852
  "POST",
10713
10853
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -10823,8 +10963,8 @@ var PlatformApiClient = class {
10823
10963
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
10824
10964
  */
10825
10965
  validateGatewayPlans(plans, options = {}) {
10826
- const path99 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10827
- return this.request("POST", path99, {
10966
+ const path100 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10967
+ return this.request("POST", path100, {
10828
10968
  headers: { "Content-Type": "application/json" },
10829
10969
  body: JSON.stringify({ plans, planTier: options.planTier })
10830
10970
  });
@@ -11052,11 +11192,11 @@ import {
11052
11192
  rm as rm8,
11053
11193
  writeFile as writeFile15
11054
11194
  } from "node:fs/promises";
11055
- import path50 from "node:path";
11195
+ import path51 from "node:path";
11056
11196
 
11057
11197
  // src/vite/build.ts
11058
11198
  import { mkdir as mkdir11, readFile as readFile13, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
11059
- import path48 from "node:path";
11199
+ import path49 from "node:path";
11060
11200
  import { build as esbuild } from "esbuild";
11061
11201
  import { build as viteBuild, mergeConfig as mergeConfig3 } from "vite";
11062
11202
 
@@ -11192,7 +11332,7 @@ init_invariant();
11192
11332
  init_joinUrl();
11193
11333
 
11194
11334
  // src/vite/config.ts
11195
- import path43 from "node:path";
11335
+ import path44 from "node:path";
11196
11336
  import dotenv from "dotenv";
11197
11337
  import colors8 from "picocolors";
11198
11338
  import {
@@ -11203,7 +11343,7 @@ import {
11203
11343
  // package.json
11204
11344
  var package_default = {
11205
11345
  name: "@lukoweb/apitogo",
11206
- version: "0.2.60",
11346
+ version: "0.2.61",
11207
11347
  type: "module",
11208
11348
  sideEffects: [
11209
11349
  "**/*.css",
@@ -11988,14 +12128,14 @@ var flattenAllOf = (schema2) => {
11988
12128
  };
11989
12129
 
11990
12130
  // src/lib/util/traverse.ts
11991
- var traverse = (specification, transform, path99 = []) => {
11992
- const transformed = transform(specification, path99);
12131
+ var traverse = (specification, transform, path100 = []) => {
12132
+ const transformed = transform(specification, path100);
11993
12133
  if (typeof transformed !== "object" || transformed === null) {
11994
12134
  return transformed;
11995
12135
  }
11996
12136
  const result = Array.isArray(transformed) ? [] : {};
11997
12137
  for (const [key, value] of Object.entries(transformed)) {
11998
- const currentPath = [...path99, key];
12138
+ const currentPath = [...path100, key];
11999
12139
  if (Array.isArray(value)) {
12000
12140
  result[key] = value.map(
12001
12141
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -12023,9 +12163,9 @@ var resolveLocalRef = (schema2, ref) => {
12023
12163
  if (schemaCache?.has(ref)) {
12024
12164
  return schemaCache.get(ref);
12025
12165
  }
12026
- const path99 = ref.split("/").slice(1);
12166
+ const path100 = ref.split("/").slice(1);
12027
12167
  let current = schema2;
12028
- for (const segment of path99) {
12168
+ for (const segment of path100) {
12029
12169
  if (!current || typeof current !== "object") {
12030
12170
  current = null;
12031
12171
  }
@@ -12044,7 +12184,7 @@ var dereference = async (schema2, resolvers = []) => {
12044
12184
  }
12045
12185
  const cloned = structuredClone(schema2);
12046
12186
  const visited = /* @__PURE__ */ new Set();
12047
- const resolve = async (current, path99) => {
12187
+ const resolve = async (current, path100) => {
12048
12188
  if (isIndexableObject(current)) {
12049
12189
  if (visited.has(current)) {
12050
12190
  return CIRCULAR_REF;
@@ -12052,7 +12192,7 @@ var dereference = async (schema2, resolvers = []) => {
12052
12192
  visited.add(current);
12053
12193
  if (Array.isArray(current)) {
12054
12194
  for (let index = 0; index < current.length; index++) {
12055
- current[index] = await resolve(current[index], `${path99}/${index}`);
12195
+ current[index] = await resolve(current[index], `${path100}/${index}`);
12056
12196
  }
12057
12197
  } else {
12058
12198
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -12063,13 +12203,13 @@ var dereference = async (schema2, resolvers = []) => {
12063
12203
  for (const resolver of resolvers) {
12064
12204
  const resolved = await resolver($ref);
12065
12205
  if (resolved) {
12066
- result2 = await resolve(resolved, path99);
12206
+ result2 = await resolve(resolved, path100);
12067
12207
  break;
12068
12208
  }
12069
12209
  }
12070
12210
  if (result2 === void 0) {
12071
12211
  const resolved = await resolveLocalRef(cloned, $ref);
12072
- result2 = await resolve(resolved, path99);
12212
+ result2 = await resolve(resolved, path100);
12073
12213
  }
12074
12214
  if (hasSiblings) {
12075
12215
  if (result2 === CIRCULAR_REF) {
@@ -12082,7 +12222,7 @@ var dereference = async (schema2, resolvers = []) => {
12082
12222
  return result2;
12083
12223
  }
12084
12224
  for (const key in current) {
12085
- current[key] = await resolve(current[key], `${path99}/${key}`);
12225
+ current[key] = await resolve(current[key], `${path100}/${key}`);
12086
12226
  }
12087
12227
  }
12088
12228
  visited.delete(current);
@@ -12121,9 +12261,9 @@ var upgradeSchema = (schema2) => {
12121
12261
  }
12122
12262
  return sub;
12123
12263
  });
12124
- schema2 = traverse(schema2, (sub, path99) => {
12264
+ schema2 = traverse(schema2, (sub, path100) => {
12125
12265
  if (sub.example !== void 0) {
12126
- if (isSchemaPath(path99 ?? [])) {
12266
+ if (isSchemaPath(path100 ?? [])) {
12127
12267
  sub.examples = [sub.example];
12128
12268
  } else {
12129
12269
  sub.examples = {
@@ -12136,11 +12276,11 @@ var upgradeSchema = (schema2) => {
12136
12276
  }
12137
12277
  return sub;
12138
12278
  });
12139
- schema2 = traverse(schema2, (schema3, path99) => {
12279
+ schema2 = traverse(schema2, (schema3, path100) => {
12140
12280
  if (schema3.type === "object" && schema3.properties !== void 0) {
12141
- const parentPath = path99?.slice(0, -1);
12281
+ const parentPath = path100?.slice(0, -1);
12142
12282
  const isMultipart = parentPath?.some((segment, index) => {
12143
- return segment === "content" && path99?.[index + 1] === "multipart/form-data";
12283
+ return segment === "content" && path100?.[index + 1] === "multipart/form-data";
12144
12284
  });
12145
12285
  if (isMultipart) {
12146
12286
  const entries = Object.entries(schema3.properties);
@@ -12154,8 +12294,8 @@ var upgradeSchema = (schema2) => {
12154
12294
  }
12155
12295
  return schema3;
12156
12296
  });
12157
- schema2 = traverse(schema2, (schema3, path99) => {
12158
- if (path99?.includes("content") && path99.includes("application/octet-stream")) {
12297
+ schema2 = traverse(schema2, (schema3, path100) => {
12298
+ if (path100?.includes("content") && path100.includes("application/octet-stream")) {
12159
12299
  return {};
12160
12300
  }
12161
12301
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -12181,11 +12321,11 @@ var upgradeSchema = (schema2) => {
12181
12321
  }
12182
12322
  return sub;
12183
12323
  });
12184
- schema2 = traverse(schema2, (schema3, path99) => {
12324
+ schema2 = traverse(schema2, (schema3, path100) => {
12185
12325
  if (schema3.type === "string" && schema3.format === "byte") {
12186
- const parentPath = path99?.slice(0, -1);
12326
+ const parentPath = path100?.slice(0, -1);
12187
12327
  const contentMediaType = parentPath?.find(
12188
- (_, index) => path99?.[index - 1] === "content"
12328
+ (_, index) => path100?.[index - 1] === "content"
12189
12329
  );
12190
12330
  return {
12191
12331
  type: "string",
@@ -12197,7 +12337,7 @@ var upgradeSchema = (schema2) => {
12197
12337
  });
12198
12338
  return schema2;
12199
12339
  };
12200
- function isSchemaPath(path99) {
12340
+ function isSchemaPath(path100) {
12201
12341
  const schemaLocations = [
12202
12342
  ["components", "schemas"],
12203
12343
  "properties",
@@ -12210,10 +12350,10 @@ function isSchemaPath(path99) {
12210
12350
  ];
12211
12351
  return schemaLocations.some((location) => {
12212
12352
  if (Array.isArray(location)) {
12213
- return location.every((segment, index) => path99[index] === segment);
12353
+ return location.every((segment, index) => path100[index] === segment);
12214
12354
  }
12215
- return path99.includes(location);
12216
- }) || path99.includes("schema") || path99.some((segment) => segment.endsWith("Schema"));
12355
+ return path100.includes(location);
12356
+ }) || path100.includes("schema") || path100.some((segment) => segment.endsWith("Schema"));
12217
12357
  }
12218
12358
 
12219
12359
  // src/lib/oas/parser/index.ts
@@ -12295,13 +12435,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
12295
12435
  "anyOf",
12296
12436
  "oneOf"
12297
12437
  ]);
12298
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path99 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
12438
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path100 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
12299
12439
  if (obj === null || typeof obj !== "object") return obj;
12300
12440
  const refPath = obj.__$ref;
12301
12441
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
12302
12442
  if (isCircular) {
12303
12443
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
12304
- const circularProp = path99.find((p) => !OPENAPI_PROPS.has(p)) || path99[0];
12444
+ const circularProp = path100.find((p) => !OPENAPI_PROPS.has(p)) || path100[0];
12305
12445
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
12306
12446
  }
12307
12447
  if (refs.has(obj)) return refs.get(obj);
@@ -12311,7 +12451,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
12311
12451
  value,
12312
12452
  currentPath,
12313
12453
  refs,
12314
- [...path99, key],
12454
+ [...path100, key],
12315
12455
  currentRefPaths
12316
12456
  );
12317
12457
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -12346,7 +12486,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
12346
12486
  var getAllTags = (schema2) => {
12347
12487
  const rootTags = schema2.tags ?? [];
12348
12488
  const operations = Object.values(schema2.paths ?? {}).flatMap(
12349
- (path99) => HttpMethods.map((k) => path99?.[k]).filter((op) => op != null)
12489
+ (path100) => HttpMethods.map((k) => path100?.[k]).filter((op) => op != null)
12350
12490
  );
12351
12491
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
12352
12492
  const hasUntaggedOperations = operations.some(
@@ -12401,7 +12541,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
12401
12541
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
12402
12542
  var getAllOperations = (paths) => {
12403
12543
  const operations = Object.entries(paths ?? {}).flatMap(
12404
- ([path99, value]) => HttpMethods.flatMap((method) => {
12544
+ ([path100, value]) => HttpMethods.flatMap((method) => {
12405
12545
  if (!value?.[method]) return [];
12406
12546
  const operation = value[method];
12407
12547
  const pathParameters = value.parameters ?? [];
@@ -12421,7 +12561,7 @@ var getAllOperations = (paths) => {
12421
12561
  return {
12422
12562
  ...operation,
12423
12563
  method,
12424
- path: path99,
12564
+ path: path100,
12425
12565
  parameters,
12426
12566
  servers,
12427
12567
  tags: operation.tags ?? []
@@ -12779,8 +12919,8 @@ var Schema = builder.objectRef("Schema").implement({
12779
12919
  }),
12780
12920
  paths: t.field({
12781
12921
  type: [PathItem],
12782
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path99, value]) => ({
12783
- path: path99,
12922
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path100, value]) => ({
12923
+ path: path100,
12784
12924
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
12785
12925
  methods: Object.keys(value)
12786
12926
  }))
@@ -12945,7 +13085,7 @@ init_joinUrl();
12945
13085
 
12946
13086
  // src/vite/api/schema-codegen.ts
12947
13087
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
12948
- var getSegmentsFromPath = (path99) => path99.split("/").slice(1).map(unescapeJsonPointer);
13088
+ var getSegmentsFromPath = (path100) => path100.split("/").slice(1).map(unescapeJsonPointer);
12949
13089
  var createLocalRefMap = (obj) => {
12950
13090
  const refMap = /* @__PURE__ */ new Map();
12951
13091
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -12976,16 +13116,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
12976
13116
  /"__refMap\+Siblings:(.*?)"/g,
12977
13117
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
12978
13118
  );
12979
- var lookup = (schema2, path99, filePath) => {
12980
- const parts = getSegmentsFromPath(path99);
13119
+ var lookup = (schema2, path100, filePath) => {
13120
+ const parts = getSegmentsFromPath(path100);
12981
13121
  let val = schema2;
12982
13122
  for (const part of parts) {
12983
13123
  while (val.$ref?.startsWith("#/")) {
12984
- val = val.$ref === path99 ? val : lookup(schema2, val.$ref, filePath);
13124
+ val = val.$ref === path100 ? val : lookup(schema2, val.$ref, filePath);
12985
13125
  }
12986
13126
  if (val[part] === void 0) {
12987
13127
  throw new Error(
12988
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path99}`
13128
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path100}`
12989
13129
  );
12990
13130
  }
12991
13131
  val = val[part];
@@ -13232,8 +13372,8 @@ var SchemaManager = class {
13232
13372
  }
13233
13373
  }
13234
13374
  };
13235
- getLatestSchema = (path99) => this.processedSchemas[path99]?.at(0);
13236
- getSchemasForPath = (path99) => this.processedSchemas[path99];
13375
+ getLatestSchema = (path100) => this.processedSchemas[path100]?.at(0);
13376
+ getSchemasForPath = (path100) => this.processedSchemas[path100];
13237
13377
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
13238
13378
  getUrlToFilePathMap = () => {
13239
13379
  const map = /* @__PURE__ */ new Map();
@@ -14357,8 +14497,32 @@ init_loader();
14357
14497
  init_ProtectedRoutesSchema();
14358
14498
  init_joinUrl();
14359
14499
  import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
14360
- import path36 from "node:path";
14500
+ import path37 from "node:path";
14361
14501
  import { matchPath } from "react-router";
14502
+
14503
+ // src/vite/markdown-output-path.ts
14504
+ import { rm as rm6 } from "node:fs/promises";
14505
+ import path36 from "node:path";
14506
+ var markdownOutputPath = (distDir, routePath) => {
14507
+ const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
14508
+ return `${path36.join(distDir, ...segments)}.md`;
14509
+ };
14510
+ var removableMarkdownOutput = (distDir, routePath) => {
14511
+ const output4 = path36.resolve(markdownOutputPath(distDir, routePath));
14512
+ const relative = path36.relative(path36.resolve(distDir), output4);
14513
+ if (!relative || relative.startsWith("..") || path36.isAbsolute(relative)) {
14514
+ return void 0;
14515
+ }
14516
+ return output4;
14517
+ };
14518
+ var removeExportedMarkdown = async (distDir, pages) => {
14519
+ await Promise.all(
14520
+ pages.map((page) => removableMarkdownOutput(distDir, page.routePath)).filter((output4) => output4 !== void 0).map((output4) => rm6(output4, { force: true }).catch(() => {
14521
+ }))
14522
+ );
14523
+ };
14524
+
14525
+ // src/vite/plugin-markdown-export.ts
14362
14526
  var processMarkdownFile = async (filePath) => {
14363
14527
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
14364
14528
  let finalMarkdown = markdownContent;
@@ -14445,7 +14609,7 @@ var viteMarkdownExportPlugin = () => {
14445
14609
  if (process.env.NODE_ENV !== "production" || Object.keys(markdownFiles).length === 0 || !needsMdFiles) {
14446
14610
  return;
14447
14611
  }
14448
- const distDir = path36.join(
14612
+ const distDir = path37.join(
14449
14613
  config2.__meta.rootDir,
14450
14614
  "dist",
14451
14615
  config2.basePath ?? ""
@@ -14465,16 +14629,15 @@ var viteMarkdownExportPlugin = () => {
14465
14629
  description,
14466
14630
  content: finalMarkdown
14467
14631
  });
14468
- const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
14469
- const outputPath = `${path36.join(distDir, ...segments)}.md`;
14470
- await mkdir8(path36.dirname(outputPath), { recursive: true });
14632
+ const outputPath = markdownOutputPath(distDir, routePath);
14633
+ await mkdir8(path37.dirname(outputPath), { recursive: true });
14471
14634
  await writeFile10(outputPath, finalMarkdown, "utf-8");
14472
14635
  } catch (error) {
14473
14636
  console.warn(`Failed to export markdown for ${routePath}:`, error);
14474
14637
  }
14475
14638
  }
14476
14639
  if (config2.docs?.llms?.llmsTxt || config2.docs?.llms?.llmsTxtFull) {
14477
- const markdownInfoPath = path36.join(
14640
+ const markdownInfoPath = path37.join(
14478
14641
  config2.__meta.rootDir,
14479
14642
  "node_modules/.apitogo/markdown-info.json"
14480
14643
  );
@@ -14626,9 +14789,9 @@ var remarkCodeTabs = () => (tree) => {
14626
14789
  };
14627
14790
 
14628
14791
  // src/vite/mdx/remark-inject-filepath.ts
14629
- import path37 from "node:path";
14792
+ import path38 from "node:path";
14630
14793
  var remarkInjectFilepath = (rootDir) => (tree, vfile) => {
14631
- const relativePath = path37.relative(rootDir, vfile.path).split(path37.sep).join(path37.posix.sep);
14794
+ const relativePath = path38.relative(rootDir, vfile.path).split(path38.sep).join(path38.posix.sep);
14632
14795
  tree.children.unshift(exportMdxjsConst("__filepath", relativePath));
14633
14796
  };
14634
14797
 
@@ -14715,18 +14878,18 @@ var remarkLastModified = () => {
14715
14878
  };
14716
14879
 
14717
14880
  // src/vite/mdx/remark-link-rewrite.ts
14718
- import path38 from "node:path";
14881
+ import path39 from "node:path";
14719
14882
  import { visit as visit5 } from "unist-util-visit";
14720
14883
  var remarkLinkRewrite = (basePath = "") => (tree) => {
14721
14884
  visit5(tree, "link", (node) => {
14722
14885
  if (!node.url) return;
14723
14886
  if (node.url.startsWith("http") || node.url.startsWith("mailto:")) return;
14724
14887
  node.url = node.url.replace(/\\/g, "/");
14725
- const base = path38.posix.join(basePath);
14888
+ const base = path39.posix.join(basePath);
14726
14889
  if (basePath && node.url.startsWith(base)) {
14727
14890
  node.url = node.url.slice(base.length);
14728
14891
  } else if (!node.url.startsWith("/") && !node.url.startsWith("#")) {
14729
- node.url = path38.posix.join("..", node.url);
14892
+ node.url = path39.posix.join("..", node.url);
14730
14893
  }
14731
14894
  node.url = node.url.replace(/\.mdx?(#.*)?$/, "$1");
14732
14895
  });
@@ -14946,14 +15109,14 @@ var plugin_mdx_default = viteMdxPlugin;
14946
15109
 
14947
15110
  // src/vite/plugin-modules.ts
14948
15111
  init_loader();
14949
- import path39 from "node:path";
15112
+ import path40 from "node:path";
14950
15113
  import { normalizePath as normalizePath2 } from "vite";
14951
- var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14952
- var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
15114
+ var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path40.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
15115
+ var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path40.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
14953
15116
  var getBillingPluginImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(
14954
- path39.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
15117
+ path40.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14955
15118
  ) : "@lukoweb/apitogo-plugin-dev-portal-billing";
14956
- var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path39.resolve(rootDir, pagePath));
15119
+ var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path40.resolve(rootDir, pagePath));
14957
15120
  var viteModulesPlugin = () => {
14958
15121
  const virtualModuleId4 = "virtual:zudoku-modules-plugin";
14959
15122
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -15095,17 +15258,17 @@ var plugin_modules_default = viteModulesPlugin;
15095
15258
 
15096
15259
  // src/vite/plugin-offroot-resolve.ts
15097
15260
  init_loader();
15098
- import path40 from "node:path";
15261
+ import path41 from "node:path";
15099
15262
  function stripQuery(id) {
15100
15263
  const query = id.indexOf("?");
15101
15264
  return query === -1 ? id : id.slice(0, query);
15102
15265
  }
15103
15266
  function isInside(parent, child) {
15104
- const relative = path40.relative(path40.resolve(parent), path40.resolve(child));
15105
- return relative === "" || !relative.startsWith("..") && !path40.isAbsolute(relative);
15267
+ const relative = path41.relative(path41.resolve(parent), path41.resolve(child));
15268
+ return relative === "" || !relative.startsWith("..") && !path41.isAbsolute(relative);
15106
15269
  }
15107
15270
  function isBareSpecifier(source) {
15108
- return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path40.isAbsolute(source);
15271
+ return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path41.isAbsolute(source);
15109
15272
  }
15110
15273
  function isInsideNodeModules(filePath) {
15111
15274
  return filePath.split(/[\\/]/).includes("node_modules");
@@ -15129,7 +15292,7 @@ function viteOffrootResolvePlugin() {
15129
15292
  if (!shouldResolveFromSiteRoot(source, importer, rootDir)) {
15130
15293
  return;
15131
15294
  }
15132
- return this.resolve(source, path40.join(rootDir, "package.json"), {
15295
+ return this.resolve(source, path41.join(rootDir, "package.json"), {
15133
15296
  ...options,
15134
15297
  skipSelf: true
15135
15298
  });
@@ -15139,7 +15302,7 @@ function viteOffrootResolvePlugin() {
15139
15302
 
15140
15303
  // src/vite/plugin-search.ts
15141
15304
  init_loader();
15142
- import path41 from "node:path";
15305
+ import path42 from "node:path";
15143
15306
  var viteSearchPlugin = () => {
15144
15307
  const virtualModuleId4 = "virtual:zudoku-search-plugin";
15145
15308
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -15157,7 +15320,7 @@ var viteSearchPlugin = () => {
15157
15320
  return resolvedVirtualModuleId5;
15158
15321
  }
15159
15322
  if (id === "/pagefind/pagefind.js" && resolvedViteConfig?.publicDir) {
15160
- return path41.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
15323
+ return path42.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
15161
15324
  }
15162
15325
  },
15163
15326
  async load(id) {
@@ -15298,27 +15461,27 @@ function vitePlugin() {
15298
15461
 
15299
15462
  // src/vite/watch-ignore.ts
15300
15463
  import { lstatSync, realpathSync } from "node:fs";
15301
- import path42 from "node:path";
15464
+ import path43 from "node:path";
15302
15465
  var STATE_INSIDE_CONTENT = /[\\/]content[\\/](?:\.apitogo|apitogo[\\/]\.state)(?:[\\/]|$)/;
15303
15466
  var within = (base, file) => {
15304
- const resolvedBase = path42.resolve(base);
15305
- const resolvedFile = path42.resolve(file);
15306
- return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path42.sep);
15467
+ const resolvedBase = path43.resolve(base);
15468
+ const resolvedFile = path43.resolve(file);
15469
+ return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path43.sep);
15307
15470
  };
15308
15471
  var unique = (paths) => [...new Set(paths)];
15309
15472
  function isWorkspace(root) {
15310
15473
  try {
15311
- return path42.basename(root) === "generated" && lstatSync(path42.join(root, "content")).isSymbolicLink();
15474
+ return path43.basename(root) === "generated" && lstatSync(path43.join(root, "content")).isSymbolicLink();
15312
15475
  } catch {
15313
15476
  return false;
15314
15477
  }
15315
15478
  }
15316
15479
  function tenantDir(root) {
15317
- return isWorkspace(root) ? realpathSync(path42.join(root, "content")) : void 0;
15480
+ return isWorkspace(root) ? realpathSync(path43.join(root, "content")) : void 0;
15318
15481
  }
15319
15482
  function stateIgnore(root) {
15320
15483
  if (!isWorkspace(root)) return void 0;
15321
- const state = path42.dirname(root);
15484
+ const state = path43.dirname(root);
15322
15485
  const states = unique([state, realpathSync(state)]);
15323
15486
  const roots = unique([root, realpathSync(root)]);
15324
15487
  return (file) => {
@@ -15327,19 +15490,28 @@ function stateIgnore(root) {
15327
15490
  return STATE_INSIDE_CONTENT.test(file);
15328
15491
  };
15329
15492
  }
15493
+ var STATE_FOLDER = /(?:^|[\\/])(?:\.apitogo|apitogo[\\/]\.state)(?:[\\/]|$)/;
15494
+ function nestedStateIgnore(root) {
15495
+ const resolvedRoot = path43.resolve(root);
15496
+ return (file) => {
15497
+ const relative = path43.relative(resolvedRoot, path43.resolve(file));
15498
+ if (relative.startsWith("..") || path43.isAbsolute(relative)) return false;
15499
+ return STATE_FOLDER.test(relative);
15500
+ };
15501
+ }
15330
15502
  function devServerAllow(root, moduleDir) {
15331
15503
  const tenant = tenantDir(root);
15332
- return [root, path42.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15504
+ return [root, path43.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15333
15505
  }
15334
15506
 
15335
15507
  // src/vite/config.ts
15336
15508
  var resolveMergedPublicDir = (rootDir, merged) => {
15337
15509
  if (merged.publicDir === false) return void 0;
15338
15510
  const rel2 = typeof merged.publicDir === "string" ? merged.publicDir : "public";
15339
- return path43.resolve(rootDir, rel2);
15511
+ return path44.resolve(rootDir, rel2);
15340
15512
  };
15341
- var getAppClientEntryPath = () => path43.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
15342
- var getAppServerEntryPath = () => path43.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
15513
+ var getAppClientEntryPath = () => path44.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
15514
+ var getAppServerEntryPath = () => path44.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
15343
15515
  var hasLoggedCdnInfo = false;
15344
15516
  var MEDIA_REGEX = /\.(a?png|jpe?g|gif|bmp|svg|webp|tiff|ico|webm|ogg|mp3|wav|m4a|avif|mp4)/i;
15345
15517
  var defineEnvVars = (vars) => Object.fromEntries(
@@ -15370,7 +15542,7 @@ async function getViteConfig(dir, configEnv) {
15370
15542
  const ignoreState = stateIgnore(dir);
15371
15543
  if (ZuploEnv.isZuplo) {
15372
15544
  dotenv.config({
15373
- path: path43.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15545
+ path: path44.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15374
15546
  quiet: true
15375
15547
  });
15376
15548
  }
@@ -15425,6 +15597,7 @@ async function getViteConfig(dir, configEnv) {
15425
15597
  `${dir}/.git`,
15426
15598
  `${config2.__meta.moduleDir}/src/vite`,
15427
15599
  `${config2.__meta.moduleDir}/src/cli`,
15600
+ nestedStateIgnore(dir),
15428
15601
  ...ignoreState ? [ignoreState] : []
15429
15602
  ]
15430
15603
  }
@@ -15433,8 +15606,8 @@ async function getViteConfig(dir, configEnv) {
15433
15606
  ssr: configEnv.isSsrBuild,
15434
15607
  sourcemap: true,
15435
15608
  target: "es2022",
15436
- outDir: path43.resolve(
15437
- path43.join(
15609
+ outDir: path44.resolve(
15610
+ path44.join(
15438
15611
  dir,
15439
15612
  "dist",
15440
15613
  config2.basePath ?? "",
@@ -15453,7 +15626,7 @@ async function getViteConfig(dir, configEnv) {
15453
15626
  },
15454
15627
  experimental: {
15455
15628
  renderBuiltUrl(filename) {
15456
- if (cdnUrl?.base && [".js", ".css"].includes(path43.extname(filename))) {
15629
+ if (cdnUrl?.base && [".js", ".css"].includes(path44.extname(filename))) {
15457
15630
  return joinUrl(cdnUrl.base, filename);
15458
15631
  }
15459
15632
  if (cdnUrl?.media && MEDIA_REGEX.test(filename)) {
@@ -15466,7 +15639,7 @@ async function getViteConfig(dir, configEnv) {
15466
15639
  esbuildOptions: {
15467
15640
  target: "es2022"
15468
15641
  },
15469
- entries: [path43.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15642
+ entries: [path44.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15470
15643
  exclude: [
15471
15644
  "@lukoweb/apitogo",
15472
15645
  "@lukoweb/apitogo-plugin-dev-portal-billing"
@@ -15570,7 +15743,7 @@ init_package_json();
15570
15743
  init_joinUrl();
15571
15744
  import assert from "node:assert";
15572
15745
  import { cp, mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
15573
- import path44 from "node:path";
15746
+ import path45 from "node:path";
15574
15747
  var pkgJson = getZudokuPackageJson();
15575
15748
  function generateOutput({
15576
15749
  config: config2,
@@ -15627,9 +15800,9 @@ async function writeOutput(dir, {
15627
15800
  rewrites
15628
15801
  }) {
15629
15802
  const output4 = generateOutput({ config: config2, redirects, rewrites });
15630
- const outputDir = process.env.VERCEL ? path44.join(dir, ".vercel/output") : path44.join(dir, "dist/.output");
15803
+ const outputDir = process.env.VERCEL ? path45.join(dir, ".vercel/output") : path45.join(dir, "dist/.output");
15631
15804
  await mkdir9(outputDir, { recursive: true });
15632
- const outputFile = path44.join(outputDir, "config.json");
15805
+ const outputFile = path45.join(outputDir, "config.json");
15633
15806
  await writeFile11(outputFile, JSON.stringify(output4, null, 2), "utf-8");
15634
15807
  if (process.env.VERCEL) {
15635
15808
  console.log("Wrote Vercel output to", outputDir);
@@ -15639,9 +15812,9 @@ async function writeOutput(dir, {
15639
15812
  // src/vite/prerender/prerender.ts
15640
15813
  init_logger();
15641
15814
  init_file_exists();
15642
- import { readFile as readFile12, rm as rm6 } from "node:fs/promises";
15815
+ import { readFile as readFile12 } from "node:fs/promises";
15643
15816
  import os from "node:os";
15644
- import path47 from "node:path";
15817
+ import path48 from "node:path";
15645
15818
  import { pathToFileURL } from "node:url";
15646
15819
  import { createIndex } from "pagefind";
15647
15820
  import colors11 from "picocolors";
@@ -15683,7 +15856,7 @@ function throttle(fn) {
15683
15856
  init_joinUrl();
15684
15857
  import { createWriteStream, existsSync as existsSync5 } from "node:fs";
15685
15858
  import { mkdir as mkdir10 } from "node:fs/promises";
15686
- import path45 from "node:path";
15859
+ import path46 from "node:path";
15687
15860
  import colors9 from "picocolors";
15688
15861
  import { SitemapStream } from "sitemap";
15689
15862
  async function generateSitemap({
@@ -15697,11 +15870,11 @@ async function generateSitemap({
15697
15870
  return;
15698
15871
  }
15699
15872
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
15700
- const outputDir = path45.resolve(baseOutputDir, config2.outDir ?? "");
15873
+ const outputDir = path46.resolve(baseOutputDir, config2.outDir ?? "");
15701
15874
  if (!existsSync5(outputDir)) {
15702
15875
  await mkdir10(outputDir, { recursive: true });
15703
15876
  }
15704
- const sitemapOutputPath = path45.join(outputDir, "sitemap.xml");
15877
+ const sitemapOutputPath = path46.join(outputDir, "sitemap.xml");
15705
15878
  const writeStream = createWriteStream(sitemapOutputPath);
15706
15879
  sitemap.pipe(writeStream);
15707
15880
  let lastmod;
@@ -15731,14 +15904,14 @@ async function generateSitemap({
15731
15904
 
15732
15905
  // src/vite/prerender/utils.ts
15733
15906
  init_joinUrl();
15734
- var resolveRoutePath = (path99) => {
15735
- const segments = path99.split("/");
15907
+ var resolveRoutePath = (path100) => {
15908
+ const segments = path100.split("/");
15736
15909
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
15737
15910
  return void 0;
15738
15911
  }
15739
15912
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
15740
15913
  };
15741
- var isSkipped = (path99) => path99.includes("*") || /^\d+$/.test(path99);
15914
+ var isSkipped = (path100) => path100.includes("*") || /^\d+$/.test(path100);
15742
15915
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
15743
15916
  if (route.path && isSkipped(route.path)) return [];
15744
15917
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -15788,12 +15961,12 @@ var prerender = async ({
15788
15961
  serverConfigFilename,
15789
15962
  writeRedirects = true
15790
15963
  }) => {
15791
- const distDir = path47.join(dir, "dist", basePath);
15964
+ const distDir = path48.join(dir, "dist", basePath);
15792
15965
  const serverConfigPath = pathToFileURL(
15793
- path47.join(distDir, "server", serverConfigFilename)
15966
+ path48.join(distDir, "server", serverConfigFilename)
15794
15967
  ).href;
15795
15968
  const entryServerPath = pathToFileURL(
15796
- path47.join(distDir, "server/entry.server.js")
15969
+ path48.join(distDir, "server/entry.server.js")
15797
15970
  ).href;
15798
15971
  const rawConfig = await import(serverConfigPath).then(
15799
15972
  (m) => m.default
@@ -15878,7 +16051,7 @@ var prerender = async ({
15878
16051
  })
15879
16052
  );
15880
16053
  const pagefindWriteResult = await pagefindIndex?.writeFiles({
15881
- outputPath: path47.join(distDir, "pagefind")
16054
+ outputPath: path48.join(distDir, "pagefind")
15882
16055
  });
15883
16056
  const seconds = ((performance.now() - start) / 1e3).toFixed(1);
15884
16057
  const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
@@ -15910,7 +16083,7 @@ var prerender = async ({
15910
16083
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
15911
16084
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
15912
16085
  const llmsConfig = docsConfig.llms ?? {};
15913
- const markdownInfoPath = path47.join(
16086
+ const markdownInfoPath = path48.join(
15914
16087
  dir,
15915
16088
  "node_modules/.apitogo/markdown-info.json"
15916
16089
  );
@@ -15932,10 +16105,7 @@ var prerender = async ({
15932
16105
  });
15933
16106
  }
15934
16107
  if (!docsConfig.publishMarkdown) {
15935
- await Promise.all(
15936
- markdownFileInfos.map((info) => rm6(info.filePath).catch(() => {
15937
- }))
15938
- );
16108
+ await removeExportedMarkdown(distDir, markdownFileInfos);
15939
16109
  }
15940
16110
  }
15941
16111
  return { workerResults, rewrites };
@@ -16001,7 +16171,7 @@ async function runBuild(options) {
16001
16171
  html,
16002
16172
  basePath: config2.basePath
16003
16173
  });
16004
- await rm7(path48.join(clientOutDir, "index.html"), { force: true });
16174
+ await rm7(path49.join(clientOutDir, "index.html"), { force: true });
16005
16175
  } else {
16006
16176
  await runPrerender({
16007
16177
  dir,
@@ -16025,7 +16195,7 @@ var runPrerender = async (options) => {
16025
16195
  serverConfigFilename,
16026
16196
  writeRedirects: process.env.VERCEL === void 0
16027
16197
  });
16028
- const indexHtml = path48.join(clientOutDir, "index.html");
16198
+ const indexHtml = path49.join(clientOutDir, "index.html");
16029
16199
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
16030
16200
  await writeFile13(indexHtml, html, "utf-8");
16031
16201
  }
@@ -16035,15 +16205,15 @@ var runPrerender = async (options) => {
16035
16205
  for (const statusPage of statusPages) {
16036
16206
  await rename5(
16037
16207
  statusPage,
16038
- path48.join(dir, DIST_DIR, path48.basename(statusPage))
16208
+ path49.join(dir, DIST_DIR, path49.basename(statusPage))
16039
16209
  );
16040
16210
  }
16041
16211
  await rm7(serverOutDir, { recursive: true, force: true });
16042
16212
  if (process.env.VERCEL) {
16043
- await mkdir11(path48.join(dir, ".vercel/output/static"), { recursive: true });
16213
+ await mkdir11(path49.join(dir, ".vercel/output/static"), { recursive: true });
16044
16214
  await rename5(
16045
- path48.join(dir, DIST_DIR),
16046
- path48.join(dir, ".vercel/output/static")
16215
+ path49.join(dir, DIST_DIR),
16216
+ path49.join(dir, ".vercel/output/static")
16047
16217
  );
16048
16218
  }
16049
16219
  await writeOutput(dir, {
@@ -16053,7 +16223,7 @@ var runPrerender = async (options) => {
16053
16223
  });
16054
16224
  if (ZuploEnv.isZuplo && issuer) {
16055
16225
  await writeFile13(
16056
- path48.join(dir, DIST_DIR, ".output/zuplo.json"),
16226
+ path49.join(dir, DIST_DIR, ".output/zuplo.json"),
16057
16227
  JSON.stringify({ issuer }, null, 2),
16058
16228
  "utf-8"
16059
16229
  );
@@ -16065,10 +16235,10 @@ var runPrerender = async (options) => {
16065
16235
  };
16066
16236
  var bundleSSREntry = async (options) => {
16067
16237
  const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } = options;
16068
- const tempEntryPath = path48.join(dir, "__ssr-entry.ts");
16238
+ const tempEntryPath = path49.join(dir, "__ssr-entry.ts");
16069
16239
  const packageRoot = getZudokuRootDir();
16070
16240
  const templateContent = await readFile13(
16071
- path48.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
16241
+ path49.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
16072
16242
  "utf-8"
16073
16243
  );
16074
16244
  const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`)).replace(
@@ -16083,9 +16253,9 @@ var bundleSSREntry = async (options) => {
16083
16253
  platform: adapter === "node" ? "node" : "neutral",
16084
16254
  target: "es2022",
16085
16255
  format: "esm",
16086
- outfile: path48.join(serverOutDir, "entry.js"),
16256
+ outfile: path49.join(serverOutDir, "entry.js"),
16087
16257
  external: ["./entry.server.js", `./${serverConfigFilename}`],
16088
- nodePaths: [path48.join(packageRoot, "node_modules")],
16258
+ nodePaths: [path49.join(packageRoot, "node_modules")],
16089
16259
  banner: { js: "// Bundled SSR entry" }
16090
16260
  });
16091
16261
  } finally {
@@ -16099,7 +16269,7 @@ init_package_json();
16099
16269
  // src/cli/init/v2-packages.ts
16100
16270
  import { existsSync as existsSync6 } from "node:fs";
16101
16271
  import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
16102
- import path49 from "node:path";
16272
+ import path50 from "node:path";
16103
16273
  var CORE_PACKAGES = [
16104
16274
  "@lukoweb/apitogo",
16105
16275
  "react",
@@ -16116,7 +16286,7 @@ function packageNameFrom(name) {
16116
16286
  return slug || "portal";
16117
16287
  }
16118
16288
  async function ensurePackageJson(dir, name) {
16119
- const file = path49.join(dir, "package.json");
16289
+ const file = path50.join(dir, "package.json");
16120
16290
  if (existsSync6(file)) return false;
16121
16291
  await writeFile14(
16122
16292
  file,
@@ -16135,11 +16305,11 @@ async function ensurePackageJson(dir, name) {
16135
16305
  return true;
16136
16306
  }
16137
16307
  function isPackageInstalled(startDir, name) {
16138
- let dir = path49.resolve(startDir);
16308
+ let dir = path50.resolve(startDir);
16139
16309
  for (; ; ) {
16140
- if (existsSync6(path49.join(dir, "node_modules", name, "package.json")))
16310
+ if (existsSync6(path50.join(dir, "node_modules", name, "package.json")))
16141
16311
  return true;
16142
- const parent = path49.dirname(dir);
16312
+ const parent = path50.dirname(dir);
16143
16313
  if (parent === dir) return false;
16144
16314
  dir = parent;
16145
16315
  }
@@ -16196,7 +16366,7 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
16196
16366
  return {
16197
16367
  name: "build",
16198
16368
  hash: () => hash2,
16199
- stillValid: () => access4(path50.join(buildDir, "index.html")).then(
16369
+ stillValid: () => access4(path51.join(buildDir, "index.html")).then(
16200
16370
  () => true,
16201
16371
  () => false
16202
16372
  ),
@@ -16213,11 +16383,11 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
16213
16383
  else process.env.NODE_ENV = originalNodeEnv;
16214
16384
  }
16215
16385
  await rm8(buildDir, { recursive: true, force: true });
16216
- await mkdir12(path50.dirname(buildDir), { recursive: true });
16217
- await rename6(path50.join(root, "dist"), buildDir);
16386
+ await mkdir12(path51.dirname(buildDir), { recursive: true });
16387
+ await rename6(path51.join(root, "dist"), buildDir);
16218
16388
  if (prepared.publishedManifest) {
16219
16389
  await writeFile15(
16220
- path50.join(buildDir, MANIFEST_FILE),
16390
+ path51.join(buildDir, MANIFEST_FILE),
16221
16391
  `${JSON.stringify(prepared.publishedManifest, null, 2)}
16222
16392
  `
16223
16393
  );
@@ -16248,15 +16418,15 @@ async function buildV2(options) {
16248
16418
  // src/cli/check/gateway-online.ts
16249
16419
  import { readFile as readFile16 } from "node:fs/promises";
16250
16420
  var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
16251
- var issue2 = (code, path99, message, hint) => ({ code, path: path99, message, hint });
16421
+ var issue2 = (code, path100, message, hint) => ({ code, path: path100, message, hint });
16252
16422
  function capabilityIssues(gateway, answer) {
16253
16423
  const can = answer.capabilities;
16254
16424
  const plan2 = `your ${answer.tier} plan`;
16255
16425
  const found = [];
16256
- const unsupported = (path99, what, hint) => found.push(
16426
+ const unsupported = (path100, what, hint) => found.push(
16257
16427
  issue2(
16258
16428
  "GATEWAY_CAPABILITY_UNSUPPORTED",
16259
- path99,
16429
+ path100,
16260
16430
  `${what} is not available on ${plan2}.`,
16261
16431
  hint
16262
16432
  )
@@ -16467,9 +16637,9 @@ function toPlatformPlans(plans, options = {}) {
16467
16637
  }
16468
16638
 
16469
16639
  // src/cli/check/services-online.ts
16470
- var issue3 = (code, path99, message, hint) => ({
16640
+ var issue3 = (code, path100, message, hint) => ({
16471
16641
  code,
16472
- path: path99,
16642
+ path: path100,
16473
16643
  message,
16474
16644
  hint
16475
16645
  });
@@ -16581,7 +16751,7 @@ var withPlanName = (message, sku) => !sku || message.toLowerCase().startsWith(`p
16581
16751
  async function isConfigV2(dir) {
16582
16752
  try {
16583
16753
  return /"version"\s*:\s*2\b/.test(
16584
- await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16754
+ await readFile18(path52.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16585
16755
  );
16586
16756
  } catch {
16587
16757
  return false;
@@ -16590,7 +16760,7 @@ async function isConfigV2(dir) {
16590
16760
  async function isLegacyConfig(dir) {
16591
16761
  let text2;
16592
16762
  try {
16593
- text2 = await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8");
16763
+ text2 = await readFile18(path52.join(dir, APITOGO_CONFIG_FILENAME), "utf8");
16594
16764
  } catch {
16595
16765
  return false;
16596
16766
  }
@@ -16738,7 +16908,7 @@ async function validatePortalBuild(dir) {
16738
16908
  }
16739
16909
  }
16740
16910
  async function runCheckV2(dir, args = {}, deps = {}) {
16741
- const configPath = path51.join(dir, APITOGO_CONFIG_FILENAME);
16911
+ const configPath = path52.join(dir, APITOGO_CONFIG_FILENAME);
16742
16912
  let report;
16743
16913
  try {
16744
16914
  const { config: config2, resolved } = loadConfigV2(dir);
@@ -16848,7 +17018,7 @@ async function checkV2(dir, args, deps = platformDependencies(args)) {
16848
17018
  init_package_json();
16849
17019
 
16850
17020
  // src/cli/init/v2-handler.ts
16851
- import path52 from "node:path";
17021
+ import path53 from "node:path";
16852
17022
  import { createInterface } from "node:readline/promises";
16853
17023
  function parseDocsFlag(value) {
16854
17024
  if (value === void 0) return void 0;
@@ -16870,15 +17040,15 @@ async function ask(question) {
16870
17040
  }
16871
17041
  function describePlan(plan2) {
16872
17042
  return {
16873
- willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path52.relative(plan2.dir, entry.path)),
16874
- willKeep: plan2.keep.map((target) => path52.relative(plan2.dir, target)),
17043
+ willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path53.relative(plan2.dir, entry.path)),
17044
+ willKeep: plan2.keep.map((target) => path53.relative(plan2.dir, target)),
16875
17045
  replacesConfig: plan2.replacesConfig
16876
17046
  };
16877
17047
  }
16878
17048
  async function initV2(argv) {
16879
17049
  const json = argv.json === true;
16880
17050
  try {
16881
- const dir = path52.resolve(process.cwd(), argv.dir ?? ".");
17051
+ const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
16882
17052
  const plan2 = await planInit({
16883
17053
  dir,
16884
17054
  name: argv.name,
@@ -16919,8 +17089,8 @@ async function initV2(argv) {
16919
17089
  status: "created",
16920
17090
  dir,
16921
17091
  ...describePlan(plan2),
16922
- created: result.created.map((p) => path52.relative(dir, p)),
16923
- kept: result.kept.map((p) => path52.relative(dir, p))
17092
+ created: result.created.map((p) => path53.relative(dir, p)),
17093
+ kept: result.kept.map((p) => path53.relative(dir, p))
16924
17094
  },
16925
17095
  null,
16926
17096
  2
@@ -16929,7 +17099,7 @@ async function initV2(argv) {
16929
17099
  return;
16930
17100
  }
16931
17101
  printResultToConsole(
16932
- `Created ${path52.basename(plan2.configPath)} (config version 2) in ${dir}.`
17102
+ `Created ${path53.basename(plan2.configPath)} (config version 2) in ${dir}.`
16933
17103
  );
16934
17104
  printHintToConsole(
16935
17105
  "Next: add a service with `apitogo add <portal|gateway|identity|plans>`, or run `apitogo create` for a guided setup."
@@ -16956,7 +17126,7 @@ async function initV2(argv) {
16956
17126
 
16957
17127
  // src/cli/services/packages.ts
16958
17128
  import { existsSync as existsSync7 } from "node:fs";
16959
- import path53 from "node:path";
17129
+ import path54 from "node:path";
16960
17130
 
16961
17131
  // src/cli/common/spawn-command.ts
16962
17132
  import {
@@ -16989,18 +17159,18 @@ function packagesForConfig(config2) {
16989
17159
  ];
16990
17160
  }
16991
17161
  function isInstalled(dir, name) {
16992
- for (let current = path53.resolve(dir); ; current = path53.dirname(current)) {
16993
- if (existsSync7(path53.join(current, "node_modules", name, "package.json")))
17162
+ for (let current = path54.resolve(dir); ; current = path54.dirname(current)) {
17163
+ if (existsSync7(path54.join(current, "node_modules", name, "package.json")))
16994
17164
  return true;
16995
- if (path53.dirname(current) === current) return false;
17165
+ if (path54.dirname(current) === current) return false;
16996
17166
  }
16997
17167
  }
16998
17168
  function missingPackages(dir, names) {
16999
17169
  return names.filter((name) => !isInstalled(dir, name));
17000
17170
  }
17001
17171
  function detectPackageManager(dir) {
17002
- if (existsSync7(path53.join(dir, "pnpm-lock.yaml"))) return "pnpm";
17003
- if (existsSync7(path53.join(dir, "yarn.lock"))) return "yarn";
17172
+ if (existsSync7(path54.join(dir, "pnpm-lock.yaml"))) return "pnpm";
17173
+ if (existsSync7(path54.join(dir, "yarn.lock"))) return "yarn";
17004
17174
  return "npm";
17005
17175
  }
17006
17176
  function installSpecsCommand(manager, specs) {
@@ -17058,7 +17228,7 @@ async function installMissingPackages(dir, config2, version, options) {
17058
17228
  reason: "--no-install was given",
17059
17229
  command: shown
17060
17230
  };
17061
- await ensurePackageJson(dir, config2.project?.name ?? path53.basename(dir));
17231
+ await ensurePackageJson(dir, config2.project?.name ?? path54.basename(dir));
17062
17232
  await (options.installer ?? runInstaller)(dir, command2);
17063
17233
  return { status: "installed", packages: missing, command: shown };
17064
17234
  }
@@ -17121,7 +17291,7 @@ var list = (value) => {
17121
17291
  return items.length > 0 ? items : void 0;
17122
17292
  };
17123
17293
  function addOptionsFromArguments(service, argv) {
17124
- const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
17294
+ const dir = path55.resolve(process.cwd(), argv.dir ?? ".");
17125
17295
  const issuers = list(argv.issuer);
17126
17296
  return {
17127
17297
  dir,
@@ -17175,8 +17345,8 @@ function describe(edit, dir) {
17175
17345
  return {
17176
17346
  service: edit.service,
17177
17347
  action: edit.action,
17178
- willCreate: edit.files.map((file) => path54.relative(dir, file.path)),
17179
- keep: edit.keep.map((file) => path54.relative(dir, file)),
17348
+ willCreate: edit.files.map((file) => path55.relative(dir, file.path)),
17349
+ keep: edit.keep.map((file) => path55.relative(dir, file)),
17180
17350
  notes: edit.notes
17181
17351
  };
17182
17352
  }
@@ -17190,7 +17360,7 @@ async function run(action, argv, plan2, deps = {}) {
17190
17360
  `Choose one of ${SERVICES.join(", ")}.`
17191
17361
  );
17192
17362
  }
17193
- const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
17363
+ const dir = path55.resolve(process.cwd(), argv.dir ?? ".");
17194
17364
  if (!await isConfigV2(dir)) {
17195
17365
  throw new ServiceEditError(
17196
17366
  `${action} works with a config version 2 project, and there is none in ${dir}.`,
@@ -17258,7 +17428,7 @@ async function run(action, argv, plan2, deps = {}) {
17258
17428
  {
17259
17429
  status: action === "add" ? "added" : "removed",
17260
17430
  ...describe(edit, dir),
17261
- created: created.map((file) => path54.relative(dir, file)),
17431
+ created: created.map((file) => path55.relative(dir, file)),
17262
17432
  ...install ? { packages: install } : {}
17263
17433
  },
17264
17434
  null,
@@ -17268,7 +17438,7 @@ async function run(action, argv, plan2, deps = {}) {
17268
17438
  return;
17269
17439
  }
17270
17440
  printResultToConsole(
17271
- `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path54.basename(edit.configPath)}.`
17441
+ `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path55.basename(edit.configPath)}.`
17272
17442
  );
17273
17443
  } catch (error) {
17274
17444
  const message = error instanceof Error ? error.message : String(error);
@@ -17329,7 +17499,7 @@ var removeService = (argv) => run(
17329
17499
  "remove",
17330
17500
  argv,
17331
17501
  (service) => planRemoveService(service, {
17332
- dir: path54.resolve(process.cwd(), argv.dir ?? "."),
17502
+ dir: path55.resolve(process.cwd(), argv.dir ?? "."),
17333
17503
  deleteFiles: argv.deleteFiles === true
17334
17504
  })
17335
17505
  );
@@ -17481,16 +17651,16 @@ var add_default = {
17481
17651
  };
17482
17652
 
17483
17653
  // src/cli/build/handler.ts
17484
- import path56 from "node:path";
17654
+ import path57 from "node:path";
17485
17655
  init_logger();
17486
17656
  init_package_json();
17487
17657
 
17488
17658
  // src/cli/preview/handler.ts
17489
- import path55 from "node:path";
17659
+ import path56 from "node:path";
17490
17660
  import { preview as vitePreview } from "vite";
17491
17661
  var DEFAULT_PREVIEW_PORT = 4e3;
17492
17662
  async function preview(argv) {
17493
- const dir = path55.resolve(process.cwd(), argv.dir);
17663
+ const dir = path56.resolve(process.cwd(), argv.dir);
17494
17664
  const viteConfig = await getViteConfig(dir, {
17495
17665
  command: "serve",
17496
17666
  mode: "production",
@@ -17533,7 +17703,7 @@ async function build(argv) {
17533
17703
  printDiagnosticsToConsole("");
17534
17704
  printDiagnosticsToConsole("");
17535
17705
  }
17536
- const dir = path56.resolve(process.cwd(), argv.dir);
17706
+ const dir = path57.resolve(process.cwd(), argv.dir);
17537
17707
  try {
17538
17708
  if (await isConfigV2(dir)) {
17539
17709
  const result = await buildV2({ dir, jsonOnly });
@@ -17598,10 +17768,10 @@ var build_default = {
17598
17768
  };
17599
17769
 
17600
17770
  // src/cli/check/handler.ts
17601
- import path62 from "node:path";
17771
+ import path63 from "node:path";
17602
17772
 
17603
17773
  // src/cli/platform/tools/check-publish-readiness.ts
17604
- import path61 from "node:path";
17774
+ import path62 from "node:path";
17605
17775
 
17606
17776
  // src/cli/platform/helpers/dev-portal-backend.ts
17607
17777
  function deriveProvisioningState(backend) {
@@ -17686,7 +17856,7 @@ function formatDevPortalBackendResponse(projectId, backend) {
17686
17856
  init_gateway_limits();
17687
17857
  import { existsSync as existsSync8 } from "node:fs";
17688
17858
  import { access as access5, mkdir as mkdir13, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
17689
- import path57 from "node:path";
17859
+ import path58 from "node:path";
17690
17860
 
17691
17861
  // src/cli/make-config/header-pricing-nav.ts
17692
17862
  var HEADER_PRICING_PATH = "/pricing";
@@ -17808,7 +17978,7 @@ function isPlanFree(plan2) {
17808
17978
  return (plan2.priceAmount ?? 0) <= 0;
17809
17979
  }
17810
17980
  function manifestFilePath(siteDirectory) {
17811
- return path57.join(path57.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17981
+ return path58.join(path58.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17812
17982
  }
17813
17983
  var STORED_MANIFEST_KEYS = [
17814
17984
  "authentication",
@@ -18232,23 +18402,23 @@ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
18232
18402
  if (!trimmed) {
18233
18403
  return void 0;
18234
18404
  }
18235
- return path57.resolve(siteDirectory, trimmed);
18405
+ return path58.resolve(siteDirectory, trimmed);
18236
18406
  }
18237
18407
  function resolveBackendOpenApiPath(openApiPath, options) {
18238
18408
  const trimmed = openApiPath?.trim();
18239
18409
  if (!trimmed) {
18240
18410
  return void 0;
18241
18411
  }
18242
- if (path57.isAbsolute(trimmed)) {
18412
+ if (path58.isAbsolute(trimmed)) {
18243
18413
  return trimmed;
18244
18414
  }
18245
- const siteResolved = path57.resolve(options.siteDirectory, trimmed);
18415
+ const siteResolved = path58.resolve(options.siteDirectory, trimmed);
18246
18416
  if (existsSync8(siteResolved)) {
18247
18417
  return siteResolved;
18248
18418
  }
18249
18419
  const backendDir = options.backendSourceDirectory?.trim();
18250
18420
  if (backendDir) {
18251
- const backendResolved = path57.resolve(backendDir, trimmed);
18421
+ const backendResolved = path58.resolve(backendDir, trimmed);
18252
18422
  if (existsSync8(backendResolved)) {
18253
18423
  return backendResolved;
18254
18424
  }
@@ -18273,7 +18443,7 @@ async function readManifest2(siteDirectory) {
18273
18443
  }
18274
18444
  async function writeManifest2(siteDirectory, manifest) {
18275
18445
  const filePath = manifestFilePath(siteDirectory);
18276
- await mkdir13(path57.dirname(filePath), { recursive: true });
18446
+ await mkdir13(path58.dirname(filePath), { recursive: true });
18277
18447
  let config2 = {};
18278
18448
  if (await pathExists(filePath)) {
18279
18449
  const raw = await readFile19(filePath, "utf8");
@@ -18299,7 +18469,7 @@ import {
18299
18469
  unlink,
18300
18470
  writeFile as writeFile17
18301
18471
  } from "node:fs/promises";
18302
- import path58 from "node:path";
18472
+ import path59 from "node:path";
18303
18473
  var ENV_FILENAME = ".env";
18304
18474
  var COLLAPSED_UPPERCASE_ALIASES2 = {
18305
18475
  SECRETKEY: "secretKey",
@@ -18418,12 +18588,12 @@ async function readEnvFile(filePath) {
18418
18588
  }
18419
18589
  }
18420
18590
  async function loadSiteEnv(siteDirectory) {
18421
- const resolvedDirectory = path58.resolve(siteDirectory);
18591
+ const resolvedDirectory = path59.resolve(siteDirectory);
18422
18592
  const merged = { ...process.env };
18423
18593
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18424
18594
  Object.assign(
18425
18595
  merged,
18426
- await readEnvFile(path58.join(resolvedDirectory, fileName))
18596
+ await readEnvFile(path59.join(resolvedDirectory, fileName))
18427
18597
  );
18428
18598
  }
18429
18599
  return merged;
@@ -18460,7 +18630,7 @@ function buildAuthSecretsJson(secrets2, options) {
18460
18630
  return payload;
18461
18631
  }
18462
18632
  function envFilePath(siteDirectory) {
18463
- return path58.join(path58.resolve(siteDirectory), ENV_FILENAME);
18633
+ return path59.join(path59.resolve(siteDirectory), ENV_FILENAME);
18464
18634
  }
18465
18635
  function getSecretsStatus(siteDirectory, manifest, secrets2, envExists) {
18466
18636
  const providerSecretStatus = {};
@@ -18496,7 +18666,7 @@ function validateSecretsForPublish(manifest, secrets2, emailOptions) {
18496
18666
  }
18497
18667
  async function siteEnvFileHasEntries(siteDirectory) {
18498
18668
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18499
- const loaded = await readEnvFile(path58.join(siteDirectory, fileName));
18669
+ const loaded = await readEnvFile(path59.join(siteDirectory, fileName));
18500
18670
  if (Object.keys(loaded).length > 0) {
18501
18671
  return true;
18502
18672
  }
@@ -18504,7 +18674,7 @@ async function siteEnvFileHasEntries(siteDirectory) {
18504
18674
  return false;
18505
18675
  }
18506
18676
  async function resolvePublishConfig(siteDirectory, emailOptions) {
18507
- const resolvedDirectory = path58.resolve(siteDirectory);
18677
+ const resolvedDirectory = path59.resolve(siteDirectory);
18508
18678
  const manifestRaw = await readManifest2(resolvedDirectory);
18509
18679
  if (!manifestRaw) {
18510
18680
  throw new Error("apitogo.config.json not found in sourceDirectory.");
@@ -18536,11 +18706,11 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
18536
18706
  init_gateway_limits();
18537
18707
  import { readFile as readFile21 } from "node:fs/promises";
18538
18708
  import { createRequire as createRequire2 } from "node:module";
18539
- import path59 from "node:path";
18709
+ import path60 from "node:path";
18540
18710
  var cachedFallbackTs;
18541
18711
  async function loadTypeScriptForSite(sourceDirectory) {
18542
18712
  const require2 = createRequire2(
18543
- path59.join(path59.resolve(sourceDirectory), "package.json")
18713
+ path60.join(path60.resolve(sourceDirectory), "package.json")
18544
18714
  );
18545
18715
  try {
18546
18716
  return require2("typescript");
@@ -18587,7 +18757,7 @@ async function referencedFileExists(sourceDirectory, reference) {
18587
18757
  return true;
18588
18758
  }
18589
18759
  return pathExists(
18590
- path59.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18760
+ path60.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18591
18761
  );
18592
18762
  }
18593
18763
  async function validateSiteConfigStructure(config2, sourceDirectory) {
@@ -18702,11 +18872,11 @@ var DEFAULT_TSX_CANDIDATES = [
18702
18872
  "landing/LandingPage.tsx"
18703
18873
  ];
18704
18874
  async function validateDevPortalSiteConfig(sourceDirectory, options) {
18705
- const resolvedDir = path59.resolve(sourceDirectory);
18875
+ const resolvedDir = path60.resolve(sourceDirectory);
18706
18876
  const issues = [];
18707
18877
  const checkedFiles = [];
18708
18878
  const skippedChecks = [];
18709
- const jsonPath = path59.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18879
+ const jsonPath = path60.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18710
18880
  if (await pathExists(jsonPath)) {
18711
18881
  checkedFiles.push(APITOGO_CONFIG_FILENAME2);
18712
18882
  let parsed;
@@ -18738,7 +18908,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18738
18908
  );
18739
18909
  const existingTsxFiles = [];
18740
18910
  for (const relativeFile of tsxFiles) {
18741
- if (await pathExists(path59.join(resolvedDir, relativeFile))) {
18911
+ if (await pathExists(path60.join(resolvedDir, relativeFile))) {
18742
18912
  existingTsxFiles.push(relativeFile);
18743
18913
  }
18744
18914
  }
@@ -18752,7 +18922,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18752
18922
  for (const relativeFile of existingTsxFiles) {
18753
18923
  checkedFiles.push(relativeFile);
18754
18924
  const content = await readFile21(
18755
- path59.join(resolvedDir, relativeFile),
18925
+ path60.join(resolvedDir, relativeFile),
18756
18926
  "utf8"
18757
18927
  );
18758
18928
  issues.push(
@@ -18774,7 +18944,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18774
18944
 
18775
18945
  // src/cli/platform/helpers/npm-build.ts
18776
18946
  import { access as access7, readFile as readFile22 } from "node:fs/promises";
18777
- import path60 from "node:path";
18947
+ import path61 from "node:path";
18778
18948
  var BUILD_LOG_TAIL_BYTES = 8192;
18779
18949
  var MIN_NODE_MAJOR = 20;
18780
18950
  var NpmBuildError = class extends Error {
@@ -18840,13 +19010,13 @@ function runCommand(command2, args, cwd, env) {
18840
19010
  }
18841
19011
  async function runNpmInstall(sourceDirectory, options) {
18842
19012
  assertNodeVersion(options?.nodeVersion);
18843
- const cwd = path60.resolve(sourceDirectory);
18844
- const packageJsonPath = path60.join(cwd, "package.json");
19013
+ const cwd = path61.resolve(sourceDirectory);
19014
+ const packageJsonPath = path61.join(cwd, "package.json");
18845
19015
  if (!await pathExists2(packageJsonPath)) {
18846
19016
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18847
19017
  }
18848
19018
  const startedAt = Date.now();
18849
- const lockPath = path60.join(cwd, "package-lock.json");
19019
+ const lockPath = path61.join(cwd, "package-lock.json");
18850
19020
  const installArgs = await pathExists2(lockPath) ? ["ci"] : ["install"];
18851
19021
  const install = await runCommand("npm", installArgs, cwd);
18852
19022
  const installLog = install.output;
@@ -18864,8 +19034,8 @@ async function runNpmInstall(sourceDirectory, options) {
18864
19034
  }
18865
19035
  async function runNpmBuild(sourceDirectory, options) {
18866
19036
  assertNodeVersion(options?.nodeVersion);
18867
- const cwd = path60.resolve(sourceDirectory);
18868
- const packageJsonPath = path60.join(cwd, "package.json");
19037
+ const cwd = path61.resolve(sourceDirectory);
19038
+ const packageJsonPath = path61.join(cwd, "package.json");
18869
19039
  if (!await pathExists2(packageJsonPath)) {
18870
19040
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18871
19041
  }
@@ -18882,8 +19052,8 @@ async function runNpmBuild(sourceDirectory, options) {
18882
19052
  build2.exitCode
18883
19053
  );
18884
19054
  }
18885
- const distPath = path60.join(cwd, "dist");
18886
- const indexPath = path60.join(distPath, "index.html");
19055
+ const distPath = path61.join(cwd, "dist");
19056
+ const indexPath = path61.join(distPath, "index.html");
18887
19057
  if (!await pathExists2(indexPath)) {
18888
19058
  throw new NpmBuildError(
18889
19059
  `Build completed but dist/index.html was not found at ${indexPath}`,
@@ -19267,7 +19437,7 @@ function validateManifestForPlatformPublish(manifest) {
19267
19437
 
19268
19438
  // src/cli/platform/tools/check-publish-readiness.ts
19269
19439
  async function checkPublishReadiness(client, _config, args) {
19270
- const sourceDirectory = path61.resolve(args.sourceDirectory);
19440
+ const sourceDirectory = path62.resolve(args.sourceDirectory);
19271
19441
  const manifest = await readManifest2(sourceDirectory) ?? createScaffoldManifest();
19272
19442
  const emailOptions = await resolveEmailOptionsForPublish(
19273
19443
  client,
@@ -19345,7 +19515,7 @@ async function checkPublishReadiness(client, _config, args) {
19345
19515
 
19346
19516
  // src/cli/check/handler.ts
19347
19517
  async function check(argv) {
19348
- const sourceDirectory = path62.resolve(process.cwd(), argv.dir);
19518
+ const sourceDirectory = path63.resolve(process.cwd(), argv.dir);
19349
19519
  if (await isConfigV2(sourceDirectory)) {
19350
19520
  await checkV2(sourceDirectory, argv);
19351
19521
  return;
@@ -19409,7 +19579,7 @@ var check_default = {
19409
19579
 
19410
19580
  // src/cli/make-config/handler.ts
19411
19581
  init_invariant();
19412
- import path64 from "node:path";
19582
+ import path65 from "node:path";
19413
19583
  import { stdin as input2 } from "node:process";
19414
19584
 
19415
19585
  // src/cli/make-config/interactive.ts
@@ -19419,7 +19589,7 @@ import { createInterface as createInterface3 } from "node:readline/promises";
19419
19589
  // src/cli/make-config/v2-edits.ts
19420
19590
  init_apitogo_config_core();
19421
19591
  import { access as access8, mkdir as mkdir15, readFile as readFile23, writeFile as writeFile18 } from "node:fs/promises";
19422
- import path63 from "node:path";
19592
+ import path64 from "node:path";
19423
19593
  import { glob as glob8 } from "glob";
19424
19594
  init_invariant();
19425
19595
  var ConfigEditError = class extends ApitogoError {
@@ -19432,10 +19602,10 @@ var exists4 = (target) => access8(target).then(
19432
19602
  () => true,
19433
19603
  () => false
19434
19604
  );
19435
- var toPosix2 = (value) => value.split(path63.sep).join("/");
19436
- var relativeToConfig = (project, target) => toPosix2(path63.relative(project.resolved.configDir, target)) || ".";
19605
+ var toPosix2 = (value) => value.split(path64.sep).join("/");
19606
+ var relativeToConfig = (project, target) => toPosix2(path64.relative(project.resolved.configDir, target)) || ".";
19437
19607
  async function openConfigProject(dir) {
19438
- const configPath = path63.join(dir, APITOGO_CONFIG_FILENAME);
19608
+ const configPath = path64.join(dir, APITOGO_CONFIG_FILENAME);
19439
19609
  if (!await exists4(configPath)) {
19440
19610
  throw new ConfigEditError(
19441
19611
  `There is no ${APITOGO_CONFIG_FILENAME} in ${dir}.`,
@@ -19500,7 +19670,7 @@ function setPortalName(project, name) {
19500
19670
  }
19501
19671
  async function addGatewaySpec(project, specPath) {
19502
19672
  const gateway = requireGateway(project);
19503
- const absolute = path63.resolve(project.dir, specPath);
19673
+ const absolute = path64.resolve(project.dir, specPath);
19504
19674
  if (!await exists4(absolute)) {
19505
19675
  throw new ConfigEditError(`The OpenAPI file ${specPath} does not exist.`);
19506
19676
  }
@@ -19544,7 +19714,7 @@ function parseBackendInput(input5) {
19544
19714
  }
19545
19715
  async function setGatewayOrigin(project, backend) {
19546
19716
  const gateway = requireGateway(project);
19547
- const source = path63.resolve(project.dir, backend.source);
19717
+ const source = path64.resolve(project.dir, backend.source);
19548
19718
  if (!await exists4(source)) {
19549
19719
  throw new ConfigEditError(
19550
19720
  `The API source folder ${backend.source} does not exist.`
@@ -19622,6 +19792,164 @@ export default function ${pageComponentName(slug)}() {
19622
19792
  }
19623
19793
  `;
19624
19794
  }
19795
+ var HEADER_PAGES_FOLDER = "header-pages";
19796
+ var RESERVED_HEADER_SLUGS = [
19797
+ "index",
19798
+ "api",
19799
+ "pricing",
19800
+ "account",
19801
+ "login",
19802
+ "signup"
19803
+ ];
19804
+ function buildTsxHeaderPage(slug, title, file, content) {
19805
+ const given = content?.trim() ? withoutLeadingH1(content) : "";
19806
+ const text2 = given ? `{${JSON.stringify(given)}}` : "Coming soon.";
19807
+ return `import { Callout, Heading, Typography } from "@lukoweb/apitogo/components";
19808
+
19809
+ /** The page's label in the header's top menu (portal.header.links in ${APITOGO_CONFIG_FILENAME}). */
19810
+ export const title = ${JSON.stringify(title)};
19811
+
19812
+ // This page is a React component: change it like any other one (add state, fetch data, use the portal's components).
19813
+ export default function ${pageComponentName(slug)}() {
19814
+ return (
19815
+ <section className="mx-auto max-w-screen-lg px-4 py-12 pt-(--padding-content-top)">
19816
+ <Typography>
19817
+ <Heading level={1}>{title}</Heading>
19818
+ <p>${text2}</p>
19819
+ {/* A note for you while you run the portal locally (\`apitogo run\`); the deployed portal does not show it. */}
19820
+ {import.meta.env.DEV && (
19821
+ <Callout type="info" title="Where this page lives">
19822
+ Edit <code>${file}</code>. The header's top menu links to it; the docs sidebar does not.
19823
+ </Callout>
19824
+ )}
19825
+ </Typography>
19826
+ </section>
19827
+ );
19828
+ }
19829
+ `;
19830
+ }
19831
+ var TSX_TITLE = /^export\s+const\s+title\s*(?::\s*string\s*)?=\s*.*$/m;
19832
+ async function writeHeaderPage(project, input5) {
19833
+ const portalConfig = requirePortal(project);
19834
+ const portal = project.resolved.portal;
19835
+ if (!portal?.docs.enabled || portal.docs.provider !== "apitogo") {
19836
+ throw new ConfigEditError(
19837
+ "This portal does not host pages.",
19838
+ 'Header pages are served with the docs pages: they need portal.docs.provider "apitogo" and portal.docs.enabled not false.'
19839
+ );
19840
+ }
19841
+ const slug = normalizeDocsSlug(input5.slug);
19842
+ if (!slug) throw new ConfigEditError("The page path must not be empty.");
19843
+ if (slug.includes("/")) {
19844
+ throw new ConfigEditError(
19845
+ `A header page has a plain name, not a path: "${input5.slug}".`,
19846
+ "Pass one word, for example --page-slug partners (the page is then at /partners)."
19847
+ );
19848
+ }
19849
+ if (RESERVED_HEADER_SLUGS.includes(slug)) {
19850
+ throw new ConfigEditError(
19851
+ `The portal uses /${slug} itself, so a header page cannot have it.`,
19852
+ "Pick another name."
19853
+ );
19854
+ }
19855
+ const relative = (file2) => toPosix2(path64.relative(project.dir, file2));
19856
+ const docsBase = path64.join(portal.dir, "pages", slug);
19857
+ for (const file2 of [`${docsBase}.md`, `${docsBase}.mdx`, `${docsBase}.tsx`]) {
19858
+ if (await exists4(file2)) {
19859
+ throw new ConfigEditError(
19860
+ `The docs page ${relative(file2)} already has the address /${slug}.`,
19861
+ "Pick another name for the header page."
19862
+ );
19863
+ }
19864
+ }
19865
+ const base = path64.join(portal.dir, HEADER_PAGES_FOLDER, slug);
19866
+ const existing = (await Promise.all(
19867
+ [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
19868
+ async (file2) => await exists4(file2) ? file2 : null
19869
+ )
19870
+ )).find((file2) => file2 !== null);
19871
+ let file;
19872
+ let title;
19873
+ let action;
19874
+ if (input5.update) {
19875
+ if (!existing) {
19876
+ throw new ConfigEditError(
19877
+ `There is no header page at ${relative(`${base}.${input5.format ?? "tsx"}`)}.`,
19878
+ "Leave out --page-update to add it."
19879
+ );
19880
+ }
19881
+ const source = await readFile23(existing, "utf8");
19882
+ const react2 = existing.endsWith(".tsx");
19883
+ title = input5.title?.trim() || (react2 ? tsxPageLabel(source, existing) : titleOf(source)) || slug;
19884
+ file = existing;
19885
+ action = "updated";
19886
+ if (react2) {
19887
+ await writeFile18(
19888
+ file,
19889
+ input5.content !== void 0 ? buildTsxHeaderPage(slug, title, relative(file), input5.content) : TSX_TITLE.test(source) ? source.replace(
19890
+ TSX_TITLE,
19891
+ `export const title = ${JSON.stringify(title)};`
19892
+ ) : source,
19893
+ "utf8"
19894
+ );
19895
+ } else {
19896
+ const body = input5.content ?? source.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
19897
+ await writeFile18(file, buildDocsPage(title, body), "utf8");
19898
+ }
19899
+ } else {
19900
+ if (existing) {
19901
+ throw new ConfigEditError(
19902
+ `A header page already exists at ${relative(existing)}.`,
19903
+ "Pass --page-update to change it."
19904
+ );
19905
+ }
19906
+ const given = input5.title?.trim();
19907
+ if (!given) {
19908
+ throw new ConfigEditError("A new page needs a title (--page-title).");
19909
+ }
19910
+ title = given;
19911
+ file = `${base}.${input5.format ?? "tsx"}`;
19912
+ action = "added";
19913
+ await mkdir15(path64.dirname(file), { recursive: true });
19914
+ await writeFile18(
19915
+ file,
19916
+ file.endsWith(".tsx") ? buildTsxHeaderPage(slug, title, relative(file), input5.content) : buildDocsPage(
19917
+ title,
19918
+ input5.content?.trim() ? input5.content : `{/* Write this page in Markdown. It is linked from the header's top menu (portal.header.links in ${APITOGO_CONFIG_FILENAME}), not from the docs sidebar. */}
19919
+
19920
+ Coming soon.`
19921
+ ),
19922
+ "utf8"
19923
+ );
19924
+ }
19925
+ const page = relativeToConfig(project, file);
19926
+ const target = path64.resolve(file);
19927
+ const links = portalConfig.header?.links ?? [];
19928
+ const known = links.some(
19929
+ (link) => "page" in link && path64.resolve(project.resolved.configDir, link.page) === target
19930
+ );
19931
+ const nextLinks = known ? links.map(
19932
+ (link) => "page" in link && path64.resolve(project.resolved.configDir, link.page) === target ? { label: title, page: link.page } : link
19933
+ ) : [...links, { label: title, page }];
19934
+ return {
19935
+ project: withConfig(project, {
19936
+ ...project.config,
19937
+ portal: {
19938
+ ...portalConfig,
19939
+ header: { ...portalConfig.header, links: nextLinks }
19940
+ }
19941
+ }),
19942
+ page: {
19943
+ action,
19944
+ slug,
19945
+ route: `/${slug}`,
19946
+ file: relative(file),
19947
+ format: path64.extname(file).slice(1),
19948
+ inPortal: true,
19949
+ header: true
19950
+ }
19951
+ };
19952
+ }
19625
19953
  async function writeDocsPage(project, input5) {
19626
19954
  requirePortal(project);
19627
19955
  if (/^[A-Za-z]:[\\/]/.test(input5.slug.trim())) {
@@ -19645,13 +19973,13 @@ async function writeDocsPage(project, input5) {
19645
19973
  "For example --page-order 1 puts the page first in the sidebar."
19646
19974
  );
19647
19975
  }
19648
- const base = path63.join(portal.dir, "pages", ...slug.split("/"));
19976
+ const base = path64.join(portal.dir, "pages", ...slug.split("/"));
19649
19977
  const existing = (await Promise.all(
19650
19978
  [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
19651
19979
  async (file2) => await exists4(file2) ? file2 : null
19652
19980
  )
19653
19981
  )).find((file2) => file2 !== null);
19654
- const relative = (file2) => toPosix2(path63.relative(project.dir, file2));
19982
+ const relative = (file2) => toPosix2(path64.relative(project.dir, file2));
19655
19983
  const write = (file2, title, order = input5.order) => writeFile18(
19656
19984
  file2,
19657
19985
  file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content, order) : buildDocsPage(title, input5.content, order),
@@ -19685,17 +20013,17 @@ async function writeDocsPage(project, input5) {
19685
20013
  }
19686
20014
  file = `${base}.${input5.format ?? "tsx"}`;
19687
20015
  action = "added";
19688
- await mkdir15(path63.dirname(file), { recursive: true });
20016
+ await mkdir15(path64.dirname(file), { recursive: true });
19689
20017
  await write(file, title);
19690
20018
  }
19691
- const same = (match) => path63.resolve(match) === path63.resolve(file);
20019
+ const same = (match) => path64.resolve(match) === path64.resolve(file);
19692
20020
  const inPortal = file.endsWith(".tsx") ? (await findTsxPages(portal.docs.content)).some((page) => same(page.file)) : (await glob8(portal.docs.content, { absolute: true })).some(same);
19693
20021
  return {
19694
20022
  action,
19695
20023
  slug,
19696
20024
  route: `/${slug}`,
19697
20025
  file: relative(file),
19698
- format: path63.extname(file).slice(1),
20026
+ format: path64.extname(file).slice(1),
19699
20027
  inPortal
19700
20028
  };
19701
20029
  }
@@ -19774,7 +20102,7 @@ async function readPlanCatalog(project) {
19774
20102
  }
19775
20103
  var skuOf = (item) => isPlainObject3(item) && typeof item.sku === "string" ? item.sku.trim().toLowerCase() : void 0;
19776
20104
  async function writePlanCatalog(file, catalog, plans) {
19777
- await mkdir15(path63.dirname(file), { recursive: true });
20105
+ await mkdir15(path64.dirname(file), { recursive: true });
19778
20106
  await writeFile18(
19779
20107
  file,
19780
20108
  `${JSON.stringify({ ...catalog, plans }, null, 2)}
@@ -19797,7 +20125,7 @@ async function upsertPlan(project, plan2) {
19797
20125
  return {
19798
20126
  action: index === -1 ? "added" : "updated",
19799
20127
  sku: plan2.sku,
19800
- file: toPosix2(path63.relative(project.dir, file)),
20128
+ file: toPosix2(path64.relative(project.dir, file)),
19801
20129
  paid,
19802
20130
  needsBilling: paid && billing.provider !== "stripe"
19803
20131
  };
@@ -19887,12 +20215,12 @@ async function removePlan(project, rawSku) {
19887
20215
  if (next.length === catalog.plans.length) {
19888
20216
  const known = catalog.plans.map(skuOf).filter(Boolean).join(", ");
19889
20217
  throw new ConfigEditError(
19890
- `There is no plan "${sku}" in ${toPosix2(path63.relative(project.dir, file))}.`,
20218
+ `There is no plan "${sku}" in ${toPosix2(path64.relative(project.dir, file))}.`,
19891
20219
  known ? `The plans are: ${known}.` : "The plans file has no plans."
19892
20220
  );
19893
20221
  }
19894
20222
  await writePlanCatalog(file, catalog, next);
19895
- return { sku, file: toPosix2(path63.relative(project.dir, file)) };
20223
+ return { sku, file: toPosix2(path64.relative(project.dir, file)) };
19896
20224
  }
19897
20225
 
19898
20226
  // src/cli/make-config/wrap-up.ts
@@ -19915,6 +20243,12 @@ function printPageResult(page) {
19915
20243
  printSuccessToConsole(
19916
20244
  `Page ${page.action}: ${page.file} (route ${page.route})`
19917
20245
  );
20246
+ if (page.header) {
20247
+ printHintToConsole(
20248
+ `The header's top menu links to it (portal.header.links); the docs sidebar does not. Edit the file to change the content. Run \`apitogo deploy --only portal\` to publish it.`
20249
+ );
20250
+ return;
20251
+ }
19918
20252
  if (page.inPortal) {
19919
20253
  printHintToConsole(
19920
20254
  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."
@@ -20173,7 +20507,7 @@ async function applyConfigFlags(project, argv, changes) {
20173
20507
  return { project: next, openApi };
20174
20508
  }
20175
20509
  async function makeConfig(argv) {
20176
- const dir = path64.resolve(process.cwd(), argv.dir);
20510
+ const dir = path65.resolve(process.cwd(), argv.dir);
20177
20511
  const interactive = shouldRunInteractive(argv);
20178
20512
  const json = argv.json === true;
20179
20513
  const changes = [];
@@ -20216,7 +20550,32 @@ async function makeConfig(argv) {
20216
20550
  const applied = await applyConfigFlags(project, argv, changes);
20217
20551
  project = applied.project;
20218
20552
  openApi = applied.openApi;
20219
- if (argv.pageSlug !== void 0) {
20553
+ if (argv.pageHeader === true && argv.pageSlug === void 0) {
20554
+ throw new ApitogoError("--page-header needs --page-slug.", {
20555
+ developerHint: "Name the page, for example --page-slug partners --page-title Partners --page-header."
20556
+ });
20557
+ }
20558
+ if (argv.pageSlug !== void 0 && argv.pageHeader === true) {
20559
+ if (argv.pageOrder !== void 0) {
20560
+ throw new ApitogoError(
20561
+ "--page-order sets a place in the docs sidebar, which a header page is not in.",
20562
+ {
20563
+ developerHint: "Header links show in the order of portal.header.links in apitogo.config.json."
20564
+ }
20565
+ );
20566
+ }
20567
+ const written = await writeHeaderPage(project, {
20568
+ slug: argv.pageSlug,
20569
+ title: argv.pageTitle,
20570
+ content: argv.pageContent,
20571
+ update: argv.pageUpdate === true,
20572
+ format: argv.pageFormat
20573
+ });
20574
+ project = written.project;
20575
+ await saveConfigProject(project);
20576
+ page = written.page;
20577
+ changes.push(page.action === "added" ? "page_add" : "page_update");
20578
+ } else if (argv.pageSlug !== void 0) {
20220
20579
  page = await writeDocsPage(project, {
20221
20580
  slug: argv.pageSlug,
20222
20581
  title: argv.pageTitle,
@@ -20365,6 +20724,10 @@ function addConfigOptions(yargs2) {
20365
20724
  type: "boolean",
20366
20725
  default: false,
20367
20726
  describe: "Update an existing page instead of adding one"
20727
+ }).option("page-header", {
20728
+ type: "boolean",
20729
+ default: false,
20730
+ describe: "Make --page-slug a page of its own, linked from the header's top menu instead of the docs sidebar (<portal>/header-pages/<slug>.tsx; --page-format mdx for Markdown)"
20368
20731
  }).option("plan-sku", {
20369
20732
  type: "string",
20370
20733
  describe: "Add or change the plan with this SKU in the plans file; the --plan-* options set its fields"
@@ -20402,7 +20765,10 @@ function addConfigOptions(yargs2) {
20402
20765
  ).example(
20403
20766
  "$0 config --plan-sku enterprise --plan-price custom",
20404
20767
  "A contact-us plan"
20405
- ).example("$0 config --remove-plan enterprise", "Take a plan out").option("plan", {
20768
+ ).example("$0 config --remove-plan enterprise", "Take a plan out").example(
20769
+ "$0 config --page-slug partners --page-title Partners --page-header",
20770
+ "A page at /partners, linked from the header's top menu"
20771
+ ).option("plan", {
20406
20772
  type: "string",
20407
20773
  describe: "JSON plan added to the plans file (plans.file, default apitogo/plans.json), or replacing the one with the same sku"
20408
20774
  }).option("patch", {
@@ -20424,27 +20790,27 @@ var config_default = {
20424
20790
  init_apitogo_config_core();
20425
20791
  import { execFile } from "node:child_process";
20426
20792
  import { readFile as readFile24 } from "node:fs/promises";
20427
- import path66 from "node:path";
20793
+ import path67 from "node:path";
20428
20794
  import { promisify } from "node:util";
20429
20795
 
20430
20796
  // src/cli/create/dry-run.ts
20431
20797
  import { mkdir as mkdir16, mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
20432
20798
  import { tmpdir } from "node:os";
20433
- import path65 from "node:path";
20799
+ import path66 from "node:path";
20434
20800
  async function listFiles(root, dir = root) {
20435
20801
  const entries = await readdir4(dir, { withFileTypes: true });
20436
20802
  const nested = await Promise.all(
20437
20803
  entries.map(async (entry) => {
20438
- const full = path65.join(dir, entry.name);
20804
+ const full = path66.join(dir, entry.name);
20439
20805
  if (entry.isDirectory()) return listFiles(root, full);
20440
- return [path65.relative(root, full).split(path65.sep).join("/")];
20806
+ return [path66.relative(root, full).split(path66.sep).join("/")];
20441
20807
  })
20442
20808
  );
20443
20809
  return nested.flat();
20444
20810
  }
20445
20811
  async function previewCreate(input5) {
20446
- const scratch = await mkdtemp(path65.join(tmpdir(), "apitogo-create-"));
20447
- const dir = path65.join(scratch, "project");
20812
+ const scratch = await mkdtemp(path66.join(tmpdir(), "apitogo-create-"));
20813
+ const dir = path66.join(scratch, "project");
20448
20814
  try {
20449
20815
  await mkdir16(dir);
20450
20816
  await applyInitPlan(await planInit({ dir, name: input5.name }));
@@ -20477,9 +20843,9 @@ async function previewCreate(input5) {
20477
20843
  var execFileAsync = promisify(execFile);
20478
20844
  function nextStepHint(dir) {
20479
20845
  const linked = loadConfigV2(dir).resolved.project.id;
20480
- const cd = path66.resolve(dir) === path66.resolve(process.cwd());
20846
+ const cd = path67.resolve(dir) === path67.resolve(process.cwd());
20481
20847
  const steps = linked ? "apitogo deploy" : "apitogo project create, then apitogo deploy";
20482
- return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path66.relative(process.cwd(), dir))}, then ${steps}`;
20848
+ return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path67.relative(process.cwd(), dir))}, then ${steps}`;
20483
20849
  }
20484
20850
  function projectFolderName(name) {
20485
20851
  return name.trim().replace(/[<>:"/\\|?*\s]+/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "apitogo-project";
@@ -20505,13 +20871,13 @@ async function gitHasUncommittedChanges(dir) {
20505
20871
  async function defaultName(dir) {
20506
20872
  try {
20507
20873
  const parsed = JSON.parse(
20508
- await readFile24(path66.join(dir, "package.json"), "utf8")
20874
+ await readFile24(path67.join(dir, "package.json"), "utf8")
20509
20875
  );
20510
20876
  if (typeof parsed.name === "string" && parsed.name.trim())
20511
20877
  return parsed.name.replace(/^@[^/]+\//, "");
20512
20878
  } catch {
20513
20879
  }
20514
- return path66.basename(dir);
20880
+ return path67.basename(dir);
20515
20881
  }
20516
20882
  function parseServicesFlag(value) {
20517
20883
  const names = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
@@ -20537,7 +20903,7 @@ async function createProject(argv, deps = {}) {
20537
20903
  if (!json) printDiagnosticsToConsole(line2);
20538
20904
  };
20539
20905
  try {
20540
- const base = path66.resolve(process.cwd(), argv.dir ?? ".");
20906
+ const base = path67.resolve(process.cwd(), argv.dir ?? ".");
20541
20907
  let dir = base;
20542
20908
  let requested = parseServicesFlag(argv.services);
20543
20909
  let template = argv.template;
@@ -20560,8 +20926,8 @@ async function createProject(argv, deps = {}) {
20560
20926
  default: await defaultName(base)
20561
20927
  }) : await defaultName(base));
20562
20928
  const folder = projectFolderName(name);
20563
- if (argv.dir === void 0 && folder !== path66.basename(base))
20564
- dir = path66.join(base, folder);
20929
+ if (argv.dir === void 0 && folder !== path67.basename(base))
20930
+ dir = path67.join(base, folder);
20565
20931
  }
20566
20932
  if (await (deps.hasUncommittedChanges ?? gitHasUncommittedChanges)(dir))
20567
20933
  say(
@@ -20574,7 +20940,7 @@ async function createProject(argv, deps = {}) {
20574
20940
  if (argv.dryRun) {
20575
20941
  const services = requested.length > 0 ? requested : templateServices ? STARTER_SERVICES : [];
20576
20942
  const preview2 = services.length > 0 ? await previewCreate({ name, template, services }) : void 0;
20577
- const folder = path66.relative(process.cwd(), dir) || ".";
20943
+ const folder = path67.relative(process.cwd(), dir) || ".";
20578
20944
  if (json)
20579
20945
  console.log(
20580
20946
  JSON.stringify({
@@ -20607,7 +20973,7 @@ async function createProject(argv, deps = {}) {
20607
20973
  }
20608
20974
  await applyInitPlan(plan2);
20609
20975
  say(
20610
- dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path66.relative(base, dir)}${path66.sep}${APITOGO_CONFIG_FILENAME}.`
20976
+ dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path67.relative(base, dir)}${path67.sep}${APITOGO_CONFIG_FILENAME}.`
20611
20977
  );
20612
20978
  } else {
20613
20979
  say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
@@ -20749,10 +21115,10 @@ var create_default = {
20749
21115
  };
20750
21116
 
20751
21117
  // src/cli/cmds/deploy.ts
20752
- import path85 from "node:path";
21118
+ import path86 from "node:path";
20753
21119
 
20754
21120
  // src/cli/apply/handler.ts
20755
- import path78 from "node:path";
21121
+ import path79 from "node:path";
20756
21122
 
20757
21123
  // src/cli/platform/helpers/gateway-plan-validation.ts
20758
21124
  init_gateway_limits();
@@ -20971,11 +21337,11 @@ init_gateway_limits();
20971
21337
  // src/cli/platform/helpers/deploy-artifact-hash.ts
20972
21338
  import { createHash as createHash3 } from "node:crypto";
20973
21339
  import { createReadStream, promises as fs6 } from "node:fs";
20974
- import path68 from "node:path";
21340
+ import path69 from "node:path";
20975
21341
 
20976
21342
  // src/cli/platform/helpers/package-files.ts
20977
21343
  import { promises as fs5 } from "node:fs";
20978
- import path67 from "node:path";
21344
+ import path68 from "node:path";
20979
21345
  import ignore from "ignore";
20980
21346
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
20981
21347
  "node_modules",
@@ -20989,7 +21355,7 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20989
21355
  const ig = ignore();
20990
21356
  ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
20991
21357
  if (extraExcludes.length > 0) ig.add([...extraExcludes]);
20992
- const gitignorePath = path67.join(rootDir, ".gitignore");
21358
+ const gitignorePath = path68.join(rootDir, ".gitignore");
20993
21359
  try {
20994
21360
  const content = await fs5.readFile(gitignorePath, "utf8");
20995
21361
  ig.add(content);
@@ -20998,11 +21364,11 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20998
21364
  return ig;
20999
21365
  }
21000
21366
  async function collectFiles(rootDir, relativeDir, ig, files) {
21001
- const absDir = path67.join(rootDir, relativeDir);
21367
+ const absDir = path68.join(rootDir, relativeDir);
21002
21368
  const entries = await fs5.readdir(absDir, { withFileTypes: true });
21003
21369
  for (const entry of entries) {
21004
- const rel2 = relativeDir ? path67.join(relativeDir, entry.name) : entry.name;
21005
- const relPosix = rel2.split(path67.sep).join("/");
21370
+ const rel2 = relativeDir ? path68.join(relativeDir, entry.name) : entry.name;
21371
+ const relPosix = rel2.split(path68.sep).join("/");
21006
21372
  if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
21007
21373
  continue;
21008
21374
  }
@@ -21017,7 +21383,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
21017
21383
  }
21018
21384
  }
21019
21385
  async function collectPackageableFiles(sourceDirectory, extraExcludes = []) {
21020
- const resolved = path67.resolve(sourceDirectory);
21386
+ const resolved = path68.resolve(sourceDirectory);
21021
21387
  const stat7 = await fs5.stat(resolved).catch(() => null);
21022
21388
  if (!stat7?.isDirectory()) {
21023
21389
  throw new Error(`sourceDirectory is not a directory: ${resolved}`);
@@ -21043,7 +21409,7 @@ async function sha256File(filePath) {
21043
21409
  return hash2.digest("hex");
21044
21410
  }
21045
21411
  async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
21046
- const resolved = path68.resolve(rootDirectory);
21412
+ const resolved = path69.resolve(rootDirectory);
21047
21413
  const files = (await collectPackageableFiles(resolved, extraExcludes)).sort(
21048
21414
  (a, b) => a.localeCompare(b)
21049
21415
  );
@@ -21051,13 +21417,13 @@ async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
21051
21417
  for (const rel2 of files) {
21052
21418
  hash2.update(rel2);
21053
21419
  hash2.update("\0");
21054
- hash2.update(await fs6.readFile(path68.join(resolved, rel2)));
21420
+ hash2.update(await fs6.readFile(path69.join(resolved, rel2)));
21055
21421
  hash2.update("\0");
21056
21422
  }
21057
21423
  return hash2.digest("hex");
21058
21424
  }
21059
21425
  async function hashDevPortalSiteSources(siteDirectory, options) {
21060
- const resolved = path68.resolve(siteDirectory);
21426
+ const resolved = path69.resolve(siteDirectory);
21061
21427
  const hash2 = createHash3("sha256");
21062
21428
  const candidates = [
21063
21429
  "apitogo.config.json",
@@ -21067,7 +21433,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
21067
21433
  "zudoku.config.tsx"
21068
21434
  ];
21069
21435
  for (const name of candidates) {
21070
- const full = path68.join(resolved, name);
21436
+ const full = path69.join(resolved, name);
21071
21437
  try {
21072
21438
  hash2.update(name);
21073
21439
  hash2.update("\0");
@@ -21077,7 +21443,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
21077
21443
  }
21078
21444
  }
21079
21445
  for (const dir of ["pages", "landing", "src"]) {
21080
- const full = path68.join(resolved, dir);
21446
+ const full = path69.join(resolved, dir);
21081
21447
  try {
21082
21448
  const st = await fs6.stat(full);
21083
21449
  if (!st.isDirectory()) continue;
@@ -21306,8 +21672,8 @@ function normalizeActionKey(actionKey2) {
21306
21672
  const trimmed = actionKey2.trim();
21307
21673
  if (trimmed.includes(":")) {
21308
21674
  const [method2, ...pathParts] = trimmed.split(":");
21309
- const path100 = pathParts.join(":").trim();
21310
- return `${method2?.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21675
+ const path101 = pathParts.join(":").trim();
21676
+ return `${method2?.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
21311
21677
  }
21312
21678
  const spaceIndex = trimmed.indexOf(" ");
21313
21679
  if (spaceIndex <= 0) {
@@ -21316,8 +21682,8 @@ function normalizeActionKey(actionKey2) {
21316
21682
  );
21317
21683
  }
21318
21684
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21319
- const path99 = trimmed.slice(spaceIndex + 1).trim();
21320
- return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21685
+ const path100 = trimmed.slice(spaceIndex + 1).trim();
21686
+ return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21321
21687
  }
21322
21688
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
21323
21689
  const applied = [];
@@ -21796,7 +22162,7 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
21796
22162
 
21797
22163
  // src/cli/platform/helpers/openapi-gateway-servers.ts
21798
22164
  import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile19 } from "node:fs/promises";
21799
- import path69 from "node:path";
22165
+ import path70 from "node:path";
21800
22166
  import YAML2 from "yaml";
21801
22167
  var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
21802
22168
  var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
@@ -21828,7 +22194,7 @@ async function resolveProjectGatewayUrl(client, projectId) {
21828
22194
  }
21829
22195
  async function readOpenApiDocument(filePath) {
21830
22196
  const content = await readFile25(filePath, "utf8");
21831
- const ext = path69.extname(filePath).toLowerCase();
22197
+ const ext = path70.extname(filePath).toLowerCase();
21832
22198
  const parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
21833
22199
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21834
22200
  throw new Error(`OpenAPI document must be an object: ${filePath}`);
@@ -21836,13 +22202,13 @@ async function readOpenApiDocument(filePath) {
21836
22202
  return parsed;
21837
22203
  }
21838
22204
  async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
21839
- const site = path69.resolve(siteDirectory);
21840
- const source = path69.resolve(sourceOpenApiPath);
22205
+ const site = path70.resolve(siteDirectory);
22206
+ const source = path70.resolve(sourceOpenApiPath);
21841
22207
  const relativeInput = PUBLISH_OPENAPI_RELATIVE;
21842
- const outputPath = path69.join(site, ...relativeInput.split("/"));
22208
+ const outputPath = path70.join(site, ...relativeInput.split("/"));
21843
22209
  const doc = await readOpenApiDocument(source);
21844
22210
  const rewritten = withGatewayServers(doc, gatewayUrl);
21845
- await mkdir17(path69.dirname(outputPath), { recursive: true });
22211
+ await mkdir17(path70.dirname(outputPath), { recursive: true });
21846
22212
  await writeFile19(
21847
22213
  outputPath,
21848
22214
  `${JSON.stringify(rewritten, null, 2)}
@@ -21852,8 +22218,8 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
21852
22218
  return { outputPath, relativeInput };
21853
22219
  }
21854
22220
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21855
- const configPath = path69.join(
21856
- path69.resolve(siteDirectory),
22221
+ const configPath = path70.join(
22222
+ path70.resolve(siteDirectory),
21857
22223
  APITOGO_CONFIG_FILENAME2
21858
22224
  );
21859
22225
  const raw = await readFile25(configPath, "utf8");
@@ -21895,7 +22261,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21895
22261
  };
21896
22262
  }
21897
22263
  async function prepareOpenApiServersForGatewayBuild(options) {
21898
- const siteDirectory = path69.resolve(options.siteDirectory);
22264
+ const siteDirectory = path70.resolve(options.siteDirectory);
21899
22265
  let openApiPath = resolveBackendOpenApiPath(
21900
22266
  options.manifest?.backend?.openApiPath,
21901
22267
  {
@@ -21909,7 +22275,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
21909
22275
  if (!openApiPath) {
21910
22276
  try {
21911
22277
  const raw = await readFile25(
21912
- path69.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
22278
+ path70.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21913
22279
  "utf8"
21914
22280
  );
21915
22281
  const config2 = JSON.parse(raw);
@@ -21938,8 +22304,8 @@ function normalizeActionKeyFormat(actionKey2) {
21938
22304
  const trimmed = actionKey2.trim();
21939
22305
  if (trimmed.includes(":")) {
21940
22306
  const [method2, ...pathParts] = trimmed.split(":");
21941
- const path100 = pathParts.join(":").trim();
21942
- return `${method2.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
22307
+ const path101 = pathParts.join(":").trim();
22308
+ return `${method2.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
21943
22309
  }
21944
22310
  const spaceIndex = trimmed.indexOf(" ");
21945
22311
  if (spaceIndex <= 0) {
@@ -21948,8 +22314,8 @@ function normalizeActionKeyFormat(actionKey2) {
21948
22314
  );
21949
22315
  }
21950
22316
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21951
- const path99 = trimmed.slice(spaceIndex + 1).trim();
21952
- return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
22317
+ const path100 = trimmed.slice(spaceIndex + 1).trim();
22318
+ return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21953
22319
  }
21954
22320
  function normalizePublicRoute(route) {
21955
22321
  const trimmed = route.trim();
@@ -21983,10 +22349,10 @@ function collectManifestPublicActionKeys(gateway) {
21983
22349
 
21984
22350
  // src/cli/platform/helpers/publish-ledger.ts
21985
22351
  import { mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
21986
- import path70 from "node:path";
22352
+ import path71 from "node:path";
21987
22353
  var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
21988
22354
  function publishLedgerPath(siteDirectory) {
21989
- return path70.join(path70.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
22355
+ return path71.join(path71.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21990
22356
  }
21991
22357
  async function readPublishLedger(siteDirectory) {
21992
22358
  const filePath = publishLedgerPath(siteDirectory);
@@ -22003,7 +22369,7 @@ async function readPublishLedger(siteDirectory) {
22003
22369
  }
22004
22370
  async function writePublishLedger(siteDirectory, ledger) {
22005
22371
  const filePath = publishLedgerPath(siteDirectory);
22006
- await mkdir18(path70.dirname(filePath), { recursive: true });
22372
+ await mkdir18(path71.dirname(filePath), { recursive: true });
22007
22373
  const next = {
22008
22374
  ...ledger,
22009
22375
  version: 1,
@@ -22524,7 +22890,7 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
22524
22890
  // src/cli/platform/helpers/backend-packaging.ts
22525
22891
  import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10 } from "node:fs/promises";
22526
22892
  import { tmpdir as tmpdir2 } from "node:os";
22527
- import path71 from "node:path";
22893
+ import path72 from "node:path";
22528
22894
  async function directoryHasRuntimeConfig(dir) {
22529
22895
  if (!await pathExists(dir)) {
22530
22896
  return false;
@@ -22566,20 +22932,20 @@ function runDotnetPublish(projectDir, outputDir) {
22566
22932
  async function findCsproj(projectDir) {
22567
22933
  const entries = await readdir5(projectDir);
22568
22934
  const csproj = entries.find((name) => name.endsWith(".csproj"));
22569
- return csproj ? path71.join(projectDir, csproj) : null;
22935
+ return csproj ? path72.join(projectDir, csproj) : null;
22570
22936
  }
22571
22937
  async function prepareBackendPackagingDirectory(args) {
22572
- const sourceDirectory = path71.resolve(args.sourceDirectory);
22938
+ const sourceDirectory = path72.resolve(args.sourceDirectory);
22573
22939
  if (args.publishDirectory?.trim()) {
22574
22940
  return {
22575
- directory: path71.resolve(args.publishDirectory),
22941
+ directory: path72.resolve(args.publishDirectory),
22576
22942
  packagingMode: "published"
22577
22943
  };
22578
22944
  }
22579
22945
  if (await directoryHasRuntimeConfig(sourceDirectory)) {
22580
22946
  return { directory: sourceDirectory, packagingMode: "published" };
22581
22947
  }
22582
- const publishSubdir = path71.join(sourceDirectory, "publish");
22948
+ const publishSubdir = path72.join(sourceDirectory, "publish");
22583
22949
  if (await directoryHasRuntimeConfig(publishSubdir)) {
22584
22950
  return { directory: publishSubdir, packagingMode: "published" };
22585
22951
  }
@@ -22591,9 +22957,9 @@ async function prepareBackendPackagingDirectory(args) {
22591
22957
  return { directory: sourceDirectory, packagingMode: "source" };
22592
22958
  }
22593
22959
  const tempRoot = await mkdtemp2(
22594
- path71.join(tmpdir2(), "apitogo-dotnet-publish-")
22960
+ path72.join(tmpdir2(), "apitogo-dotnet-publish-")
22595
22961
  );
22596
- const outputDir = path71.join(tempRoot, "publish");
22962
+ const outputDir = path72.join(tempRoot, "publish");
22597
22963
  const result = await runDotnetPublish(sourceDirectory, outputDir);
22598
22964
  if (result.exitCode !== 0) {
22599
22965
  await rm10(tempRoot, { recursive: true, force: true });
@@ -22638,13 +23004,13 @@ function formatJobStatusResponse(options) {
22638
23004
 
22639
23005
  // src/cli/platform/helpers/openapi.ts
22640
23006
  import { promises as fs7 } from "node:fs";
22641
- import path72 from "node:path";
23007
+ import path73 from "node:path";
22642
23008
  import SwaggerParser from "@apidevtools/swagger-parser";
22643
23009
  import YAML3 from "yaml";
22644
23010
  async function validateOpenApiFile(filePath) {
22645
- const resolved = path72.resolve(filePath);
23011
+ const resolved = path73.resolve(filePath);
22646
23012
  const content = await fs7.readFile(resolved, "utf8");
22647
- const ext = path72.extname(resolved).toLowerCase();
23013
+ const ext = path73.extname(resolved).toLowerCase();
22648
23014
  let parsed;
22649
23015
  try {
22650
23016
  parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
@@ -22688,12 +23054,12 @@ async function validateOpenApiFile(filePath) {
22688
23054
  // src/cli/platform/helpers/zip.ts
22689
23055
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs8 } from "node:fs";
22690
23056
  import { tmpdir as tmpdir3 } from "node:os";
22691
- import path73 from "node:path";
23057
+ import path74 from "node:path";
22692
23058
  import { ZipArchive } from "archiver";
22693
23059
  async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22694
- const resolved = path73.resolve(sourceDirectory);
23060
+ const resolved = path74.resolve(sourceDirectory);
22695
23061
  const files = await collectPackageableFiles(resolved, extraExcludes);
22696
- const zipPath = path73.join(
23062
+ const zipPath = path74.join(
22697
23063
  tmpdir3(),
22698
23064
  `apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
22699
23065
  );
@@ -22705,8 +23071,8 @@ async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22705
23071
  output4.on("error", reject);
22706
23072
  archive.pipe(output4);
22707
23073
  for (const rel2 of files) {
22708
- archive.append(createReadStream2(path73.join(resolved, rel2)), {
22709
- name: rel2.split(path73.sep).join("/")
23074
+ archive.append(createReadStream2(path74.join(resolved, rel2)), {
23075
+ name: rel2.split(path74.sep).join("/")
22710
23076
  });
22711
23077
  }
22712
23078
  void archive.finalize();
@@ -24276,11 +24642,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
24276
24642
  }
24277
24643
 
24278
24644
  // src/cli/platform/tools/dev-portal-site-build.ts
24279
- import path77 from "node:path";
24645
+ import path78 from "node:path";
24280
24646
 
24281
24647
  // src/cli/platform/helpers/dev-portal-site-api-url.ts
24282
24648
  import { readdir as readdir6, readFile as readFile27, writeFile as writeFile21 } from "node:fs/promises";
24283
- import path74 from "node:path";
24649
+ import path75 from "node:path";
24284
24650
  var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
24285
24651
  var PLACEHOLDER_HOST_MARKERS = [
24286
24652
  "dp-example.azurecontainerapps.io",
@@ -24301,10 +24667,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
24301
24667
  return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
24302
24668
  }
24303
24669
  async function readSiteApiBaseUrl(sourceDirectory) {
24304
- const resolved = path74.resolve(sourceDirectory);
24670
+ const resolved = path75.resolve(sourceDirectory);
24305
24671
  for (const envFile of [".env", ".env.production"]) {
24306
24672
  try {
24307
- const content = await readFile27(path74.join(resolved, envFile), "utf8");
24673
+ const content = await readFile27(path75.join(resolved, envFile), "utf8");
24308
24674
  const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
24309
24675
  if (lines.length === 0) {
24310
24676
  continue;
@@ -24329,11 +24695,11 @@ async function readSiteApiBaseUrl(sourceDirectory) {
24329
24695
  return null;
24330
24696
  }
24331
24697
  async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
24332
- const resolved = path74.resolve(sourceDirectory);
24698
+ const resolved = path75.resolve(sourceDirectory);
24333
24699
  const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
24334
24700
  let patchedExisting = false;
24335
24701
  for (const envFile of [".env.production", ".env"]) {
24336
- const envPath = path74.join(resolved, envFile);
24702
+ const envPath = path75.join(resolved, envFile);
24337
24703
  try {
24338
24704
  const content = await readFile27(envPath, "utf8");
24339
24705
  const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
@@ -24347,7 +24713,7 @@ ${line2}
24347
24713
  }
24348
24714
  }
24349
24715
  if (!patchedExisting) {
24350
- await writeFile21(path74.join(resolved, ".env"), `${line2}
24716
+ await writeFile21(path75.join(resolved, ".env"), `${line2}
24351
24717
  `);
24352
24718
  }
24353
24719
  }
@@ -24355,7 +24721,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
24355
24721
  return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
24356
24722
  }
24357
24723
  async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
24358
- const sourceDirectory = path74.resolve(args.sourceDirectory);
24724
+ const sourceDirectory = path75.resolve(args.sourceDirectory);
24359
24725
  const explicit = args.apiBaseUrl?.trim();
24360
24726
  if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
24361
24727
  await patchSiteApiBaseUrl(sourceDirectory, explicit);
@@ -24391,7 +24757,7 @@ async function* walkFiles(root) {
24391
24757
  return;
24392
24758
  }
24393
24759
  for (const entry of entries) {
24394
- const full = path74.join(root, entry.name);
24760
+ const full = path75.join(root, entry.name);
24395
24761
  if (entry.isDirectory()) {
24396
24762
  if (entry.name === "node_modules" || entry.name === ".git") {
24397
24763
  continue;
@@ -24403,8 +24769,8 @@ async function* walkFiles(root) {
24403
24769
  }
24404
24770
  }
24405
24771
  async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
24406
- const dist = path74.resolve(distDirectory);
24407
- const siteRoot = path74.dirname(dist);
24772
+ const dist = path75.resolve(distDirectory);
24773
+ const siteRoot = path75.dirname(dist);
24408
24774
  const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
24409
24775
  if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
24410
24776
  throw new Error(
@@ -24440,7 +24806,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
24440
24806
 
24441
24807
  // src/cli/platform/tools/dev-portal.ts
24442
24808
  import { promises as fs10 } from "node:fs";
24443
- import path76 from "node:path";
24809
+ import path77 from "node:path";
24444
24810
 
24445
24811
  // src/cli/platform/helpers/dev-portal-backend-job.ts
24446
24812
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
@@ -24550,7 +24916,7 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
24550
24916
  // src/cli/platform/helpers/tarball.ts
24551
24917
  import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs9 } from "node:fs";
24552
24918
  import { tmpdir as tmpdir4 } from "node:os";
24553
- import path75 from "node:path";
24919
+ import path76 from "node:path";
24554
24920
  import { TarArchive } from "archiver";
24555
24921
  var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
24556
24922
  function normalizeArchiveRoot(archiveRoot) {
@@ -24561,10 +24927,10 @@ function normalizeArchiveRoot(archiveRoot) {
24561
24927
  return `${trimmed}/`;
24562
24928
  }
24563
24929
  async function packageDirectoryAsTarGz(sourceDirectory, options) {
24564
- const resolved = path75.resolve(sourceDirectory);
24930
+ const resolved = path76.resolve(sourceDirectory);
24565
24931
  const files = await collectPackageableFiles(resolved);
24566
24932
  const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
24567
- const archivePath = path75.join(
24933
+ const archivePath = path76.join(
24568
24934
  tmpdir4(),
24569
24935
  `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
24570
24936
  );
@@ -24576,8 +24942,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
24576
24942
  output4.on("error", reject);
24577
24943
  archive.pipe(output4);
24578
24944
  for (const rel2 of files) {
24579
- const entryName = entryPrefix + rel2.split(path75.sep).join("/");
24580
- archive.append(createReadStream3(path75.join(resolved, rel2)), {
24945
+ const entryName = entryPrefix + rel2.split(path76.sep).join("/");
24946
+ archive.append(createReadStream3(path76.join(resolved, rel2)), {
24581
24947
  name: entryName
24582
24948
  });
24583
24949
  }
@@ -24819,7 +25185,7 @@ function validateDeployArchiveInput(args) {
24819
25185
  async function resolveDevPortalArchive(args) {
24820
25186
  validateDeployArchiveInput(args);
24821
25187
  if (args.archivePath?.trim()) {
24822
- const archivePath = path76.resolve(args.archivePath.trim());
25188
+ const archivePath = path77.resolve(args.archivePath.trim());
24823
25189
  const stat7 = await fs10.stat(archivePath).catch(() => null);
24824
25190
  if (!stat7?.isFile()) {
24825
25191
  throw new Error(`archivePath is not a file: ${archivePath}`);
@@ -24884,7 +25250,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
24884
25250
  // src/cli/platform/tools/dev-portal-site-build.ts
24885
25251
  async function handleBuildDevPortalSite(client, config2, args, progress) {
24886
25252
  const buildProgress = progress?.child("Build dev portal site");
24887
- const sourceDirectory = path77.resolve(args.sourceDirectory);
25253
+ const sourceDirectory = path78.resolve(args.sourceDirectory);
24888
25254
  await buildProgress?.onStep("Ensuring live portal API URL");
24889
25255
  const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
24890
25256
  client,
@@ -26390,7 +26756,7 @@ function resultLooksFailed(payload) {
26390
26756
  return parsed.status === "failed" || parsed.status === "blocked";
26391
26757
  }
26392
26758
  async function apply(argv) {
26393
- const sourceDirectory = path78.resolve(process.cwd(), argv.dir);
26759
+ const sourceDirectory = path79.resolve(process.cwd(), argv.dir);
26394
26760
  let config2;
26395
26761
  try {
26396
26762
  config2 = loadConfig();
@@ -26460,7 +26826,7 @@ async function apply(argv) {
26460
26826
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
26461
26827
  const resolution = await resolveProjectNotFoundInteractively(
26462
26828
  parsed,
26463
- path78.basename(sourceDirectory)
26829
+ path79.basename(sourceDirectory)
26464
26830
  );
26465
26831
  if (resolution) {
26466
26832
  effectiveProjectId = void 0;
@@ -26480,7 +26846,7 @@ async function apply(argv) {
26480
26846
  while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
26481
26847
  const resolution = await resolveProjectNameExistsInteractively(
26482
26848
  parsed,
26483
- effectiveProjectName ?? path78.basename(sourceDirectory)
26849
+ effectiveProjectName ?? path79.basename(sourceDirectory)
26484
26850
  );
26485
26851
  if (!resolution) {
26486
26852
  break;
@@ -26551,11 +26917,11 @@ async function apply(argv) {
26551
26917
  }
26552
26918
 
26553
26919
  // src/cli/deploy/command.ts
26554
- import path84 from "node:path";
26920
+ import path85 from "node:path";
26555
26921
  init_invariant();
26556
26922
 
26557
26923
  // src/cli/project/create-handler.ts
26558
- import path79 from "node:path";
26924
+ import path80 from "node:path";
26559
26925
  import open2 from "open";
26560
26926
  init_invariant();
26561
26927
 
@@ -26869,7 +27235,7 @@ async function createLinkedProject(argv, dir, options = {}) {
26869
27235
  }
26870
27236
  }
26871
27237
  async function createProject2(argv) {
26872
- const dir = path79.resolve(process.cwd(), argv.dir ?? ".");
27238
+ const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
26873
27239
  if (await isConfigV2(dir)) {
26874
27240
  await createLinkedProject(argv, dir);
26875
27241
  return;
@@ -26929,7 +27295,7 @@ async function createProject2(argv) {
26929
27295
  }
26930
27296
 
26931
27297
  // src/cli/secrets/handler.ts
26932
- import path80 from "node:path";
27298
+ import path81 from "node:path";
26933
27299
  init_invariant();
26934
27300
 
26935
27301
  // src/cli/secrets/input.ts
@@ -26994,7 +27360,7 @@ var SecretsError = class extends ApitogoError {
26994
27360
  async function resolveProjectId2(argv, deps) {
26995
27361
  const explicit = argv.projectId?.trim();
26996
27362
  if (explicit) return explicit;
26997
- const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
27363
+ const dir = path81.resolve(process.cwd(), argv.dir ?? ".");
26998
27364
  if (await isConfigV2(dir)) {
26999
27365
  const id = loadConfigV2(dir).resolved.project.id;
27000
27366
  if (id) return id;
@@ -27079,7 +27445,7 @@ var KNOWN_SECRET_PLACES = {
27079
27445
  async function wireKnownSecret(argv, name) {
27080
27446
  const place = KNOWN_SECRET_PLACES[name.toUpperCase()];
27081
27447
  if (!place) return void 0;
27082
- const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
27448
+ const dir = path81.resolve(process.cwd(), argv.dir ?? ".");
27083
27449
  if (!await isConfigV2(dir)) return void 0;
27084
27450
  const project = await openConfigProject(dir);
27085
27451
  const plans = project.resolved.plans;
@@ -27140,7 +27506,7 @@ async function secrets(argv) {
27140
27506
  }
27141
27507
 
27142
27508
  // src/cli/deploy/stripe-key.ts
27143
- import path81 from "node:path";
27509
+ import path82 from "node:path";
27144
27510
  init_invariant();
27145
27511
  var StripeKeyError = class extends ApitogoError {
27146
27512
  constructor(message, developerHint) {
@@ -27158,7 +27524,7 @@ async function ensureStripeKey(argv, deps) {
27158
27524
  if (!wantsPlans(argv.only)) return { status: "not-needed" };
27159
27525
  let resolved;
27160
27526
  try {
27161
- resolved = loadConfigV2(path81.resolve(process.cwd(), argv.dir)).resolved;
27527
+ resolved = loadConfigV2(path82.resolve(process.cwd(), argv.dir)).resolved;
27162
27528
  } catch {
27163
27529
  return { status: "not-needed" };
27164
27530
  }
@@ -28095,7 +28461,7 @@ init_invariant();
28095
28461
 
28096
28462
  // src/cli/portal/report.ts
28097
28463
  import { access as access9 } from "node:fs/promises";
28098
- import path82 from "node:path";
28464
+ import path83 from "node:path";
28099
28465
 
28100
28466
  // src/cli/deploy/identity.ts
28101
28467
  init_invariant();
@@ -28866,7 +29232,7 @@ async function planPortalService(resolved, ledger) {
28866
29232
  const hash2 = await portalBuildHash(resolved, prepared);
28867
29233
  const previous = stepOf(ledger, "portal", "build");
28868
29234
  const built = await exists5(
28869
- path82.join(resolved.state.buildDir, "index.html")
29235
+ path83.join(resolved.state.buildDir, "index.html")
28870
29236
  );
28871
29237
  if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
28872
29238
  return {
@@ -29252,7 +29618,7 @@ async function statusV2(dir, only, options = {}) {
29252
29618
  if (service === "portal") {
29253
29619
  const build2 = stepOf(ledger, "portal", "build");
29254
29620
  const built = await exists5(
29255
- path82.join(resolved.state.buildDir, "index.html")
29621
+ path83.join(resolved.state.buildDir, "index.html")
29256
29622
  );
29257
29623
  const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
29258
29624
  services.push({
@@ -29263,7 +29629,7 @@ async function statusV2(dir, only, options = {}) {
29263
29629
  ...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
29264
29630
  ...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
29265
29631
  ...state === "built" ? [
29266
- `output: ${path82.relative(resolved.configDir, resolved.state.buildDir)}`
29632
+ `output: ${path83.relative(resolved.configDir, resolved.state.buildDir)}`
29267
29633
  ] : [],
29268
29634
  ...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"],
29269
29635
  ...await hostingLine(resolved, manifest, options.hostingRemote)
@@ -29330,7 +29696,7 @@ function formatStatus(output4) {
29330
29696
 
29331
29697
  // src/cli/deploy/static-web-config.ts
29332
29698
  import { access as access10, writeFile as writeFile22 } from "node:fs/promises";
29333
- import path83 from "node:path";
29699
+ import path84 from "node:path";
29334
29700
  var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
29335
29701
  var STATIC_WEB_CONFIG_VERSION = 3;
29336
29702
  var CLIENT_ONLY_ROUTES = [
@@ -29348,7 +29714,7 @@ var DEFAULT_STATIC_WEB_CONFIG = {
29348
29714
  }
29349
29715
  };
29350
29716
  async function ensureStaticWebConfig(buildDir) {
29351
- const file = path83.join(buildDir, STATIC_WEB_CONFIG_FILE);
29717
+ const file = path84.join(buildDir, STATIC_WEB_CONFIG_FILE);
29352
29718
  try {
29353
29719
  await access10(file);
29354
29720
  return false;
@@ -29703,7 +30069,7 @@ function formatDeploy(output4) {
29703
30069
  // src/cli/deploy/command.ts
29704
30070
  async function ensureLinkedProject(argv, deps) {
29705
30071
  if (argv.projectId?.trim() || deps.envProjectId?.trim()) return true;
29706
- const dir = path84.resolve(process.cwd(), argv.dir);
30072
+ const dir = path85.resolve(process.cwd(), argv.dir);
29707
30073
  let linked;
29708
30074
  try {
29709
30075
  linked = Boolean(loadConfigV2(dir).resolved.project.id);
@@ -29754,7 +30120,7 @@ async function deployProject(argv) {
29754
30120
  try {
29755
30121
  const config2 = loadConfig();
29756
30122
  const client = new PlatformApiClient(config2);
29757
- const dir = path84.resolve(process.cwd(), argv.dir);
30123
+ const dir = path85.resolve(process.cwd(), argv.dir);
29758
30124
  const deploy = (only) => deployV2(
29759
30125
  {
29760
30126
  dir,
@@ -29862,7 +30228,7 @@ var deploy_default = {
29862
30228
  }),
29863
30229
  handler: async (argv) => {
29864
30230
  await captureEvent({ argv, event: "apitogo deploy" });
29865
- if (!await isLegacyConfig(path85.resolve(process.cwd(), argv.dir))) {
30231
+ if (!await isLegacyConfig(path86.resolve(process.cwd(), argv.dir))) {
29866
30232
  await deployProject(argv);
29867
30233
  return;
29868
30234
  }
@@ -29872,7 +30238,7 @@ var deploy_default = {
29872
30238
 
29873
30239
  // src/cli/export/handler.ts
29874
30240
  import { mkdir as mkdir19, writeFile as writeFile23 } from "node:fs/promises";
29875
- import path86 from "node:path";
30241
+ import path87 from "node:path";
29876
30242
  init_invariant();
29877
30243
  var ExportError = class extends ApitogoError {
29878
30244
  constructor(message, developerHint) {
@@ -29886,7 +30252,7 @@ async function exportOpenApi(args, deps) {
29886
30252
  `Cannot export "${args.target}".`,
29887
30253
  "Only `apitogo export openapi` exists."
29888
30254
  );
29889
- const dir = path86.resolve(process.cwd(), args.dir ?? ".");
30255
+ const dir = path87.resolve(process.cwd(), args.dir ?? ".");
29890
30256
  if (!await isConfigV2(dir))
29891
30257
  throw new ExportError(
29892
30258
  "This command needs a config version 2 project.",
@@ -29911,8 +30277,8 @@ async function exportOpenApi(args, deps) {
29911
30277
  throw error;
29912
30278
  }
29913
30279
  if (!args.out) return { text: text2 };
29914
- const file = path86.resolve(process.cwd(), args.out);
29915
- await mkdir19(path86.dirname(file), { recursive: true });
30280
+ const file = path87.resolve(process.cwd(), args.out);
30281
+ await mkdir19(path87.dirname(file), { recursive: true });
29916
30282
  await writeFile23(file, text2);
29917
30283
  return { text: text2, file };
29918
30284
  }
@@ -30005,8 +30371,8 @@ import open3 from "open";
30005
30371
  import colors14 from "picocolors";
30006
30372
 
30007
30373
  // src/cli/platform/device-auth.ts
30008
- function url(apiUrl, path99) {
30009
- return `${apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
30374
+ function url(apiUrl, path100) {
30375
+ return `${apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
30010
30376
  }
30011
30377
  async function requestDeviceCode(apiUrl) {
30012
30378
  const response = await fetchWithRetry(
@@ -30188,7 +30554,7 @@ var logout_default = {
30188
30554
  };
30189
30555
 
30190
30556
  // src/cli/portal/commands.ts
30191
- import path87 from "node:path";
30557
+ import path88 from "node:path";
30192
30558
  init_invariant();
30193
30559
  function parseOnly(value) {
30194
30560
  if (value === void 0) return void 0;
@@ -30205,7 +30571,7 @@ function parseOnly(value) {
30205
30571
  async function run2(command2, argv, compute, format) {
30206
30572
  const json = argv.json === true;
30207
30573
  try {
30208
- const dir = path87.resolve(process.cwd(), argv.dir ?? ".");
30574
+ const dir = path88.resolve(process.cwd(), argv.dir ?? ".");
30209
30575
  if (!await isConfigV2(dir)) {
30210
30576
  throw new ServiceEditError(
30211
30577
  `${command2} works with a config version 2 project, and there is none in ${dir}.`,
@@ -30567,14 +30933,14 @@ var remove_default = {
30567
30933
 
30568
30934
  // src/cli/dev/handler.ts
30569
30935
  init_joinUrl();
30570
- import path90 from "node:path";
30936
+ import path91 from "node:path";
30571
30937
 
30572
30938
  // src/vite/dev-server.ts
30573
30939
  init_logger();
30574
30940
  import fs11 from "node:fs/promises";
30575
30941
  import http from "node:http";
30576
30942
  import https from "node:https";
30577
- import path89 from "node:path";
30943
+ import path90 from "node:path";
30578
30944
  import { createHttpTerminator } from "http-terminator";
30579
30945
  import {
30580
30946
  createServer as createViteServer,
@@ -30619,7 +30985,7 @@ function withSsrGraphParts(config2, ssrConfig) {
30619
30985
  // src/vite/pagefind-dev-index.ts
30620
30986
  init_invariant();
30621
30987
  init_joinUrl();
30622
- import path88 from "node:path";
30988
+ import path89 from "node:path";
30623
30989
  import { createIndex as createIndex2 } from "pagefind";
30624
30990
  import { isRunnableDevEnvironment } from "vite";
30625
30991
  async function* buildPagefindDevIndex(vite, config2) {
@@ -30672,7 +31038,7 @@ async function* buildPagefindDevIndex(vite, config2) {
30672
31038
  path: urlPath
30673
31039
  };
30674
31040
  }
30675
- const outputPath = path88.join(vite.config.publicDir, "pagefind");
31041
+ const outputPath = path89.join(vite.config.publicDir, "pagefind");
30676
31042
  await pagefindIndex.writeFiles({ outputPath });
30677
31043
  yield { type: "complete", success: true, indexed };
30678
31044
  }
@@ -30691,9 +31057,9 @@ var DevServer = class {
30691
31057
  this.protocol = "https";
30692
31058
  const { dir } = this.options;
30693
31059
  const [key, cert, ca] = await Promise.all([
30694
- fs11.readFile(path89.resolve(dir, config2.https.key)),
30695
- fs11.readFile(path89.resolve(dir, config2.https.cert)),
30696
- config2.https.ca ? fs11.readFile(path89.resolve(dir, config2.https.ca)) : void 0
31060
+ fs11.readFile(path90.resolve(dir, config2.https.key)),
31061
+ fs11.readFile(path90.resolve(dir, config2.https.cert)),
31062
+ config2.https.ca ? fs11.readFile(path90.resolve(dir, config2.https.ca)) : void 0
30697
31063
  ]);
30698
31064
  return https.createServer({ key, cert, ca });
30699
31065
  }
@@ -30710,7 +31076,7 @@ var DevServer = class {
30710
31076
  );
30711
31077
  const server = await this.createNodeServer(config2);
30712
31078
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
30713
- const publicRoot = path89.resolve(
31079
+ const publicRoot = path90.resolve(
30714
31080
  this.options.dir,
30715
31081
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
30716
31082
  );
@@ -30727,7 +31093,7 @@ var DevServer = class {
30727
31093
  // built-in transform middleware which would treat the path as a static asset.
30728
31094
  name: "apitogo:entry-client",
30729
31095
  configureServer(server2) {
30730
- const entryPath = path89.posix.join(
31096
+ const entryPath = path90.posix.join(
30731
31097
  server2.config.base,
30732
31098
  "/__z/entry.client.tsx"
30733
31099
  );
@@ -30895,7 +31261,7 @@ init_package_json();
30895
31261
  async function dev(argv) {
30896
31262
  const packageJson3 = getZudokuPackageJson();
30897
31263
  process.env.NODE_ENV = "development";
30898
- let dir = path90.resolve(process.cwd(), argv.dir);
31264
+ let dir = path91.resolve(process.cwd(), argv.dir);
30899
31265
  if (await isConfigV2(dir)) {
30900
31266
  const { root, prepared } = await preparePortalWorkspace(dir, "development");
30901
31267
  dir = root;
@@ -31030,12 +31396,12 @@ var secrets_default = {
31030
31396
  };
31031
31397
 
31032
31398
  // src/cli/cmds/status.ts
31033
- import path92 from "node:path";
31399
+ import path93 from "node:path";
31034
31400
 
31035
31401
  // src/cli/status/handler.ts
31036
- import path91 from "node:path";
31402
+ import path92 from "node:path";
31037
31403
  async function status(argv) {
31038
- const sourceDirectory = path91.resolve(process.cwd(), argv.dir);
31404
+ const sourceDirectory = path92.resolve(process.cwd(), argv.dir);
31039
31405
  let config2;
31040
31406
  try {
31041
31407
  config2 = loadConfig();
@@ -31161,7 +31527,7 @@ var status_default = {
31161
31527
  }),
31162
31528
  handler: async (argv) => {
31163
31529
  await captureEvent({ argv, event: "apitogo status" });
31164
- if (await isConfigV2(path92.resolve(process.cwd(), argv.dir))) {
31530
+ if (await isConfigV2(path93.resolve(process.cwd(), argv.dir))) {
31165
31531
  await statusOfProject(argv);
31166
31532
  return;
31167
31533
  }
@@ -31170,11 +31536,11 @@ var status_default = {
31170
31536
  };
31171
31537
 
31172
31538
  // src/cli/cmds/teardown.ts
31173
- import path95 from "node:path";
31539
+ import path96 from "node:path";
31174
31540
 
31175
31541
  // src/cli/teardown/command.ts
31176
31542
  init_invariant();
31177
- import path93 from "node:path";
31543
+ import path94 from "node:path";
31178
31544
  import { createInterface as createInterface7 } from "node:readline/promises";
31179
31545
 
31180
31546
  // src/cli/teardown/v2.ts
@@ -31535,7 +31901,7 @@ async function teardownProject(argv) {
31535
31901
  const progress = createConsoleProgressReporter();
31536
31902
  const result = await teardownV2(
31537
31903
  {
31538
- dir: path93.resolve(process.cwd(), argv.dir),
31904
+ dir: path94.resolve(process.cwd(), argv.dir),
31539
31905
  projectId: argv.projectId,
31540
31906
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
31541
31907
  yes: argv.yes === true,
@@ -31594,7 +31960,7 @@ async function teardownProject(argv) {
31594
31960
  }
31595
31961
 
31596
31962
  // src/cli/teardown/handler.ts
31597
- import path94 from "node:path";
31963
+ import path95 from "node:path";
31598
31964
  function resultLooksFailed3(payload) {
31599
31965
  try {
31600
31966
  const parsed = JSON.parse(payload);
@@ -31610,7 +31976,7 @@ async function teardown(argv) {
31610
31976
  );
31611
31977
  return;
31612
31978
  }
31613
- const sourceDirectory = path94.resolve(process.cwd(), argv.dir);
31979
+ const sourceDirectory = path95.resolve(process.cwd(), argv.dir);
31614
31980
  let config2;
31615
31981
  try {
31616
31982
  config2 = loadConfig();
@@ -31688,7 +32054,7 @@ var teardown_default = {
31688
32054
  }),
31689
32055
  handler: async (argv) => {
31690
32056
  await captureEvent({ argv, event: "apitogo teardown" });
31691
- if (!await isLegacyConfig(path95.resolve(process.cwd(), argv.dir))) {
32057
+ if (!await isLegacyConfig(path96.resolve(process.cwd(), argv.dir))) {
31692
32058
  await teardownProject(argv);
31693
32059
  return;
31694
32060
  }
@@ -31698,7 +32064,7 @@ var teardown_default = {
31698
32064
 
31699
32065
  // src/cli/teardown/reset-command.ts
31700
32066
  init_invariant();
31701
- import path96 from "node:path";
32067
+ import path97 from "node:path";
31702
32068
 
31703
32069
  // src/cli/teardown/reset.ts
31704
32070
  init_invariant();
@@ -31821,7 +32187,7 @@ async function resetProject(argv) {
31821
32187
  const config2 = loadConfig();
31822
32188
  const result = await resetProjectV2(
31823
32189
  {
31824
- dir: path96.resolve(process.cwd(), argv.dir),
32190
+ dir: path97.resolve(process.cwd(), argv.dir),
31825
32191
  projectId: argv.projectId,
31826
32192
  yes: argv.yes === true
31827
32193
  },
@@ -31885,7 +32251,7 @@ var test_reset_default = {
31885
32251
  };
31886
32252
 
31887
32253
  // src/cli/test-sign-in/handler.ts
31888
- import path97 from "node:path";
32254
+ import path98 from "node:path";
31889
32255
  init_invariant();
31890
32256
  var TestSignInError = class extends ApitogoError {
31891
32257
  constructor(message, developerHint) {
@@ -31896,7 +32262,7 @@ var TestSignInError = class extends ApitogoError {
31896
32262
  async function resolveProjectId3(argv) {
31897
32263
  const explicit = argv.projectId?.trim();
31898
32264
  if (explicit) return explicit;
31899
- const dir = path97.resolve(process.cwd(), argv.dir ?? ".");
32265
+ const dir = path98.resolve(process.cwd(), argv.dir ?? ".");
31900
32266
  if (await isConfigV2(dir)) {
31901
32267
  const id = loadConfigV2(dir).resolved.project.id;
31902
32268
  if (id) return id;
@@ -32036,36 +32402,36 @@ function createApitogoCli(argv) {
32036
32402
  // src/cli/local-handoff.ts
32037
32403
  import { spawn as spawn2 } from "node:child_process";
32038
32404
  import { existsSync as existsSync9, realpathSync as realpathSync2 } from "node:fs";
32039
- import path98 from "node:path";
32405
+ import path99 from "node:path";
32040
32406
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
32041
32407
  var PACKAGE_NAME = "@lukoweb/apitogo";
32042
32408
  var resolveProjectDir = (argv, cwd) => {
32043
32409
  for (let i = 0; i < argv.length; i++) {
32044
32410
  const arg = argv[i];
32045
32411
  const next = argv[i + 1];
32046
- if (arg === "--dir" && next) return path98.resolve(cwd, next);
32412
+ if (arg === "--dir" && next) return path99.resolve(cwd, next);
32047
32413
  if (arg?.startsWith("--dir=")) {
32048
- return path98.resolve(cwd, arg.slice("--dir=".length));
32414
+ return path99.resolve(cwd, arg.slice("--dir=".length));
32049
32415
  }
32050
32416
  }
32051
32417
  return cwd;
32052
32418
  };
32053
32419
  var findLocalCli = (projectDir, runningRoot) => {
32054
- let dir = path98.resolve(projectDir);
32420
+ let dir = path99.resolve(projectDir);
32055
32421
  let localPackageRoot;
32056
32422
  while (!localPackageRoot) {
32057
- const candidate = path98.join(dir, "node_modules", PACKAGE_NAME);
32058
- if (existsSync9(path98.join(candidate, "package.json"))) {
32423
+ const candidate = path99.join(dir, "node_modules", PACKAGE_NAME);
32424
+ if (existsSync9(path99.join(candidate, "package.json"))) {
32059
32425
  localPackageRoot = candidate;
32060
- } else if (dir === path98.dirname(dir)) {
32426
+ } else if (dir === path99.dirname(dir)) {
32061
32427
  return void 0;
32062
32428
  } else {
32063
- dir = path98.dirname(dir);
32429
+ dir = path99.dirname(dir);
32064
32430
  }
32065
32431
  }
32066
32432
  const localRoot = realpathSync2(localPackageRoot);
32067
32433
  if (localRoot === realpathSync2(runningRoot)) return void 0;
32068
- return path98.join(localRoot, "cli.js");
32434
+ return path99.join(localRoot, "cli.js");
32069
32435
  };
32070
32436
  var handoffToLocalCli = async (argv, runningRoot) => {
32071
32437
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -32077,7 +32443,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
32077
32443
  );
32078
32444
  if (!localCli) return void 0;
32079
32445
  printDiagnosticsToConsole(
32080
- `Using the project's local ${PACKAGE_NAME} (${path98.dirname(localCli)})`
32446
+ `Using the project's local ${PACKAGE_NAME} (${path99.dirname(localCli)})`
32081
32447
  );
32082
32448
  const child = spawn2(process.execPath, [localCli, ...argv], {
32083
32449
  stdio: "inherit",