@lukoweb/apitogo 0.2.59 → 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 +897 -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 +85 -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;
@@ -8267,6 +8368,9 @@ function planPortal(profile, manifest) {
8267
8368
  preview: manifest.preview === true
8268
8369
  };
8269
8370
  if (!profile.enabled) return plan2;
8371
+ if (profile.pipelines.search === "pagefind") plan2.search = "pagefind";
8372
+ else if (profile.pipelines.search === "inkeep")
8373
+ skip("search", "inkeep needs keys that portal.search cannot hold yet");
8270
8374
  if (profile.pipelines.openapi) {
8271
8375
  if (!gateway) skip("api reference", "services.json has no gateway entry");
8272
8376
  else if (!serviceReady(gateway))
@@ -8343,11 +8447,38 @@ function planPortal(profile, manifest) {
8343
8447
  var SITE_CONFIG_FILE = "apitogo.config.json";
8344
8448
  var REFERENCE_SPEC_FILE = "openapi/gateway.json";
8345
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
+ }
8346
8461
  function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8347
8462
  const portal = resolved.portal;
8348
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
+ }));
8349
8473
  const navigation = [
8350
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
+ })),
8351
8482
  ...plan2.apiReference ? [
8352
8483
  {
8353
8484
  type: "link",
@@ -8386,11 +8517,13 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8386
8517
  },
8387
8518
  ...plan2.docs ? {
8388
8519
  docs: {
8389
- files: portal?.docs.content.map(
8390
- (glob9) => contentGlob(resolved.configDir, glob9)
8391
- )
8520
+ files: [
8521
+ ...portal?.docs.content ?? [],
8522
+ ...headerPages.map(toGlobPath)
8523
+ ].map((glob9) => contentGlob(resolved.configDir, glob9))
8392
8524
  }
8393
8525
  } : {},
8526
+ ...plan2.search ? { search: { type: plan2.search } } : {},
8394
8527
  ...plan2.apiReference ? {
8395
8528
  apis: [
8396
8529
  {
@@ -8402,12 +8535,13 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8402
8535
  } : {},
8403
8536
  ...navigation.length > 0 ? { navigation } : {},
8404
8537
  // The sidebar link above is only seen inside the docs; the header is what every page shows. A header navigation
8405
- // replaces the docs tab, so the docs get a header link of their own.
8406
- ...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 ? {
8407
8540
  header: {
8408
8541
  navigation: [
8409
8542
  ...plan2.docs && extras.docsPath ? [{ label: "Documentation", to: extras.docsPath }] : [],
8410
- { label: "API Reference", to: `/${API_REFERENCE_PATH}` }
8543
+ ...plan2.apiReference ? [{ label: "API Reference", to: `/${API_REFERENCE_PATH}` }] : [],
8544
+ ...shownHeaderLinks
8411
8545
  ]
8412
8546
  }
8413
8547
  } : {},
@@ -8613,10 +8747,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8613
8747
  const tsxPages = plan2.docs ? (await findTsxPages(profile.docs.content)).filter(
8614
8748
  (page) => !markdownRoutes.includes(page.route)
8615
8749
  ) : [];
8750
+ const importPathOf = (file) => `./${CONTENT_LINK}/${path14.relative(resolved.configDir, file).split(path14.sep).join("/")}`;
8616
8751
  const wrapperPages = tsxPages.map((page, index) => ({
8617
8752
  id: `${TSX_PAGE_ELEMENT_PREFIX}${index}`,
8618
- importPath: `./${CONTENT_LINK}/${path14.relative(resolved.configDir, page.file).split(path14.sep).join("/")}`
8753
+ importPath: importPathOf(page.file)
8619
8754
  }));
8755
+ const headerWrapperPages = headerTsxPages(resolved, plan2).map(
8756
+ (page) => ({ id: page.id, importPath: importPathOf(page.file) })
8757
+ );
8620
8758
  const hostedDocRoutes = [
8621
8759
  .../* @__PURE__ */ new Set([...markdownRoutes, ...tsxPages.map((page) => page.route)])
8622
8760
  ].sort();
@@ -8687,14 +8825,20 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8687
8825
  protectedRoutes: protectedRoutesFor({
8688
8826
  requireAuth: resolved.portal?.requireAuth,
8689
8827
  hasAuth: !!plan2.auth,
8690
- 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,
8691
8835
  apiReferencePath: plan2.apiReference ? `/${API_REFERENCE_PATH}` : void 0
8692
8836
  })
8693
8837
  }
8694
8838
  );
8695
8839
  const wrapper = renderWrapper(
8696
8840
  plan2,
8697
- wrapperPages,
8841
+ [...wrapperPages, ...headerWrapperPages],
8698
8842
  resolved.portal?.footer ? wrapperImport(contentFile(resolved.configDir, resolved.portal.footer)) : void 0
8699
8843
  );
8700
8844
  const wrapperPath = path14.join(resolved.state.generatedDir, WRAPPER_FILE);
@@ -10101,7 +10245,7 @@ init_invariant();
10101
10245
  // src/cli/check/v2.ts
10102
10246
  init_apitogo_config_core();
10103
10247
  import { readFile as readFile18 } from "node:fs/promises";
10104
- import path51 from "node:path";
10248
+ import path52 from "node:path";
10105
10249
 
10106
10250
  // src/cli/platform/client/platform-api.ts
10107
10251
  import { basename } from "node:path";
@@ -10214,8 +10358,8 @@ var PlatformApiClient = class {
10214
10358
  constructor(config2) {
10215
10359
  this.config = config2;
10216
10360
  }
10217
- url(path99) {
10218
- return `${this.config.apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
10361
+ url(path100) {
10362
+ return `${this.config.apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
10219
10363
  }
10220
10364
  headers(extra) {
10221
10365
  return {
@@ -10225,9 +10369,9 @@ var PlatformApiClient = class {
10225
10369
  ...extra
10226
10370
  };
10227
10371
  }
10228
- async request(method, path99, init3) {
10372
+ async request(method, path100, init3) {
10229
10373
  const response = await fetchWithRetry(
10230
- this.url(path99),
10374
+ this.url(path100),
10231
10375
  {
10232
10376
  method,
10233
10377
  ...init3,
@@ -10695,15 +10839,15 @@ var PlatformApiClient = class {
10695
10839
  }
10696
10840
  );
10697
10841
  }
10698
- deleteDevPortalPage(projectId, path99) {
10699
- const query = new URLSearchParams({ path: path99 });
10842
+ deleteDevPortalPage(projectId, path100) {
10843
+ const query = new URLSearchParams({ path: path100 });
10700
10844
  return this.request(
10701
10845
  "DELETE",
10702
10846
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
10703
10847
  );
10704
10848
  }
10705
- renameDevPortalPage(projectId, path99, newName) {
10706
- const query = new URLSearchParams({ path: path99 });
10849
+ renameDevPortalPage(projectId, path100, newName) {
10850
+ const query = new URLSearchParams({ path: path100 });
10707
10851
  return this.request(
10708
10852
  "POST",
10709
10853
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -10819,8 +10963,8 @@ var PlatformApiClient = class {
10819
10963
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
10820
10964
  */
10821
10965
  validateGatewayPlans(plans, options = {}) {
10822
- const path99 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10823
- 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, {
10824
10968
  headers: { "Content-Type": "application/json" },
10825
10969
  body: JSON.stringify({ plans, planTier: options.planTier })
10826
10970
  });
@@ -11048,11 +11192,11 @@ import {
11048
11192
  rm as rm8,
11049
11193
  writeFile as writeFile15
11050
11194
  } from "node:fs/promises";
11051
- import path50 from "node:path";
11195
+ import path51 from "node:path";
11052
11196
 
11053
11197
  // src/vite/build.ts
11054
11198
  import { mkdir as mkdir11, readFile as readFile13, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
11055
- import path48 from "node:path";
11199
+ import path49 from "node:path";
11056
11200
  import { build as esbuild } from "esbuild";
11057
11201
  import { build as viteBuild, mergeConfig as mergeConfig3 } from "vite";
11058
11202
 
@@ -11188,7 +11332,7 @@ init_invariant();
11188
11332
  init_joinUrl();
11189
11333
 
11190
11334
  // src/vite/config.ts
11191
- import path43 from "node:path";
11335
+ import path44 from "node:path";
11192
11336
  import dotenv from "dotenv";
11193
11337
  import colors8 from "picocolors";
11194
11338
  import {
@@ -11199,7 +11343,7 @@ import {
11199
11343
  // package.json
11200
11344
  var package_default = {
11201
11345
  name: "@lukoweb/apitogo",
11202
- version: "0.2.59",
11346
+ version: "0.2.61",
11203
11347
  type: "module",
11204
11348
  sideEffects: [
11205
11349
  "**/*.css",
@@ -11984,14 +12128,14 @@ var flattenAllOf = (schema2) => {
11984
12128
  };
11985
12129
 
11986
12130
  // src/lib/util/traverse.ts
11987
- var traverse = (specification, transform, path99 = []) => {
11988
- const transformed = transform(specification, path99);
12131
+ var traverse = (specification, transform, path100 = []) => {
12132
+ const transformed = transform(specification, path100);
11989
12133
  if (typeof transformed !== "object" || transformed === null) {
11990
12134
  return transformed;
11991
12135
  }
11992
12136
  const result = Array.isArray(transformed) ? [] : {};
11993
12137
  for (const [key, value] of Object.entries(transformed)) {
11994
- const currentPath = [...path99, key];
12138
+ const currentPath = [...path100, key];
11995
12139
  if (Array.isArray(value)) {
11996
12140
  result[key] = value.map(
11997
12141
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -12019,9 +12163,9 @@ var resolveLocalRef = (schema2, ref) => {
12019
12163
  if (schemaCache?.has(ref)) {
12020
12164
  return schemaCache.get(ref);
12021
12165
  }
12022
- const path99 = ref.split("/").slice(1);
12166
+ const path100 = ref.split("/").slice(1);
12023
12167
  let current = schema2;
12024
- for (const segment of path99) {
12168
+ for (const segment of path100) {
12025
12169
  if (!current || typeof current !== "object") {
12026
12170
  current = null;
12027
12171
  }
@@ -12040,7 +12184,7 @@ var dereference = async (schema2, resolvers = []) => {
12040
12184
  }
12041
12185
  const cloned = structuredClone(schema2);
12042
12186
  const visited = /* @__PURE__ */ new Set();
12043
- const resolve = async (current, path99) => {
12187
+ const resolve = async (current, path100) => {
12044
12188
  if (isIndexableObject(current)) {
12045
12189
  if (visited.has(current)) {
12046
12190
  return CIRCULAR_REF;
@@ -12048,7 +12192,7 @@ var dereference = async (schema2, resolvers = []) => {
12048
12192
  visited.add(current);
12049
12193
  if (Array.isArray(current)) {
12050
12194
  for (let index = 0; index < current.length; index++) {
12051
- current[index] = await resolve(current[index], `${path99}/${index}`);
12195
+ current[index] = await resolve(current[index], `${path100}/${index}`);
12052
12196
  }
12053
12197
  } else {
12054
12198
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -12059,13 +12203,13 @@ var dereference = async (schema2, resolvers = []) => {
12059
12203
  for (const resolver of resolvers) {
12060
12204
  const resolved = await resolver($ref);
12061
12205
  if (resolved) {
12062
- result2 = await resolve(resolved, path99);
12206
+ result2 = await resolve(resolved, path100);
12063
12207
  break;
12064
12208
  }
12065
12209
  }
12066
12210
  if (result2 === void 0) {
12067
12211
  const resolved = await resolveLocalRef(cloned, $ref);
12068
- result2 = await resolve(resolved, path99);
12212
+ result2 = await resolve(resolved, path100);
12069
12213
  }
12070
12214
  if (hasSiblings) {
12071
12215
  if (result2 === CIRCULAR_REF) {
@@ -12078,7 +12222,7 @@ var dereference = async (schema2, resolvers = []) => {
12078
12222
  return result2;
12079
12223
  }
12080
12224
  for (const key in current) {
12081
- current[key] = await resolve(current[key], `${path99}/${key}`);
12225
+ current[key] = await resolve(current[key], `${path100}/${key}`);
12082
12226
  }
12083
12227
  }
12084
12228
  visited.delete(current);
@@ -12117,9 +12261,9 @@ var upgradeSchema = (schema2) => {
12117
12261
  }
12118
12262
  return sub;
12119
12263
  });
12120
- schema2 = traverse(schema2, (sub, path99) => {
12264
+ schema2 = traverse(schema2, (sub, path100) => {
12121
12265
  if (sub.example !== void 0) {
12122
- if (isSchemaPath(path99 ?? [])) {
12266
+ if (isSchemaPath(path100 ?? [])) {
12123
12267
  sub.examples = [sub.example];
12124
12268
  } else {
12125
12269
  sub.examples = {
@@ -12132,11 +12276,11 @@ var upgradeSchema = (schema2) => {
12132
12276
  }
12133
12277
  return sub;
12134
12278
  });
12135
- schema2 = traverse(schema2, (schema3, path99) => {
12279
+ schema2 = traverse(schema2, (schema3, path100) => {
12136
12280
  if (schema3.type === "object" && schema3.properties !== void 0) {
12137
- const parentPath = path99?.slice(0, -1);
12281
+ const parentPath = path100?.slice(0, -1);
12138
12282
  const isMultipart = parentPath?.some((segment, index) => {
12139
- return segment === "content" && path99?.[index + 1] === "multipart/form-data";
12283
+ return segment === "content" && path100?.[index + 1] === "multipart/form-data";
12140
12284
  });
12141
12285
  if (isMultipart) {
12142
12286
  const entries = Object.entries(schema3.properties);
@@ -12150,8 +12294,8 @@ var upgradeSchema = (schema2) => {
12150
12294
  }
12151
12295
  return schema3;
12152
12296
  });
12153
- schema2 = traverse(schema2, (schema3, path99) => {
12154
- 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")) {
12155
12299
  return {};
12156
12300
  }
12157
12301
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -12177,11 +12321,11 @@ var upgradeSchema = (schema2) => {
12177
12321
  }
12178
12322
  return sub;
12179
12323
  });
12180
- schema2 = traverse(schema2, (schema3, path99) => {
12324
+ schema2 = traverse(schema2, (schema3, path100) => {
12181
12325
  if (schema3.type === "string" && schema3.format === "byte") {
12182
- const parentPath = path99?.slice(0, -1);
12326
+ const parentPath = path100?.slice(0, -1);
12183
12327
  const contentMediaType = parentPath?.find(
12184
- (_, index) => path99?.[index - 1] === "content"
12328
+ (_, index) => path100?.[index - 1] === "content"
12185
12329
  );
12186
12330
  return {
12187
12331
  type: "string",
@@ -12193,7 +12337,7 @@ var upgradeSchema = (schema2) => {
12193
12337
  });
12194
12338
  return schema2;
12195
12339
  };
12196
- function isSchemaPath(path99) {
12340
+ function isSchemaPath(path100) {
12197
12341
  const schemaLocations = [
12198
12342
  ["components", "schemas"],
12199
12343
  "properties",
@@ -12206,10 +12350,10 @@ function isSchemaPath(path99) {
12206
12350
  ];
12207
12351
  return schemaLocations.some((location) => {
12208
12352
  if (Array.isArray(location)) {
12209
- return location.every((segment, index) => path99[index] === segment);
12353
+ return location.every((segment, index) => path100[index] === segment);
12210
12354
  }
12211
- return path99.includes(location);
12212
- }) || path99.includes("schema") || path99.some((segment) => segment.endsWith("Schema"));
12355
+ return path100.includes(location);
12356
+ }) || path100.includes("schema") || path100.some((segment) => segment.endsWith("Schema"));
12213
12357
  }
12214
12358
 
12215
12359
  // src/lib/oas/parser/index.ts
@@ -12291,13 +12435,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
12291
12435
  "anyOf",
12292
12436
  "oneOf"
12293
12437
  ]);
12294
- 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()) => {
12295
12439
  if (obj === null || typeof obj !== "object") return obj;
12296
12440
  const refPath = obj.__$ref;
12297
12441
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
12298
12442
  if (isCircular) {
12299
12443
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
12300
- const circularProp = path99.find((p) => !OPENAPI_PROPS.has(p)) || path99[0];
12444
+ const circularProp = path100.find((p) => !OPENAPI_PROPS.has(p)) || path100[0];
12301
12445
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
12302
12446
  }
12303
12447
  if (refs.has(obj)) return refs.get(obj);
@@ -12307,7 +12451,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
12307
12451
  value,
12308
12452
  currentPath,
12309
12453
  refs,
12310
- [...path99, key],
12454
+ [...path100, key],
12311
12455
  currentRefPaths
12312
12456
  );
12313
12457
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -12342,7 +12486,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
12342
12486
  var getAllTags = (schema2) => {
12343
12487
  const rootTags = schema2.tags ?? [];
12344
12488
  const operations = Object.values(schema2.paths ?? {}).flatMap(
12345
- (path99) => HttpMethods.map((k) => path99?.[k]).filter((op) => op != null)
12489
+ (path100) => HttpMethods.map((k) => path100?.[k]).filter((op) => op != null)
12346
12490
  );
12347
12491
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
12348
12492
  const hasUntaggedOperations = operations.some(
@@ -12397,7 +12541,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
12397
12541
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
12398
12542
  var getAllOperations = (paths) => {
12399
12543
  const operations = Object.entries(paths ?? {}).flatMap(
12400
- ([path99, value]) => HttpMethods.flatMap((method) => {
12544
+ ([path100, value]) => HttpMethods.flatMap((method) => {
12401
12545
  if (!value?.[method]) return [];
12402
12546
  const operation = value[method];
12403
12547
  const pathParameters = value.parameters ?? [];
@@ -12417,7 +12561,7 @@ var getAllOperations = (paths) => {
12417
12561
  return {
12418
12562
  ...operation,
12419
12563
  method,
12420
- path: path99,
12564
+ path: path100,
12421
12565
  parameters,
12422
12566
  servers,
12423
12567
  tags: operation.tags ?? []
@@ -12775,8 +12919,8 @@ var Schema = builder.objectRef("Schema").implement({
12775
12919
  }),
12776
12920
  paths: t.field({
12777
12921
  type: [PathItem],
12778
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path99, value]) => ({
12779
- path: path99,
12922
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path100, value]) => ({
12923
+ path: path100,
12780
12924
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
12781
12925
  methods: Object.keys(value)
12782
12926
  }))
@@ -12941,7 +13085,7 @@ init_joinUrl();
12941
13085
 
12942
13086
  // src/vite/api/schema-codegen.ts
12943
13087
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
12944
- var getSegmentsFromPath = (path99) => path99.split("/").slice(1).map(unescapeJsonPointer);
13088
+ var getSegmentsFromPath = (path100) => path100.split("/").slice(1).map(unescapeJsonPointer);
12945
13089
  var createLocalRefMap = (obj) => {
12946
13090
  const refMap = /* @__PURE__ */ new Map();
12947
13091
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -12972,16 +13116,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
12972
13116
  /"__refMap\+Siblings:(.*?)"/g,
12973
13117
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
12974
13118
  );
12975
- var lookup = (schema2, path99, filePath) => {
12976
- const parts = getSegmentsFromPath(path99);
13119
+ var lookup = (schema2, path100, filePath) => {
13120
+ const parts = getSegmentsFromPath(path100);
12977
13121
  let val = schema2;
12978
13122
  for (const part of parts) {
12979
13123
  while (val.$ref?.startsWith("#/")) {
12980
- val = val.$ref === path99 ? val : lookup(schema2, val.$ref, filePath);
13124
+ val = val.$ref === path100 ? val : lookup(schema2, val.$ref, filePath);
12981
13125
  }
12982
13126
  if (val[part] === void 0) {
12983
13127
  throw new Error(
12984
- `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}`
12985
13129
  );
12986
13130
  }
12987
13131
  val = val[part];
@@ -13228,8 +13372,8 @@ var SchemaManager = class {
13228
13372
  }
13229
13373
  }
13230
13374
  };
13231
- getLatestSchema = (path99) => this.processedSchemas[path99]?.at(0);
13232
- getSchemasForPath = (path99) => this.processedSchemas[path99];
13375
+ getLatestSchema = (path100) => this.processedSchemas[path100]?.at(0);
13376
+ getSchemasForPath = (path100) => this.processedSchemas[path100];
13233
13377
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
13234
13378
  getUrlToFilePathMap = () => {
13235
13379
  const map = /* @__PURE__ */ new Map();
@@ -14353,8 +14497,32 @@ init_loader();
14353
14497
  init_ProtectedRoutesSchema();
14354
14498
  init_joinUrl();
14355
14499
  import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
14356
- import path36 from "node:path";
14500
+ import path37 from "node:path";
14357
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
14358
14526
  var processMarkdownFile = async (filePath) => {
14359
14527
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
14360
14528
  let finalMarkdown = markdownContent;
@@ -14441,7 +14609,7 @@ var viteMarkdownExportPlugin = () => {
14441
14609
  if (process.env.NODE_ENV !== "production" || Object.keys(markdownFiles).length === 0 || !needsMdFiles) {
14442
14610
  return;
14443
14611
  }
14444
- const distDir = path36.join(
14612
+ const distDir = path37.join(
14445
14613
  config2.__meta.rootDir,
14446
14614
  "dist",
14447
14615
  config2.basePath ?? ""
@@ -14461,16 +14629,15 @@ var viteMarkdownExportPlugin = () => {
14461
14629
  description,
14462
14630
  content: finalMarkdown
14463
14631
  });
14464
- const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
14465
- const outputPath = `${path36.join(distDir, ...segments)}.md`;
14466
- await mkdir8(path36.dirname(outputPath), { recursive: true });
14632
+ const outputPath = markdownOutputPath(distDir, routePath);
14633
+ await mkdir8(path37.dirname(outputPath), { recursive: true });
14467
14634
  await writeFile10(outputPath, finalMarkdown, "utf-8");
14468
14635
  } catch (error) {
14469
14636
  console.warn(`Failed to export markdown for ${routePath}:`, error);
14470
14637
  }
14471
14638
  }
14472
14639
  if (config2.docs?.llms?.llmsTxt || config2.docs?.llms?.llmsTxtFull) {
14473
- const markdownInfoPath = path36.join(
14640
+ const markdownInfoPath = path37.join(
14474
14641
  config2.__meta.rootDir,
14475
14642
  "node_modules/.apitogo/markdown-info.json"
14476
14643
  );
@@ -14622,9 +14789,9 @@ var remarkCodeTabs = () => (tree) => {
14622
14789
  };
14623
14790
 
14624
14791
  // src/vite/mdx/remark-inject-filepath.ts
14625
- import path37 from "node:path";
14792
+ import path38 from "node:path";
14626
14793
  var remarkInjectFilepath = (rootDir) => (tree, vfile) => {
14627
- 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);
14628
14795
  tree.children.unshift(exportMdxjsConst("__filepath", relativePath));
14629
14796
  };
14630
14797
 
@@ -14711,18 +14878,18 @@ var remarkLastModified = () => {
14711
14878
  };
14712
14879
 
14713
14880
  // src/vite/mdx/remark-link-rewrite.ts
14714
- import path38 from "node:path";
14881
+ import path39 from "node:path";
14715
14882
  import { visit as visit5 } from "unist-util-visit";
14716
14883
  var remarkLinkRewrite = (basePath = "") => (tree) => {
14717
14884
  visit5(tree, "link", (node) => {
14718
14885
  if (!node.url) return;
14719
14886
  if (node.url.startsWith("http") || node.url.startsWith("mailto:")) return;
14720
14887
  node.url = node.url.replace(/\\/g, "/");
14721
- const base = path38.posix.join(basePath);
14888
+ const base = path39.posix.join(basePath);
14722
14889
  if (basePath && node.url.startsWith(base)) {
14723
14890
  node.url = node.url.slice(base.length);
14724
14891
  } else if (!node.url.startsWith("/") && !node.url.startsWith("#")) {
14725
- node.url = path38.posix.join("..", node.url);
14892
+ node.url = path39.posix.join("..", node.url);
14726
14893
  }
14727
14894
  node.url = node.url.replace(/\.mdx?(#.*)?$/, "$1");
14728
14895
  });
@@ -14942,14 +15109,14 @@ var plugin_mdx_default = viteMdxPlugin;
14942
15109
 
14943
15110
  // src/vite/plugin-modules.ts
14944
15111
  init_loader();
14945
- import path39 from "node:path";
15112
+ import path40 from "node:path";
14946
15113
  import { normalizePath as normalizePath2 } from "vite";
14947
- var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14948
- 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";
14949
15116
  var getBillingPluginImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(
14950
- path39.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
15117
+ path40.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14951
15118
  ) : "@lukoweb/apitogo-plugin-dev-portal-billing";
14952
- var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path39.resolve(rootDir, pagePath));
15119
+ var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path40.resolve(rootDir, pagePath));
14953
15120
  var viteModulesPlugin = () => {
14954
15121
  const virtualModuleId4 = "virtual:zudoku-modules-plugin";
14955
15122
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -15091,17 +15258,17 @@ var plugin_modules_default = viteModulesPlugin;
15091
15258
 
15092
15259
  // src/vite/plugin-offroot-resolve.ts
15093
15260
  init_loader();
15094
- import path40 from "node:path";
15261
+ import path41 from "node:path";
15095
15262
  function stripQuery(id) {
15096
15263
  const query = id.indexOf("?");
15097
15264
  return query === -1 ? id : id.slice(0, query);
15098
15265
  }
15099
15266
  function isInside(parent, child) {
15100
- const relative = path40.relative(path40.resolve(parent), path40.resolve(child));
15101
- 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);
15102
15269
  }
15103
15270
  function isBareSpecifier(source) {
15104
- return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path40.isAbsolute(source);
15271
+ return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path41.isAbsolute(source);
15105
15272
  }
15106
15273
  function isInsideNodeModules(filePath) {
15107
15274
  return filePath.split(/[\\/]/).includes("node_modules");
@@ -15125,7 +15292,7 @@ function viteOffrootResolvePlugin() {
15125
15292
  if (!shouldResolveFromSiteRoot(source, importer, rootDir)) {
15126
15293
  return;
15127
15294
  }
15128
- return this.resolve(source, path40.join(rootDir, "package.json"), {
15295
+ return this.resolve(source, path41.join(rootDir, "package.json"), {
15129
15296
  ...options,
15130
15297
  skipSelf: true
15131
15298
  });
@@ -15135,7 +15302,7 @@ function viteOffrootResolvePlugin() {
15135
15302
 
15136
15303
  // src/vite/plugin-search.ts
15137
15304
  init_loader();
15138
- import path41 from "node:path";
15305
+ import path42 from "node:path";
15139
15306
  var viteSearchPlugin = () => {
15140
15307
  const virtualModuleId4 = "virtual:zudoku-search-plugin";
15141
15308
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -15153,7 +15320,7 @@ var viteSearchPlugin = () => {
15153
15320
  return resolvedVirtualModuleId5;
15154
15321
  }
15155
15322
  if (id === "/pagefind/pagefind.js" && resolvedViteConfig?.publicDir) {
15156
- return path41.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
15323
+ return path42.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
15157
15324
  }
15158
15325
  },
15159
15326
  async load(id) {
@@ -15294,27 +15461,27 @@ function vitePlugin() {
15294
15461
 
15295
15462
  // src/vite/watch-ignore.ts
15296
15463
  import { lstatSync, realpathSync } from "node:fs";
15297
- import path42 from "node:path";
15464
+ import path43 from "node:path";
15298
15465
  var STATE_INSIDE_CONTENT = /[\\/]content[\\/](?:\.apitogo|apitogo[\\/]\.state)(?:[\\/]|$)/;
15299
15466
  var within = (base, file) => {
15300
- const resolvedBase = path42.resolve(base);
15301
- const resolvedFile = path42.resolve(file);
15302
- 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);
15303
15470
  };
15304
15471
  var unique = (paths) => [...new Set(paths)];
15305
15472
  function isWorkspace(root) {
15306
15473
  try {
15307
- return path42.basename(root) === "generated" && lstatSync(path42.join(root, "content")).isSymbolicLink();
15474
+ return path43.basename(root) === "generated" && lstatSync(path43.join(root, "content")).isSymbolicLink();
15308
15475
  } catch {
15309
15476
  return false;
15310
15477
  }
15311
15478
  }
15312
15479
  function tenantDir(root) {
15313
- return isWorkspace(root) ? realpathSync(path42.join(root, "content")) : void 0;
15480
+ return isWorkspace(root) ? realpathSync(path43.join(root, "content")) : void 0;
15314
15481
  }
15315
15482
  function stateIgnore(root) {
15316
15483
  if (!isWorkspace(root)) return void 0;
15317
- const state = path42.dirname(root);
15484
+ const state = path43.dirname(root);
15318
15485
  const states = unique([state, realpathSync(state)]);
15319
15486
  const roots = unique([root, realpathSync(root)]);
15320
15487
  return (file) => {
@@ -15323,19 +15490,28 @@ function stateIgnore(root) {
15323
15490
  return STATE_INSIDE_CONTENT.test(file);
15324
15491
  };
15325
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
+ }
15326
15502
  function devServerAllow(root, moduleDir) {
15327
15503
  const tenant = tenantDir(root);
15328
- return [root, path42.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15504
+ return [root, path43.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15329
15505
  }
15330
15506
 
15331
15507
  // src/vite/config.ts
15332
15508
  var resolveMergedPublicDir = (rootDir, merged) => {
15333
15509
  if (merged.publicDir === false) return void 0;
15334
15510
  const rel2 = typeof merged.publicDir === "string" ? merged.publicDir : "public";
15335
- return path43.resolve(rootDir, rel2);
15511
+ return path44.resolve(rootDir, rel2);
15336
15512
  };
15337
- var getAppClientEntryPath = () => path43.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
15338
- 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");
15339
15515
  var hasLoggedCdnInfo = false;
15340
15516
  var MEDIA_REGEX = /\.(a?png|jpe?g|gif|bmp|svg|webp|tiff|ico|webm|ogg|mp3|wav|m4a|avif|mp4)/i;
15341
15517
  var defineEnvVars = (vars) => Object.fromEntries(
@@ -15366,7 +15542,7 @@ async function getViteConfig(dir, configEnv) {
15366
15542
  const ignoreState = stateIgnore(dir);
15367
15543
  if (ZuploEnv.isZuplo) {
15368
15544
  dotenv.config({
15369
- path: path43.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15545
+ path: path44.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15370
15546
  quiet: true
15371
15547
  });
15372
15548
  }
@@ -15421,6 +15597,7 @@ async function getViteConfig(dir, configEnv) {
15421
15597
  `${dir}/.git`,
15422
15598
  `${config2.__meta.moduleDir}/src/vite`,
15423
15599
  `${config2.__meta.moduleDir}/src/cli`,
15600
+ nestedStateIgnore(dir),
15424
15601
  ...ignoreState ? [ignoreState] : []
15425
15602
  ]
15426
15603
  }
@@ -15429,8 +15606,8 @@ async function getViteConfig(dir, configEnv) {
15429
15606
  ssr: configEnv.isSsrBuild,
15430
15607
  sourcemap: true,
15431
15608
  target: "es2022",
15432
- outDir: path43.resolve(
15433
- path43.join(
15609
+ outDir: path44.resolve(
15610
+ path44.join(
15434
15611
  dir,
15435
15612
  "dist",
15436
15613
  config2.basePath ?? "",
@@ -15449,7 +15626,7 @@ async function getViteConfig(dir, configEnv) {
15449
15626
  },
15450
15627
  experimental: {
15451
15628
  renderBuiltUrl(filename) {
15452
- if (cdnUrl?.base && [".js", ".css"].includes(path43.extname(filename))) {
15629
+ if (cdnUrl?.base && [".js", ".css"].includes(path44.extname(filename))) {
15453
15630
  return joinUrl(cdnUrl.base, filename);
15454
15631
  }
15455
15632
  if (cdnUrl?.media && MEDIA_REGEX.test(filename)) {
@@ -15462,7 +15639,7 @@ async function getViteConfig(dir, configEnv) {
15462
15639
  esbuildOptions: {
15463
15640
  target: "es2022"
15464
15641
  },
15465
- entries: [path43.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15642
+ entries: [path44.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15466
15643
  exclude: [
15467
15644
  "@lukoweb/apitogo",
15468
15645
  "@lukoweb/apitogo-plugin-dev-portal-billing"
@@ -15566,7 +15743,7 @@ init_package_json();
15566
15743
  init_joinUrl();
15567
15744
  import assert from "node:assert";
15568
15745
  import { cp, mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
15569
- import path44 from "node:path";
15746
+ import path45 from "node:path";
15570
15747
  var pkgJson = getZudokuPackageJson();
15571
15748
  function generateOutput({
15572
15749
  config: config2,
@@ -15623,9 +15800,9 @@ async function writeOutput(dir, {
15623
15800
  rewrites
15624
15801
  }) {
15625
15802
  const output4 = generateOutput({ config: config2, redirects, rewrites });
15626
- 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");
15627
15804
  await mkdir9(outputDir, { recursive: true });
15628
- const outputFile = path44.join(outputDir, "config.json");
15805
+ const outputFile = path45.join(outputDir, "config.json");
15629
15806
  await writeFile11(outputFile, JSON.stringify(output4, null, 2), "utf-8");
15630
15807
  if (process.env.VERCEL) {
15631
15808
  console.log("Wrote Vercel output to", outputDir);
@@ -15635,9 +15812,9 @@ async function writeOutput(dir, {
15635
15812
  // src/vite/prerender/prerender.ts
15636
15813
  init_logger();
15637
15814
  init_file_exists();
15638
- import { readFile as readFile12, rm as rm6 } from "node:fs/promises";
15815
+ import { readFile as readFile12 } from "node:fs/promises";
15639
15816
  import os from "node:os";
15640
- import path47 from "node:path";
15817
+ import path48 from "node:path";
15641
15818
  import { pathToFileURL } from "node:url";
15642
15819
  import { createIndex } from "pagefind";
15643
15820
  import colors11 from "picocolors";
@@ -15679,7 +15856,7 @@ function throttle(fn) {
15679
15856
  init_joinUrl();
15680
15857
  import { createWriteStream, existsSync as existsSync5 } from "node:fs";
15681
15858
  import { mkdir as mkdir10 } from "node:fs/promises";
15682
- import path45 from "node:path";
15859
+ import path46 from "node:path";
15683
15860
  import colors9 from "picocolors";
15684
15861
  import { SitemapStream } from "sitemap";
15685
15862
  async function generateSitemap({
@@ -15693,11 +15870,11 @@ async function generateSitemap({
15693
15870
  return;
15694
15871
  }
15695
15872
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
15696
- const outputDir = path45.resolve(baseOutputDir, config2.outDir ?? "");
15873
+ const outputDir = path46.resolve(baseOutputDir, config2.outDir ?? "");
15697
15874
  if (!existsSync5(outputDir)) {
15698
15875
  await mkdir10(outputDir, { recursive: true });
15699
15876
  }
15700
- const sitemapOutputPath = path45.join(outputDir, "sitemap.xml");
15877
+ const sitemapOutputPath = path46.join(outputDir, "sitemap.xml");
15701
15878
  const writeStream = createWriteStream(sitemapOutputPath);
15702
15879
  sitemap.pipe(writeStream);
15703
15880
  let lastmod;
@@ -15727,14 +15904,14 @@ async function generateSitemap({
15727
15904
 
15728
15905
  // src/vite/prerender/utils.ts
15729
15906
  init_joinUrl();
15730
- var resolveRoutePath = (path99) => {
15731
- const segments = path99.split("/");
15907
+ var resolveRoutePath = (path100) => {
15908
+ const segments = path100.split("/");
15732
15909
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
15733
15910
  return void 0;
15734
15911
  }
15735
15912
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
15736
15913
  };
15737
- var isSkipped = (path99) => path99.includes("*") || /^\d+$/.test(path99);
15914
+ var isSkipped = (path100) => path100.includes("*") || /^\d+$/.test(path100);
15738
15915
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
15739
15916
  if (route.path && isSkipped(route.path)) return [];
15740
15917
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -15784,12 +15961,12 @@ var prerender = async ({
15784
15961
  serverConfigFilename,
15785
15962
  writeRedirects = true
15786
15963
  }) => {
15787
- const distDir = path47.join(dir, "dist", basePath);
15964
+ const distDir = path48.join(dir, "dist", basePath);
15788
15965
  const serverConfigPath = pathToFileURL(
15789
- path47.join(distDir, "server", serverConfigFilename)
15966
+ path48.join(distDir, "server", serverConfigFilename)
15790
15967
  ).href;
15791
15968
  const entryServerPath = pathToFileURL(
15792
- path47.join(distDir, "server/entry.server.js")
15969
+ path48.join(distDir, "server/entry.server.js")
15793
15970
  ).href;
15794
15971
  const rawConfig = await import(serverConfigPath).then(
15795
15972
  (m) => m.default
@@ -15874,7 +16051,7 @@ var prerender = async ({
15874
16051
  })
15875
16052
  );
15876
16053
  const pagefindWriteResult = await pagefindIndex?.writeFiles({
15877
- outputPath: path47.join(distDir, "pagefind")
16054
+ outputPath: path48.join(distDir, "pagefind")
15878
16055
  });
15879
16056
  const seconds = ((performance.now() - start) / 1e3).toFixed(1);
15880
16057
  const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
@@ -15906,7 +16083,7 @@ var prerender = async ({
15906
16083
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
15907
16084
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
15908
16085
  const llmsConfig = docsConfig.llms ?? {};
15909
- const markdownInfoPath = path47.join(
16086
+ const markdownInfoPath = path48.join(
15910
16087
  dir,
15911
16088
  "node_modules/.apitogo/markdown-info.json"
15912
16089
  );
@@ -15928,10 +16105,7 @@ var prerender = async ({
15928
16105
  });
15929
16106
  }
15930
16107
  if (!docsConfig.publishMarkdown) {
15931
- await Promise.all(
15932
- markdownFileInfos.map((info) => rm6(info.filePath).catch(() => {
15933
- }))
15934
- );
16108
+ await removeExportedMarkdown(distDir, markdownFileInfos);
15935
16109
  }
15936
16110
  }
15937
16111
  return { workerResults, rewrites };
@@ -15997,7 +16171,7 @@ async function runBuild(options) {
15997
16171
  html,
15998
16172
  basePath: config2.basePath
15999
16173
  });
16000
- await rm7(path48.join(clientOutDir, "index.html"), { force: true });
16174
+ await rm7(path49.join(clientOutDir, "index.html"), { force: true });
16001
16175
  } else {
16002
16176
  await runPrerender({
16003
16177
  dir,
@@ -16021,7 +16195,7 @@ var runPrerender = async (options) => {
16021
16195
  serverConfigFilename,
16022
16196
  writeRedirects: process.env.VERCEL === void 0
16023
16197
  });
16024
- const indexHtml = path48.join(clientOutDir, "index.html");
16198
+ const indexHtml = path49.join(clientOutDir, "index.html");
16025
16199
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
16026
16200
  await writeFile13(indexHtml, html, "utf-8");
16027
16201
  }
@@ -16031,15 +16205,15 @@ var runPrerender = async (options) => {
16031
16205
  for (const statusPage of statusPages) {
16032
16206
  await rename5(
16033
16207
  statusPage,
16034
- path48.join(dir, DIST_DIR, path48.basename(statusPage))
16208
+ path49.join(dir, DIST_DIR, path49.basename(statusPage))
16035
16209
  );
16036
16210
  }
16037
16211
  await rm7(serverOutDir, { recursive: true, force: true });
16038
16212
  if (process.env.VERCEL) {
16039
- await mkdir11(path48.join(dir, ".vercel/output/static"), { recursive: true });
16213
+ await mkdir11(path49.join(dir, ".vercel/output/static"), { recursive: true });
16040
16214
  await rename5(
16041
- path48.join(dir, DIST_DIR),
16042
- path48.join(dir, ".vercel/output/static")
16215
+ path49.join(dir, DIST_DIR),
16216
+ path49.join(dir, ".vercel/output/static")
16043
16217
  );
16044
16218
  }
16045
16219
  await writeOutput(dir, {
@@ -16049,7 +16223,7 @@ var runPrerender = async (options) => {
16049
16223
  });
16050
16224
  if (ZuploEnv.isZuplo && issuer) {
16051
16225
  await writeFile13(
16052
- path48.join(dir, DIST_DIR, ".output/zuplo.json"),
16226
+ path49.join(dir, DIST_DIR, ".output/zuplo.json"),
16053
16227
  JSON.stringify({ issuer }, null, 2),
16054
16228
  "utf-8"
16055
16229
  );
@@ -16061,10 +16235,10 @@ var runPrerender = async (options) => {
16061
16235
  };
16062
16236
  var bundleSSREntry = async (options) => {
16063
16237
  const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } = options;
16064
- const tempEntryPath = path48.join(dir, "__ssr-entry.ts");
16238
+ const tempEntryPath = path49.join(dir, "__ssr-entry.ts");
16065
16239
  const packageRoot = getZudokuRootDir();
16066
16240
  const templateContent = await readFile13(
16067
- path48.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
16241
+ path49.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
16068
16242
  "utf-8"
16069
16243
  );
16070
16244
  const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`)).replace(
@@ -16079,9 +16253,9 @@ var bundleSSREntry = async (options) => {
16079
16253
  platform: adapter === "node" ? "node" : "neutral",
16080
16254
  target: "es2022",
16081
16255
  format: "esm",
16082
- outfile: path48.join(serverOutDir, "entry.js"),
16256
+ outfile: path49.join(serverOutDir, "entry.js"),
16083
16257
  external: ["./entry.server.js", `./${serverConfigFilename}`],
16084
- nodePaths: [path48.join(packageRoot, "node_modules")],
16258
+ nodePaths: [path49.join(packageRoot, "node_modules")],
16085
16259
  banner: { js: "// Bundled SSR entry" }
16086
16260
  });
16087
16261
  } finally {
@@ -16095,7 +16269,7 @@ init_package_json();
16095
16269
  // src/cli/init/v2-packages.ts
16096
16270
  import { existsSync as existsSync6 } from "node:fs";
16097
16271
  import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
16098
- import path49 from "node:path";
16272
+ import path50 from "node:path";
16099
16273
  var CORE_PACKAGES = [
16100
16274
  "@lukoweb/apitogo",
16101
16275
  "react",
@@ -16112,7 +16286,7 @@ function packageNameFrom(name) {
16112
16286
  return slug || "portal";
16113
16287
  }
16114
16288
  async function ensurePackageJson(dir, name) {
16115
- const file = path49.join(dir, "package.json");
16289
+ const file = path50.join(dir, "package.json");
16116
16290
  if (existsSync6(file)) return false;
16117
16291
  await writeFile14(
16118
16292
  file,
@@ -16131,11 +16305,11 @@ async function ensurePackageJson(dir, name) {
16131
16305
  return true;
16132
16306
  }
16133
16307
  function isPackageInstalled(startDir, name) {
16134
- let dir = path49.resolve(startDir);
16308
+ let dir = path50.resolve(startDir);
16135
16309
  for (; ; ) {
16136
- if (existsSync6(path49.join(dir, "node_modules", name, "package.json")))
16310
+ if (existsSync6(path50.join(dir, "node_modules", name, "package.json")))
16137
16311
  return true;
16138
- const parent = path49.dirname(dir);
16312
+ const parent = path50.dirname(dir);
16139
16313
  if (parent === dir) return false;
16140
16314
  dir = parent;
16141
16315
  }
@@ -16192,7 +16366,7 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
16192
16366
  return {
16193
16367
  name: "build",
16194
16368
  hash: () => hash2,
16195
- stillValid: () => access4(path50.join(buildDir, "index.html")).then(
16369
+ stillValid: () => access4(path51.join(buildDir, "index.html")).then(
16196
16370
  () => true,
16197
16371
  () => false
16198
16372
  ),
@@ -16209,11 +16383,11 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
16209
16383
  else process.env.NODE_ENV = originalNodeEnv;
16210
16384
  }
16211
16385
  await rm8(buildDir, { recursive: true, force: true });
16212
- await mkdir12(path50.dirname(buildDir), { recursive: true });
16213
- await rename6(path50.join(root, "dist"), buildDir);
16386
+ await mkdir12(path51.dirname(buildDir), { recursive: true });
16387
+ await rename6(path51.join(root, "dist"), buildDir);
16214
16388
  if (prepared.publishedManifest) {
16215
16389
  await writeFile15(
16216
- path50.join(buildDir, MANIFEST_FILE),
16390
+ path51.join(buildDir, MANIFEST_FILE),
16217
16391
  `${JSON.stringify(prepared.publishedManifest, null, 2)}
16218
16392
  `
16219
16393
  );
@@ -16244,15 +16418,15 @@ async function buildV2(options) {
16244
16418
  // src/cli/check/gateway-online.ts
16245
16419
  import { readFile as readFile16 } from "node:fs/promises";
16246
16420
  var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
16247
- var issue2 = (code, path99, message, hint) => ({ code, path: path99, message, hint });
16421
+ var issue2 = (code, path100, message, hint) => ({ code, path: path100, message, hint });
16248
16422
  function capabilityIssues(gateway, answer) {
16249
16423
  const can = answer.capabilities;
16250
16424
  const plan2 = `your ${answer.tier} plan`;
16251
16425
  const found = [];
16252
- const unsupported = (path99, what, hint) => found.push(
16426
+ const unsupported = (path100, what, hint) => found.push(
16253
16427
  issue2(
16254
16428
  "GATEWAY_CAPABILITY_UNSUPPORTED",
16255
- path99,
16429
+ path100,
16256
16430
  `${what} is not available on ${plan2}.`,
16257
16431
  hint
16258
16432
  )
@@ -16463,9 +16637,9 @@ function toPlatformPlans(plans, options = {}) {
16463
16637
  }
16464
16638
 
16465
16639
  // src/cli/check/services-online.ts
16466
- var issue3 = (code, path99, message, hint) => ({
16640
+ var issue3 = (code, path100, message, hint) => ({
16467
16641
  code,
16468
- path: path99,
16642
+ path: path100,
16469
16643
  message,
16470
16644
  hint
16471
16645
  });
@@ -16577,7 +16751,7 @@ var withPlanName = (message, sku) => !sku || message.toLowerCase().startsWith(`p
16577
16751
  async function isConfigV2(dir) {
16578
16752
  try {
16579
16753
  return /"version"\s*:\s*2\b/.test(
16580
- await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16754
+ await readFile18(path52.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16581
16755
  );
16582
16756
  } catch {
16583
16757
  return false;
@@ -16586,7 +16760,7 @@ async function isConfigV2(dir) {
16586
16760
  async function isLegacyConfig(dir) {
16587
16761
  let text2;
16588
16762
  try {
16589
- text2 = await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8");
16763
+ text2 = await readFile18(path52.join(dir, APITOGO_CONFIG_FILENAME), "utf8");
16590
16764
  } catch {
16591
16765
  return false;
16592
16766
  }
@@ -16734,7 +16908,7 @@ async function validatePortalBuild(dir) {
16734
16908
  }
16735
16909
  }
16736
16910
  async function runCheckV2(dir, args = {}, deps = {}) {
16737
- const configPath = path51.join(dir, APITOGO_CONFIG_FILENAME);
16911
+ const configPath = path52.join(dir, APITOGO_CONFIG_FILENAME);
16738
16912
  let report;
16739
16913
  try {
16740
16914
  const { config: config2, resolved } = loadConfigV2(dir);
@@ -16844,7 +17018,7 @@ async function checkV2(dir, args, deps = platformDependencies(args)) {
16844
17018
  init_package_json();
16845
17019
 
16846
17020
  // src/cli/init/v2-handler.ts
16847
- import path52 from "node:path";
17021
+ import path53 from "node:path";
16848
17022
  import { createInterface } from "node:readline/promises";
16849
17023
  function parseDocsFlag(value) {
16850
17024
  if (value === void 0) return void 0;
@@ -16866,15 +17040,15 @@ async function ask(question) {
16866
17040
  }
16867
17041
  function describePlan(plan2) {
16868
17042
  return {
16869
- willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path52.relative(plan2.dir, entry.path)),
16870
- 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)),
16871
17045
  replacesConfig: plan2.replacesConfig
16872
17046
  };
16873
17047
  }
16874
17048
  async function initV2(argv) {
16875
17049
  const json = argv.json === true;
16876
17050
  try {
16877
- const dir = path52.resolve(process.cwd(), argv.dir ?? ".");
17051
+ const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
16878
17052
  const plan2 = await planInit({
16879
17053
  dir,
16880
17054
  name: argv.name,
@@ -16915,8 +17089,8 @@ async function initV2(argv) {
16915
17089
  status: "created",
16916
17090
  dir,
16917
17091
  ...describePlan(plan2),
16918
- created: result.created.map((p) => path52.relative(dir, p)),
16919
- 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))
16920
17094
  },
16921
17095
  null,
16922
17096
  2
@@ -16925,7 +17099,7 @@ async function initV2(argv) {
16925
17099
  return;
16926
17100
  }
16927
17101
  printResultToConsole(
16928
- `Created ${path52.basename(plan2.configPath)} (config version 2) in ${dir}.`
17102
+ `Created ${path53.basename(plan2.configPath)} (config version 2) in ${dir}.`
16929
17103
  );
16930
17104
  printHintToConsole(
16931
17105
  "Next: add a service with `apitogo add <portal|gateway|identity|plans>`, or run `apitogo create` for a guided setup."
@@ -16952,7 +17126,7 @@ async function initV2(argv) {
16952
17126
 
16953
17127
  // src/cli/services/packages.ts
16954
17128
  import { existsSync as existsSync7 } from "node:fs";
16955
- import path53 from "node:path";
17129
+ import path54 from "node:path";
16956
17130
 
16957
17131
  // src/cli/common/spawn-command.ts
16958
17132
  import {
@@ -16985,18 +17159,18 @@ function packagesForConfig(config2) {
16985
17159
  ];
16986
17160
  }
16987
17161
  function isInstalled(dir, name) {
16988
- for (let current = path53.resolve(dir); ; current = path53.dirname(current)) {
16989
- 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")))
16990
17164
  return true;
16991
- if (path53.dirname(current) === current) return false;
17165
+ if (path54.dirname(current) === current) return false;
16992
17166
  }
16993
17167
  }
16994
17168
  function missingPackages(dir, names) {
16995
17169
  return names.filter((name) => !isInstalled(dir, name));
16996
17170
  }
16997
17171
  function detectPackageManager(dir) {
16998
- if (existsSync7(path53.join(dir, "pnpm-lock.yaml"))) return "pnpm";
16999
- 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";
17000
17174
  return "npm";
17001
17175
  }
17002
17176
  function installSpecsCommand(manager, specs) {
@@ -17054,7 +17228,7 @@ async function installMissingPackages(dir, config2, version, options) {
17054
17228
  reason: "--no-install was given",
17055
17229
  command: shown
17056
17230
  };
17057
- await ensurePackageJson(dir, config2.project?.name ?? path53.basename(dir));
17231
+ await ensurePackageJson(dir, config2.project?.name ?? path54.basename(dir));
17058
17232
  await (options.installer ?? runInstaller)(dir, command2);
17059
17233
  return { status: "installed", packages: missing, command: shown };
17060
17234
  }
@@ -17117,7 +17291,7 @@ var list = (value) => {
17117
17291
  return items.length > 0 ? items : void 0;
17118
17292
  };
17119
17293
  function addOptionsFromArguments(service, argv) {
17120
- const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
17294
+ const dir = path55.resolve(process.cwd(), argv.dir ?? ".");
17121
17295
  const issuers = list(argv.issuer);
17122
17296
  return {
17123
17297
  dir,
@@ -17171,8 +17345,8 @@ function describe(edit, dir) {
17171
17345
  return {
17172
17346
  service: edit.service,
17173
17347
  action: edit.action,
17174
- willCreate: edit.files.map((file) => path54.relative(dir, file.path)),
17175
- 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)),
17176
17350
  notes: edit.notes
17177
17351
  };
17178
17352
  }
@@ -17186,7 +17360,7 @@ async function run(action, argv, plan2, deps = {}) {
17186
17360
  `Choose one of ${SERVICES.join(", ")}.`
17187
17361
  );
17188
17362
  }
17189
- const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
17363
+ const dir = path55.resolve(process.cwd(), argv.dir ?? ".");
17190
17364
  if (!await isConfigV2(dir)) {
17191
17365
  throw new ServiceEditError(
17192
17366
  `${action} works with a config version 2 project, and there is none in ${dir}.`,
@@ -17254,7 +17428,7 @@ async function run(action, argv, plan2, deps = {}) {
17254
17428
  {
17255
17429
  status: action === "add" ? "added" : "removed",
17256
17430
  ...describe(edit, dir),
17257
- created: created.map((file) => path54.relative(dir, file)),
17431
+ created: created.map((file) => path55.relative(dir, file)),
17258
17432
  ...install ? { packages: install } : {}
17259
17433
  },
17260
17434
  null,
@@ -17264,7 +17438,7 @@ async function run(action, argv, plan2, deps = {}) {
17264
17438
  return;
17265
17439
  }
17266
17440
  printResultToConsole(
17267
- `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path54.basename(edit.configPath)}.`
17441
+ `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path55.basename(edit.configPath)}.`
17268
17442
  );
17269
17443
  } catch (error) {
17270
17444
  const message = error instanceof Error ? error.message : String(error);
@@ -17325,7 +17499,7 @@ var removeService = (argv) => run(
17325
17499
  "remove",
17326
17500
  argv,
17327
17501
  (service) => planRemoveService(service, {
17328
- dir: path54.resolve(process.cwd(), argv.dir ?? "."),
17502
+ dir: path55.resolve(process.cwd(), argv.dir ?? "."),
17329
17503
  deleteFiles: argv.deleteFiles === true
17330
17504
  })
17331
17505
  );
@@ -17477,16 +17651,16 @@ var add_default = {
17477
17651
  };
17478
17652
 
17479
17653
  // src/cli/build/handler.ts
17480
- import path56 from "node:path";
17654
+ import path57 from "node:path";
17481
17655
  init_logger();
17482
17656
  init_package_json();
17483
17657
 
17484
17658
  // src/cli/preview/handler.ts
17485
- import path55 from "node:path";
17659
+ import path56 from "node:path";
17486
17660
  import { preview as vitePreview } from "vite";
17487
17661
  var DEFAULT_PREVIEW_PORT = 4e3;
17488
17662
  async function preview(argv) {
17489
- const dir = path55.resolve(process.cwd(), argv.dir);
17663
+ const dir = path56.resolve(process.cwd(), argv.dir);
17490
17664
  const viteConfig = await getViteConfig(dir, {
17491
17665
  command: "serve",
17492
17666
  mode: "production",
@@ -17529,7 +17703,7 @@ async function build(argv) {
17529
17703
  printDiagnosticsToConsole("");
17530
17704
  printDiagnosticsToConsole("");
17531
17705
  }
17532
- const dir = path56.resolve(process.cwd(), argv.dir);
17706
+ const dir = path57.resolve(process.cwd(), argv.dir);
17533
17707
  try {
17534
17708
  if (await isConfigV2(dir)) {
17535
17709
  const result = await buildV2({ dir, jsonOnly });
@@ -17594,10 +17768,10 @@ var build_default = {
17594
17768
  };
17595
17769
 
17596
17770
  // src/cli/check/handler.ts
17597
- import path62 from "node:path";
17771
+ import path63 from "node:path";
17598
17772
 
17599
17773
  // src/cli/platform/tools/check-publish-readiness.ts
17600
- import path61 from "node:path";
17774
+ import path62 from "node:path";
17601
17775
 
17602
17776
  // src/cli/platform/helpers/dev-portal-backend.ts
17603
17777
  function deriveProvisioningState(backend) {
@@ -17682,7 +17856,7 @@ function formatDevPortalBackendResponse(projectId, backend) {
17682
17856
  init_gateway_limits();
17683
17857
  import { existsSync as existsSync8 } from "node:fs";
17684
17858
  import { access as access5, mkdir as mkdir13, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
17685
- import path57 from "node:path";
17859
+ import path58 from "node:path";
17686
17860
 
17687
17861
  // src/cli/make-config/header-pricing-nav.ts
17688
17862
  var HEADER_PRICING_PATH = "/pricing";
@@ -17804,7 +17978,7 @@ function isPlanFree(plan2) {
17804
17978
  return (plan2.priceAmount ?? 0) <= 0;
17805
17979
  }
17806
17980
  function manifestFilePath(siteDirectory) {
17807
- return path57.join(path57.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17981
+ return path58.join(path58.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17808
17982
  }
17809
17983
  var STORED_MANIFEST_KEYS = [
17810
17984
  "authentication",
@@ -18228,23 +18402,23 @@ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
18228
18402
  if (!trimmed) {
18229
18403
  return void 0;
18230
18404
  }
18231
- return path57.resolve(siteDirectory, trimmed);
18405
+ return path58.resolve(siteDirectory, trimmed);
18232
18406
  }
18233
18407
  function resolveBackendOpenApiPath(openApiPath, options) {
18234
18408
  const trimmed = openApiPath?.trim();
18235
18409
  if (!trimmed) {
18236
18410
  return void 0;
18237
18411
  }
18238
- if (path57.isAbsolute(trimmed)) {
18412
+ if (path58.isAbsolute(trimmed)) {
18239
18413
  return trimmed;
18240
18414
  }
18241
- const siteResolved = path57.resolve(options.siteDirectory, trimmed);
18415
+ const siteResolved = path58.resolve(options.siteDirectory, trimmed);
18242
18416
  if (existsSync8(siteResolved)) {
18243
18417
  return siteResolved;
18244
18418
  }
18245
18419
  const backendDir = options.backendSourceDirectory?.trim();
18246
18420
  if (backendDir) {
18247
- const backendResolved = path57.resolve(backendDir, trimmed);
18421
+ const backendResolved = path58.resolve(backendDir, trimmed);
18248
18422
  if (existsSync8(backendResolved)) {
18249
18423
  return backendResolved;
18250
18424
  }
@@ -18269,7 +18443,7 @@ async function readManifest2(siteDirectory) {
18269
18443
  }
18270
18444
  async function writeManifest2(siteDirectory, manifest) {
18271
18445
  const filePath = manifestFilePath(siteDirectory);
18272
- await mkdir13(path57.dirname(filePath), { recursive: true });
18446
+ await mkdir13(path58.dirname(filePath), { recursive: true });
18273
18447
  let config2 = {};
18274
18448
  if (await pathExists(filePath)) {
18275
18449
  const raw = await readFile19(filePath, "utf8");
@@ -18295,7 +18469,7 @@ import {
18295
18469
  unlink,
18296
18470
  writeFile as writeFile17
18297
18471
  } from "node:fs/promises";
18298
- import path58 from "node:path";
18472
+ import path59 from "node:path";
18299
18473
  var ENV_FILENAME = ".env";
18300
18474
  var COLLAPSED_UPPERCASE_ALIASES2 = {
18301
18475
  SECRETKEY: "secretKey",
@@ -18414,12 +18588,12 @@ async function readEnvFile(filePath) {
18414
18588
  }
18415
18589
  }
18416
18590
  async function loadSiteEnv(siteDirectory) {
18417
- const resolvedDirectory = path58.resolve(siteDirectory);
18591
+ const resolvedDirectory = path59.resolve(siteDirectory);
18418
18592
  const merged = { ...process.env };
18419
18593
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18420
18594
  Object.assign(
18421
18595
  merged,
18422
- await readEnvFile(path58.join(resolvedDirectory, fileName))
18596
+ await readEnvFile(path59.join(resolvedDirectory, fileName))
18423
18597
  );
18424
18598
  }
18425
18599
  return merged;
@@ -18456,7 +18630,7 @@ function buildAuthSecretsJson(secrets2, options) {
18456
18630
  return payload;
18457
18631
  }
18458
18632
  function envFilePath(siteDirectory) {
18459
- return path58.join(path58.resolve(siteDirectory), ENV_FILENAME);
18633
+ return path59.join(path59.resolve(siteDirectory), ENV_FILENAME);
18460
18634
  }
18461
18635
  function getSecretsStatus(siteDirectory, manifest, secrets2, envExists) {
18462
18636
  const providerSecretStatus = {};
@@ -18492,7 +18666,7 @@ function validateSecretsForPublish(manifest, secrets2, emailOptions) {
18492
18666
  }
18493
18667
  async function siteEnvFileHasEntries(siteDirectory) {
18494
18668
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18495
- const loaded = await readEnvFile(path58.join(siteDirectory, fileName));
18669
+ const loaded = await readEnvFile(path59.join(siteDirectory, fileName));
18496
18670
  if (Object.keys(loaded).length > 0) {
18497
18671
  return true;
18498
18672
  }
@@ -18500,7 +18674,7 @@ async function siteEnvFileHasEntries(siteDirectory) {
18500
18674
  return false;
18501
18675
  }
18502
18676
  async function resolvePublishConfig(siteDirectory, emailOptions) {
18503
- const resolvedDirectory = path58.resolve(siteDirectory);
18677
+ const resolvedDirectory = path59.resolve(siteDirectory);
18504
18678
  const manifestRaw = await readManifest2(resolvedDirectory);
18505
18679
  if (!manifestRaw) {
18506
18680
  throw new Error("apitogo.config.json not found in sourceDirectory.");
@@ -18532,11 +18706,11 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
18532
18706
  init_gateway_limits();
18533
18707
  import { readFile as readFile21 } from "node:fs/promises";
18534
18708
  import { createRequire as createRequire2 } from "node:module";
18535
- import path59 from "node:path";
18709
+ import path60 from "node:path";
18536
18710
  var cachedFallbackTs;
18537
18711
  async function loadTypeScriptForSite(sourceDirectory) {
18538
18712
  const require2 = createRequire2(
18539
- path59.join(path59.resolve(sourceDirectory), "package.json")
18713
+ path60.join(path60.resolve(sourceDirectory), "package.json")
18540
18714
  );
18541
18715
  try {
18542
18716
  return require2("typescript");
@@ -18583,7 +18757,7 @@ async function referencedFileExists(sourceDirectory, reference) {
18583
18757
  return true;
18584
18758
  }
18585
18759
  return pathExists(
18586
- path59.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18760
+ path60.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18587
18761
  );
18588
18762
  }
18589
18763
  async function validateSiteConfigStructure(config2, sourceDirectory) {
@@ -18698,11 +18872,11 @@ var DEFAULT_TSX_CANDIDATES = [
18698
18872
  "landing/LandingPage.tsx"
18699
18873
  ];
18700
18874
  async function validateDevPortalSiteConfig(sourceDirectory, options) {
18701
- const resolvedDir = path59.resolve(sourceDirectory);
18875
+ const resolvedDir = path60.resolve(sourceDirectory);
18702
18876
  const issues = [];
18703
18877
  const checkedFiles = [];
18704
18878
  const skippedChecks = [];
18705
- const jsonPath = path59.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18879
+ const jsonPath = path60.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18706
18880
  if (await pathExists(jsonPath)) {
18707
18881
  checkedFiles.push(APITOGO_CONFIG_FILENAME2);
18708
18882
  let parsed;
@@ -18734,7 +18908,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18734
18908
  );
18735
18909
  const existingTsxFiles = [];
18736
18910
  for (const relativeFile of tsxFiles) {
18737
- if (await pathExists(path59.join(resolvedDir, relativeFile))) {
18911
+ if (await pathExists(path60.join(resolvedDir, relativeFile))) {
18738
18912
  existingTsxFiles.push(relativeFile);
18739
18913
  }
18740
18914
  }
@@ -18748,7 +18922,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18748
18922
  for (const relativeFile of existingTsxFiles) {
18749
18923
  checkedFiles.push(relativeFile);
18750
18924
  const content = await readFile21(
18751
- path59.join(resolvedDir, relativeFile),
18925
+ path60.join(resolvedDir, relativeFile),
18752
18926
  "utf8"
18753
18927
  );
18754
18928
  issues.push(
@@ -18770,7 +18944,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18770
18944
 
18771
18945
  // src/cli/platform/helpers/npm-build.ts
18772
18946
  import { access as access7, readFile as readFile22 } from "node:fs/promises";
18773
- import path60 from "node:path";
18947
+ import path61 from "node:path";
18774
18948
  var BUILD_LOG_TAIL_BYTES = 8192;
18775
18949
  var MIN_NODE_MAJOR = 20;
18776
18950
  var NpmBuildError = class extends Error {
@@ -18836,13 +19010,13 @@ function runCommand(command2, args, cwd, env) {
18836
19010
  }
18837
19011
  async function runNpmInstall(sourceDirectory, options) {
18838
19012
  assertNodeVersion(options?.nodeVersion);
18839
- const cwd = path60.resolve(sourceDirectory);
18840
- const packageJsonPath = path60.join(cwd, "package.json");
19013
+ const cwd = path61.resolve(sourceDirectory);
19014
+ const packageJsonPath = path61.join(cwd, "package.json");
18841
19015
  if (!await pathExists2(packageJsonPath)) {
18842
19016
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18843
19017
  }
18844
19018
  const startedAt = Date.now();
18845
- const lockPath = path60.join(cwd, "package-lock.json");
19019
+ const lockPath = path61.join(cwd, "package-lock.json");
18846
19020
  const installArgs = await pathExists2(lockPath) ? ["ci"] : ["install"];
18847
19021
  const install = await runCommand("npm", installArgs, cwd);
18848
19022
  const installLog = install.output;
@@ -18860,8 +19034,8 @@ async function runNpmInstall(sourceDirectory, options) {
18860
19034
  }
18861
19035
  async function runNpmBuild(sourceDirectory, options) {
18862
19036
  assertNodeVersion(options?.nodeVersion);
18863
- const cwd = path60.resolve(sourceDirectory);
18864
- const packageJsonPath = path60.join(cwd, "package.json");
19037
+ const cwd = path61.resolve(sourceDirectory);
19038
+ const packageJsonPath = path61.join(cwd, "package.json");
18865
19039
  if (!await pathExists2(packageJsonPath)) {
18866
19040
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18867
19041
  }
@@ -18878,8 +19052,8 @@ async function runNpmBuild(sourceDirectory, options) {
18878
19052
  build2.exitCode
18879
19053
  );
18880
19054
  }
18881
- const distPath = path60.join(cwd, "dist");
18882
- const indexPath = path60.join(distPath, "index.html");
19055
+ const distPath = path61.join(cwd, "dist");
19056
+ const indexPath = path61.join(distPath, "index.html");
18883
19057
  if (!await pathExists2(indexPath)) {
18884
19058
  throw new NpmBuildError(
18885
19059
  `Build completed but dist/index.html was not found at ${indexPath}`,
@@ -19263,7 +19437,7 @@ function validateManifestForPlatformPublish(manifest) {
19263
19437
 
19264
19438
  // src/cli/platform/tools/check-publish-readiness.ts
19265
19439
  async function checkPublishReadiness(client, _config, args) {
19266
- const sourceDirectory = path61.resolve(args.sourceDirectory);
19440
+ const sourceDirectory = path62.resolve(args.sourceDirectory);
19267
19441
  const manifest = await readManifest2(sourceDirectory) ?? createScaffoldManifest();
19268
19442
  const emailOptions = await resolveEmailOptionsForPublish(
19269
19443
  client,
@@ -19341,7 +19515,7 @@ async function checkPublishReadiness(client, _config, args) {
19341
19515
 
19342
19516
  // src/cli/check/handler.ts
19343
19517
  async function check(argv) {
19344
- const sourceDirectory = path62.resolve(process.cwd(), argv.dir);
19518
+ const sourceDirectory = path63.resolve(process.cwd(), argv.dir);
19345
19519
  if (await isConfigV2(sourceDirectory)) {
19346
19520
  await checkV2(sourceDirectory, argv);
19347
19521
  return;
@@ -19405,7 +19579,7 @@ var check_default = {
19405
19579
 
19406
19580
  // src/cli/make-config/handler.ts
19407
19581
  init_invariant();
19408
- import path64 from "node:path";
19582
+ import path65 from "node:path";
19409
19583
  import { stdin as input2 } from "node:process";
19410
19584
 
19411
19585
  // src/cli/make-config/interactive.ts
@@ -19415,7 +19589,7 @@ import { createInterface as createInterface3 } from "node:readline/promises";
19415
19589
  // src/cli/make-config/v2-edits.ts
19416
19590
  init_apitogo_config_core();
19417
19591
  import { access as access8, mkdir as mkdir15, readFile as readFile23, writeFile as writeFile18 } from "node:fs/promises";
19418
- import path63 from "node:path";
19592
+ import path64 from "node:path";
19419
19593
  import { glob as glob8 } from "glob";
19420
19594
  init_invariant();
19421
19595
  var ConfigEditError = class extends ApitogoError {
@@ -19428,10 +19602,10 @@ var exists4 = (target) => access8(target).then(
19428
19602
  () => true,
19429
19603
  () => false
19430
19604
  );
19431
- var toPosix2 = (value) => value.split(path63.sep).join("/");
19432
- 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)) || ".";
19433
19607
  async function openConfigProject(dir) {
19434
- const configPath = path63.join(dir, APITOGO_CONFIG_FILENAME);
19608
+ const configPath = path64.join(dir, APITOGO_CONFIG_FILENAME);
19435
19609
  if (!await exists4(configPath)) {
19436
19610
  throw new ConfigEditError(
19437
19611
  `There is no ${APITOGO_CONFIG_FILENAME} in ${dir}.`,
@@ -19496,7 +19670,7 @@ function setPortalName(project, name) {
19496
19670
  }
19497
19671
  async function addGatewaySpec(project, specPath) {
19498
19672
  const gateway = requireGateway(project);
19499
- const absolute = path63.resolve(project.dir, specPath);
19673
+ const absolute = path64.resolve(project.dir, specPath);
19500
19674
  if (!await exists4(absolute)) {
19501
19675
  throw new ConfigEditError(`The OpenAPI file ${specPath} does not exist.`);
19502
19676
  }
@@ -19540,7 +19714,7 @@ function parseBackendInput(input5) {
19540
19714
  }
19541
19715
  async function setGatewayOrigin(project, backend) {
19542
19716
  const gateway = requireGateway(project);
19543
- const source = path63.resolve(project.dir, backend.source);
19717
+ const source = path64.resolve(project.dir, backend.source);
19544
19718
  if (!await exists4(source)) {
19545
19719
  throw new ConfigEditError(
19546
19720
  `The API source folder ${backend.source} does not exist.`
@@ -19618,6 +19792,164 @@ export default function ${pageComponentName(slug)}() {
19618
19792
  }
19619
19793
  `;
19620
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
+ }
19621
19953
  async function writeDocsPage(project, input5) {
19622
19954
  requirePortal(project);
19623
19955
  if (/^[A-Za-z]:[\\/]/.test(input5.slug.trim())) {
@@ -19641,13 +19973,13 @@ async function writeDocsPage(project, input5) {
19641
19973
  "For example --page-order 1 puts the page first in the sidebar."
19642
19974
  );
19643
19975
  }
19644
- const base = path63.join(portal.dir, "pages", ...slug.split("/"));
19976
+ const base = path64.join(portal.dir, "pages", ...slug.split("/"));
19645
19977
  const existing = (await Promise.all(
19646
19978
  [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
19647
19979
  async (file2) => await exists4(file2) ? file2 : null
19648
19980
  )
19649
19981
  )).find((file2) => file2 !== null);
19650
- const relative = (file2) => toPosix2(path63.relative(project.dir, file2));
19982
+ const relative = (file2) => toPosix2(path64.relative(project.dir, file2));
19651
19983
  const write = (file2, title, order = input5.order) => writeFile18(
19652
19984
  file2,
19653
19985
  file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content, order) : buildDocsPage(title, input5.content, order),
@@ -19681,17 +20013,17 @@ async function writeDocsPage(project, input5) {
19681
20013
  }
19682
20014
  file = `${base}.${input5.format ?? "tsx"}`;
19683
20015
  action = "added";
19684
- await mkdir15(path63.dirname(file), { recursive: true });
20016
+ await mkdir15(path64.dirname(file), { recursive: true });
19685
20017
  await write(file, title);
19686
20018
  }
19687
- const same = (match) => path63.resolve(match) === path63.resolve(file);
20019
+ const same = (match) => path64.resolve(match) === path64.resolve(file);
19688
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);
19689
20021
  return {
19690
20022
  action,
19691
20023
  slug,
19692
20024
  route: `/${slug}`,
19693
20025
  file: relative(file),
19694
- format: path63.extname(file).slice(1),
20026
+ format: path64.extname(file).slice(1),
19695
20027
  inPortal
19696
20028
  };
19697
20029
  }
@@ -19770,7 +20102,7 @@ async function readPlanCatalog(project) {
19770
20102
  }
19771
20103
  var skuOf = (item) => isPlainObject3(item) && typeof item.sku === "string" ? item.sku.trim().toLowerCase() : void 0;
19772
20104
  async function writePlanCatalog(file, catalog, plans) {
19773
- await mkdir15(path63.dirname(file), { recursive: true });
20105
+ await mkdir15(path64.dirname(file), { recursive: true });
19774
20106
  await writeFile18(
19775
20107
  file,
19776
20108
  `${JSON.stringify({ ...catalog, plans }, null, 2)}
@@ -19793,7 +20125,7 @@ async function upsertPlan(project, plan2) {
19793
20125
  return {
19794
20126
  action: index === -1 ? "added" : "updated",
19795
20127
  sku: plan2.sku,
19796
- file: toPosix2(path63.relative(project.dir, file)),
20128
+ file: toPosix2(path64.relative(project.dir, file)),
19797
20129
  paid,
19798
20130
  needsBilling: paid && billing.provider !== "stripe"
19799
20131
  };
@@ -19883,12 +20215,12 @@ async function removePlan(project, rawSku) {
19883
20215
  if (next.length === catalog.plans.length) {
19884
20216
  const known = catalog.plans.map(skuOf).filter(Boolean).join(", ");
19885
20217
  throw new ConfigEditError(
19886
- `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))}.`,
19887
20219
  known ? `The plans are: ${known}.` : "The plans file has no plans."
19888
20220
  );
19889
20221
  }
19890
20222
  await writePlanCatalog(file, catalog, next);
19891
- return { sku, file: toPosix2(path63.relative(project.dir, file)) };
20223
+ return { sku, file: toPosix2(path64.relative(project.dir, file)) };
19892
20224
  }
19893
20225
 
19894
20226
  // src/cli/make-config/wrap-up.ts
@@ -19911,6 +20243,12 @@ function printPageResult(page) {
19911
20243
  printSuccessToConsole(
19912
20244
  `Page ${page.action}: ${page.file} (route ${page.route})`
19913
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
+ }
19914
20252
  if (page.inPortal) {
19915
20253
  printHintToConsole(
19916
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."
@@ -20169,7 +20507,7 @@ async function applyConfigFlags(project, argv, changes) {
20169
20507
  return { project: next, openApi };
20170
20508
  }
20171
20509
  async function makeConfig(argv) {
20172
- const dir = path64.resolve(process.cwd(), argv.dir);
20510
+ const dir = path65.resolve(process.cwd(), argv.dir);
20173
20511
  const interactive = shouldRunInteractive(argv);
20174
20512
  const json = argv.json === true;
20175
20513
  const changes = [];
@@ -20212,7 +20550,32 @@ async function makeConfig(argv) {
20212
20550
  const applied = await applyConfigFlags(project, argv, changes);
20213
20551
  project = applied.project;
20214
20552
  openApi = applied.openApi;
20215
- 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) {
20216
20579
  page = await writeDocsPage(project, {
20217
20580
  slug: argv.pageSlug,
20218
20581
  title: argv.pageTitle,
@@ -20361,6 +20724,10 @@ function addConfigOptions(yargs2) {
20361
20724
  type: "boolean",
20362
20725
  default: false,
20363
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)"
20364
20731
  }).option("plan-sku", {
20365
20732
  type: "string",
20366
20733
  describe: "Add or change the plan with this SKU in the plans file; the --plan-* options set its fields"
@@ -20398,7 +20765,10 @@ function addConfigOptions(yargs2) {
20398
20765
  ).example(
20399
20766
  "$0 config --plan-sku enterprise --plan-price custom",
20400
20767
  "A contact-us plan"
20401
- ).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", {
20402
20772
  type: "string",
20403
20773
  describe: "JSON plan added to the plans file (plans.file, default apitogo/plans.json), or replacing the one with the same sku"
20404
20774
  }).option("patch", {
@@ -20420,27 +20790,27 @@ var config_default = {
20420
20790
  init_apitogo_config_core();
20421
20791
  import { execFile } from "node:child_process";
20422
20792
  import { readFile as readFile24 } from "node:fs/promises";
20423
- import path66 from "node:path";
20793
+ import path67 from "node:path";
20424
20794
  import { promisify } from "node:util";
20425
20795
 
20426
20796
  // src/cli/create/dry-run.ts
20427
20797
  import { mkdir as mkdir16, mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
20428
20798
  import { tmpdir } from "node:os";
20429
- import path65 from "node:path";
20799
+ import path66 from "node:path";
20430
20800
  async function listFiles(root, dir = root) {
20431
20801
  const entries = await readdir4(dir, { withFileTypes: true });
20432
20802
  const nested = await Promise.all(
20433
20803
  entries.map(async (entry) => {
20434
- const full = path65.join(dir, entry.name);
20804
+ const full = path66.join(dir, entry.name);
20435
20805
  if (entry.isDirectory()) return listFiles(root, full);
20436
- return [path65.relative(root, full).split(path65.sep).join("/")];
20806
+ return [path66.relative(root, full).split(path66.sep).join("/")];
20437
20807
  })
20438
20808
  );
20439
20809
  return nested.flat();
20440
20810
  }
20441
20811
  async function previewCreate(input5) {
20442
- const scratch = await mkdtemp(path65.join(tmpdir(), "apitogo-create-"));
20443
- const dir = path65.join(scratch, "project");
20812
+ const scratch = await mkdtemp(path66.join(tmpdir(), "apitogo-create-"));
20813
+ const dir = path66.join(scratch, "project");
20444
20814
  try {
20445
20815
  await mkdir16(dir);
20446
20816
  await applyInitPlan(await planInit({ dir, name: input5.name }));
@@ -20473,9 +20843,9 @@ async function previewCreate(input5) {
20473
20843
  var execFileAsync = promisify(execFile);
20474
20844
  function nextStepHint(dir) {
20475
20845
  const linked = loadConfigV2(dir).resolved.project.id;
20476
- const cd = path66.resolve(dir) === path66.resolve(process.cwd());
20846
+ const cd = path67.resolve(dir) === path67.resolve(process.cwd());
20477
20847
  const steps = linked ? "apitogo deploy" : "apitogo project create, then apitogo deploy";
20478
- 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}`;
20479
20849
  }
20480
20850
  function projectFolderName(name) {
20481
20851
  return name.trim().replace(/[<>:"/\\|?*\s]+/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "apitogo-project";
@@ -20501,13 +20871,13 @@ async function gitHasUncommittedChanges(dir) {
20501
20871
  async function defaultName(dir) {
20502
20872
  try {
20503
20873
  const parsed = JSON.parse(
20504
- await readFile24(path66.join(dir, "package.json"), "utf8")
20874
+ await readFile24(path67.join(dir, "package.json"), "utf8")
20505
20875
  );
20506
20876
  if (typeof parsed.name === "string" && parsed.name.trim())
20507
20877
  return parsed.name.replace(/^@[^/]+\//, "");
20508
20878
  } catch {
20509
20879
  }
20510
- return path66.basename(dir);
20880
+ return path67.basename(dir);
20511
20881
  }
20512
20882
  function parseServicesFlag(value) {
20513
20883
  const names = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
@@ -20533,7 +20903,7 @@ async function createProject(argv, deps = {}) {
20533
20903
  if (!json) printDiagnosticsToConsole(line2);
20534
20904
  };
20535
20905
  try {
20536
- const base = path66.resolve(process.cwd(), argv.dir ?? ".");
20906
+ const base = path67.resolve(process.cwd(), argv.dir ?? ".");
20537
20907
  let dir = base;
20538
20908
  let requested = parseServicesFlag(argv.services);
20539
20909
  let template = argv.template;
@@ -20556,8 +20926,8 @@ async function createProject(argv, deps = {}) {
20556
20926
  default: await defaultName(base)
20557
20927
  }) : await defaultName(base));
20558
20928
  const folder = projectFolderName(name);
20559
- if (argv.dir === void 0 && folder !== path66.basename(base))
20560
- dir = path66.join(base, folder);
20929
+ if (argv.dir === void 0 && folder !== path67.basename(base))
20930
+ dir = path67.join(base, folder);
20561
20931
  }
20562
20932
  if (await (deps.hasUncommittedChanges ?? gitHasUncommittedChanges)(dir))
20563
20933
  say(
@@ -20570,7 +20940,7 @@ async function createProject(argv, deps = {}) {
20570
20940
  if (argv.dryRun) {
20571
20941
  const services = requested.length > 0 ? requested : templateServices ? STARTER_SERVICES : [];
20572
20942
  const preview2 = services.length > 0 ? await previewCreate({ name, template, services }) : void 0;
20573
- const folder = path66.relative(process.cwd(), dir) || ".";
20943
+ const folder = path67.relative(process.cwd(), dir) || ".";
20574
20944
  if (json)
20575
20945
  console.log(
20576
20946
  JSON.stringify({
@@ -20603,7 +20973,7 @@ async function createProject(argv, deps = {}) {
20603
20973
  }
20604
20974
  await applyInitPlan(plan2);
20605
20975
  say(
20606
- 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}.`
20607
20977
  );
20608
20978
  } else {
20609
20979
  say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
@@ -20745,10 +21115,10 @@ var create_default = {
20745
21115
  };
20746
21116
 
20747
21117
  // src/cli/cmds/deploy.ts
20748
- import path85 from "node:path";
21118
+ import path86 from "node:path";
20749
21119
 
20750
21120
  // src/cli/apply/handler.ts
20751
- import path78 from "node:path";
21121
+ import path79 from "node:path";
20752
21122
 
20753
21123
  // src/cli/platform/helpers/gateway-plan-validation.ts
20754
21124
  init_gateway_limits();
@@ -20967,11 +21337,11 @@ init_gateway_limits();
20967
21337
  // src/cli/platform/helpers/deploy-artifact-hash.ts
20968
21338
  import { createHash as createHash3 } from "node:crypto";
20969
21339
  import { createReadStream, promises as fs6 } from "node:fs";
20970
- import path68 from "node:path";
21340
+ import path69 from "node:path";
20971
21341
 
20972
21342
  // src/cli/platform/helpers/package-files.ts
20973
21343
  import { promises as fs5 } from "node:fs";
20974
- import path67 from "node:path";
21344
+ import path68 from "node:path";
20975
21345
  import ignore from "ignore";
20976
21346
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
20977
21347
  "node_modules",
@@ -20985,7 +21355,7 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20985
21355
  const ig = ignore();
20986
21356
  ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
20987
21357
  if (extraExcludes.length > 0) ig.add([...extraExcludes]);
20988
- const gitignorePath = path67.join(rootDir, ".gitignore");
21358
+ const gitignorePath = path68.join(rootDir, ".gitignore");
20989
21359
  try {
20990
21360
  const content = await fs5.readFile(gitignorePath, "utf8");
20991
21361
  ig.add(content);
@@ -20994,11 +21364,11 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20994
21364
  return ig;
20995
21365
  }
20996
21366
  async function collectFiles(rootDir, relativeDir, ig, files) {
20997
- const absDir = path67.join(rootDir, relativeDir);
21367
+ const absDir = path68.join(rootDir, relativeDir);
20998
21368
  const entries = await fs5.readdir(absDir, { withFileTypes: true });
20999
21369
  for (const entry of entries) {
21000
- const rel2 = relativeDir ? path67.join(relativeDir, entry.name) : entry.name;
21001
- 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("/");
21002
21372
  if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
21003
21373
  continue;
21004
21374
  }
@@ -21013,7 +21383,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
21013
21383
  }
21014
21384
  }
21015
21385
  async function collectPackageableFiles(sourceDirectory, extraExcludes = []) {
21016
- const resolved = path67.resolve(sourceDirectory);
21386
+ const resolved = path68.resolve(sourceDirectory);
21017
21387
  const stat7 = await fs5.stat(resolved).catch(() => null);
21018
21388
  if (!stat7?.isDirectory()) {
21019
21389
  throw new Error(`sourceDirectory is not a directory: ${resolved}`);
@@ -21039,7 +21409,7 @@ async function sha256File(filePath) {
21039
21409
  return hash2.digest("hex");
21040
21410
  }
21041
21411
  async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
21042
- const resolved = path68.resolve(rootDirectory);
21412
+ const resolved = path69.resolve(rootDirectory);
21043
21413
  const files = (await collectPackageableFiles(resolved, extraExcludes)).sort(
21044
21414
  (a, b) => a.localeCompare(b)
21045
21415
  );
@@ -21047,13 +21417,13 @@ async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
21047
21417
  for (const rel2 of files) {
21048
21418
  hash2.update(rel2);
21049
21419
  hash2.update("\0");
21050
- hash2.update(await fs6.readFile(path68.join(resolved, rel2)));
21420
+ hash2.update(await fs6.readFile(path69.join(resolved, rel2)));
21051
21421
  hash2.update("\0");
21052
21422
  }
21053
21423
  return hash2.digest("hex");
21054
21424
  }
21055
21425
  async function hashDevPortalSiteSources(siteDirectory, options) {
21056
- const resolved = path68.resolve(siteDirectory);
21426
+ const resolved = path69.resolve(siteDirectory);
21057
21427
  const hash2 = createHash3("sha256");
21058
21428
  const candidates = [
21059
21429
  "apitogo.config.json",
@@ -21063,7 +21433,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
21063
21433
  "zudoku.config.tsx"
21064
21434
  ];
21065
21435
  for (const name of candidates) {
21066
- const full = path68.join(resolved, name);
21436
+ const full = path69.join(resolved, name);
21067
21437
  try {
21068
21438
  hash2.update(name);
21069
21439
  hash2.update("\0");
@@ -21073,7 +21443,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
21073
21443
  }
21074
21444
  }
21075
21445
  for (const dir of ["pages", "landing", "src"]) {
21076
- const full = path68.join(resolved, dir);
21446
+ const full = path69.join(resolved, dir);
21077
21447
  try {
21078
21448
  const st = await fs6.stat(full);
21079
21449
  if (!st.isDirectory()) continue;
@@ -21302,8 +21672,8 @@ function normalizeActionKey(actionKey2) {
21302
21672
  const trimmed = actionKey2.trim();
21303
21673
  if (trimmed.includes(":")) {
21304
21674
  const [method2, ...pathParts] = trimmed.split(":");
21305
- const path100 = pathParts.join(":").trim();
21306
- return `${method2?.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21675
+ const path101 = pathParts.join(":").trim();
21676
+ return `${method2?.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
21307
21677
  }
21308
21678
  const spaceIndex = trimmed.indexOf(" ");
21309
21679
  if (spaceIndex <= 0) {
@@ -21312,8 +21682,8 @@ function normalizeActionKey(actionKey2) {
21312
21682
  );
21313
21683
  }
21314
21684
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21315
- const path99 = trimmed.slice(spaceIndex + 1).trim();
21316
- return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21685
+ const path100 = trimmed.slice(spaceIndex + 1).trim();
21686
+ return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21317
21687
  }
21318
21688
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
21319
21689
  const applied = [];
@@ -21792,7 +22162,7 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
21792
22162
 
21793
22163
  // src/cli/platform/helpers/openapi-gateway-servers.ts
21794
22164
  import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile19 } from "node:fs/promises";
21795
- import path69 from "node:path";
22165
+ import path70 from "node:path";
21796
22166
  import YAML2 from "yaml";
21797
22167
  var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
21798
22168
  var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
@@ -21824,7 +22194,7 @@ async function resolveProjectGatewayUrl(client, projectId) {
21824
22194
  }
21825
22195
  async function readOpenApiDocument(filePath) {
21826
22196
  const content = await readFile25(filePath, "utf8");
21827
- const ext = path69.extname(filePath).toLowerCase();
22197
+ const ext = path70.extname(filePath).toLowerCase();
21828
22198
  const parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
21829
22199
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21830
22200
  throw new Error(`OpenAPI document must be an object: ${filePath}`);
@@ -21832,13 +22202,13 @@ async function readOpenApiDocument(filePath) {
21832
22202
  return parsed;
21833
22203
  }
21834
22204
  async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
21835
- const site = path69.resolve(siteDirectory);
21836
- const source = path69.resolve(sourceOpenApiPath);
22205
+ const site = path70.resolve(siteDirectory);
22206
+ const source = path70.resolve(sourceOpenApiPath);
21837
22207
  const relativeInput = PUBLISH_OPENAPI_RELATIVE;
21838
- const outputPath = path69.join(site, ...relativeInput.split("/"));
22208
+ const outputPath = path70.join(site, ...relativeInput.split("/"));
21839
22209
  const doc = await readOpenApiDocument(source);
21840
22210
  const rewritten = withGatewayServers(doc, gatewayUrl);
21841
- await mkdir17(path69.dirname(outputPath), { recursive: true });
22211
+ await mkdir17(path70.dirname(outputPath), { recursive: true });
21842
22212
  await writeFile19(
21843
22213
  outputPath,
21844
22214
  `${JSON.stringify(rewritten, null, 2)}
@@ -21848,8 +22218,8 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
21848
22218
  return { outputPath, relativeInput };
21849
22219
  }
21850
22220
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21851
- const configPath = path69.join(
21852
- path69.resolve(siteDirectory),
22221
+ const configPath = path70.join(
22222
+ path70.resolve(siteDirectory),
21853
22223
  APITOGO_CONFIG_FILENAME2
21854
22224
  );
21855
22225
  const raw = await readFile25(configPath, "utf8");
@@ -21891,7 +22261,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21891
22261
  };
21892
22262
  }
21893
22263
  async function prepareOpenApiServersForGatewayBuild(options) {
21894
- const siteDirectory = path69.resolve(options.siteDirectory);
22264
+ const siteDirectory = path70.resolve(options.siteDirectory);
21895
22265
  let openApiPath = resolveBackendOpenApiPath(
21896
22266
  options.manifest?.backend?.openApiPath,
21897
22267
  {
@@ -21905,7 +22275,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
21905
22275
  if (!openApiPath) {
21906
22276
  try {
21907
22277
  const raw = await readFile25(
21908
- path69.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
22278
+ path70.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21909
22279
  "utf8"
21910
22280
  );
21911
22281
  const config2 = JSON.parse(raw);
@@ -21934,8 +22304,8 @@ function normalizeActionKeyFormat(actionKey2) {
21934
22304
  const trimmed = actionKey2.trim();
21935
22305
  if (trimmed.includes(":")) {
21936
22306
  const [method2, ...pathParts] = trimmed.split(":");
21937
- const path100 = pathParts.join(":").trim();
21938
- return `${method2.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
22307
+ const path101 = pathParts.join(":").trim();
22308
+ return `${method2.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
21939
22309
  }
21940
22310
  const spaceIndex = trimmed.indexOf(" ");
21941
22311
  if (spaceIndex <= 0) {
@@ -21944,8 +22314,8 @@ function normalizeActionKeyFormat(actionKey2) {
21944
22314
  );
21945
22315
  }
21946
22316
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21947
- const path99 = trimmed.slice(spaceIndex + 1).trim();
21948
- return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
22317
+ const path100 = trimmed.slice(spaceIndex + 1).trim();
22318
+ return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21949
22319
  }
21950
22320
  function normalizePublicRoute(route) {
21951
22321
  const trimmed = route.trim();
@@ -21979,10 +22349,10 @@ function collectManifestPublicActionKeys(gateway) {
21979
22349
 
21980
22350
  // src/cli/platform/helpers/publish-ledger.ts
21981
22351
  import { mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
21982
- import path70 from "node:path";
22352
+ import path71 from "node:path";
21983
22353
  var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
21984
22354
  function publishLedgerPath(siteDirectory) {
21985
- return path70.join(path70.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
22355
+ return path71.join(path71.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21986
22356
  }
21987
22357
  async function readPublishLedger(siteDirectory) {
21988
22358
  const filePath = publishLedgerPath(siteDirectory);
@@ -21999,7 +22369,7 @@ async function readPublishLedger(siteDirectory) {
21999
22369
  }
22000
22370
  async function writePublishLedger(siteDirectory, ledger) {
22001
22371
  const filePath = publishLedgerPath(siteDirectory);
22002
- await mkdir18(path70.dirname(filePath), { recursive: true });
22372
+ await mkdir18(path71.dirname(filePath), { recursive: true });
22003
22373
  const next = {
22004
22374
  ...ledger,
22005
22375
  version: 1,
@@ -22520,7 +22890,7 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
22520
22890
  // src/cli/platform/helpers/backend-packaging.ts
22521
22891
  import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10 } from "node:fs/promises";
22522
22892
  import { tmpdir as tmpdir2 } from "node:os";
22523
- import path71 from "node:path";
22893
+ import path72 from "node:path";
22524
22894
  async function directoryHasRuntimeConfig(dir) {
22525
22895
  if (!await pathExists(dir)) {
22526
22896
  return false;
@@ -22562,20 +22932,20 @@ function runDotnetPublish(projectDir, outputDir) {
22562
22932
  async function findCsproj(projectDir) {
22563
22933
  const entries = await readdir5(projectDir);
22564
22934
  const csproj = entries.find((name) => name.endsWith(".csproj"));
22565
- return csproj ? path71.join(projectDir, csproj) : null;
22935
+ return csproj ? path72.join(projectDir, csproj) : null;
22566
22936
  }
22567
22937
  async function prepareBackendPackagingDirectory(args) {
22568
- const sourceDirectory = path71.resolve(args.sourceDirectory);
22938
+ const sourceDirectory = path72.resolve(args.sourceDirectory);
22569
22939
  if (args.publishDirectory?.trim()) {
22570
22940
  return {
22571
- directory: path71.resolve(args.publishDirectory),
22941
+ directory: path72.resolve(args.publishDirectory),
22572
22942
  packagingMode: "published"
22573
22943
  };
22574
22944
  }
22575
22945
  if (await directoryHasRuntimeConfig(sourceDirectory)) {
22576
22946
  return { directory: sourceDirectory, packagingMode: "published" };
22577
22947
  }
22578
- const publishSubdir = path71.join(sourceDirectory, "publish");
22948
+ const publishSubdir = path72.join(sourceDirectory, "publish");
22579
22949
  if (await directoryHasRuntimeConfig(publishSubdir)) {
22580
22950
  return { directory: publishSubdir, packagingMode: "published" };
22581
22951
  }
@@ -22587,9 +22957,9 @@ async function prepareBackendPackagingDirectory(args) {
22587
22957
  return { directory: sourceDirectory, packagingMode: "source" };
22588
22958
  }
22589
22959
  const tempRoot = await mkdtemp2(
22590
- path71.join(tmpdir2(), "apitogo-dotnet-publish-")
22960
+ path72.join(tmpdir2(), "apitogo-dotnet-publish-")
22591
22961
  );
22592
- const outputDir = path71.join(tempRoot, "publish");
22962
+ const outputDir = path72.join(tempRoot, "publish");
22593
22963
  const result = await runDotnetPublish(sourceDirectory, outputDir);
22594
22964
  if (result.exitCode !== 0) {
22595
22965
  await rm10(tempRoot, { recursive: true, force: true });
@@ -22634,13 +23004,13 @@ function formatJobStatusResponse(options) {
22634
23004
 
22635
23005
  // src/cli/platform/helpers/openapi.ts
22636
23006
  import { promises as fs7 } from "node:fs";
22637
- import path72 from "node:path";
23007
+ import path73 from "node:path";
22638
23008
  import SwaggerParser from "@apidevtools/swagger-parser";
22639
23009
  import YAML3 from "yaml";
22640
23010
  async function validateOpenApiFile(filePath) {
22641
- const resolved = path72.resolve(filePath);
23011
+ const resolved = path73.resolve(filePath);
22642
23012
  const content = await fs7.readFile(resolved, "utf8");
22643
- const ext = path72.extname(resolved).toLowerCase();
23013
+ const ext = path73.extname(resolved).toLowerCase();
22644
23014
  let parsed;
22645
23015
  try {
22646
23016
  parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
@@ -22684,12 +23054,12 @@ async function validateOpenApiFile(filePath) {
22684
23054
  // src/cli/platform/helpers/zip.ts
22685
23055
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs8 } from "node:fs";
22686
23056
  import { tmpdir as tmpdir3 } from "node:os";
22687
- import path73 from "node:path";
23057
+ import path74 from "node:path";
22688
23058
  import { ZipArchive } from "archiver";
22689
23059
  async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22690
- const resolved = path73.resolve(sourceDirectory);
23060
+ const resolved = path74.resolve(sourceDirectory);
22691
23061
  const files = await collectPackageableFiles(resolved, extraExcludes);
22692
- const zipPath = path73.join(
23062
+ const zipPath = path74.join(
22693
23063
  tmpdir3(),
22694
23064
  `apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
22695
23065
  );
@@ -22701,8 +23071,8 @@ async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22701
23071
  output4.on("error", reject);
22702
23072
  archive.pipe(output4);
22703
23073
  for (const rel2 of files) {
22704
- archive.append(createReadStream2(path73.join(resolved, rel2)), {
22705
- name: rel2.split(path73.sep).join("/")
23074
+ archive.append(createReadStream2(path74.join(resolved, rel2)), {
23075
+ name: rel2.split(path74.sep).join("/")
22706
23076
  });
22707
23077
  }
22708
23078
  void archive.finalize();
@@ -24272,11 +24642,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
24272
24642
  }
24273
24643
 
24274
24644
  // src/cli/platform/tools/dev-portal-site-build.ts
24275
- import path77 from "node:path";
24645
+ import path78 from "node:path";
24276
24646
 
24277
24647
  // src/cli/platform/helpers/dev-portal-site-api-url.ts
24278
24648
  import { readdir as readdir6, readFile as readFile27, writeFile as writeFile21 } from "node:fs/promises";
24279
- import path74 from "node:path";
24649
+ import path75 from "node:path";
24280
24650
  var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
24281
24651
  var PLACEHOLDER_HOST_MARKERS = [
24282
24652
  "dp-example.azurecontainerapps.io",
@@ -24297,10 +24667,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
24297
24667
  return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
24298
24668
  }
24299
24669
  async function readSiteApiBaseUrl(sourceDirectory) {
24300
- const resolved = path74.resolve(sourceDirectory);
24670
+ const resolved = path75.resolve(sourceDirectory);
24301
24671
  for (const envFile of [".env", ".env.production"]) {
24302
24672
  try {
24303
- const content = await readFile27(path74.join(resolved, envFile), "utf8");
24673
+ const content = await readFile27(path75.join(resolved, envFile), "utf8");
24304
24674
  const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
24305
24675
  if (lines.length === 0) {
24306
24676
  continue;
@@ -24325,11 +24695,11 @@ async function readSiteApiBaseUrl(sourceDirectory) {
24325
24695
  return null;
24326
24696
  }
24327
24697
  async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
24328
- const resolved = path74.resolve(sourceDirectory);
24698
+ const resolved = path75.resolve(sourceDirectory);
24329
24699
  const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
24330
24700
  let patchedExisting = false;
24331
24701
  for (const envFile of [".env.production", ".env"]) {
24332
- const envPath = path74.join(resolved, envFile);
24702
+ const envPath = path75.join(resolved, envFile);
24333
24703
  try {
24334
24704
  const content = await readFile27(envPath, "utf8");
24335
24705
  const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
@@ -24343,7 +24713,7 @@ ${line2}
24343
24713
  }
24344
24714
  }
24345
24715
  if (!patchedExisting) {
24346
- await writeFile21(path74.join(resolved, ".env"), `${line2}
24716
+ await writeFile21(path75.join(resolved, ".env"), `${line2}
24347
24717
  `);
24348
24718
  }
24349
24719
  }
@@ -24351,7 +24721,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
24351
24721
  return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
24352
24722
  }
24353
24723
  async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
24354
- const sourceDirectory = path74.resolve(args.sourceDirectory);
24724
+ const sourceDirectory = path75.resolve(args.sourceDirectory);
24355
24725
  const explicit = args.apiBaseUrl?.trim();
24356
24726
  if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
24357
24727
  await patchSiteApiBaseUrl(sourceDirectory, explicit);
@@ -24387,7 +24757,7 @@ async function* walkFiles(root) {
24387
24757
  return;
24388
24758
  }
24389
24759
  for (const entry of entries) {
24390
- const full = path74.join(root, entry.name);
24760
+ const full = path75.join(root, entry.name);
24391
24761
  if (entry.isDirectory()) {
24392
24762
  if (entry.name === "node_modules" || entry.name === ".git") {
24393
24763
  continue;
@@ -24399,8 +24769,8 @@ async function* walkFiles(root) {
24399
24769
  }
24400
24770
  }
24401
24771
  async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
24402
- const dist = path74.resolve(distDirectory);
24403
- const siteRoot = path74.dirname(dist);
24772
+ const dist = path75.resolve(distDirectory);
24773
+ const siteRoot = path75.dirname(dist);
24404
24774
  const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
24405
24775
  if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
24406
24776
  throw new Error(
@@ -24436,7 +24806,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
24436
24806
 
24437
24807
  // src/cli/platform/tools/dev-portal.ts
24438
24808
  import { promises as fs10 } from "node:fs";
24439
- import path76 from "node:path";
24809
+ import path77 from "node:path";
24440
24810
 
24441
24811
  // src/cli/platform/helpers/dev-portal-backend-job.ts
24442
24812
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
@@ -24546,7 +24916,7 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
24546
24916
  // src/cli/platform/helpers/tarball.ts
24547
24917
  import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs9 } from "node:fs";
24548
24918
  import { tmpdir as tmpdir4 } from "node:os";
24549
- import path75 from "node:path";
24919
+ import path76 from "node:path";
24550
24920
  import { TarArchive } from "archiver";
24551
24921
  var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
24552
24922
  function normalizeArchiveRoot(archiveRoot) {
@@ -24557,10 +24927,10 @@ function normalizeArchiveRoot(archiveRoot) {
24557
24927
  return `${trimmed}/`;
24558
24928
  }
24559
24929
  async function packageDirectoryAsTarGz(sourceDirectory, options) {
24560
- const resolved = path75.resolve(sourceDirectory);
24930
+ const resolved = path76.resolve(sourceDirectory);
24561
24931
  const files = await collectPackageableFiles(resolved);
24562
24932
  const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
24563
- const archivePath = path75.join(
24933
+ const archivePath = path76.join(
24564
24934
  tmpdir4(),
24565
24935
  `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
24566
24936
  );
@@ -24572,8 +24942,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
24572
24942
  output4.on("error", reject);
24573
24943
  archive.pipe(output4);
24574
24944
  for (const rel2 of files) {
24575
- const entryName = entryPrefix + rel2.split(path75.sep).join("/");
24576
- archive.append(createReadStream3(path75.join(resolved, rel2)), {
24945
+ const entryName = entryPrefix + rel2.split(path76.sep).join("/");
24946
+ archive.append(createReadStream3(path76.join(resolved, rel2)), {
24577
24947
  name: entryName
24578
24948
  });
24579
24949
  }
@@ -24815,7 +25185,7 @@ function validateDeployArchiveInput(args) {
24815
25185
  async function resolveDevPortalArchive(args) {
24816
25186
  validateDeployArchiveInput(args);
24817
25187
  if (args.archivePath?.trim()) {
24818
- const archivePath = path76.resolve(args.archivePath.trim());
25188
+ const archivePath = path77.resolve(args.archivePath.trim());
24819
25189
  const stat7 = await fs10.stat(archivePath).catch(() => null);
24820
25190
  if (!stat7?.isFile()) {
24821
25191
  throw new Error(`archivePath is not a file: ${archivePath}`);
@@ -24880,7 +25250,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
24880
25250
  // src/cli/platform/tools/dev-portal-site-build.ts
24881
25251
  async function handleBuildDevPortalSite(client, config2, args, progress) {
24882
25252
  const buildProgress = progress?.child("Build dev portal site");
24883
- const sourceDirectory = path77.resolve(args.sourceDirectory);
25253
+ const sourceDirectory = path78.resolve(args.sourceDirectory);
24884
25254
  await buildProgress?.onStep("Ensuring live portal API URL");
24885
25255
  const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
24886
25256
  client,
@@ -26386,7 +26756,7 @@ function resultLooksFailed(payload) {
26386
26756
  return parsed.status === "failed" || parsed.status === "blocked";
26387
26757
  }
26388
26758
  async function apply(argv) {
26389
- const sourceDirectory = path78.resolve(process.cwd(), argv.dir);
26759
+ const sourceDirectory = path79.resolve(process.cwd(), argv.dir);
26390
26760
  let config2;
26391
26761
  try {
26392
26762
  config2 = loadConfig();
@@ -26456,7 +26826,7 @@ async function apply(argv) {
26456
26826
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
26457
26827
  const resolution = await resolveProjectNotFoundInteractively(
26458
26828
  parsed,
26459
- path78.basename(sourceDirectory)
26829
+ path79.basename(sourceDirectory)
26460
26830
  );
26461
26831
  if (resolution) {
26462
26832
  effectiveProjectId = void 0;
@@ -26476,7 +26846,7 @@ async function apply(argv) {
26476
26846
  while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
26477
26847
  const resolution = await resolveProjectNameExistsInteractively(
26478
26848
  parsed,
26479
- effectiveProjectName ?? path78.basename(sourceDirectory)
26849
+ effectiveProjectName ?? path79.basename(sourceDirectory)
26480
26850
  );
26481
26851
  if (!resolution) {
26482
26852
  break;
@@ -26547,11 +26917,11 @@ async function apply(argv) {
26547
26917
  }
26548
26918
 
26549
26919
  // src/cli/deploy/command.ts
26550
- import path84 from "node:path";
26920
+ import path85 from "node:path";
26551
26921
  init_invariant();
26552
26922
 
26553
26923
  // src/cli/project/create-handler.ts
26554
- import path79 from "node:path";
26924
+ import path80 from "node:path";
26555
26925
  import open2 from "open";
26556
26926
  init_invariant();
26557
26927
 
@@ -26865,7 +27235,7 @@ async function createLinkedProject(argv, dir, options = {}) {
26865
27235
  }
26866
27236
  }
26867
27237
  async function createProject2(argv) {
26868
- const dir = path79.resolve(process.cwd(), argv.dir ?? ".");
27238
+ const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
26869
27239
  if (await isConfigV2(dir)) {
26870
27240
  await createLinkedProject(argv, dir);
26871
27241
  return;
@@ -26925,7 +27295,7 @@ async function createProject2(argv) {
26925
27295
  }
26926
27296
 
26927
27297
  // src/cli/secrets/handler.ts
26928
- import path80 from "node:path";
27298
+ import path81 from "node:path";
26929
27299
  init_invariant();
26930
27300
 
26931
27301
  // src/cli/secrets/input.ts
@@ -26990,7 +27360,7 @@ var SecretsError = class extends ApitogoError {
26990
27360
  async function resolveProjectId2(argv, deps) {
26991
27361
  const explicit = argv.projectId?.trim();
26992
27362
  if (explicit) return explicit;
26993
- const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
27363
+ const dir = path81.resolve(process.cwd(), argv.dir ?? ".");
26994
27364
  if (await isConfigV2(dir)) {
26995
27365
  const id = loadConfigV2(dir).resolved.project.id;
26996
27366
  if (id) return id;
@@ -27075,7 +27445,7 @@ var KNOWN_SECRET_PLACES = {
27075
27445
  async function wireKnownSecret(argv, name) {
27076
27446
  const place = KNOWN_SECRET_PLACES[name.toUpperCase()];
27077
27447
  if (!place) return void 0;
27078
- const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
27448
+ const dir = path81.resolve(process.cwd(), argv.dir ?? ".");
27079
27449
  if (!await isConfigV2(dir)) return void 0;
27080
27450
  const project = await openConfigProject(dir);
27081
27451
  const plans = project.resolved.plans;
@@ -27136,7 +27506,7 @@ async function secrets(argv) {
27136
27506
  }
27137
27507
 
27138
27508
  // src/cli/deploy/stripe-key.ts
27139
- import path81 from "node:path";
27509
+ import path82 from "node:path";
27140
27510
  init_invariant();
27141
27511
  var StripeKeyError = class extends ApitogoError {
27142
27512
  constructor(message, developerHint) {
@@ -27154,7 +27524,7 @@ async function ensureStripeKey(argv, deps) {
27154
27524
  if (!wantsPlans(argv.only)) return { status: "not-needed" };
27155
27525
  let resolved;
27156
27526
  try {
27157
- resolved = loadConfigV2(path81.resolve(process.cwd(), argv.dir)).resolved;
27527
+ resolved = loadConfigV2(path82.resolve(process.cwd(), argv.dir)).resolved;
27158
27528
  } catch {
27159
27529
  return { status: "not-needed" };
27160
27530
  }
@@ -28091,7 +28461,7 @@ init_invariant();
28091
28461
 
28092
28462
  // src/cli/portal/report.ts
28093
28463
  import { access as access9 } from "node:fs/promises";
28094
- import path82 from "node:path";
28464
+ import path83 from "node:path";
28095
28465
 
28096
28466
  // src/cli/deploy/identity.ts
28097
28467
  init_invariant();
@@ -28862,7 +29232,7 @@ async function planPortalService(resolved, ledger) {
28862
29232
  const hash2 = await portalBuildHash(resolved, prepared);
28863
29233
  const previous = stepOf(ledger, "portal", "build");
28864
29234
  const built = await exists5(
28865
- path82.join(resolved.state.buildDir, "index.html")
29235
+ path83.join(resolved.state.buildDir, "index.html")
28866
29236
  );
28867
29237
  if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
28868
29238
  return {
@@ -29248,7 +29618,7 @@ async function statusV2(dir, only, options = {}) {
29248
29618
  if (service === "portal") {
29249
29619
  const build2 = stepOf(ledger, "portal", "build");
29250
29620
  const built = await exists5(
29251
- path82.join(resolved.state.buildDir, "index.html")
29621
+ path83.join(resolved.state.buildDir, "index.html")
29252
29622
  );
29253
29623
  const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
29254
29624
  services.push({
@@ -29259,7 +29629,7 @@ async function statusV2(dir, only, options = {}) {
29259
29629
  ...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
29260
29630
  ...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
29261
29631
  ...state === "built" ? [
29262
- `output: ${path82.relative(resolved.configDir, resolved.state.buildDir)}`
29632
+ `output: ${path83.relative(resolved.configDir, resolved.state.buildDir)}`
29263
29633
  ] : [],
29264
29634
  ...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"],
29265
29635
  ...await hostingLine(resolved, manifest, options.hostingRemote)
@@ -29326,7 +29696,7 @@ function formatStatus(output4) {
29326
29696
 
29327
29697
  // src/cli/deploy/static-web-config.ts
29328
29698
  import { access as access10, writeFile as writeFile22 } from "node:fs/promises";
29329
- import path83 from "node:path";
29699
+ import path84 from "node:path";
29330
29700
  var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
29331
29701
  var STATIC_WEB_CONFIG_VERSION = 3;
29332
29702
  var CLIENT_ONLY_ROUTES = [
@@ -29344,7 +29714,7 @@ var DEFAULT_STATIC_WEB_CONFIG = {
29344
29714
  }
29345
29715
  };
29346
29716
  async function ensureStaticWebConfig(buildDir) {
29347
- const file = path83.join(buildDir, STATIC_WEB_CONFIG_FILE);
29717
+ const file = path84.join(buildDir, STATIC_WEB_CONFIG_FILE);
29348
29718
  try {
29349
29719
  await access10(file);
29350
29720
  return false;
@@ -29699,7 +30069,7 @@ function formatDeploy(output4) {
29699
30069
  // src/cli/deploy/command.ts
29700
30070
  async function ensureLinkedProject(argv, deps) {
29701
30071
  if (argv.projectId?.trim() || deps.envProjectId?.trim()) return true;
29702
- const dir = path84.resolve(process.cwd(), argv.dir);
30072
+ const dir = path85.resolve(process.cwd(), argv.dir);
29703
30073
  let linked;
29704
30074
  try {
29705
30075
  linked = Boolean(loadConfigV2(dir).resolved.project.id);
@@ -29750,7 +30120,7 @@ async function deployProject(argv) {
29750
30120
  try {
29751
30121
  const config2 = loadConfig();
29752
30122
  const client = new PlatformApiClient(config2);
29753
- const dir = path84.resolve(process.cwd(), argv.dir);
30123
+ const dir = path85.resolve(process.cwd(), argv.dir);
29754
30124
  const deploy = (only) => deployV2(
29755
30125
  {
29756
30126
  dir,
@@ -29858,7 +30228,7 @@ var deploy_default = {
29858
30228
  }),
29859
30229
  handler: async (argv) => {
29860
30230
  await captureEvent({ argv, event: "apitogo deploy" });
29861
- if (!await isLegacyConfig(path85.resolve(process.cwd(), argv.dir))) {
30231
+ if (!await isLegacyConfig(path86.resolve(process.cwd(), argv.dir))) {
29862
30232
  await deployProject(argv);
29863
30233
  return;
29864
30234
  }
@@ -29868,7 +30238,7 @@ var deploy_default = {
29868
30238
 
29869
30239
  // src/cli/export/handler.ts
29870
30240
  import { mkdir as mkdir19, writeFile as writeFile23 } from "node:fs/promises";
29871
- import path86 from "node:path";
30241
+ import path87 from "node:path";
29872
30242
  init_invariant();
29873
30243
  var ExportError = class extends ApitogoError {
29874
30244
  constructor(message, developerHint) {
@@ -29882,7 +30252,7 @@ async function exportOpenApi(args, deps) {
29882
30252
  `Cannot export "${args.target}".`,
29883
30253
  "Only `apitogo export openapi` exists."
29884
30254
  );
29885
- const dir = path86.resolve(process.cwd(), args.dir ?? ".");
30255
+ const dir = path87.resolve(process.cwd(), args.dir ?? ".");
29886
30256
  if (!await isConfigV2(dir))
29887
30257
  throw new ExportError(
29888
30258
  "This command needs a config version 2 project.",
@@ -29907,8 +30277,8 @@ async function exportOpenApi(args, deps) {
29907
30277
  throw error;
29908
30278
  }
29909
30279
  if (!args.out) return { text: text2 };
29910
- const file = path86.resolve(process.cwd(), args.out);
29911
- await mkdir19(path86.dirname(file), { recursive: true });
30280
+ const file = path87.resolve(process.cwd(), args.out);
30281
+ await mkdir19(path87.dirname(file), { recursive: true });
29912
30282
  await writeFile23(file, text2);
29913
30283
  return { text: text2, file };
29914
30284
  }
@@ -30001,8 +30371,8 @@ import open3 from "open";
30001
30371
  import colors14 from "picocolors";
30002
30372
 
30003
30373
  // src/cli/platform/device-auth.ts
30004
- function url(apiUrl, path99) {
30005
- return `${apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
30374
+ function url(apiUrl, path100) {
30375
+ return `${apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
30006
30376
  }
30007
30377
  async function requestDeviceCode(apiUrl) {
30008
30378
  const response = await fetchWithRetry(
@@ -30184,7 +30554,7 @@ var logout_default = {
30184
30554
  };
30185
30555
 
30186
30556
  // src/cli/portal/commands.ts
30187
- import path87 from "node:path";
30557
+ import path88 from "node:path";
30188
30558
  init_invariant();
30189
30559
  function parseOnly(value) {
30190
30560
  if (value === void 0) return void 0;
@@ -30201,7 +30571,7 @@ function parseOnly(value) {
30201
30571
  async function run2(command2, argv, compute, format) {
30202
30572
  const json = argv.json === true;
30203
30573
  try {
30204
- const dir = path87.resolve(process.cwd(), argv.dir ?? ".");
30574
+ const dir = path88.resolve(process.cwd(), argv.dir ?? ".");
30205
30575
  if (!await isConfigV2(dir)) {
30206
30576
  throw new ServiceEditError(
30207
30577
  `${command2} works with a config version 2 project, and there is none in ${dir}.`,
@@ -30563,14 +30933,14 @@ var remove_default = {
30563
30933
 
30564
30934
  // src/cli/dev/handler.ts
30565
30935
  init_joinUrl();
30566
- import path90 from "node:path";
30936
+ import path91 from "node:path";
30567
30937
 
30568
30938
  // src/vite/dev-server.ts
30569
30939
  init_logger();
30570
30940
  import fs11 from "node:fs/promises";
30571
30941
  import http from "node:http";
30572
30942
  import https from "node:https";
30573
- import path89 from "node:path";
30943
+ import path90 from "node:path";
30574
30944
  import { createHttpTerminator } from "http-terminator";
30575
30945
  import {
30576
30946
  createServer as createViteServer,
@@ -30615,7 +30985,7 @@ function withSsrGraphParts(config2, ssrConfig) {
30615
30985
  // src/vite/pagefind-dev-index.ts
30616
30986
  init_invariant();
30617
30987
  init_joinUrl();
30618
- import path88 from "node:path";
30988
+ import path89 from "node:path";
30619
30989
  import { createIndex as createIndex2 } from "pagefind";
30620
30990
  import { isRunnableDevEnvironment } from "vite";
30621
30991
  async function* buildPagefindDevIndex(vite, config2) {
@@ -30668,7 +31038,7 @@ async function* buildPagefindDevIndex(vite, config2) {
30668
31038
  path: urlPath
30669
31039
  };
30670
31040
  }
30671
- const outputPath = path88.join(vite.config.publicDir, "pagefind");
31041
+ const outputPath = path89.join(vite.config.publicDir, "pagefind");
30672
31042
  await pagefindIndex.writeFiles({ outputPath });
30673
31043
  yield { type: "complete", success: true, indexed };
30674
31044
  }
@@ -30687,9 +31057,9 @@ var DevServer = class {
30687
31057
  this.protocol = "https";
30688
31058
  const { dir } = this.options;
30689
31059
  const [key, cert, ca] = await Promise.all([
30690
- fs11.readFile(path89.resolve(dir, config2.https.key)),
30691
- fs11.readFile(path89.resolve(dir, config2.https.cert)),
30692
- 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
30693
31063
  ]);
30694
31064
  return https.createServer({ key, cert, ca });
30695
31065
  }
@@ -30706,7 +31076,7 @@ var DevServer = class {
30706
31076
  );
30707
31077
  const server = await this.createNodeServer(config2);
30708
31078
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
30709
- const publicRoot = path89.resolve(
31079
+ const publicRoot = path90.resolve(
30710
31080
  this.options.dir,
30711
31081
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
30712
31082
  );
@@ -30723,7 +31093,7 @@ var DevServer = class {
30723
31093
  // built-in transform middleware which would treat the path as a static asset.
30724
31094
  name: "apitogo:entry-client",
30725
31095
  configureServer(server2) {
30726
- const entryPath = path89.posix.join(
31096
+ const entryPath = path90.posix.join(
30727
31097
  server2.config.base,
30728
31098
  "/__z/entry.client.tsx"
30729
31099
  );
@@ -30891,7 +31261,7 @@ init_package_json();
30891
31261
  async function dev(argv) {
30892
31262
  const packageJson3 = getZudokuPackageJson();
30893
31263
  process.env.NODE_ENV = "development";
30894
- let dir = path90.resolve(process.cwd(), argv.dir);
31264
+ let dir = path91.resolve(process.cwd(), argv.dir);
30895
31265
  if (await isConfigV2(dir)) {
30896
31266
  const { root, prepared } = await preparePortalWorkspace(dir, "development");
30897
31267
  dir = root;
@@ -31026,12 +31396,12 @@ var secrets_default = {
31026
31396
  };
31027
31397
 
31028
31398
  // src/cli/cmds/status.ts
31029
- import path92 from "node:path";
31399
+ import path93 from "node:path";
31030
31400
 
31031
31401
  // src/cli/status/handler.ts
31032
- import path91 from "node:path";
31402
+ import path92 from "node:path";
31033
31403
  async function status(argv) {
31034
- const sourceDirectory = path91.resolve(process.cwd(), argv.dir);
31404
+ const sourceDirectory = path92.resolve(process.cwd(), argv.dir);
31035
31405
  let config2;
31036
31406
  try {
31037
31407
  config2 = loadConfig();
@@ -31157,7 +31527,7 @@ var status_default = {
31157
31527
  }),
31158
31528
  handler: async (argv) => {
31159
31529
  await captureEvent({ argv, event: "apitogo status" });
31160
- if (await isConfigV2(path92.resolve(process.cwd(), argv.dir))) {
31530
+ if (await isConfigV2(path93.resolve(process.cwd(), argv.dir))) {
31161
31531
  await statusOfProject(argv);
31162
31532
  return;
31163
31533
  }
@@ -31166,11 +31536,11 @@ var status_default = {
31166
31536
  };
31167
31537
 
31168
31538
  // src/cli/cmds/teardown.ts
31169
- import path95 from "node:path";
31539
+ import path96 from "node:path";
31170
31540
 
31171
31541
  // src/cli/teardown/command.ts
31172
31542
  init_invariant();
31173
- import path93 from "node:path";
31543
+ import path94 from "node:path";
31174
31544
  import { createInterface as createInterface7 } from "node:readline/promises";
31175
31545
 
31176
31546
  // src/cli/teardown/v2.ts
@@ -31531,7 +31901,7 @@ async function teardownProject(argv) {
31531
31901
  const progress = createConsoleProgressReporter();
31532
31902
  const result = await teardownV2(
31533
31903
  {
31534
- dir: path93.resolve(process.cwd(), argv.dir),
31904
+ dir: path94.resolve(process.cwd(), argv.dir),
31535
31905
  projectId: argv.projectId,
31536
31906
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
31537
31907
  yes: argv.yes === true,
@@ -31590,7 +31960,7 @@ async function teardownProject(argv) {
31590
31960
  }
31591
31961
 
31592
31962
  // src/cli/teardown/handler.ts
31593
- import path94 from "node:path";
31963
+ import path95 from "node:path";
31594
31964
  function resultLooksFailed3(payload) {
31595
31965
  try {
31596
31966
  const parsed = JSON.parse(payload);
@@ -31606,7 +31976,7 @@ async function teardown(argv) {
31606
31976
  );
31607
31977
  return;
31608
31978
  }
31609
- const sourceDirectory = path94.resolve(process.cwd(), argv.dir);
31979
+ const sourceDirectory = path95.resolve(process.cwd(), argv.dir);
31610
31980
  let config2;
31611
31981
  try {
31612
31982
  config2 = loadConfig();
@@ -31684,7 +32054,7 @@ var teardown_default = {
31684
32054
  }),
31685
32055
  handler: async (argv) => {
31686
32056
  await captureEvent({ argv, event: "apitogo teardown" });
31687
- if (!await isLegacyConfig(path95.resolve(process.cwd(), argv.dir))) {
32057
+ if (!await isLegacyConfig(path96.resolve(process.cwd(), argv.dir))) {
31688
32058
  await teardownProject(argv);
31689
32059
  return;
31690
32060
  }
@@ -31694,7 +32064,7 @@ var teardown_default = {
31694
32064
 
31695
32065
  // src/cli/teardown/reset-command.ts
31696
32066
  init_invariant();
31697
- import path96 from "node:path";
32067
+ import path97 from "node:path";
31698
32068
 
31699
32069
  // src/cli/teardown/reset.ts
31700
32070
  init_invariant();
@@ -31817,7 +32187,7 @@ async function resetProject(argv) {
31817
32187
  const config2 = loadConfig();
31818
32188
  const result = await resetProjectV2(
31819
32189
  {
31820
- dir: path96.resolve(process.cwd(), argv.dir),
32190
+ dir: path97.resolve(process.cwd(), argv.dir),
31821
32191
  projectId: argv.projectId,
31822
32192
  yes: argv.yes === true
31823
32193
  },
@@ -31881,7 +32251,7 @@ var test_reset_default = {
31881
32251
  };
31882
32252
 
31883
32253
  // src/cli/test-sign-in/handler.ts
31884
- import path97 from "node:path";
32254
+ import path98 from "node:path";
31885
32255
  init_invariant();
31886
32256
  var TestSignInError = class extends ApitogoError {
31887
32257
  constructor(message, developerHint) {
@@ -31892,7 +32262,7 @@ var TestSignInError = class extends ApitogoError {
31892
32262
  async function resolveProjectId3(argv) {
31893
32263
  const explicit = argv.projectId?.trim();
31894
32264
  if (explicit) return explicit;
31895
- const dir = path97.resolve(process.cwd(), argv.dir ?? ".");
32265
+ const dir = path98.resolve(process.cwd(), argv.dir ?? ".");
31896
32266
  if (await isConfigV2(dir)) {
31897
32267
  const id = loadConfigV2(dir).resolved.project.id;
31898
32268
  if (id) return id;
@@ -32032,36 +32402,36 @@ function createApitogoCli(argv) {
32032
32402
  // src/cli/local-handoff.ts
32033
32403
  import { spawn as spawn2 } from "node:child_process";
32034
32404
  import { existsSync as existsSync9, realpathSync as realpathSync2 } from "node:fs";
32035
- import path98 from "node:path";
32405
+ import path99 from "node:path";
32036
32406
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
32037
32407
  var PACKAGE_NAME = "@lukoweb/apitogo";
32038
32408
  var resolveProjectDir = (argv, cwd) => {
32039
32409
  for (let i = 0; i < argv.length; i++) {
32040
32410
  const arg = argv[i];
32041
32411
  const next = argv[i + 1];
32042
- if (arg === "--dir" && next) return path98.resolve(cwd, next);
32412
+ if (arg === "--dir" && next) return path99.resolve(cwd, next);
32043
32413
  if (arg?.startsWith("--dir=")) {
32044
- return path98.resolve(cwd, arg.slice("--dir=".length));
32414
+ return path99.resolve(cwd, arg.slice("--dir=".length));
32045
32415
  }
32046
32416
  }
32047
32417
  return cwd;
32048
32418
  };
32049
32419
  var findLocalCli = (projectDir, runningRoot) => {
32050
- let dir = path98.resolve(projectDir);
32420
+ let dir = path99.resolve(projectDir);
32051
32421
  let localPackageRoot;
32052
32422
  while (!localPackageRoot) {
32053
- const candidate = path98.join(dir, "node_modules", PACKAGE_NAME);
32054
- 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"))) {
32055
32425
  localPackageRoot = candidate;
32056
- } else if (dir === path98.dirname(dir)) {
32426
+ } else if (dir === path99.dirname(dir)) {
32057
32427
  return void 0;
32058
32428
  } else {
32059
- dir = path98.dirname(dir);
32429
+ dir = path99.dirname(dir);
32060
32430
  }
32061
32431
  }
32062
32432
  const localRoot = realpathSync2(localPackageRoot);
32063
32433
  if (localRoot === realpathSync2(runningRoot)) return void 0;
32064
- return path98.join(localRoot, "cli.js");
32434
+ return path99.join(localRoot, "cli.js");
32065
32435
  };
32066
32436
  var handoffToLocalCli = async (argv, runningRoot) => {
32067
32437
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -32073,7 +32443,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
32073
32443
  );
32074
32444
  if (!localCli) return void 0;
32075
32445
  printDiagnosticsToConsole(
32076
- `Using the project's local ${PACKAGE_NAME} (${path98.dirname(localCli)})`
32446
+ `Using the project's local ${PACKAGE_NAME} (${path99.dirname(localCli)})`
32077
32447
  );
32078
32448
  const child = spawn2(process.execPath, [localCli, ...argv], {
32079
32449
  stdio: "inherit",