@lukoweb/apitogo 0.2.56 → 0.2.57

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 (80) hide show
  1. package/dist/cli/cli.js +1026 -540
  2. package/dist/declarations/cli/platform/client/platform-api.d.ts +6 -0
  3. package/dist/declarations/config/validators/ZudokuConfig.d.ts +2 -0
  4. package/dist/declarations/lib/authentication/providers/openid.d.ts +2 -1
  5. package/dist/declarations/lib/plugins/markdown/index.d.ts +2 -1
  6. package/dist/flat-config.d.ts +1 -0
  7. package/docs/cli/commands.md +50 -32
  8. package/docs/cli/go-live.md +66 -93
  9. package/docs/cli/overview.md +18 -17
  10. package/docs/cli/setup.md +130 -141
  11. package/docs/cli/shape.md +94 -11
  12. package/docs/components/slot.mdx +116 -119
  13. package/docs/configuration/api-reference.md +98 -397
  14. package/docs/configuration/authentication.md +118 -159
  15. package/docs/configuration/footer.mdx +89 -118
  16. package/docs/configuration/make-config.mdx +2 -2
  17. package/docs/configuration/overview.md +97 -166
  18. package/docs/configuration/plans.md +100 -110
  19. package/docs/configuration/portal.md +116 -0
  20. package/docs/customization/colors-theme.mdx +255 -278
  21. package/docs/customization/fonts.md +105 -110
  22. package/docs/customization/landing-page.md +189 -188
  23. package/docs/deploy/apitogo.md +13 -17
  24. package/docs/deployment.md +15 -9
  25. package/docs/guides/stripe-setup.mdx +123 -0
  26. package/docs/markdown/frontmatter.md +172 -173
  27. package/docs/mcp/go-live.md +100 -100
  28. package/docs/mcp/overview.md +76 -76
  29. package/docs/quickstart.md +88 -72
  30. package/docs/writing.mdx +68 -72
  31. package/package.json +1 -1
  32. package/src/config/v2/docs-navigation.ts +7 -2
  33. package/src/config/v2/index.ts +1 -0
  34. package/src/config/v2/resolve.ts +28 -0
  35. package/src/config/v2/rules.ts +32 -1
  36. package/src/config/v2/schema.ts +9 -0
  37. package/src/config/v2/services.ts +78 -2
  38. package/src/config/v2/starter-landing.ts +41 -0
  39. package/src/config/v2/wrapper.ts +127 -7
  40. package/src/config/validators/ZudokuConfig.ts +794 -792
  41. package/src/lib/authentication/providers/openid.tsx +6 -1
  42. package/src/lib/plugins/markdown/index.tsx +86 -82
  43. package/src/vite/dev-server.ts +9 -1
  44. package/src/vite/dev-ssr-config.ts +32 -0
  45. package/src/vite/plugin-config.ts +4 -2
  46. package/src/vite/plugin-docs.ts +213 -205
  47. package/docs/concepts/auth-provider-api-identities.md +0 -105
  48. package/docs/configuration/ai-assistants.md +0 -65
  49. package/docs/configuration/api-catalog.md +0 -109
  50. package/docs/configuration/authentication-auth0.md +0 -181
  51. package/docs/configuration/authentication-azure-ad.md +0 -245
  52. package/docs/configuration/authentication-clerk.md +0 -117
  53. package/docs/configuration/authentication-firebase.md +0 -69
  54. package/docs/configuration/authentication-pingfederate.md +0 -143
  55. package/docs/configuration/authentication-supabase.md +0 -233
  56. package/docs/configuration/build-configuration.mdx +0 -147
  57. package/docs/configuration/docs.md +0 -282
  58. package/docs/configuration/llms.md +0 -90
  59. package/docs/configuration/manifest.md +0 -129
  60. package/docs/configuration/navigation.mdx +0 -415
  61. package/docs/configuration/protected-routes.md +0 -150
  62. package/docs/configuration/search.md +0 -164
  63. package/docs/configuration/sentry.mdx +0 -44
  64. package/docs/configuration/site.md +0 -124
  65. package/docs/configuration/slots.mdx +0 -125
  66. package/docs/configuration/vite-config.md +0 -18
  67. package/docs/custom-plugins.md +0 -285
  68. package/docs/deploy/apache-nginx.md +0 -41
  69. package/docs/deploy/cloudflare-pages.md +0 -74
  70. package/docs/deploy/direct-upload.md +0 -17
  71. package/docs/deploy/github-pages.md +0 -49
  72. package/docs/deploy/vercel.md +0 -101
  73. package/docs/extending/events.md +0 -124
  74. package/docs/guides/environment-variables.md +0 -94
  75. package/docs/guides/managing-api-keys-and-identities.md +0 -89
  76. package/docs/guides/navigation-rules.mdx +0 -203
  77. package/docs/guides/processors.mdx +0 -234
  78. package/docs/guides/transforming-examples.md +0 -156
  79. package/docs/guides/using-multiple-apis.md +0 -87
  80. package/docs/plugins.md +0 -24
package/dist/cli/cli.js CHANGED
@@ -756,16 +756,16 @@ var init_transform_config = __esm({
756
756
  });
757
757
 
758
758
  // src/config/apitogo-config-loader.node.ts
759
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
760
- import path18 from "node:path";
759
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
760
+ import path19 from "node:path";
761
761
  function loadApitogoConfig(rootDir = process.cwd()) {
762
- const configPath = path18.join(path18.resolve(rootDir), APITOGO_CONFIG_FILENAME);
763
- if (!existsSync3(configPath)) {
762
+ const configPath = path19.join(path19.resolve(rootDir), APITOGO_CONFIG_FILENAME);
763
+ if (!existsSync4(configPath)) {
764
764
  throw new Error(
765
- `Missing ${APITOGO_CONFIG_FILENAME} in ${path18.resolve(rootDir)}`
765
+ `Missing ${APITOGO_CONFIG_FILENAME} in ${path19.resolve(rootDir)}`
766
766
  );
767
767
  }
768
- const raw = readFileSync3(configPath, "utf8");
768
+ const raw = readFileSync4(configPath, "utf8");
769
769
  const parsed = JSON.parse(raw);
770
770
  return normalizeApitogoConfig(applyEnvOverrides(parsed, process.env));
771
771
  }
@@ -837,7 +837,7 @@ import { stat as stat4 } from "node:fs/promises";
837
837
  var fileExists;
838
838
  var init_file_exists = __esm({
839
839
  "src/config/file-exists.ts"() {
840
- fileExists = (path98) => stat4(path98).then(() => true).catch(() => false);
840
+ fileExists = (path99) => stat4(path99).then(() => true).catch(() => false);
841
841
  }
842
842
  });
843
843
 
@@ -1095,7 +1095,7 @@ var init_landing_manifest = __esm({
1095
1095
  });
1096
1096
 
1097
1097
  // src/config/local-manifest.ts
1098
- import path19 from "node:path";
1098
+ import path20 from "node:path";
1099
1099
  import { z as z7 } from "zod";
1100
1100
  function isPricingEnabled(manifest) {
1101
1101
  return manifest?.pricing?.enabled === true;
@@ -1148,8 +1148,8 @@ function resolveManifestEnv(viteMode) {
1148
1148
  return viteMode === "production" ? "production" : "development";
1149
1149
  }
1150
1150
  function manifestPathsForEnv(rootDir, _env) {
1151
- const resolvedRoot = path19.resolve(rootDir);
1152
- const configPath = path19.join(resolvedRoot, APITOGO_CONFIG_FILENAME);
1151
+ const resolvedRoot = path20.resolve(rootDir);
1152
+ const configPath = path20.join(resolvedRoot, APITOGO_CONFIG_FILENAME);
1153
1153
  return {
1154
1154
  basePath: configPath,
1155
1155
  overridePath: configPath,
@@ -1841,7 +1841,7 @@ var init_shadcn_registry = __esm({
1841
1841
 
1842
1842
  // src/vite/plugin-theme.ts
1843
1843
  import { createRequire } from "node:module";
1844
- import path20 from "node:path";
1844
+ import path21 from "node:path";
1845
1845
  import { normalizeTheme } from "shiki";
1846
1846
  var THEME_VARIABLES, uncamelize, processColorValue, generateCss, generateRegistryCss, processCustomCss, MAIN_REPLACE, DEFAULT_THEME_REPLACE, GOOGLE_FONTS, getGoogleFontUrl, processFont, processFontConfig, bundledFont, DEFAULT_FONT_SANS, DEFAULT_FONT_MONO, DEFAULT_FONT_HEADING, processFonts, MODULE_SOURCE_PACKAGES, resolveModuleSourceDirs, virtualModuleId, resolvedVirtualModuleId, viteThemePlugin;
1847
1847
  var init_plugin_theme = __esm({
@@ -1956,11 +1956,11 @@ var init_plugin_theme = __esm({
1956
1956
  };
1957
1957
  };
1958
1958
  bundledFont = (pkg) => {
1959
- const dir = path20.dirname(
1959
+ const dir = path21.dirname(
1960
1960
  createRequire(import.meta.url).resolve(`@fontsource/${pkg}/package.json`)
1961
1961
  );
1962
1962
  return [400, 500, 600, 700].map(
1963
- (weight) => path20.join(dir, `latin-${weight}.css`).split(path20.sep).join("/")
1963
+ (weight) => path21.join(dir, `latin-${weight}.css`).split(path21.sep).join("/")
1964
1964
  );
1965
1965
  };
1966
1966
  DEFAULT_FONT_SANS = '"IBM Plex Sans", system-ui, sans-serif';
@@ -2029,7 +2029,7 @@ var init_plugin_theme = __esm({
2029
2029
  resolveModuleSourceDirs = (config2) => {
2030
2030
  if (config2.__meta.mode === "internal") {
2031
2031
  return MODULE_SOURCE_PACKAGES.map(
2032
- (pkg) => path20.join(
2032
+ (pkg) => path21.join(
2033
2033
  config2.__meta.moduleDir,
2034
2034
  "..",
2035
2035
  pkg.replace("@lukoweb/apitogo-", ""),
@@ -2038,12 +2038,12 @@ var init_plugin_theme = __esm({
2038
2038
  );
2039
2039
  }
2040
2040
  const require2 = createRequire(
2041
- path20.join(config2.__meta.rootDir, "package.json")
2041
+ path21.join(config2.__meta.rootDir, "package.json")
2042
2042
  );
2043
2043
  const dirs = [];
2044
2044
  for (const pkg of MODULE_SOURCE_PACKAGES) {
2045
2045
  try {
2046
- dirs.push(path20.dirname(require2.resolve(pkg)));
2046
+ dirs.push(path21.dirname(require2.resolve(pkg)));
2047
2047
  } catch {
2048
2048
  }
2049
2049
  }
@@ -2170,7 +2170,7 @@ ${rootVars.join("\n")}
2170
2170
  ...config2.__meta.dependencies,
2171
2171
  ...config2.__pluginDirs ?? [],
2172
2172
  ...resolveModuleSourceDirs(config2)
2173
- ].map((file) => path20.relative(path20.dirname(id), file))
2173
+ ].map((file) => path21.relative(path21.dirname(id), file))
2174
2174
  );
2175
2175
  const code = [...files].map((file) => `@source "${file}";`);
2176
2176
  code.push("@theme inline {");
@@ -4775,7 +4775,9 @@ var init_ZudokuConfig = __esm({
4775
4775
  scopes: z12.array(z12.string()).optional(),
4776
4776
  redirectToAfterSignUp: z12.string().optional(),
4777
4777
  redirectToAfterSignIn: z12.string().optional(),
4778
- redirectToAfterSignOut: z12.string().optional()
4778
+ redirectToAfterSignOut: z12.string().optional(),
4779
+ /** Try it offers "Signed-in user" (the visitor's token); false when the gateway checks keys, not tokens (API-129). */
4780
+ tokenIdentity: z12.boolean().optional()
4779
4781
  }),
4780
4782
  z12.object({
4781
4783
  type: z12.literal("azureb2c"),
@@ -5012,7 +5014,7 @@ var init_ZudokuConfig = __esm({
5012
5014
 
5013
5015
  // src/config/loader.ts
5014
5016
  import { stat as stat5 } from "node:fs/promises";
5015
- import path21 from "node:path";
5017
+ import path22 from "node:path";
5016
5018
  import colors6 from "picocolors";
5017
5019
  import {
5018
5020
  runnerImport,
@@ -5039,7 +5041,7 @@ function mergeJsonConfigWithRuntimeOverrides(fileConfig, importedConfig) {
5039
5041
  }
5040
5042
  async function getConfigFilePath(rootDir) {
5041
5043
  for (const fileName of zudokuConfigFiles) {
5042
- const filepath = path21.join(rootDir, fileName);
5044
+ const filepath = path22.join(rootDir, fileName);
5043
5045
  if (await fileExists(filepath)) {
5044
5046
  return filepath;
5045
5047
  }
@@ -5268,7 +5270,7 @@ __export(llms_exports, {
5268
5270
  generateLlmsTxtFiles: () => generateLlmsTxtFiles
5269
5271
  });
5270
5272
  import { writeFile as writeFile12 } from "node:fs/promises";
5271
- import path45 from "node:path";
5273
+ import path46 from "node:path";
5272
5274
  import colors10 from "picocolors";
5273
5275
  async function generateLlmsTxtFiles({
5274
5276
  markdownFileInfos,
@@ -5303,7 +5305,7 @@ async function generateLlmsTxtFiles({
5303
5305
  }
5304
5306
  }
5305
5307
  const llmsTxt2 = llmsTxtParts.join("\n");
5306
- await writeFile12(path45.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5308
+ await writeFile12(path46.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5307
5309
  if (process.env.APITOGO_JSON_ONLY !== "1") {
5308
5310
  console.log(colors10.blue("\u2713 generated llms.txt"));
5309
5311
  }
@@ -5331,7 +5333,7 @@ ${info.content}
5331
5333
  }
5332
5334
  const llmsFull = llmsFullParts.join("\n");
5333
5335
  await writeFile12(
5334
- path45.join(baseOutputDir, "llms-full.txt"),
5336
+ path46.join(baseOutputDir, "llms-full.txt"),
5335
5337
  llmsFull,
5336
5338
  "utf-8"
5337
5339
  );
@@ -5834,7 +5836,7 @@ Run the command with --help to see its options.`;
5834
5836
  import yargs from "yargs/yargs";
5835
5837
 
5836
5838
  // src/cli/services/handler.ts
5837
- import path53 from "node:path";
5839
+ import path54 from "node:path";
5838
5840
 
5839
5841
  // src/config/v2/billing.ts
5840
5842
  import { readFile as readFile2 } from "node:fs/promises";
@@ -5927,6 +5929,13 @@ var PortalSchema = z.strictObject({
5927
5929
  theme: path3.optional(),
5928
5930
  /** A custom home page: a React component file that the portal serves at `/`. */
5929
5931
  landing: path3.optional(),
5932
+ /** The footer: a React component file shown under every page except the console's (API-129). */
5933
+ footer: path3.optional(),
5934
+ /** The logo in the portal header: one image, or one per color scheme (API-129). */
5935
+ logo: z.union([
5936
+ path3,
5937
+ z.strictObject({ light: path3, dark: path3, alt: z.string().optional() })
5938
+ ]).optional(),
5930
5939
  requireAuth: z.union([z.boolean(), z.strictObject({ exclude: pathList })], {
5931
5940
  error: 'must be true, false or { "exclude": ["/route", ...] }'
5932
5941
  }).optional(),
@@ -6073,8 +6082,8 @@ var ConfigV2Schema = z.strictObject({
6073
6082
  var REMOVED_KEYS = {
6074
6083
  "plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
6075
6084
  };
6076
- function pathToString(path98) {
6077
- return path98.reduce(
6085
+ function pathToString(path99) {
6086
+ return path99.reduce(
6078
6087
  (acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
6079
6088
  ""
6080
6089
  );
@@ -6094,9 +6103,9 @@ function step(schema2, segment) {
6094
6103
  }
6095
6104
  return void 0;
6096
6105
  }
6097
- function knownKeysAt(path98) {
6106
+ function knownKeysAt(path99) {
6098
6107
  let current = ConfigV2Schema;
6099
- for (const segment of path98) {
6108
+ for (const segment of path99) {
6100
6109
  if (!current) return [];
6101
6110
  current = step(current, segment);
6102
6111
  }
@@ -6132,7 +6141,7 @@ function unknownKeyHint(fullPath, key, known) {
6132
6141
  return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
6133
6142
  }
6134
6143
  function mapIssue(issue4) {
6135
- const path98 = pathToString(issue4.path);
6144
+ const path99 = pathToString(issue4.path);
6136
6145
  switch (issue4.code) {
6137
6146
  case "unrecognized_keys":
6138
6147
  return issue4.keys.map((key) => {
@@ -6148,20 +6157,20 @@ function mapIssue(issue4) {
6148
6157
  return [
6149
6158
  {
6150
6159
  code: "CONFIG_INVALID_VALUE",
6151
- path: path98,
6160
+ path: path99,
6152
6161
  message: `must be one of: ${issue4.values.map((v) => JSON.stringify(v)).join(", ")}.`
6153
6162
  }
6154
6163
  ];
6155
6164
  case "custom": {
6156
6165
  const code = typeof issue4.params?.code === "string" ? issue4.params.code : "CONFIG_INVALID_VALUE";
6157
- return [{ code, path: path98, message: issue4.message ?? "is invalid." }];
6166
+ return [{ code, path: path99, message: issue4.message ?? "is invalid." }];
6158
6167
  }
6159
6168
  case "invalid_type":
6160
6169
  return [
6161
- /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path98, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path98, message: issue4.message }
6170
+ /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path99, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path99, message: issue4.message }
6162
6171
  ];
6163
6172
  default:
6164
- return [{ code: "CONFIG_INVALID_VALUE", path: path98, message: issue4.message }];
6173
+ return [{ code: "CONFIG_INVALID_VALUE", path: path99, message: issue4.message }];
6165
6174
  }
6166
6175
  }
6167
6176
  function isRecord(value) {
@@ -6457,6 +6466,8 @@ function toGlobPath(value) {
6457
6466
  function fromConfig(configDir, value) {
6458
6467
  return value.startsWith("!") ? `!${toGlobPath(path7.resolve(configDir, value.slice(1)))}` : toGlobPath(path7.resolve(configDir, value));
6459
6468
  }
6469
+ var isLogoUrl = (value) => value.toLowerCase().startsWith("https://");
6470
+ var logoSource = (configDir, value) => isLogoUrl(value) ? value : path7.resolve(configDir, value);
6460
6471
  function resolveConfigV2(config2, configPath) {
6461
6472
  const absoluteConfigPath = path7.resolve(configPath);
6462
6473
  const configDir = path7.dirname(absoluteConfigPath);
@@ -6489,6 +6500,17 @@ function resolveConfigV2(config2, configPath) {
6489
6500
  console: { enabled: portal.console?.enabled ?? true },
6490
6501
  theme: portal.theme ? path7.resolve(configDir, portal.theme) : path7.join(dir, "theme.json"),
6491
6502
  ...portal.landing ? { landing: path7.resolve(configDir, portal.landing) } : {},
6503
+ ...portal.footer ? { footer: path7.resolve(configDir, portal.footer) } : {},
6504
+ ...portal.logo ? {
6505
+ logo: typeof portal.logo === "string" ? {
6506
+ light: logoSource(configDir, portal.logo),
6507
+ dark: logoSource(configDir, portal.logo)
6508
+ } : {
6509
+ light: logoSource(configDir, portal.logo.light),
6510
+ dark: logoSource(configDir, portal.logo.dark),
6511
+ ...portal.logo.alt ? { alt: portal.logo.alt } : {}
6512
+ }
6513
+ } : {},
6492
6514
  requireAuth: portal.requireAuth ?? false,
6493
6515
  search: provider === "external" ? "none" : portal.search ?? "pagefind"
6494
6516
  };
@@ -6666,12 +6688,12 @@ function folderLabel(name) {
6666
6688
  return words.charAt(0).toUpperCase() + words.slice(1);
6667
6689
  }
6668
6690
  var byName = (a, b) => a.localeCompare(b);
6669
- var toItems = (branch, prefix, orders) => {
6691
+ var toItems = (branch, prefix, orders, homeTaken = false) => {
6670
6692
  const names = [...branch.pages.keys()].sort(
6671
6693
  (a, b) => comparePages(`/${prefix}${a}`, `/${prefix}${b}`, orders)
6672
6694
  );
6673
6695
  const folders = [...branch.folders.keys()].sort(byName);
6674
- const folderPath = prefix === "" ? "/" : `/${prefix.slice(0, -1)}`;
6696
+ const folderPath = prefix === "" ? homeTaken ? "/index" : "/" : `/${prefix.slice(0, -1)}`;
6675
6697
  return [
6676
6698
  ...names.map((name) => {
6677
6699
  const page = branch.pages.get(name);
@@ -6695,7 +6717,8 @@ var toItems = (branch, prefix, orders) => {
6695
6717
  items: toItems(
6696
6718
  branch.folders.get(folder) ?? emptyBranch(),
6697
6719
  `${prefix}${folder}/`,
6698
- orders
6720
+ orders,
6721
+ homeTaken
6699
6722
  )
6700
6723
  })
6701
6724
  )
@@ -6711,7 +6734,7 @@ var branchOf = (root, route) => {
6711
6734
  }, root);
6712
6735
  return { branch, page };
6713
6736
  };
6714
- function docsNavigation(routes, customPages = [], orders = /* @__PURE__ */ new Map()) {
6737
+ function docsNavigation(routes, customPages = [], orders = /* @__PURE__ */ new Map(), options = {}) {
6715
6738
  const root = emptyBranch();
6716
6739
  for (const route of routes) {
6717
6740
  const { branch, page } = branchOf(root, route);
@@ -6722,7 +6745,7 @@ function docsNavigation(routes, customPages = [], orders = /* @__PURE__ */ new M
6722
6745
  if (page && !branch.pages.has(page))
6723
6746
  branch.pages.set(page, { kind: "custom", ...custom });
6724
6747
  }
6725
- const items = toItems(root, "", orders);
6748
+ const items = toItems(root, "", orders, options.homeTaken);
6726
6749
  return items.length === 0 ? [] : [{ type: "category", label: DOCS_NAVIGATION_LABEL, items }];
6727
6750
  }
6728
6751
 
@@ -6935,6 +6958,35 @@ async function portalRules(config2, resolved, out) {
6935
6958
  hint: "Enable identity or plans, or enable portal.docs."
6936
6959
  });
6937
6960
  }
6961
+ if (portal.footer && !await isFile(portal.footer))
6962
+ out.error({
6963
+ code: "PORTAL_FILE_MISSING",
6964
+ path: "portal.footer",
6965
+ message: `the file ${path10.relative(resolved.configDir, portal.footer)} does not exist.`,
6966
+ hint: "Create it, or remove portal.footer."
6967
+ });
6968
+ const publicDir = path10.join(portal.dir, "public");
6969
+ for (const file of new Set(
6970
+ [portal.logo?.light, portal.logo?.dark].filter(
6971
+ (value) => !!value && !isLogoUrl(value)
6972
+ )
6973
+ )) {
6974
+ const inside = path10.relative(publicDir, file);
6975
+ if (inside.startsWith("..") || path10.isAbsolute(inside))
6976
+ out.error({
6977
+ code: "PORTAL_LOGO_OUTSIDE_PUBLIC",
6978
+ path: "portal.logo",
6979
+ message: `the logo ${path10.relative(resolved.configDir, file)} is not in the portal's public folder, which is what the site serves.`,
6980
+ hint: `Move it into ${path10.relative(resolved.configDir, publicDir)}/, or use an https:// address.`
6981
+ });
6982
+ else if (!await isFile(file))
6983
+ out.error({
6984
+ code: "PORTAL_FILE_MISSING",
6985
+ path: "portal.logo",
6986
+ message: `the file ${path10.relative(resolved.configDir, file)} does not exist.`,
6987
+ hint: "Create it, or remove portal.logo."
6988
+ });
6989
+ }
6938
6990
  if (await isFile(portal.theme)) {
6939
6991
  try {
6940
6992
  JSON.parse(await readFile4(portal.theme, "utf8"));
@@ -7455,7 +7507,7 @@ var ServiceManifestSchema = z3.looseObject({
7455
7507
  }).optional()
7456
7508
  }).default({})
7457
7509
  });
7458
- var issue = (code, path98, message, hint) => ({ code, path: path98, message, hint });
7510
+ var issue = (code, path99, message, hint) => ({ code, path: path99, message, hint });
7459
7511
  function parseManifest(raw, file = "services.json") {
7460
7512
  const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
7461
7513
  if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
@@ -7718,8 +7770,8 @@ var METHODS = [
7718
7770
  "trace"
7719
7771
  ];
7720
7772
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7721
- function actionKey(method, path98) {
7722
- const trimmed = path98.trim();
7773
+ function actionKey(method, path99) {
7774
+ const trimmed = path99.trim();
7723
7775
  const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
7724
7776
  const normalized = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") || "/" : withSlash;
7725
7777
  return `${method.trim().toLowerCase()}:${normalized}`;
@@ -7756,8 +7808,8 @@ var ReferenceSpecError = class extends Error {
7756
7808
  };
7757
7809
  function operationsOf(paths) {
7758
7810
  return Object.entries(paths).flatMap(
7759
- ([path98, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7760
- (method) => [path98, item, method]
7811
+ ([path99, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7812
+ (method) => [path99, item, method]
7761
7813
  ) : []
7762
7814
  );
7763
7815
  }
@@ -7772,14 +7824,14 @@ function buildReferenceSpec(specText, options) {
7772
7824
  throw new ReferenceSpecError("The OpenAPI file could not be read.");
7773
7825
  const doc = structuredClone(root);
7774
7826
  const paths = isObject(doc.paths) ? doc.paths : {};
7775
- for (const [path98, item, method] of operationsOf(paths))
7776
- if (options.deniedOperations.has(actionKey(method, path98)))
7827
+ for (const [path99, item, method] of operationsOf(paths))
7828
+ if (options.deniedOperations.has(actionKey(method, path99)))
7777
7829
  delete item[method];
7778
- for (const [path98, item] of Object.entries(paths))
7830
+ for (const [path99, item] of Object.entries(paths))
7779
7831
  if (!isObject(item) || !METHODS.some((method) => method in item))
7780
- delete paths[path98];
7781
- for (const [path98, item, method] of operationsOf(paths))
7782
- if (options.publicOperations.has(actionKey(method, path98)))
7832
+ delete paths[path99];
7833
+ for (const [path99, item, method] of operationsOf(paths))
7834
+ if (options.publicOperations.has(actionKey(method, path99)))
7783
7835
  item[method].security = [];
7784
7836
  doc.servers = [{ url: options.gatewayBaseUrl.replace(/\/+$/, "") }];
7785
7837
  for (const item of Object.values(paths)) {
@@ -7819,11 +7871,11 @@ function buildReferenceSpec(specText, options) {
7819
7871
  if (Object.keys(components).length > 0) doc.components = components;
7820
7872
  else delete doc.components;
7821
7873
  doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
7822
- for (const [path98, item, method] of operationsOf(paths)) {
7874
+ for (const [path99, item, method] of operationsOf(paths)) {
7823
7875
  const operation = item[method];
7824
7876
  if (Array.isArray(operation.security) && operation.security.length > 0)
7825
7877
  delete operation.security;
7826
- if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path98)))
7878
+ if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path99)))
7827
7879
  operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
7828
7880
  else delete operation[KEY_HEADER_EXTENSION];
7829
7881
  }
@@ -7917,10 +7969,89 @@ function stableJson(value) {
7917
7969
 
7918
7970
  // src/config/v2/wrapper.ts
7919
7971
  import { mkdir as mkdir4, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
7920
- import path13 from "node:path";
7972
+ import path14 from "node:path";
7921
7973
 
7922
- // src/config/v2/home-redirect.ts
7974
+ // src/cli/platform/helpers/openapi-action-keys.ts
7975
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
7923
7976
  import path12 from "node:path";
7977
+ import YAML from "yaml";
7978
+ var HTTP_METHODS = [
7979
+ "get",
7980
+ "post",
7981
+ "put",
7982
+ "patch",
7983
+ "delete",
7984
+ "head",
7985
+ "options"
7986
+ ];
7987
+ function normalizeOpenApiPathTemplate(pathTemplate) {
7988
+ if (!pathTemplate.trim()) {
7989
+ return "/";
7990
+ }
7991
+ let value = pathTemplate.trim();
7992
+ if (!value.startsWith("/")) {
7993
+ value = `/${value}`;
7994
+ }
7995
+ if (value.length > 1 && value.endsWith("/")) {
7996
+ value = value.replace(/\/+$/, "");
7997
+ }
7998
+ return value;
7999
+ }
8000
+ function resolveRelativeServerBasePath(document) {
8001
+ const serverUrl = document.servers?.[0]?.url;
8002
+ if (!serverUrl || serverUrl === "/") {
8003
+ return null;
8004
+ }
8005
+ return serverUrl.startsWith("/") ? serverUrl.replace(/\/+$/, "") : null;
8006
+ }
8007
+ function combineBasePath(basePath, pathKey) {
8008
+ const normalizedPath = normalizeOpenApiPathTemplate(pathKey);
8009
+ if (!basePath || basePath === "/") {
8010
+ return normalizedPath;
8011
+ }
8012
+ const baseNorm = normalizeOpenApiPathTemplate(basePath);
8013
+ if (normalizedPath === "/") {
8014
+ return baseNorm;
8015
+ }
8016
+ return `${baseNorm.replace(/\/+$/, "")}${normalizedPath}`;
8017
+ }
8018
+ function extractGatewayActionKeysFromOpenApiDocument(document) {
8019
+ if (!document || typeof document !== "object") {
8020
+ return [];
8021
+ }
8022
+ const spec = document;
8023
+ if (!spec.paths || typeof spec.paths !== "object") {
8024
+ return [];
8025
+ }
8026
+ const basePath = resolveRelativeServerBasePath(spec);
8027
+ const keys = /* @__PURE__ */ new Set();
8028
+ for (const [pathKey, pathItem] of Object.entries(spec.paths)) {
8029
+ if (!pathItem || typeof pathItem !== "object") {
8030
+ continue;
8031
+ }
8032
+ const pathTemplate = combineBasePath(basePath, pathKey);
8033
+ for (const method of HTTP_METHODS) {
8034
+ if (!pathItem[method] || typeof pathItem[method] !== "object") {
8035
+ continue;
8036
+ }
8037
+ keys.add(`${method}:${pathTemplate}`);
8038
+ }
8039
+ }
8040
+ return [...keys].sort((a, b) => a.localeCompare(b));
8041
+ }
8042
+ function extractGatewayActionKeysFromOpenApiFile(filePath) {
8043
+ try {
8044
+ const raw = readFileSync3(filePath, "utf8");
8045
+ const ext = path12.extname(filePath).toLowerCase();
8046
+ const parsed = ext === ".json" ? JSON.parse(raw) : YAML.parse(raw);
8047
+ return extractGatewayActionKeysFromOpenApiDocument(parsed);
8048
+ } catch {
8049
+ return [];
8050
+ }
8051
+ }
8052
+
8053
+ // src/config/v2/home-redirect.ts
8054
+ import path13 from "node:path";
7924
8055
  import { glob as glob3 } from "glob";
7925
8056
  import globParent2 from "glob-parent";
7926
8057
 
@@ -7959,7 +8090,7 @@ async function docRoutes(patterns, options = {}) {
7959
8090
  }))
7960
8091
  )).filter(({ draft }) => !draft).map(({ file }) => file);
7961
8092
  return published.map(
7962
- (file) => `/${path12.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
8093
+ (file) => `/${path13.posix.relative(parent, file.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`
7963
8094
  );
7964
8095
  })
7965
8096
  );
@@ -7982,7 +8113,7 @@ async function docPageOrders(patterns) {
7982
8113
  for (const raw of files) {
7983
8114
  const order = orderValue((await readFrontmatter(raw)).data.order);
7984
8115
  if (order === void 0) continue;
7985
- const route = `/${path12.posix.relative(parent, raw.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`;
8116
+ const route = `/${path13.posix.relative(parent, raw.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`;
7986
8117
  if (!orders.has(route)) orders.set(route, order);
7987
8118
  }
7988
8119
  }
@@ -8028,14 +8159,14 @@ var CONTENT_LINK = "content";
8028
8159
  function contentGlob(configDir, glob9) {
8029
8160
  const negated = glob9.startsWith("!");
8030
8161
  const absolute = negated ? glob9.slice(1) : glob9;
8031
- const relative = path13.relative(configDir, absolute).split(path13.sep).join("/");
8162
+ const relative = path14.relative(configDir, absolute).split(path14.sep).join("/");
8032
8163
  return `${negated ? "!" : ""}/${CONTENT_LINK}/${relative}`;
8033
8164
  }
8034
8165
  function contentFile(configDir, file) {
8035
- const relative = path13.relative(configDir, file);
8036
- if (relative.startsWith("..") || path13.isAbsolute(relative))
8037
- return file.split(path13.sep).join("/");
8038
- return `${CONTENT_LINK}/${relative.split(path13.sep).join("/")}`;
8166
+ const relative = path14.relative(configDir, file);
8167
+ if (relative.startsWith("..") || path14.isAbsolute(relative))
8168
+ return file.split(path14.sep).join("/");
8169
+ return `${CONTENT_LINK}/${relative.split(path14.sep).join("/")}`;
8039
8170
  }
8040
8171
  var PLANS_CONSOLE_CAPABILITIES = [
8041
8172
  "catalog",
@@ -8195,6 +8326,7 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
8195
8326
  return {
8196
8327
  site: {
8197
8328
  title: portal?.name ?? resolved.project.name,
8329
+ ...portal?.logo ? { logo: siteLogo(portal) } : {},
8198
8330
  ...plan2.preview ? { banner: { message: PREVIEW_BANNER_MESSAGE, color: "caution" } } : {}
8199
8331
  },
8200
8332
  modules: {
@@ -8268,13 +8400,25 @@ function authenticationFor(plan2) {
8268
8400
  return { type: "openid", ...signIn };
8269
8401
  }
8270
8402
  var PREVIEW_BANNER_MESSAGE = "Preview mode: sign in with demo@apitogo.local / password to try the console. Run `apitogo deploy` for the real services.";
8271
- function renderWrapper(plan2, pages = []) {
8403
+ function siteLogo(portal) {
8404
+ const logo = portal.logo;
8405
+ const publicDir = path14.join(portal.dir, "public");
8406
+ const src = (file) => isLogoUrl(file) ? file : `/${path14.relative(publicDir, file).split(path14.sep).join("/")}`;
8407
+ return {
8408
+ src: { light: src(logo.light), dark: src(logo.dark) },
8409
+ alt: logo.alt ?? portal.name
8410
+ };
8411
+ }
8412
+ var wrapperImport = (file) => file.startsWith(`${CONTENT_LINK}/`) ? `./${file}` : file;
8413
+ var FOOTER_HIDDEN_PATHS = ["/account", "/login", "/signup"];
8414
+ function renderWrapper(plan2, pages = [], footerImportPath) {
8272
8415
  const imports = [
8273
8416
  'import { buildApitogoConfig } from "@lukoweb/apitogo/build-config";',
8274
8417
  ...plan2.plansPlugin ? [
8275
8418
  'import { devPortalBillingPlugin } from "@lukoweb/apitogo-plugin-dev-portal-billing";'
8276
8419
  ] : [],
8277
- ...pages.length > 0 ? ['import { createElement } from "react";'] : [],
8420
+ ...pages.length > 0 || footerImportPath ? ['import { createElement } from "react";'] : [],
8421
+ ...footerImportPath ? [`import PortalFooter from ${JSON.stringify(footerImportPath)};`] : [],
8278
8422
  ...pages.map(
8279
8423
  (page, index) => `import TsxPage${index} from ${JSON.stringify(page.importPath)};`
8280
8424
  ),
@@ -8311,7 +8455,20 @@ function renderWrapper(plan2, pages = []) {
8311
8455
  "",
8312
8456
  // The components go in after buildApitogoConfig: it copies the config with structuredClone, which cannot copy a
8313
8457
  // React element.
8314
- ...pages.length > 0 ? [
8458
+ ...footerImportPath ? [
8459
+ "",
8460
+ "// The portal's footer (portal.footer) on every page except the console's.",
8461
+ `const FOOTER_HIDDEN_PATHS = ${JSON.stringify(FOOTER_HIDDEN_PATHS)};`,
8462
+ "const footerSlot = (props: any) =>",
8463
+ " FOOTER_HIDDEN_PATHS.some(",
8464
+ " (hidden) =>",
8465
+ " props.location.pathname === hidden ||",
8466
+ " props.location.pathname.startsWith(`${hidden}/`),",
8467
+ " )",
8468
+ " ? null",
8469
+ " : createElement(PortalFooter, props);"
8470
+ ] : [],
8471
+ ...pages.length > 0 || footerImportPath ? [
8315
8472
  "const config = buildApitogoConfig({",
8316
8473
  " ...siteConfig,",
8317
8474
  ` plugins: ${plugins},`,
@@ -8319,7 +8476,8 @@ function renderWrapper(plan2, pages = []) {
8319
8476
  "",
8320
8477
  "export default {",
8321
8478
  " ...config,",
8322
- " navigation: withTsxPages((config as any).navigation ?? []),",
8479
+ ...pages.length > 0 ? [" navigation: withTsxPages((config as any).navigation ?? []),"] : [],
8480
+ ...footerImportPath ? [" slots: { ...(config as any).slots, footer: footerSlot },"] : [],
8323
8481
  "};"
8324
8482
  ] : [
8325
8483
  "export default buildApitogoConfig({",
@@ -8339,6 +8497,21 @@ async function readDemoPlans(file) {
8339
8497
  return void 0;
8340
8498
  }
8341
8499
  }
8500
+ function withPreviewActions(plans, specs) {
8501
+ if (!plans) return void 0;
8502
+ let all;
8503
+ return plans.map((item) => {
8504
+ if (typeof item !== "object" || item === null) return item;
8505
+ const named = item.includedActions;
8506
+ all ??= [
8507
+ ...new Set(specs.flatMap(extractGatewayActionKeysFromOpenApiFile))
8508
+ ].sort((a, b) => a.localeCompare(b));
8509
+ return {
8510
+ ...item,
8511
+ includedActionKeys: Array.isArray(named) ? named : all
8512
+ };
8513
+ });
8514
+ }
8342
8515
  async function readTheme(resolved) {
8343
8516
  const file = resolved.portal?.theme;
8344
8517
  if (!file) return void 0;
@@ -8409,7 +8582,7 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8409
8582
  ) : [];
8410
8583
  const wrapperPages = tsxPages.map((page, index) => ({
8411
8584
  id: `${TSX_PAGE_ELEMENT_PREFIX}${index}`,
8412
- importPath: `./${CONTENT_LINK}/${path13.relative(resolved.configDir, page.file).split(path13.sep).join("/")}`
8585
+ importPath: `./${CONTENT_LINK}/${path14.relative(resolved.configDir, page.file).split(path14.sep).join("/")}`
8413
8586
  }));
8414
8587
  const hostedDocRoutes = [
8415
8588
  .../* @__PURE__ */ new Set([...markdownRoutes, ...tsxPages.map((page) => page.route)])
@@ -8425,10 +8598,26 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8425
8598
  if (featured)
8426
8599
  plan2.plansPlugin.featuredPlanSku = featured.sku.trim().toLowerCase();
8427
8600
  }
8601
+ if (plan2.plansPlugin) {
8602
+ const planFeatures = {};
8603
+ for (const item of await readDemoPlans(resolved.plans?.file) ?? []) {
8604
+ const { sku, features } = item ?? {};
8605
+ if (typeof sku !== "string" || !Array.isArray(features)) continue;
8606
+ const lines = features.filter(
8607
+ (line2) => typeof line2 === "string" && line2.trim().length > 0
8608
+ );
8609
+ if (lines.length > 0) planFeatures[sku.trim().toLowerCase()] = lines;
8610
+ }
8611
+ if (Object.keys(planFeatures).length > 0)
8612
+ plan2.plansPlugin.planFeatures = planFeatures;
8613
+ }
8614
+ const tokenIdentity = resolved.gateway?.auth !== void 0;
8615
+ if (plan2.plansPlugin) plan2.plansPlugin.tokenIdentity = tokenIdentity;
8616
+ if (plan2.auth) plan2.auth.tokenIdentity = tokenIdentity;
8428
8617
  if (plan2.plansPlugin && plan2.docs) {
8429
8618
  const landing = hostedDocRoutes.some(
8430
8619
  (route) => route === "/" || route === "/index"
8431
- ) ? "/" : pickLandingRoute(hostedDocRoutes, pageOrders);
8620
+ ) ? resolved.portal?.landing && !hostedDocRoutes.includes("/") ? "/index" : "/" : pickLandingRoute(hostedDocRoutes, pageOrders);
8432
8621
  if (landing)
8433
8622
  plan2.plansPlugin.links = { ...plan2.plansPlugin.links, docs: landing };
8434
8623
  }
@@ -8454,10 +8643,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8454
8643
  label: page.label,
8455
8644
  element: wrapperPages[index]?.id ?? ""
8456
8645
  })),
8457
- pageOrders
8646
+ pageOrders,
8647
+ { homeTaken: resolved.portal?.landing !== void 0 }
8458
8648
  ),
8459
8649
  docsPath: pickLandingRoute(hostedDocRoutes, pageOrders),
8460
- demoPlans: plan2.demo && plan2.plansPlugin ? await readDemoPlans(resolved.plans?.file) : void 0,
8650
+ demoPlans: plan2.demo && plan2.plansPlugin ? withPreviewActions(
8651
+ await readDemoPlans(resolved.plans?.file),
8652
+ resolved.gateway?.specs ?? []
8653
+ ) : void 0,
8461
8654
  protectedRoutes: protectedRoutesFor({
8462
8655
  requireAuth: resolved.portal?.requireAuth,
8463
8656
  hasAuth: !!plan2.auth,
@@ -8466,21 +8659,25 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8466
8659
  })
8467
8660
  }
8468
8661
  );
8469
- const wrapper = renderWrapper(plan2, wrapperPages);
8470
- const wrapperPath = path13.join(resolved.state.generatedDir, WRAPPER_FILE);
8662
+ const wrapper = renderWrapper(
8663
+ plan2,
8664
+ wrapperPages,
8665
+ resolved.portal?.footer ? wrapperImport(contentFile(resolved.configDir, resolved.portal.footer)) : void 0
8666
+ );
8667
+ const wrapperPath = path14.join(resolved.state.generatedDir, WRAPPER_FILE);
8471
8668
  if (options.write !== false) {
8472
- await mkdir4(path13.dirname(wrapperPath), { recursive: true });
8669
+ await mkdir4(path14.dirname(wrapperPath), { recursive: true });
8473
8670
  await writeFile5(
8474
- path13.join(path13.dirname(wrapperPath), SITE_CONFIG_FILE),
8671
+ path14.join(path14.dirname(wrapperPath), SITE_CONFIG_FILE),
8475
8672
  `${JSON.stringify(siteConfig, null, 2)}
8476
8673
  `
8477
8674
  );
8478
- const referenceFile = path13.join(
8479
- path13.dirname(wrapperPath),
8675
+ const referenceFile = path14.join(
8676
+ path14.dirname(wrapperPath),
8480
8677
  REFERENCE_SPEC_FILE
8481
8678
  );
8482
8679
  if (referenceSpec !== void 0) {
8483
- await mkdir4(path13.dirname(referenceFile), { recursive: true });
8680
+ await mkdir4(path14.dirname(referenceFile), { recursive: true });
8484
8681
  await writeFile5(referenceFile, referenceSpec);
8485
8682
  } else {
8486
8683
  await rm2(referenceFile, { force: true });
@@ -8510,7 +8707,7 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8510
8707
  // src/config/v2/workspace.ts
8511
8708
  import { createHash as createHash2 } from "node:crypto";
8512
8709
  import { access as access2, lstat, readFile as readFile9, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
8513
- import path14 from "node:path";
8710
+ import path15 from "node:path";
8514
8711
  import { glob as glob4 } from "glob";
8515
8712
 
8516
8713
  // src/config/v2/link-directory.ts
@@ -8544,18 +8741,18 @@ async function relink(target, link) {
8544
8741
  }
8545
8742
  var GENERATED_PACKAGE_JSON = '{\n "private": true,\n "type": "module"\n}\n';
8546
8743
  async function writeGeneratedPackageJson(root) {
8547
- const file = path14.join(root, "package.json");
8744
+ const file = path15.join(root, "package.json");
8548
8745
  const current = await readFile9(file, "utf8").catch(() => void 0);
8549
8746
  if (current !== GENERATED_PACKAGE_JSON)
8550
8747
  await writeFile6(file, GENERATED_PACKAGE_JSON, "utf8");
8551
8748
  }
8552
8749
  async function materializeWorkspace(resolved, prepared) {
8553
- const root = path14.dirname(prepared.wrapperPath);
8750
+ const root = path15.dirname(prepared.wrapperPath);
8554
8751
  await ensureStateDir(resolved.state);
8555
8752
  await writeGeneratedPackageJson(root);
8556
- await relink(resolved.configDir, path14.join(root, CONTENT_LINK));
8557
- const publicDir = resolved.portal && path14.join(resolved.portal.dir, "public");
8558
- const publicLink = path14.join(root, "public");
8753
+ await relink(resolved.configDir, path15.join(root, CONTENT_LINK));
8754
+ const publicDir = resolved.portal && path15.join(resolved.portal.dir, "public");
8755
+ const publicLink = path15.join(root, "public");
8559
8756
  if (publicDir && await exists2(publicDir)) {
8560
8757
  await relink(publicDir, publicLink);
8561
8758
  } else {
@@ -8583,7 +8780,7 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8583
8780
  // React docs pages and the helpers next to them are imported by the wrapper, so their contents decide the build too.
8584
8781
  ...portal?.docs.provider === "apitogo" ? tsxPagePatterns(portal.docs.content) : [],
8585
8782
  ...resolved.gateway?.specs ?? [],
8586
- ...portal ? [portal.theme, path14.join(portal.dir, "public", "**", "*")] : []
8783
+ ...portal ? [portal.theme, path15.join(portal.dir, "public", "**", "*")] : []
8587
8784
  ];
8588
8785
  const positive = patterns.filter((p) => !p.startsWith("!"));
8589
8786
  const ignore2 = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
@@ -8591,7 +8788,7 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8591
8788
  ...new Set(
8592
8789
  (await Promise.all(
8593
8790
  positive.map(
8594
- (pattern) => glob4(pattern.split(path14.sep).join("/"), {
8791
+ (pattern) => glob4(pattern.split(path15.sep).join("/"), {
8595
8792
  absolute: true,
8596
8793
  nodir: true,
8597
8794
  ignore: [...ignore2, "**/node_modules/**"]
@@ -8613,7 +8810,7 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
8613
8810
  `);
8614
8811
  }
8615
8812
  for (const file of files) {
8616
- hash2.update(`file:${path14.relative(resolved.configDir, file)}
8813
+ hash2.update(`file:${path15.relative(resolved.configDir, file)}
8617
8814
  `);
8618
8815
  hash2.update(await readFile9(file));
8619
8816
  }
@@ -8631,7 +8828,7 @@ import {
8631
8828
  stat as stat3,
8632
8829
  writeFile as writeFile7
8633
8830
  } from "node:fs/promises";
8634
- import path15 from "node:path";
8831
+ import path16 from "node:path";
8635
8832
 
8636
8833
  // src/config/v2/full-server.ts
8637
8834
  var FULL_SERVER_SOURCE = "server";
@@ -8947,6 +9144,36 @@ ${plansSection} <PricingCtaSection
8947
9144
  }
8948
9145
  `;
8949
9146
  }
9147
+ function starterFooter(options) {
9148
+ const links = options.links.map(
9149
+ (link) => ` { label: ${JSON.stringify(link.label)}, href: ${JSON.stringify(link.href)} },`
9150
+ ).join("\n");
9151
+ return `// Your portal's footer, shown under every page except the console's. Edit this file to make it yours, or remove
9152
+ // "footer" from portal in apitogo.config.json to have none.
9153
+ import { FooterSection } from "@lukoweb/apitogo-module-landing";
9154
+
9155
+ const NAME = ${JSON.stringify(options.name)};
9156
+
9157
+ export default function Footer() {
9158
+ return (
9159
+ <FooterSection
9160
+ footer={{
9161
+ brand: { name: NAME, tagline: "Documentation, API keys and plans for the ${options.name.replace(/[`$\\"]/g, "")} API." },
9162
+ columns: [
9163
+ {
9164
+ title: "Developers",
9165
+ links: [
9166
+ ${links}
9167
+ ],
9168
+ },
9169
+ ],
9170
+ copyright: \`\xA9 \${new Date().getFullYear()} \${NAME}\`,
9171
+ }}
9172
+ />
9173
+ );
9174
+ }
9175
+ `;
9176
+ }
8950
9177
 
8951
9178
  // src/config/v2/services.ts
8952
9179
  var SERVICES = ["portal", "gateway", "identity", "plans"];
@@ -9001,7 +9228,7 @@ var exists3 = (target) => access3(target).then(
9001
9228
  () => true,
9002
9229
  () => false
9003
9230
  );
9004
- var rel = (dir, target) => path15.relative(dir, target).split(path15.sep).join("/") || ".";
9231
+ var rel = (dir, target) => path16.relative(dir, target).split(path16.sep).join("/") || ".";
9005
9232
  var BLANK_SPEC = (name) => `openapi: 3.1.0
9006
9233
  info:
9007
9234
  title: ${JSON.stringify(name)}
@@ -9099,19 +9326,37 @@ function pair(value, flag, unit) {
9099
9326
  return [Number(match[1]), Number(match[2])];
9100
9327
  }
9101
9328
  function insideProject(dir, requested, what) {
9102
- const absolute = path15.resolve(dir, requested);
9103
- const relative = path15.relative(dir, absolute);
9104
- if (relative === "" || relative.startsWith("..") || path15.isAbsolute(relative)) {
9329
+ const absolute = path16.resolve(dir, requested);
9330
+ const relative = path16.relative(dir, absolute);
9331
+ if (relative === "" || relative.startsWith("..") || path16.isAbsolute(relative)) {
9105
9332
  throw new ServiceEditError(
9106
9333
  `${what} "${requested}" is outside the project directory.`
9107
9334
  );
9108
9335
  }
9109
- return relative.split(path15.sep).join("/");
9336
+ return relative.split(path16.sep).join("/");
9110
9337
  }
9111
9338
  async function addFile(context, target, content) {
9112
9339
  if (await exists3(target)) context.keep.push(target);
9113
9340
  else context.files.push({ path: target, content });
9114
9341
  }
9342
+ var STARTER_THEME = `${JSON.stringify(
9343
+ {
9344
+ light: {
9345
+ primary: "#0d9488",
9346
+ primaryForeground: "#ffffff",
9347
+ ring: "#0d9488",
9348
+ radius: "0.625rem"
9349
+ },
9350
+ dark: {
9351
+ primary: "#0d9488",
9352
+ primaryForeground: "#ffffff",
9353
+ ring: "#0d9488"
9354
+ }
9355
+ },
9356
+ null,
9357
+ 2
9358
+ )}
9359
+ `;
9115
9360
  async function buildPortal(context) {
9116
9361
  const { options, dir, resolved } = context;
9117
9362
  const o = options.portal ?? {};
@@ -9158,16 +9403,17 @@ async function buildPortal(context) {
9158
9403
  { value: "starter", label: "A starter page I can edit" },
9159
9404
  { value: "blank", label: "An empty pages folder" }
9160
9405
  ]);
9161
- const pages = path15.join(resolved.root, "portal", "pages");
9406
+ const pages = path16.join(resolved.root, "portal", "pages");
9407
+ content = [`${rel(dir, pages)}/**/*.{md,mdx}`];
9162
9408
  if (template === "blank")
9163
- await addFile(context, path15.join(pages, ".gitkeep"), "");
9409
+ await addFile(context, path16.join(pages, ".gitkeep"), "");
9164
9410
  else if (isFilled(options.template)) {
9165
9411
  await addFile(
9166
9412
  context,
9167
- path15.join(pages, "introduction.mdx"),
9413
+ path16.join(pages, "introduction.mdx"),
9168
9414
  starterPage(name)
9169
9415
  );
9170
- const landingFile = path15.join(
9416
+ const landingFile = path16.join(
9171
9417
  resolved.root,
9172
9418
  "portal",
9173
9419
  "landing",
@@ -9185,34 +9431,54 @@ async function buildPortal(context) {
9185
9431
  } else
9186
9432
  await addFile(
9187
9433
  context,
9188
- path15.join(pages, "index.mdx"),
9434
+ path16.join(pages, "introduction.mdx"),
9189
9435
  starterPage(name)
9190
9436
  );
9191
9437
  }
9438
+ await addFile(
9439
+ context,
9440
+ path16.join(resolved.root, "portal", "theme.json"),
9441
+ STARTER_THEME
9442
+ );
9192
9443
  }
9444
+ const footerFile = path16.join(resolved.root, "portal", "footer", "Footer.tsx");
9445
+ await addFile(
9446
+ context,
9447
+ footerFile,
9448
+ starterFooter({
9449
+ name,
9450
+ links: [
9451
+ ...isFilled(options.template) && !external ? [{ label: "Documentation", href: "/introduction" }] : [],
9452
+ ...context.config.gateway ? [{ label: "API Reference", href: "/api" }] : [],
9453
+ ...context.config.plans && context.config.plans.enabled !== false ? [{ label: "Pricing", href: "/pricing" }] : [],
9454
+ ...context.config.identity && !o.noConsole ? [{ label: "Sign in", href: "/login" }] : []
9455
+ ]
9456
+ })
9457
+ );
9193
9458
  return {
9194
9459
  ...options.disabled ? { enabled: false } : {},
9195
9460
  name,
9196
9461
  ...external ? { docs: { provider: "external", url: docsUrl } } : content.length > 0 ? { docs: { content } } : {},
9197
9462
  ...landing ? { landing } : {},
9463
+ footer: rel(dir, footerFile),
9198
9464
  ...o.noConsole ? { console: { enabled: false } } : {},
9199
9465
  ...o.requireAuth ? { requireAuth: true } : {},
9200
9466
  ...o.search && !external ? { search: o.search } : {}
9201
9467
  };
9202
9468
  }
9203
9469
  async function createSourceFolder(context, sourcePath, runtime, writeSample) {
9204
- const folder = path15.join(context.dir, sourcePath);
9470
+ const folder = path16.join(context.dir, sourcePath);
9205
9471
  const entries = await readdir3(folder).catch(() => []);
9206
9472
  if (entries.some((name) => name !== ".gitkeep")) return false;
9207
9473
  if (writeSample && runtime === FULL_SERVER_RUNTIME) {
9208
9474
  for (const [file, content] of Object.entries(
9209
9475
  fullServerFiles(context.resolved.project.name)
9210
9476
  ))
9211
- await addFile(context, path15.join(folder, file), content);
9477
+ await addFile(context, path16.join(folder, file), content);
9212
9478
  return true;
9213
9479
  }
9214
9480
  if (entries.length === 0)
9215
- await addFile(context, path15.join(folder, ".gitkeep"), "");
9481
+ await addFile(context, path16.join(folder, ".gitkeep"), "");
9216
9482
  context.notes.push(
9217
9483
  `The folder ${sourcePath}/ is empty: put your backend code there before you deploy.`
9218
9484
  );
@@ -9324,7 +9590,7 @@ async function buildGateway(context) {
9324
9590
  specs = [];
9325
9591
  for (const requested of requestedSpecs) {
9326
9592
  const relative = insideProject(dir, requested, "OpenAPI file");
9327
- if (!await stat3(path15.join(dir, relative)).then(
9593
+ if (!await stat3(path16.join(dir, relative)).then(
9328
9594
  (s) => s.isFile(),
9329
9595
  () => false
9330
9596
  )) {
@@ -9335,7 +9601,7 @@ async function buildGateway(context) {
9335
9601
  specs.push(relative);
9336
9602
  }
9337
9603
  } else if (createSpec) {
9338
- const target = path15.join(resolved.apisDir, "openapi.yaml");
9604
+ const target = path16.join(resolved.apisDir, "openapi.yaml");
9339
9605
  await addFile(
9340
9606
  context,
9341
9607
  target,
@@ -9432,7 +9698,7 @@ async function buildPlans(context) {
9432
9698
  const o = options.plans ?? {};
9433
9699
  const template = o.template ?? (options.template === "blank" ? "none" : isFilled(options.template) ? "starter" : "free");
9434
9700
  const file = o.file ? insideProject(dir, o.file, "Plans file") : void 0;
9435
- const target = file ? path15.join(dir, file) : resolved.plans?.file ?? path15.join(resolved.root, "plans.json");
9701
+ const target = file ? path16.join(dir, file) : resolved.plans?.file ?? path16.join(resolved.root, "plans.json");
9436
9702
  await addFile(
9437
9703
  context,
9438
9704
  target,
@@ -9465,11 +9731,11 @@ function dependencyProblems(config2, configPath) {
9465
9731
  return checkDependencies(config2, resolveConfigV2(config2, configPath));
9466
9732
  }
9467
9733
  async function planAddService(service, options) {
9468
- const dir = path15.resolve(options.dir);
9734
+ const dir = path16.resolve(options.dir);
9469
9735
  const { config: config2, resolved } = loadConfigV2(dir);
9470
9736
  if (config2[service] !== void 0) {
9471
9737
  throw new ServiceEditError(
9472
- `${service} is already in ${path15.basename(resolved.configPath)}.`,
9738
+ `${service} is already in ${path16.basename(resolved.configPath)}.`,
9473
9739
  `Edit the file, or run \`apitogo remove ${service}\` first. Its files are kept.`
9474
9740
  );
9475
9741
  }
@@ -9514,11 +9780,11 @@ async function planAddService(service, options) {
9514
9780
  };
9515
9781
  }
9516
9782
  async function planRemoveService(service, options) {
9517
- const dir = path15.resolve(options.dir);
9783
+ const dir = path16.resolve(options.dir);
9518
9784
  const { config: config2, resolved } = loadConfigV2(dir);
9519
9785
  if (config2[service] === void 0) {
9520
9786
  throw new ServiceEditError(
9521
- `${service} is not in ${path15.basename(resolved.configPath)}.`
9787
+ `${service} is not in ${path16.basename(resolved.configPath)}.`
9522
9788
  );
9523
9789
  }
9524
9790
  const { [service]: removed, ...rest } = config2;
@@ -9542,8 +9808,8 @@ async function planRemoveService(service, options) {
9542
9808
  if (service === "gateway" && r.gateway) owned.push(...r.gateway.specs);
9543
9809
  void removed;
9544
9810
  const inside = (target) => {
9545
- const relative = path15.relative(resolved.root, target);
9546
- return relative !== "" && !relative.startsWith("..") && !path15.isAbsolute(relative);
9811
+ const relative = path16.relative(resolved.root, target);
9812
+ return relative !== "" && !relative.startsWith("..") && !path16.isAbsolute(relative);
9547
9813
  };
9548
9814
  const deletes = [];
9549
9815
  const left = [];
@@ -9582,7 +9848,7 @@ async function writeConfig(configPath, config2) {
9582
9848
  async function applyServiceEdit(edit) {
9583
9849
  const created = [];
9584
9850
  for (const file of edit.files) {
9585
- await mkdir5(path15.dirname(file.path), { recursive: true });
9851
+ await mkdir5(path16.dirname(file.path), { recursive: true });
9586
9852
  try {
9587
9853
  await writeFile7(file.path, file.content, { flag: "wx" });
9588
9854
  created.push(file.path);
@@ -9598,7 +9864,7 @@ async function applyServiceEdit(edit) {
9598
9864
  function formatServiceEdit(edit) {
9599
9865
  const verb = edit.action === "add" ? "Adds" : "Removes";
9600
9866
  return [
9601
- `${verb} the ${edit.service} section in ${path15.basename(edit.configPath)}.`,
9867
+ `${verb} the ${edit.service} section in ${path16.basename(edit.configPath)}.`,
9602
9868
  ...edit.files.length > 0 ? [
9603
9869
  "Will create:",
9604
9870
  ...edit.files.map((file) => ` ${rel(edit.dir, file.path)}`)
@@ -9623,6 +9889,17 @@ async function setProjectId(dir, projectId) {
9623
9889
  await writeConfig(resolved.configPath, next);
9624
9890
  return true;
9625
9891
  }
9892
+ async function replaceProjectId(dir, projectId) {
9893
+ const { config: config2, resolved } = loadConfigV2(dir);
9894
+ const previous = config2.project?.id;
9895
+ if (previous === projectId) return previous;
9896
+ const next = reparse(resolved.configPath, {
9897
+ ...config2,
9898
+ project: { ...config2.project, id: projectId }
9899
+ });
9900
+ await writeConfig(resolved.configPath, next);
9901
+ return previous;
9902
+ }
9626
9903
  async function clearProjectId(dir, projectId) {
9627
9904
  const { config: config2, resolved } = loadConfigV2(dir);
9628
9905
  if (config2.project?.id !== projectId) return false;
@@ -9639,7 +9916,7 @@ async function clearProjectId(dir, projectId) {
9639
9916
  // src/config/v2/ledger.ts
9640
9917
  init_invariant();
9641
9918
  import { mkdir as mkdir6, readFile as readFile10, rename as rename4, rm as rm5, writeFile as writeFile8 } from "node:fs/promises";
9642
- import path16 from "node:path";
9919
+ import path17 from "node:path";
9643
9920
  var LEDGER_VERSION = 1;
9644
9921
  var LedgerError = class extends ApitogoError {
9645
9922
  constructor(code, message, developerHint) {
@@ -9694,7 +9971,7 @@ function corrupt(file, why) {
9694
9971
  );
9695
9972
  }
9696
9973
  async function writeLedger(state, ledger) {
9697
- await mkdir6(path16.dirname(state.file), { recursive: true });
9974
+ await mkdir6(path17.dirname(state.file), { recursive: true });
9698
9975
  const temporary = `${state.file}.${process.pid}.tmp`;
9699
9976
  try {
9700
9977
  await writeFile8(temporary, `${JSON.stringify(ledger, null, 2)}
@@ -9791,7 +10068,7 @@ init_invariant();
9791
10068
  // src/cli/check/v2.ts
9792
10069
  init_apitogo_config_core();
9793
10070
  import { readFile as readFile18 } from "node:fs/promises";
9794
- import path50 from "node:path";
10071
+ import path51 from "node:path";
9795
10072
 
9796
10073
  // src/cli/platform/client/platform-api.ts
9797
10074
  import { basename } from "node:path";
@@ -9904,8 +10181,8 @@ var PlatformApiClient = class {
9904
10181
  constructor(config2) {
9905
10182
  this.config = config2;
9906
10183
  }
9907
- url(path98) {
9908
- return `${this.config.apiUrl}${path98.startsWith("/") ? path98 : `/${path98}`}`;
10184
+ url(path99) {
10185
+ return `${this.config.apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
9909
10186
  }
9910
10187
  headers(extra) {
9911
10188
  return {
@@ -9915,9 +10192,9 @@ var PlatformApiClient = class {
9915
10192
  ...extra
9916
10193
  };
9917
10194
  }
9918
- async request(method, path98, init3) {
10195
+ async request(method, path99, init3) {
9919
10196
  const response = await fetchWithRetry(
9920
- this.url(path98),
10197
+ this.url(path99),
9921
10198
  {
9922
10199
  method,
9923
10200
  ...init3,
@@ -10232,6 +10509,17 @@ var PlatformApiClient = class {
10232
10509
  * Adds or updates an upstream login connection of the project's Active identity. The same request twice changes
10233
10510
  * nothing. The answer carries the redirect address to register with the provider.
10234
10511
  */
10512
+ /** The portal's name, logo and colour on the identity's account pages and mails (API-130). The identity must be Active. */
10513
+ putProjectIdentityBranding(projectId, body) {
10514
+ return this.request(
10515
+ "PUT",
10516
+ `/api/v1/projects/${projectId}/identity/branding`,
10517
+ {
10518
+ headers: { "Content-Type": "application/json" },
10519
+ body: JSON.stringify(body)
10520
+ }
10521
+ );
10522
+ }
10235
10523
  putProjectIdentityConnection(projectId, connectionId2, body) {
10236
10524
  return this.request(
10237
10525
  "PUT",
@@ -10374,15 +10662,15 @@ var PlatformApiClient = class {
10374
10662
  }
10375
10663
  );
10376
10664
  }
10377
- deleteDevPortalPage(projectId, path98) {
10378
- const query = new URLSearchParams({ path: path98 });
10665
+ deleteDevPortalPage(projectId, path99) {
10666
+ const query = new URLSearchParams({ path: path99 });
10379
10667
  return this.request(
10380
10668
  "DELETE",
10381
10669
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
10382
10670
  );
10383
10671
  }
10384
- renameDevPortalPage(projectId, path98, newName) {
10385
- const query = new URLSearchParams({ path: path98 });
10672
+ renameDevPortalPage(projectId, path99, newName) {
10673
+ const query = new URLSearchParams({ path: path99 });
10386
10674
  return this.request(
10387
10675
  "POST",
10388
10676
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -10498,8 +10786,8 @@ var PlatformApiClient = class {
10498
10786
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
10499
10787
  */
10500
10788
  validateGatewayPlans(plans, options = {}) {
10501
- const path98 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10502
- return this.request("POST", path98, {
10789
+ const path99 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10790
+ return this.request("POST", path99, {
10503
10791
  headers: { "Content-Type": "application/json" },
10504
10792
  body: JSON.stringify({ plans, planTier: options.planTier })
10505
10793
  });
@@ -10636,8 +10924,8 @@ function isNotFoundError(error) {
10636
10924
 
10637
10925
  // src/cli/platform/credentials-store.ts
10638
10926
  import fs from "node:fs";
10639
- import path17 from "node:path";
10640
- var CREDENTIALS_FILE = path17.join(
10927
+ import path18 from "node:path";
10928
+ var CREDENTIALS_FILE = path18.join(
10641
10929
  ZUDOKU_XDG_CONFIG_HOME,
10642
10930
  "credentials.json"
10643
10931
  );
@@ -10658,7 +10946,7 @@ function readStoredCredentials() {
10658
10946
  }
10659
10947
  }
10660
10948
  function writeStoredCredentials(creds) {
10661
- fs.mkdirSync(path17.dirname(CREDENTIALS_FILE), { recursive: true });
10949
+ fs.mkdirSync(path18.dirname(CREDENTIALS_FILE), { recursive: true });
10662
10950
  fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), {
10663
10951
  mode: 384
10664
10952
  });
@@ -10727,11 +11015,11 @@ import {
10727
11015
  rm as rm8,
10728
11016
  writeFile as writeFile15
10729
11017
  } from "node:fs/promises";
10730
- import path49 from "node:path";
11018
+ import path50 from "node:path";
10731
11019
 
10732
11020
  // src/vite/build.ts
10733
11021
  import { mkdir as mkdir11, readFile as readFile13, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
10734
- import path47 from "node:path";
11022
+ import path48 from "node:path";
10735
11023
  import { build as esbuild } from "esbuild";
10736
11024
  import { build as viteBuild, mergeConfig as mergeConfig3 } from "vite";
10737
11025
 
@@ -10867,7 +11155,7 @@ init_invariant();
10867
11155
  init_joinUrl();
10868
11156
 
10869
11157
  // src/vite/config.ts
10870
- import path42 from "node:path";
11158
+ import path43 from "node:path";
10871
11159
  import dotenv from "dotenv";
10872
11160
  import colors8 from "picocolors";
10873
11161
  import {
@@ -10878,7 +11166,7 @@ import {
10878
11166
  // package.json
10879
11167
  var package_default = {
10880
11168
  name: "@lukoweb/apitogo",
10881
- version: "0.2.56",
11169
+ version: "0.2.57",
10882
11170
  type: "module",
10883
11171
  sideEffects: [
10884
11172
  "**/*.css",
@@ -11315,24 +11603,24 @@ async function loadDevPortalViteDefines(rootDir, mode) {
11315
11603
 
11316
11604
  // src/vite/package-root.ts
11317
11605
  init_file_exists();
11318
- import path22 from "node:path";
11606
+ import path23 from "node:path";
11319
11607
  var findPackageRoot = async (startDir) => {
11320
11608
  let dir = startDir;
11321
- while (dir !== path22.dirname(dir)) {
11322
- if (await fileExists(path22.join(dir, "package.json"))) return dir;
11323
- dir = path22.dirname(dir);
11609
+ while (dir !== path23.dirname(dir)) {
11610
+ if (await fileExists(path23.join(dir, "package.json"))) return dir;
11611
+ dir = path23.dirname(dir);
11324
11612
  }
11325
11613
  };
11326
11614
 
11327
11615
  // src/vite/pagefind-dev-stub.ts
11328
11616
  import fs2 from "node:fs/promises";
11329
- import path23 from "node:path";
11617
+ import path24 from "node:path";
11330
11618
  var NOT_BUILT_YET = 'throw new Error("NOT_BUILT_YET");';
11331
11619
  async function ensurePagefindDevStub(publicDir) {
11332
- const pagefindPath = path23.join(publicDir, "pagefind/pagefind.js");
11620
+ const pagefindPath = path24.join(publicDir, "pagefind/pagefind.js");
11333
11621
  const exists6 = await fs2.stat(pagefindPath).catch(() => false);
11334
11622
  if (!exists6) {
11335
- await fs2.mkdir(path23.dirname(pagefindPath), { recursive: true });
11623
+ await fs2.mkdir(path24.dirname(pagefindPath), { recursive: true });
11336
11624
  await fs2.writeFile(pagefindPath, NOT_BUILT_YET);
11337
11625
  }
11338
11626
  }
@@ -11344,7 +11632,7 @@ import react from "@vitejs/plugin-react";
11344
11632
  // src/vite/css/plugin.ts
11345
11633
  init_loader();
11346
11634
  init_plugin_theme();
11347
- import path24 from "node:path";
11635
+ import path25 from "node:path";
11348
11636
  import { isCSSRequest as isCSSRequest2 } from "vite";
11349
11637
 
11350
11638
  // src/vite/css/collect.ts
@@ -11379,7 +11667,7 @@ var VIRTUAL_ENTRY = "virtual:ssr-css.css";
11379
11667
  function vitePluginSsrCss(pluginOpts) {
11380
11668
  let server;
11381
11669
  const config2 = getCurrentConfig();
11382
- const virtualHref = path24.join(
11670
+ const virtualHref = path25.join(
11383
11671
  config2.basePath ?? "",
11384
11672
  `/@id/__x00__${VIRTUAL_ENTRY}?direct`
11385
11673
  );
@@ -11516,7 +11804,7 @@ var plugin_api_keys_default = viteApiKeysPlugin;
11516
11804
 
11517
11805
  // src/vite/plugin-api.ts
11518
11806
  import fs4 from "node:fs/promises";
11519
- import path30 from "node:path";
11807
+ import path31 from "node:path";
11520
11808
  import { deepEqual as deepEqual2 } from "fast-equals";
11521
11809
  import { runnerImport as runnerImport3 } from "vite";
11522
11810
  init_package_json();
@@ -11525,7 +11813,7 @@ init_loader();
11525
11813
  // src/config/validators/BuildSchema.ts
11526
11814
  init_file_exists();
11527
11815
  init_loader();
11528
- import path25 from "node:path";
11816
+ import path26 from "node:path";
11529
11817
  import { runnerImport as runnerImport2 } from "vite";
11530
11818
  import { z as z13 } from "zod";
11531
11819
  var BuildProcessorSchema = z13.custom((val) => typeof val === "function");
@@ -11554,7 +11842,7 @@ var buildConfigFiles = [
11554
11842
  ];
11555
11843
  async function getBuildConfigFilePath(rootDir) {
11556
11844
  for (const fileName of buildConfigFiles) {
11557
- const filepath = path25.join(rootDir, fileName);
11845
+ const filepath = path26.join(rootDir, fileName);
11558
11846
  if (await fileExists(filepath)) {
11559
11847
  return filepath;
11560
11848
  }
@@ -11663,14 +11951,14 @@ var flattenAllOf = (schema2) => {
11663
11951
  };
11664
11952
 
11665
11953
  // src/lib/util/traverse.ts
11666
- var traverse = (specification, transform, path98 = []) => {
11667
- const transformed = transform(specification, path98);
11954
+ var traverse = (specification, transform, path99 = []) => {
11955
+ const transformed = transform(specification, path99);
11668
11956
  if (typeof transformed !== "object" || transformed === null) {
11669
11957
  return transformed;
11670
11958
  }
11671
11959
  const result = Array.isArray(transformed) ? [] : {};
11672
11960
  for (const [key, value] of Object.entries(transformed)) {
11673
- const currentPath = [...path98, key];
11961
+ const currentPath = [...path99, key];
11674
11962
  if (Array.isArray(value)) {
11675
11963
  result[key] = value.map(
11676
11964
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -11698,9 +11986,9 @@ var resolveLocalRef = (schema2, ref) => {
11698
11986
  if (schemaCache?.has(ref)) {
11699
11987
  return schemaCache.get(ref);
11700
11988
  }
11701
- const path98 = ref.split("/").slice(1);
11989
+ const path99 = ref.split("/").slice(1);
11702
11990
  let current = schema2;
11703
- for (const segment of path98) {
11991
+ for (const segment of path99) {
11704
11992
  if (!current || typeof current !== "object") {
11705
11993
  current = null;
11706
11994
  }
@@ -11719,7 +12007,7 @@ var dereference = async (schema2, resolvers = []) => {
11719
12007
  }
11720
12008
  const cloned = structuredClone(schema2);
11721
12009
  const visited = /* @__PURE__ */ new Set();
11722
- const resolve = async (current, path98) => {
12010
+ const resolve = async (current, path99) => {
11723
12011
  if (isIndexableObject(current)) {
11724
12012
  if (visited.has(current)) {
11725
12013
  return CIRCULAR_REF;
@@ -11727,7 +12015,7 @@ var dereference = async (schema2, resolvers = []) => {
11727
12015
  visited.add(current);
11728
12016
  if (Array.isArray(current)) {
11729
12017
  for (let index = 0; index < current.length; index++) {
11730
- current[index] = await resolve(current[index], `${path98}/${index}`);
12018
+ current[index] = await resolve(current[index], `${path99}/${index}`);
11731
12019
  }
11732
12020
  } else {
11733
12021
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -11738,13 +12026,13 @@ var dereference = async (schema2, resolvers = []) => {
11738
12026
  for (const resolver of resolvers) {
11739
12027
  const resolved = await resolver($ref);
11740
12028
  if (resolved) {
11741
- result2 = await resolve(resolved, path98);
12029
+ result2 = await resolve(resolved, path99);
11742
12030
  break;
11743
12031
  }
11744
12032
  }
11745
12033
  if (result2 === void 0) {
11746
12034
  const resolved = await resolveLocalRef(cloned, $ref);
11747
- result2 = await resolve(resolved, path98);
12035
+ result2 = await resolve(resolved, path99);
11748
12036
  }
11749
12037
  if (hasSiblings) {
11750
12038
  if (result2 === CIRCULAR_REF) {
@@ -11757,7 +12045,7 @@ var dereference = async (schema2, resolvers = []) => {
11757
12045
  return result2;
11758
12046
  }
11759
12047
  for (const key in current) {
11760
- current[key] = await resolve(current[key], `${path98}/${key}`);
12048
+ current[key] = await resolve(current[key], `${path99}/${key}`);
11761
12049
  }
11762
12050
  }
11763
12051
  visited.delete(current);
@@ -11796,9 +12084,9 @@ var upgradeSchema = (schema2) => {
11796
12084
  }
11797
12085
  return sub;
11798
12086
  });
11799
- schema2 = traverse(schema2, (sub, path98) => {
12087
+ schema2 = traverse(schema2, (sub, path99) => {
11800
12088
  if (sub.example !== void 0) {
11801
- if (isSchemaPath(path98 ?? [])) {
12089
+ if (isSchemaPath(path99 ?? [])) {
11802
12090
  sub.examples = [sub.example];
11803
12091
  } else {
11804
12092
  sub.examples = {
@@ -11811,11 +12099,11 @@ var upgradeSchema = (schema2) => {
11811
12099
  }
11812
12100
  return sub;
11813
12101
  });
11814
- schema2 = traverse(schema2, (schema3, path98) => {
12102
+ schema2 = traverse(schema2, (schema3, path99) => {
11815
12103
  if (schema3.type === "object" && schema3.properties !== void 0) {
11816
- const parentPath = path98?.slice(0, -1);
12104
+ const parentPath = path99?.slice(0, -1);
11817
12105
  const isMultipart = parentPath?.some((segment, index) => {
11818
- return segment === "content" && path98?.[index + 1] === "multipart/form-data";
12106
+ return segment === "content" && path99?.[index + 1] === "multipart/form-data";
11819
12107
  });
11820
12108
  if (isMultipart) {
11821
12109
  const entries = Object.entries(schema3.properties);
@@ -11829,8 +12117,8 @@ var upgradeSchema = (schema2) => {
11829
12117
  }
11830
12118
  return schema3;
11831
12119
  });
11832
- schema2 = traverse(schema2, (schema3, path98) => {
11833
- if (path98?.includes("content") && path98.includes("application/octet-stream")) {
12120
+ schema2 = traverse(schema2, (schema3, path99) => {
12121
+ if (path99?.includes("content") && path99.includes("application/octet-stream")) {
11834
12122
  return {};
11835
12123
  }
11836
12124
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -11856,11 +12144,11 @@ var upgradeSchema = (schema2) => {
11856
12144
  }
11857
12145
  return sub;
11858
12146
  });
11859
- schema2 = traverse(schema2, (schema3, path98) => {
12147
+ schema2 = traverse(schema2, (schema3, path99) => {
11860
12148
  if (schema3.type === "string" && schema3.format === "byte") {
11861
- const parentPath = path98?.slice(0, -1);
12149
+ const parentPath = path99?.slice(0, -1);
11862
12150
  const contentMediaType = parentPath?.find(
11863
- (_, index) => path98?.[index - 1] === "content"
12151
+ (_, index) => path99?.[index - 1] === "content"
11864
12152
  );
11865
12153
  return {
11866
12154
  type: "string",
@@ -11872,7 +12160,7 @@ var upgradeSchema = (schema2) => {
11872
12160
  });
11873
12161
  return schema2;
11874
12162
  };
11875
- function isSchemaPath(path98) {
12163
+ function isSchemaPath(path99) {
11876
12164
  const schemaLocations = [
11877
12165
  ["components", "schemas"],
11878
12166
  "properties",
@@ -11885,10 +12173,10 @@ function isSchemaPath(path98) {
11885
12173
  ];
11886
12174
  return schemaLocations.some((location) => {
11887
12175
  if (Array.isArray(location)) {
11888
- return location.every((segment, index) => path98[index] === segment);
12176
+ return location.every((segment, index) => path99[index] === segment);
11889
12177
  }
11890
- return path98.includes(location);
11891
- }) || path98.includes("schema") || path98.some((segment) => segment.endsWith("Schema"));
12178
+ return path99.includes(location);
12179
+ }) || path99.includes("schema") || path99.some((segment) => segment.endsWith("Schema"));
11892
12180
  }
11893
12181
 
11894
12182
  // src/lib/oas/parser/index.ts
@@ -11970,13 +12258,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
11970
12258
  "anyOf",
11971
12259
  "oneOf"
11972
12260
  ]);
11973
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path98 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
12261
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path99 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
11974
12262
  if (obj === null || typeof obj !== "object") return obj;
11975
12263
  const refPath = obj.__$ref;
11976
12264
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
11977
12265
  if (isCircular) {
11978
12266
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
11979
- const circularProp = path98.find((p) => !OPENAPI_PROPS.has(p)) || path98[0];
12267
+ const circularProp = path99.find((p) => !OPENAPI_PROPS.has(p)) || path99[0];
11980
12268
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
11981
12269
  }
11982
12270
  if (refs.has(obj)) return refs.get(obj);
@@ -11986,7 +12274,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
11986
12274
  value,
11987
12275
  currentPath,
11988
12276
  refs,
11989
- [...path98, key],
12277
+ [...path99, key],
11990
12278
  currentRefPaths
11991
12279
  );
11992
12280
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -12021,7 +12309,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
12021
12309
  var getAllTags = (schema2) => {
12022
12310
  const rootTags = schema2.tags ?? [];
12023
12311
  const operations = Object.values(schema2.paths ?? {}).flatMap(
12024
- (path98) => HttpMethods.map((k) => path98?.[k]).filter((op) => op != null)
12312
+ (path99) => HttpMethods.map((k) => path99?.[k]).filter((op) => op != null)
12025
12313
  );
12026
12314
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
12027
12315
  const hasUntaggedOperations = operations.some(
@@ -12076,7 +12364,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
12076
12364
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
12077
12365
  var getAllOperations = (paths) => {
12078
12366
  const operations = Object.entries(paths ?? {}).flatMap(
12079
- ([path98, value]) => HttpMethods.flatMap((method) => {
12367
+ ([path99, value]) => HttpMethods.flatMap((method) => {
12080
12368
  if (!value?.[method]) return [];
12081
12369
  const operation = value[method];
12082
12370
  const pathParameters = value.parameters ?? [];
@@ -12096,7 +12384,7 @@ var getAllOperations = (paths) => {
12096
12384
  return {
12097
12385
  ...operation,
12098
12386
  method,
12099
- path: path98,
12387
+ path: path99,
12100
12388
  parameters,
12101
12389
  servers,
12102
12390
  tags: operation.tags ?? []
@@ -12454,8 +12742,8 @@ var Schema = builder.objectRef("Schema").implement({
12454
12742
  }),
12455
12743
  paths: t.field({
12456
12744
  type: [PathItem],
12457
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path98, value]) => ({
12458
- path: path98,
12745
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path99, value]) => ({
12746
+ path: path99,
12459
12747
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
12460
12748
  methods: Object.keys(value)
12461
12749
  }))
@@ -12568,7 +12856,7 @@ var ensureArray = (value) => Array.isArray(value) ? value : [value];
12568
12856
 
12569
12857
  // src/vite/api/SchemaManager.ts
12570
12858
  import fs3 from "node:fs/promises";
12571
- import path26 from "node:path";
12859
+ import path27 from "node:path";
12572
12860
  import {
12573
12861
  $RefParser as $RefParser2
12574
12862
  } from "@apidevtools/json-schema-ref-parser";
@@ -12620,7 +12908,7 @@ init_joinUrl();
12620
12908
 
12621
12909
  // src/vite/api/schema-codegen.ts
12622
12910
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
12623
- var getSegmentsFromPath = (path98) => path98.split("/").slice(1).map(unescapeJsonPointer);
12911
+ var getSegmentsFromPath = (path99) => path99.split("/").slice(1).map(unescapeJsonPointer);
12624
12912
  var createLocalRefMap = (obj) => {
12625
12913
  const refMap = /* @__PURE__ */ new Map();
12626
12914
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -12651,16 +12939,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
12651
12939
  /"__refMap\+Siblings:(.*?)"/g,
12652
12940
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
12653
12941
  );
12654
- var lookup = (schema2, path98, filePath) => {
12655
- const parts = getSegmentsFromPath(path98);
12942
+ var lookup = (schema2, path99, filePath) => {
12943
+ const parts = getSegmentsFromPath(path99);
12656
12944
  let val = schema2;
12657
12945
  for (const part of parts) {
12658
12946
  while (val.$ref?.startsWith("#/")) {
12659
- val = val.$ref === path98 ? val : lookup(schema2, val.$ref, filePath);
12947
+ val = val.$ref === path99 ? val : lookup(schema2, val.$ref, filePath);
12660
12948
  }
12661
12949
  if (val[part] === void 0) {
12662
12950
  throw new Error(
12663
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path98}`
12951
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path99}`
12664
12952
  );
12665
12953
  }
12666
12954
  val = val[part];
@@ -12758,18 +13046,18 @@ var SchemaManager = class {
12758
13046
  ];
12759
13047
  }
12760
13048
  getPathForFile = (input5, params) => {
12761
- const filePath = path26.resolve(this.config.__meta.rootDir, input5);
13049
+ const filePath = path27.resolve(this.config.__meta.rootDir, input5);
12762
13050
  const apis = ensureArray(this.config.apis ?? []);
12763
13051
  for (const apiConfig of apis) {
12764
13052
  if (!apiConfig || apiConfig.type !== "file" || !apiConfig.path) continue;
12765
13053
  const match = normalizeInputs(apiConfig.input).some(
12766
- (i) => path26.resolve(this.config.__meta.rootDir, i.input) === filePath && deepEqual(i.params, params)
13054
+ (i) => path27.resolve(this.config.__meta.rootDir, i.input) === filePath && deepEqual(i.params, params)
12767
13055
  );
12768
13056
  if (match) return apiConfig.path;
12769
13057
  }
12770
13058
  };
12771
13059
  processSchema = async (input5) => {
12772
- const filePath = path26.resolve(this.config.__meta.rootDir, input5.input);
13060
+ const filePath = path27.resolve(this.config.__meta.rootDir, input5.input);
12773
13061
  const params = input5.params;
12774
13062
  const configuredPath = this.getPathForFile(input5.input, params);
12775
13063
  if (!configuredPath) {
@@ -12805,11 +13093,11 @@ var SchemaManager = class {
12805
13093
  const processedTime = Date.now();
12806
13094
  const code = generateCode(processedSchema, filePath);
12807
13095
  const prefixPath = slugify(configuredPath);
12808
- const processedFilePath = path26.posix.join(
13096
+ const processedFilePath = path27.posix.join(
12809
13097
  this.storeDir,
12810
- `${prefixPath}-${path26.basename(filePath)}${paramsSuffix(params)}.js`
13098
+ `${prefixPath}-${path27.basename(filePath)}${paramsSuffix(params)}.js`
12811
13099
  );
12812
- const importKey = path26.posix.basename(processedFilePath);
13100
+ const importKey = path27.posix.basename(processedFilePath);
12813
13101
  await fs3.writeFile(processedFilePath, code);
12814
13102
  const processedJsonPath = paramsSuffix(params) ? processedFilePath.replace(/\.js$/, ".json") : "";
12815
13103
  if (processedJsonPath) {
@@ -12857,7 +13145,7 @@ var SchemaManager = class {
12857
13145
  };
12858
13146
  getAllTrackedFiles = () => Array.from(this.referencedBy.keys());
12859
13147
  getFilesToReprocess = (changedFile) => {
12860
- const resolvedPath = path26.resolve(this.config.__meta.rootDir, changedFile);
13148
+ const resolvedPath = path27.resolve(this.config.__meta.rootDir, changedFile);
12861
13149
  const referencedBy = this.referencedBy.get(resolvedPath);
12862
13150
  if (!referencedBy) return [];
12863
13151
  const filesToProcess = referencedBy.size === 0 ? [resolvedPath] : Array.from(referencedBy);
@@ -12886,7 +13174,7 @@ var SchemaManager = class {
12886
13174
  version: "",
12887
13175
  path: input5.path ?? "",
12888
13176
  label: input5.label,
12889
- inputPath: path26.resolve(this.config.__meta.rootDir, input5.input),
13177
+ inputPath: path27.resolve(this.config.__meta.rootDir, input5.input),
12890
13178
  params: input5.params,
12891
13179
  importKey: "",
12892
13180
  processedFilePath: "",
@@ -12907,8 +13195,8 @@ var SchemaManager = class {
12907
13195
  }
12908
13196
  }
12909
13197
  };
12910
- getLatestSchema = (path98) => this.processedSchemas[path98]?.at(0);
12911
- getSchemasForPath = (path98) => this.processedSchemas[path98];
13198
+ getLatestSchema = (path99) => this.processedSchemas[path99]?.at(0);
13199
+ getSchemasForPath = (path99) => this.processedSchemas[path99];
12912
13200
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
12913
13201
  getUrlToFilePathMap = () => {
12914
13202
  const map = /* @__PURE__ */ new Map();
@@ -12930,7 +13218,7 @@ var SchemaManager = class {
12930
13218
  };
12931
13219
  createSchemaPath = (inputPath, versionPath, apiPath, params) => {
12932
13220
  const suffix = paramsSuffix(params);
12933
- const extension = suffix ? ".json" : path26.extname(inputPath);
13221
+ const extension = suffix ? ".json" : path27.extname(inputPath);
12934
13222
  return joinUrl(
12935
13223
  this.config.basePath,
12936
13224
  apiPath,
@@ -12953,7 +13241,7 @@ var SchemaManager = class {
12953
13241
  // src/vite/plugin-config-reload.ts
12954
13242
  init_logger();
12955
13243
  init_loader();
12956
- import path29 from "node:path";
13244
+ import path30 from "node:path";
12957
13245
  import colors7 from "picocolors";
12958
13246
 
12959
13247
  // src/vite/plugin-navigation.ts
@@ -12963,7 +13251,7 @@ import { stringify as stringify3 } from "javascript-stringify";
12963
13251
  import { isElement } from "react-is";
12964
13252
 
12965
13253
  // src/config/validators/NavigationSchema.ts
12966
- import path27 from "node:path";
13254
+ import path28 from "node:path";
12967
13255
  import { glob as glob5 } from "glob";
12968
13256
  import { fromMarkdown } from "mdast-util-from-markdown";
12969
13257
  import { mdxFromMarkdown } from "mdast-util-mdx";
@@ -12998,7 +13286,7 @@ var extractRichH1 = (content) => {
12998
13286
  }
12999
13287
  };
13000
13288
  var isNavigationItem = (item) => item !== void 0;
13001
- var toPosixPath = (filePath) => filePath.split(path27.win32.sep).join(path27.posix.sep);
13289
+ var toPosixPath = (filePath) => filePath.split(path28.win32.sep).join(path28.posix.sep);
13002
13290
  var NavigationResolver = class {
13003
13291
  rootDir;
13004
13292
  globPatterns;
@@ -13131,13 +13419,13 @@ init_invariant();
13131
13419
 
13132
13420
  // src/vite/debug.ts
13133
13421
  import { mkdir as mkdir7, writeFile as writeFile9 } from "node:fs/promises";
13134
- import path28 from "node:path";
13422
+ import path29 from "node:path";
13135
13423
  async function writePluginDebugCode(rootDir, pluginName, code, extension = "js") {
13136
13424
  if (process.env.ZUDOKU_BUILD_DEBUG) {
13137
- const debugDir = path28.join(rootDir, "dist", "debug");
13425
+ const debugDir = path29.join(rootDir, "dist", "debug");
13138
13426
  await mkdir7(debugDir, { recursive: true });
13139
13427
  await writeFile9(
13140
- path28.join(debugDir, `${pluginName}.${extension}`),
13428
+ path29.join(debugDir, `${pluginName}.${extension}`),
13141
13429
  typeof code === "string" ? code : code.join("\n")
13142
13430
  );
13143
13431
  }
@@ -13246,7 +13534,7 @@ var viteConfigReloadPlugin = () => ({
13246
13534
  reload(server);
13247
13535
  logger.info(
13248
13536
  colors7.blue(
13249
- `Config ${path29.basename(currentConfig.__meta.configPath)} changed. Reloading...`
13537
+ `Config ${path30.basename(currentConfig.__meta.configPath)} changed. Reloading...`
13250
13538
  ),
13251
13539
  { timestamp: true }
13252
13540
  );
@@ -13260,11 +13548,11 @@ var viteApiPlugin = async () => {
13260
13548
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
13261
13549
  const initialConfig = getCurrentConfig();
13262
13550
  const zuploProcessors = ZuploEnv.isZuplo ? await runnerImport3(
13263
- path30.resolve(getZudokuRootDir(), "src/zuplo/with-zuplo-processors.ts")
13551
+ path31.resolve(getZudokuRootDir(), "src/zuplo/with-zuplo-processors.ts")
13264
13552
  ).then((m) => m.module.default(initialConfig.__meta.rootDir)) : [];
13265
13553
  const buildConfig = await getBuildConfig();
13266
13554
  const buildProcessors = buildConfig?.processors ?? [];
13267
- const tmpStoreDir = path30.posix.join(
13555
+ const tmpStoreDir = path31.posix.join(
13268
13556
  initialConfig.__meta.rootDir,
13269
13557
  "node_modules/.apitogo/processed"
13270
13558
  );
@@ -13296,7 +13584,7 @@ var viteApiPlugin = async () => {
13296
13584
  const inputPath = pathMap.get(req.url);
13297
13585
  if (!inputPath) return next();
13298
13586
  const content = await fs4.readFile(inputPath, "utf-8");
13299
- const mimeType = path30.extname(inputPath).toLowerCase() === ".json" ? "application/json" : "application/x-yaml";
13587
+ const mimeType = path31.extname(inputPath).toLowerCase() === ".json" ? "application/json" : "application/x-yaml";
13300
13588
  res.setHeader("Content-Type", `${mimeType}; charset=utf-8`);
13301
13589
  return res.end(content);
13302
13590
  });
@@ -13484,8 +13772,8 @@ var viteApiPlugin = async () => {
13484
13772
  if (process.env.NODE_ENV !== "production") return;
13485
13773
  for (const [urlPath, inputPath] of pathMap) {
13486
13774
  const content = await fs4.readFile(inputPath, "utf-8");
13487
- const outputPath = path30.join(config2.__meta.rootDir, "dist", urlPath);
13488
- await fs4.mkdir(path30.dirname(outputPath), { recursive: true });
13775
+ const outputPath = path31.join(config2.__meta.rootDir, "dist", urlPath);
13776
+ await fs4.mkdir(path31.dirname(outputPath), { recursive: true });
13489
13777
  await fs4.writeFile(outputPath, content, "utf-8");
13490
13778
  }
13491
13779
  }
@@ -13528,7 +13816,7 @@ var plugin_auth_default = viteAuthPlugin;
13528
13816
 
13529
13817
  // src/vite/plugin-component.ts
13530
13818
  init_loader();
13531
- import path31 from "node:path";
13819
+ import path32 from "node:path";
13532
13820
  var viteAliasPlugin = () => {
13533
13821
  return {
13534
13822
  name: "zudoku-component-plugin",
@@ -13556,7 +13844,7 @@ var viteAliasPlugin = () => {
13556
13844
  ];
13557
13845
  const aliases = replacements.map(([find, replacement]) => ({
13558
13846
  find,
13559
- replacement: path31.posix.join(config2.__meta.moduleDir, replacement)
13847
+ replacement: path32.posix.join(config2.__meta.moduleDir, replacement)
13560
13848
  }));
13561
13849
  return config2.__meta.mode === "internal" || config2.__meta.mode === "standalone" ? { resolve: { alias: aliases } } : void 0;
13562
13850
  }
@@ -13568,11 +13856,11 @@ var plugin_component_default = viteAliasPlugin;
13568
13856
  init_package_json();
13569
13857
  init_loader();
13570
13858
  import { readFile as readFile11 } from "node:fs/promises";
13571
- import path32 from "node:path";
13859
+ import path33 from "node:path";
13572
13860
  import { normalizePath } from "vite";
13573
13861
  var virtualModuleId3 = "virtual:zudoku-config";
13574
13862
  var resolvedVirtualModuleId3 = `\0${virtualModuleId3}`;
13575
- var browserConfigLoaderPath = path32.join(
13863
+ var browserConfigLoaderPath = path33.join(
13576
13864
  getZudokuRootDir(),
13577
13865
  "src/config/apitogo-config-loader.browser.ts"
13578
13866
  );
@@ -13664,12 +13952,14 @@ import { runPluginTransformConfig } from "@lukoweb/apitogo/plugins";
13664
13952
 
13665
13953
  const embeddedConfig = ${serializeClientConfig(loadedConfig)};
13666
13954
 
13667
- // JSON embedding drops Symbol values (React element's $$typeof). Prefer live
13668
- // navigation/plugins from the TSX module so custom-page elements stay valid.
13955
+ // JSON embedding drops Symbol values (React element's $typeof) and functions. Prefer live
13956
+ // navigation/plugins/slots from the TSX module so custom-page elements and slot components (the
13957
+ // portal footer, API-129) stay valid.
13669
13958
  const config = await runPluginTransformConfig({
13670
13959
  ...embeddedConfig,
13671
13960
  navigation: rawConfig?.navigation ?? embeddedConfig.navigation,
13672
13961
  plugins: rawConfig?.plugins ?? embeddedConfig.plugins ?? [],
13962
+ slots: rawConfig?.slots ?? embeddedConfig.slots,
13673
13963
  __meta: ${JSON.stringify(clientMeta)},
13674
13964
  });
13675
13965
  export default config;
@@ -13774,7 +14064,7 @@ var viteDocMetadataPlugin = () => ({
13774
14064
 
13775
14065
  // src/vite/plugin-docs.ts
13776
14066
  init_loader();
13777
- import path33 from "node:path";
14067
+ import path34 from "node:path";
13778
14068
  import { glob as glob7 } from "glob";
13779
14069
  import globParent3 from "glob-parent";
13780
14070
  init_ZudokuConfig();
@@ -13843,7 +14133,7 @@ var globMarkdownFiles = async (config2, options = { absolute: false }) => {
13843
14133
  if (process.env.NODE_ENV !== "development") {
13844
14134
  const draftStatuses = await Promise.all(
13845
14135
  globbedFiles.map(async (file) => {
13846
- const absolutePath = path33.resolve(config2.__meta.rootDir, file);
14136
+ const absolutePath = path34.resolve(config2.__meta.rootDir, file);
13847
14137
  const { data } = await readFrontmatter(absolutePath);
13848
14138
  return { file, isDraft: data.draft === true };
13849
14139
  })
@@ -13856,9 +14146,9 @@ var globMarkdownFiles = async (config2, options = { absolute: false }) => {
13856
14146
  if (draftFiles.has(file)) {
13857
14147
  continue;
13858
14148
  }
13859
- const relativePath = path33.posix.relative(parent, file);
14149
+ const relativePath = path34.posix.relative(parent, file);
13860
14150
  const routePath = ensureLeadingSlash(relativePath.replace(/\.mdx?$/, ""));
13861
- const filePath = options.absolute ? path33.resolve(config2.__meta.rootDir, file) : file;
14151
+ const filePath = options.absolute ? path34.resolve(config2.__meta.rootDir, file) : file;
13862
14152
  fileMapping[routePath] = filePath;
13863
14153
  }
13864
14154
  }
@@ -13875,6 +14165,7 @@ var resolveCustomNavigationPaths = async (config2, fileMapping) => {
13875
14165
  const filePath = mapping[fileRoutePath];
13876
14166
  if (!filePath) return;
13877
14167
  const customPath = ensureLeadingSlash(doc.path);
14168
+ if (customPath === fileRoutePath) return;
13878
14169
  mapping[customPath] = filePath;
13879
14170
  delete mapping[fileRoutePath];
13880
14171
  };
@@ -13922,7 +14213,7 @@ var viteDocsPlugin = () => {
13922
14213
  const globbedDocuments = {};
13923
14214
  for (const [routePath, file] of Object.entries(fileMapping)) {
13924
14215
  const importPath = ensureLeadingSlash(
13925
- path33.posix.join(globImportBasePath, file)
14216
+ path34.posix.join(globImportBasePath, file)
13926
14217
  );
13927
14218
  globbedDocuments[routePath] = importPath;
13928
14219
  }
@@ -13935,6 +14226,9 @@ var viteDocsPlugin = () => {
13935
14226
  `export const configuredDocsPlugin = markdownPlugin({`,
13936
14227
  ` basePath: "${config2.basePath ?? ""}",`,
13937
14228
  ` fileImports,`,
14229
+ ...config2.__resolvedModules.landing.enabled ? [
14230
+ ` reservedPaths: ${JSON.stringify([config2.__resolvedModules.landing.path])},`
14231
+ ] : [],
13938
14232
  ` defaultOptions: ${JSON.stringify(docsConfig.defaultOptions)},`,
13939
14233
  `});`
13940
14234
  );
@@ -13948,7 +14242,7 @@ var plugin_docs_default = viteDocsPlugin;
13948
14242
  // src/vite/plugin-local-manifest.ts
13949
14243
  init_apitogo_config_loader();
13950
14244
  init_local_manifest();
13951
- import path34 from "node:path";
14245
+ import path35 from "node:path";
13952
14246
  var APITOGO_LOCAL_MANIFEST_VIRTUAL_ID = "virtual:apitogo-local-manifest";
13953
14247
  var resolvedVirtualModuleId4 = `\0${APITOGO_LOCAL_MANIFEST_VIRTUAL_ID}`;
13954
14248
  var viteLocalManifestPlugin = () => {
@@ -13998,8 +14292,8 @@ var viteLocalManifestPlugin = () => {
13998
14292
  server.watcher.add(watchPath);
13999
14293
  }
14000
14294
  server.watcher.on("change", async (changedPath) => {
14001
- const resolvedChanged = path34.resolve(changedPath);
14002
- if (!watchPaths.some((p) => path34.resolve(p) === resolvedChanged)) {
14295
+ const resolvedChanged = path35.resolve(changedPath);
14296
+ if (!watchPaths.some((p) => path35.resolve(p) === resolvedChanged)) {
14003
14297
  return;
14004
14298
  }
14005
14299
  const module = server.moduleGraph.getModuleById(
@@ -14026,7 +14320,7 @@ init_loader();
14026
14320
  init_ProtectedRoutesSchema();
14027
14321
  init_joinUrl();
14028
14322
  import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
14029
- import path35 from "node:path";
14323
+ import path36 from "node:path";
14030
14324
  import { matchPath } from "react-router";
14031
14325
  var processMarkdownFile = async (filePath) => {
14032
14326
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
@@ -14114,7 +14408,7 @@ var viteMarkdownExportPlugin = () => {
14114
14408
  if (process.env.NODE_ENV !== "production" || Object.keys(markdownFiles).length === 0 || !needsMdFiles) {
14115
14409
  return;
14116
14410
  }
14117
- const distDir = path35.join(
14411
+ const distDir = path36.join(
14118
14412
  config2.__meta.rootDir,
14119
14413
  "dist",
14120
14414
  config2.basePath ?? ""
@@ -14135,15 +14429,15 @@ var viteMarkdownExportPlugin = () => {
14135
14429
  content: finalMarkdown
14136
14430
  });
14137
14431
  const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
14138
- const outputPath = `${path35.join(distDir, ...segments)}.md`;
14139
- await mkdir8(path35.dirname(outputPath), { recursive: true });
14432
+ const outputPath = `${path36.join(distDir, ...segments)}.md`;
14433
+ await mkdir8(path36.dirname(outputPath), { recursive: true });
14140
14434
  await writeFile10(outputPath, finalMarkdown, "utf-8");
14141
14435
  } catch (error) {
14142
14436
  console.warn(`Failed to export markdown for ${routePath}:`, error);
14143
14437
  }
14144
14438
  }
14145
14439
  if (config2.docs?.llms?.llmsTxt || config2.docs?.llms?.llmsTxtFull) {
14146
- const markdownInfoPath = path35.join(
14440
+ const markdownInfoPath = path36.join(
14147
14441
  config2.__meta.rootDir,
14148
14442
  "node_modules/.apitogo/markdown-info.json"
14149
14443
  );
@@ -14295,9 +14589,9 @@ var remarkCodeTabs = () => (tree) => {
14295
14589
  };
14296
14590
 
14297
14591
  // src/vite/mdx/remark-inject-filepath.ts
14298
- import path36 from "node:path";
14592
+ import path37 from "node:path";
14299
14593
  var remarkInjectFilepath = (rootDir) => (tree, vfile) => {
14300
- const relativePath = path36.relative(rootDir, vfile.path).split(path36.sep).join(path36.posix.sep);
14594
+ const relativePath = path37.relative(rootDir, vfile.path).split(path37.sep).join(path37.posix.sep);
14301
14595
  tree.children.unshift(exportMdxjsConst("__filepath", relativePath));
14302
14596
  };
14303
14597
 
@@ -14384,18 +14678,18 @@ var remarkLastModified = () => {
14384
14678
  };
14385
14679
 
14386
14680
  // src/vite/mdx/remark-link-rewrite.ts
14387
- import path37 from "node:path";
14681
+ import path38 from "node:path";
14388
14682
  import { visit as visit5 } from "unist-util-visit";
14389
14683
  var remarkLinkRewrite = (basePath = "") => (tree) => {
14390
14684
  visit5(tree, "link", (node) => {
14391
14685
  if (!node.url) return;
14392
14686
  if (node.url.startsWith("http") || node.url.startsWith("mailto:")) return;
14393
14687
  node.url = node.url.replace(/\\/g, "/");
14394
- const base = path37.posix.join(basePath);
14688
+ const base = path38.posix.join(basePath);
14395
14689
  if (basePath && node.url.startsWith(base)) {
14396
14690
  node.url = node.url.slice(base.length);
14397
14691
  } else if (!node.url.startsWith("/") && !node.url.startsWith("#")) {
14398
- node.url = path37.posix.join("..", node.url);
14692
+ node.url = path38.posix.join("..", node.url);
14399
14693
  }
14400
14694
  node.url = node.url.replace(/\.mdx?(#.*)?$/, "$1");
14401
14695
  });
@@ -14615,14 +14909,14 @@ var plugin_mdx_default = viteMdxPlugin;
14615
14909
 
14616
14910
  // src/vite/plugin-modules.ts
14617
14911
  init_loader();
14618
- import path38 from "node:path";
14912
+ import path39 from "node:path";
14619
14913
  import { normalizePath as normalizePath2 } from "vite";
14620
- var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path38.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14621
- var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path38.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
14914
+ var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
14915
+ var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
14622
14916
  var getBillingPluginImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(
14623
- path38.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14917
+ path39.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
14624
14918
  ) : "@lukoweb/apitogo-plugin-dev-portal-billing";
14625
- var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path38.resolve(rootDir, pagePath));
14919
+ var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path39.resolve(rootDir, pagePath));
14626
14920
  var viteModulesPlugin = () => {
14627
14921
  const virtualModuleId4 = "virtual:zudoku-modules-plugin";
14628
14922
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -14764,17 +15058,17 @@ var plugin_modules_default = viteModulesPlugin;
14764
15058
 
14765
15059
  // src/vite/plugin-offroot-resolve.ts
14766
15060
  init_loader();
14767
- import path39 from "node:path";
15061
+ import path40 from "node:path";
14768
15062
  function stripQuery(id) {
14769
15063
  const query = id.indexOf("?");
14770
15064
  return query === -1 ? id : id.slice(0, query);
14771
15065
  }
14772
15066
  function isInside(parent, child) {
14773
- const relative = path39.relative(path39.resolve(parent), path39.resolve(child));
14774
- return relative === "" || !relative.startsWith("..") && !path39.isAbsolute(relative);
15067
+ const relative = path40.relative(path40.resolve(parent), path40.resolve(child));
15068
+ return relative === "" || !relative.startsWith("..") && !path40.isAbsolute(relative);
14775
15069
  }
14776
15070
  function isBareSpecifier(source) {
14777
- return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path39.isAbsolute(source);
15071
+ return !source.startsWith("\0") && !source.startsWith(".") && !source.startsWith("/") && !path40.isAbsolute(source);
14778
15072
  }
14779
15073
  function isInsideNodeModules(filePath) {
14780
15074
  return filePath.split(/[\\/]/).includes("node_modules");
@@ -14798,7 +15092,7 @@ function viteOffrootResolvePlugin() {
14798
15092
  if (!shouldResolveFromSiteRoot(source, importer, rootDir)) {
14799
15093
  return;
14800
15094
  }
14801
- return this.resolve(source, path39.join(rootDir, "package.json"), {
15095
+ return this.resolve(source, path40.join(rootDir, "package.json"), {
14802
15096
  ...options,
14803
15097
  skipSelf: true
14804
15098
  });
@@ -14808,7 +15102,7 @@ function viteOffrootResolvePlugin() {
14808
15102
 
14809
15103
  // src/vite/plugin-search.ts
14810
15104
  init_loader();
14811
- import path40 from "node:path";
15105
+ import path41 from "node:path";
14812
15106
  var viteSearchPlugin = () => {
14813
15107
  const virtualModuleId4 = "virtual:zudoku-search-plugin";
14814
15108
  const resolvedVirtualModuleId5 = `\0${virtualModuleId4}`;
@@ -14826,7 +15120,7 @@ var viteSearchPlugin = () => {
14826
15120
  return resolvedVirtualModuleId5;
14827
15121
  }
14828
15122
  if (id === "/pagefind/pagefind.js" && resolvedViteConfig?.publicDir) {
14829
- return path40.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
15123
+ return path41.join(resolvedViteConfig.publicDir, "pagefind/pagefind.js");
14830
15124
  }
14831
15125
  },
14832
15126
  async load(id) {
@@ -14967,27 +15261,27 @@ function vitePlugin() {
14967
15261
 
14968
15262
  // src/vite/watch-ignore.ts
14969
15263
  import { lstatSync, realpathSync } from "node:fs";
14970
- import path41 from "node:path";
15264
+ import path42 from "node:path";
14971
15265
  var STATE_INSIDE_CONTENT = /[\\/]content[\\/](?:\.apitogo|apitogo[\\/]\.state)(?:[\\/]|$)/;
14972
15266
  var within = (base, file) => {
14973
- const resolvedBase = path41.resolve(base);
14974
- const resolvedFile = path41.resolve(file);
14975
- return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path41.sep);
15267
+ const resolvedBase = path42.resolve(base);
15268
+ const resolvedFile = path42.resolve(file);
15269
+ return resolvedFile === resolvedBase || resolvedFile.startsWith(resolvedBase + path42.sep);
14976
15270
  };
14977
15271
  var unique = (paths) => [...new Set(paths)];
14978
15272
  function isWorkspace(root) {
14979
15273
  try {
14980
- return path41.basename(root) === "generated" && lstatSync(path41.join(root, "content")).isSymbolicLink();
15274
+ return path42.basename(root) === "generated" && lstatSync(path42.join(root, "content")).isSymbolicLink();
14981
15275
  } catch {
14982
15276
  return false;
14983
15277
  }
14984
15278
  }
14985
15279
  function tenantDir(root) {
14986
- return isWorkspace(root) ? realpathSync(path41.join(root, "content")) : void 0;
15280
+ return isWorkspace(root) ? realpathSync(path42.join(root, "content")) : void 0;
14987
15281
  }
14988
15282
  function stateIgnore(root) {
14989
15283
  if (!isWorkspace(root)) return void 0;
14990
- const state = path41.dirname(root);
15284
+ const state = path42.dirname(root);
14991
15285
  const states = unique([state, realpathSync(state)]);
14992
15286
  const roots = unique([root, realpathSync(root)]);
14993
15287
  return (file) => {
@@ -14998,17 +15292,17 @@ function stateIgnore(root) {
14998
15292
  }
14999
15293
  function devServerAllow(root, moduleDir) {
15000
15294
  const tenant = tenantDir(root);
15001
- return [root, path41.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15295
+ return [root, path42.dirname(root), moduleDir, ...tenant ? [tenant] : []];
15002
15296
  }
15003
15297
 
15004
15298
  // src/vite/config.ts
15005
15299
  var resolveMergedPublicDir = (rootDir, merged) => {
15006
15300
  if (merged.publicDir === false) return void 0;
15007
15301
  const rel2 = typeof merged.publicDir === "string" ? merged.publicDir : "public";
15008
- return path42.resolve(rootDir, rel2);
15302
+ return path43.resolve(rootDir, rel2);
15009
15303
  };
15010
- var getAppClientEntryPath = () => path42.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
15011
- var getAppServerEntryPath = () => path42.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
15304
+ var getAppClientEntryPath = () => path43.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
15305
+ var getAppServerEntryPath = () => path43.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
15012
15306
  var hasLoggedCdnInfo = false;
15013
15307
  var MEDIA_REGEX = /\.(a?png|jpe?g|gif|bmp|svg|webp|tiff|ico|webm|ogg|mp3|wav|m4a|avif|mp4)/i;
15014
15308
  var defineEnvVars = (vars) => Object.fromEntries(
@@ -15039,7 +15333,7 @@ async function getViteConfig(dir, configEnv) {
15039
15333
  const ignoreState = stateIgnore(dir);
15040
15334
  if (ZuploEnv.isZuplo) {
15041
15335
  dotenv.config({
15042
- path: path42.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15336
+ path: path43.resolve(config2.__meta.rootDir, "../.env.zuplo"),
15043
15337
  quiet: true
15044
15338
  });
15045
15339
  }
@@ -15102,8 +15396,8 @@ async function getViteConfig(dir, configEnv) {
15102
15396
  ssr: configEnv.isSsrBuild,
15103
15397
  sourcemap: true,
15104
15398
  target: "es2022",
15105
- outDir: path42.resolve(
15106
- path42.join(
15399
+ outDir: path43.resolve(
15400
+ path43.join(
15107
15401
  dir,
15108
15402
  "dist",
15109
15403
  config2.basePath ?? "",
@@ -15122,7 +15416,7 @@ async function getViteConfig(dir, configEnv) {
15122
15416
  },
15123
15417
  experimental: {
15124
15418
  renderBuiltUrl(filename) {
15125
- if (cdnUrl?.base && [".js", ".css"].includes(path42.extname(filename))) {
15419
+ if (cdnUrl?.base && [".js", ".css"].includes(path43.extname(filename))) {
15126
15420
  return joinUrl(cdnUrl.base, filename);
15127
15421
  }
15128
15422
  if (cdnUrl?.media && MEDIA_REGEX.test(filename)) {
@@ -15135,7 +15429,7 @@ async function getViteConfig(dir, configEnv) {
15135
15429
  esbuildOptions: {
15136
15430
  target: "es2022"
15137
15431
  },
15138
- entries: [path42.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15432
+ entries: [path43.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
15139
15433
  exclude: [
15140
15434
  "@lukoweb/apitogo",
15141
15435
  "@lukoweb/apitogo-plugin-dev-portal-billing"
@@ -15239,7 +15533,7 @@ init_package_json();
15239
15533
  init_joinUrl();
15240
15534
  import assert from "node:assert";
15241
15535
  import { cp, mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
15242
- import path43 from "node:path";
15536
+ import path44 from "node:path";
15243
15537
  var pkgJson = getZudokuPackageJson();
15244
15538
  function generateOutput({
15245
15539
  config: config2,
@@ -15296,9 +15590,9 @@ async function writeOutput(dir, {
15296
15590
  rewrites
15297
15591
  }) {
15298
15592
  const output4 = generateOutput({ config: config2, redirects, rewrites });
15299
- const outputDir = process.env.VERCEL ? path43.join(dir, ".vercel/output") : path43.join(dir, "dist/.output");
15593
+ const outputDir = process.env.VERCEL ? path44.join(dir, ".vercel/output") : path44.join(dir, "dist/.output");
15300
15594
  await mkdir9(outputDir, { recursive: true });
15301
- const outputFile = path43.join(outputDir, "config.json");
15595
+ const outputFile = path44.join(outputDir, "config.json");
15302
15596
  await writeFile11(outputFile, JSON.stringify(output4, null, 2), "utf-8");
15303
15597
  if (process.env.VERCEL) {
15304
15598
  console.log("Wrote Vercel output to", outputDir);
@@ -15310,7 +15604,7 @@ init_logger();
15310
15604
  init_file_exists();
15311
15605
  import { readFile as readFile12, rm as rm6 } from "node:fs/promises";
15312
15606
  import os from "node:os";
15313
- import path46 from "node:path";
15607
+ import path47 from "node:path";
15314
15608
  import { pathToFileURL } from "node:url";
15315
15609
  import { createIndex } from "pagefind";
15316
15610
  import colors11 from "picocolors";
@@ -15350,9 +15644,9 @@ function throttle(fn) {
15350
15644
 
15351
15645
  // src/vite/sitemap.ts
15352
15646
  init_joinUrl();
15353
- import { createWriteStream, existsSync as existsSync4 } from "node:fs";
15647
+ import { createWriteStream, existsSync as existsSync5 } from "node:fs";
15354
15648
  import { mkdir as mkdir10 } from "node:fs/promises";
15355
- import path44 from "node:path";
15649
+ import path45 from "node:path";
15356
15650
  import colors9 from "picocolors";
15357
15651
  import { SitemapStream } from "sitemap";
15358
15652
  async function generateSitemap({
@@ -15366,11 +15660,11 @@ async function generateSitemap({
15366
15660
  return;
15367
15661
  }
15368
15662
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
15369
- const outputDir = path44.resolve(baseOutputDir, config2.outDir ?? "");
15370
- if (!existsSync4(outputDir)) {
15663
+ const outputDir = path45.resolve(baseOutputDir, config2.outDir ?? "");
15664
+ if (!existsSync5(outputDir)) {
15371
15665
  await mkdir10(outputDir, { recursive: true });
15372
15666
  }
15373
- const sitemapOutputPath = path44.join(outputDir, "sitemap.xml");
15667
+ const sitemapOutputPath = path45.join(outputDir, "sitemap.xml");
15374
15668
  const writeStream = createWriteStream(sitemapOutputPath);
15375
15669
  sitemap.pipe(writeStream);
15376
15670
  let lastmod;
@@ -15400,14 +15694,14 @@ async function generateSitemap({
15400
15694
 
15401
15695
  // src/vite/prerender/utils.ts
15402
15696
  init_joinUrl();
15403
- var resolveRoutePath = (path98) => {
15404
- const segments = path98.split("/");
15697
+ var resolveRoutePath = (path99) => {
15698
+ const segments = path99.split("/");
15405
15699
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
15406
15700
  return void 0;
15407
15701
  }
15408
15702
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
15409
15703
  };
15410
- var isSkipped = (path98) => path98.includes("*") || /^\d+$/.test(path98);
15704
+ var isSkipped = (path99) => path99.includes("*") || /^\d+$/.test(path99);
15411
15705
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
15412
15706
  if (route.path && isSkipped(route.path)) return [];
15413
15707
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -15456,12 +15750,12 @@ var prerender = async ({
15456
15750
  serverConfigFilename,
15457
15751
  writeRedirects = true
15458
15752
  }) => {
15459
- const distDir = path46.join(dir, "dist", basePath);
15753
+ const distDir = path47.join(dir, "dist", basePath);
15460
15754
  const serverConfigPath = pathToFileURL(
15461
- path46.join(distDir, "server", serverConfigFilename)
15755
+ path47.join(distDir, "server", serverConfigFilename)
15462
15756
  ).href;
15463
15757
  const entryServerPath = pathToFileURL(
15464
- path46.join(distDir, "server/entry.server.js")
15758
+ path47.join(distDir, "server/entry.server.js")
15465
15759
  ).href;
15466
15760
  const rawConfig = await import(serverConfigPath).then(
15467
15761
  (m) => m.default
@@ -15546,7 +15840,7 @@ var prerender = async ({
15546
15840
  })
15547
15841
  );
15548
15842
  const pagefindWriteResult = await pagefindIndex?.writeFiles({
15549
- outputPath: path46.join(distDir, "pagefind")
15843
+ outputPath: path47.join(distDir, "pagefind")
15550
15844
  });
15551
15845
  const seconds = ((performance.now() - start) / 1e3).toFixed(1);
15552
15846
  const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
@@ -15578,7 +15872,7 @@ var prerender = async ({
15578
15872
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
15579
15873
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
15580
15874
  const llmsConfig = docsConfig.llms ?? {};
15581
- const markdownInfoPath = path46.join(
15875
+ const markdownInfoPath = path47.join(
15582
15876
  dir,
15583
15877
  "node_modules/.apitogo/markdown-info.json"
15584
15878
  );
@@ -15669,7 +15963,7 @@ async function runBuild(options) {
15669
15963
  html,
15670
15964
  basePath: config2.basePath
15671
15965
  });
15672
- await rm7(path47.join(clientOutDir, "index.html"), { force: true });
15966
+ await rm7(path48.join(clientOutDir, "index.html"), { force: true });
15673
15967
  } else {
15674
15968
  await runPrerender({
15675
15969
  dir,
@@ -15693,7 +15987,7 @@ var runPrerender = async (options) => {
15693
15987
  serverConfigFilename,
15694
15988
  writeRedirects: process.env.VERCEL === void 0
15695
15989
  });
15696
- const indexHtml = path47.join(clientOutDir, "index.html");
15990
+ const indexHtml = path48.join(clientOutDir, "index.html");
15697
15991
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
15698
15992
  await writeFile13(indexHtml, html, "utf-8");
15699
15993
  }
@@ -15703,15 +15997,15 @@ var runPrerender = async (options) => {
15703
15997
  for (const statusPage of statusPages) {
15704
15998
  await rename5(
15705
15999
  statusPage,
15706
- path47.join(dir, DIST_DIR, path47.basename(statusPage))
16000
+ path48.join(dir, DIST_DIR, path48.basename(statusPage))
15707
16001
  );
15708
16002
  }
15709
16003
  await rm7(serverOutDir, { recursive: true, force: true });
15710
16004
  if (process.env.VERCEL) {
15711
- await mkdir11(path47.join(dir, ".vercel/output/static"), { recursive: true });
16005
+ await mkdir11(path48.join(dir, ".vercel/output/static"), { recursive: true });
15712
16006
  await rename5(
15713
- path47.join(dir, DIST_DIR),
15714
- path47.join(dir, ".vercel/output/static")
16007
+ path48.join(dir, DIST_DIR),
16008
+ path48.join(dir, ".vercel/output/static")
15715
16009
  );
15716
16010
  }
15717
16011
  await writeOutput(dir, {
@@ -15721,7 +16015,7 @@ var runPrerender = async (options) => {
15721
16015
  });
15722
16016
  if (ZuploEnv.isZuplo && issuer) {
15723
16017
  await writeFile13(
15724
- path47.join(dir, DIST_DIR, ".output/zuplo.json"),
16018
+ path48.join(dir, DIST_DIR, ".output/zuplo.json"),
15725
16019
  JSON.stringify({ issuer }, null, 2),
15726
16020
  "utf-8"
15727
16021
  );
@@ -15733,10 +16027,10 @@ var runPrerender = async (options) => {
15733
16027
  };
15734
16028
  var bundleSSREntry = async (options) => {
15735
16029
  const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } = options;
15736
- const tempEntryPath = path47.join(dir, "__ssr-entry.ts");
16030
+ const tempEntryPath = path48.join(dir, "__ssr-entry.ts");
15737
16031
  const packageRoot = getZudokuRootDir();
15738
16032
  const templateContent = await readFile13(
15739
- path47.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
16033
+ path48.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
15740
16034
  "utf-8"
15741
16035
  );
15742
16036
  const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`)).replace(
@@ -15751,9 +16045,9 @@ var bundleSSREntry = async (options) => {
15751
16045
  platform: adapter === "node" ? "node" : "neutral",
15752
16046
  target: "es2022",
15753
16047
  format: "esm",
15754
- outfile: path47.join(serverOutDir, "entry.js"),
16048
+ outfile: path48.join(serverOutDir, "entry.js"),
15755
16049
  external: ["./entry.server.js", `./${serverConfigFilename}`],
15756
- nodePaths: [path47.join(packageRoot, "node_modules")],
16050
+ nodePaths: [path48.join(packageRoot, "node_modules")],
15757
16051
  banner: { js: "// Bundled SSR entry" }
15758
16052
  });
15759
16053
  } finally {
@@ -15765,9 +16059,9 @@ var bundleSSREntry = async (options) => {
15765
16059
  init_package_json();
15766
16060
 
15767
16061
  // src/cli/init/v2-packages.ts
15768
- import { existsSync as existsSync5 } from "node:fs";
16062
+ import { existsSync as existsSync6 } from "node:fs";
15769
16063
  import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
15770
- import path48 from "node:path";
16064
+ import path49 from "node:path";
15771
16065
  var CORE_PACKAGES = [
15772
16066
  "@lukoweb/apitogo",
15773
16067
  "react",
@@ -15784,8 +16078,8 @@ function packageNameFrom(name) {
15784
16078
  return slug || "portal";
15785
16079
  }
15786
16080
  async function ensurePackageJson(dir, name) {
15787
- const file = path48.join(dir, "package.json");
15788
- if (existsSync5(file)) return false;
16081
+ const file = path49.join(dir, "package.json");
16082
+ if (existsSync6(file)) return false;
15789
16083
  await writeFile14(
15790
16084
  file,
15791
16085
  `${JSON.stringify(
@@ -15803,11 +16097,11 @@ async function ensurePackageJson(dir, name) {
15803
16097
  return true;
15804
16098
  }
15805
16099
  function isPackageInstalled(startDir, name) {
15806
- let dir = path48.resolve(startDir);
16100
+ let dir = path49.resolve(startDir);
15807
16101
  for (; ; ) {
15808
- if (existsSync5(path48.join(dir, "node_modules", name, "package.json")))
16102
+ if (existsSync6(path49.join(dir, "node_modules", name, "package.json")))
15809
16103
  return true;
15810
- const parent = path48.dirname(dir);
16104
+ const parent = path49.dirname(dir);
15811
16105
  if (parent === dir) return false;
15812
16106
  dir = parent;
15813
16107
  }
@@ -15864,7 +16158,7 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
15864
16158
  return {
15865
16159
  name: "build",
15866
16160
  hash: () => hash2,
15867
- stillValid: () => access4(path49.join(buildDir, "index.html")).then(
16161
+ stillValid: () => access4(path50.join(buildDir, "index.html")).then(
15868
16162
  () => true,
15869
16163
  () => false
15870
16164
  ),
@@ -15881,11 +16175,11 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
15881
16175
  else process.env.NODE_ENV = originalNodeEnv;
15882
16176
  }
15883
16177
  await rm8(buildDir, { recursive: true, force: true });
15884
- await mkdir12(path49.dirname(buildDir), { recursive: true });
15885
- await rename6(path49.join(root, "dist"), buildDir);
16178
+ await mkdir12(path50.dirname(buildDir), { recursive: true });
16179
+ await rename6(path50.join(root, "dist"), buildDir);
15886
16180
  if (prepared.publishedManifest) {
15887
16181
  await writeFile15(
15888
- path49.join(buildDir, MANIFEST_FILE),
16182
+ path50.join(buildDir, MANIFEST_FILE),
15889
16183
  `${JSON.stringify(prepared.publishedManifest, null, 2)}
15890
16184
  `
15891
16185
  );
@@ -15916,15 +16210,15 @@ async function buildV2(options) {
15916
16210
  // src/cli/check/gateway-online.ts
15917
16211
  import { readFile as readFile16 } from "node:fs/promises";
15918
16212
  var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
15919
- var issue2 = (code, path98, message, hint) => ({ code, path: path98, message, hint });
16213
+ var issue2 = (code, path99, message, hint) => ({ code, path: path99, message, hint });
15920
16214
  function capabilityIssues(gateway, answer) {
15921
16215
  const can = answer.capabilities;
15922
16216
  const plan2 = `your ${answer.tier} plan`;
15923
16217
  const found = [];
15924
- const unsupported = (path98, what, hint) => found.push(
16218
+ const unsupported = (path99, what, hint) => found.push(
15925
16219
  issue2(
15926
16220
  "GATEWAY_CAPABILITY_UNSUPPORTED",
15927
- path98,
16221
+ path99,
15928
16222
  `${what} is not available on ${plan2}.`,
15929
16223
  hint
15930
16224
  )
@@ -16039,6 +16333,8 @@ var PlanFilePlanSchema = z14.strictObject({
16039
16333
  sortOrder: z14.int().optional(),
16040
16334
  /** Shown as "Popular" on the pricing page (API-124). Only the portal reads it; the platform never sees it. */
16041
16335
  featured: z14.boolean().optional(),
16336
+ /** Lines the pricing card lists, such as "Email support" (API-129). Only the portal reads them. */
16337
+ features: z14.array(z14.string().trim().min(1)).optional(),
16042
16338
  /** Action keys such as `get:/orders`. Left out, the plan includes every action of the gateway. */
16043
16339
  includedActions: z14.array(z14.string().trim().min(1)).optional()
16044
16340
  });
@@ -16120,9 +16416,11 @@ function toPlatformPlans(plans, options = {}) {
16120
16416
  displayName: plan2.displayName,
16121
16417
  ...limits ? { limitsJson: JSON.stringify(limits) } : {},
16122
16418
  includedActionKeys: plan2.includedActions ?? null,
16123
- ...plan2.pricingModel ? { pricingModel: plan2.pricingModel } : {},
16419
+ // Always said: the platform keeps the stored model when none is sent, so a plan that was a contact-us plan
16420
+ // stayed one and its new price was refused (API-128). A label the file no longer has is cleared the same way.
16421
+ pricingModel: custom ? "custom" : "fixed",
16124
16422
  ...plan2.priceAmount !== void 0 && !custom ? { priceAmount: plan2.priceAmount } : {},
16125
- ...plan2.priceLabel !== void 0 ? { priceLabel: plan2.priceLabel } : {},
16423
+ priceLabel: plan2.priceLabel ?? "",
16126
16424
  ...plan2.priceCurrency && !custom ? { priceCurrency: plan2.priceCurrency } : {},
16127
16425
  ...plan2.billingInterval ? { billingPeriod: BILLING_PERIOD[plan2.billingInterval] } : {},
16128
16426
  sortOrder: plan2.sortOrder ?? index
@@ -16131,9 +16429,9 @@ function toPlatformPlans(plans, options = {}) {
16131
16429
  }
16132
16430
 
16133
16431
  // src/cli/check/services-online.ts
16134
- var issue3 = (code, path98, message, hint) => ({
16432
+ var issue3 = (code, path99, message, hint) => ({
16135
16433
  code,
16136
- path: path98,
16434
+ path: path99,
16137
16435
  message,
16138
16436
  hint
16139
16437
  });
@@ -16245,7 +16543,7 @@ var withPlanName = (message, sku) => !sku || message.toLowerCase().startsWith(`p
16245
16543
  async function isConfigV2(dir) {
16246
16544
  try {
16247
16545
  return /"version"\s*:\s*2\b/.test(
16248
- await readFile18(path50.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16546
+ await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
16249
16547
  );
16250
16548
  } catch {
16251
16549
  return false;
@@ -16392,7 +16690,7 @@ async function validatePortalBuild(dir) {
16392
16690
  }
16393
16691
  }
16394
16692
  async function runCheckV2(dir, args = {}, deps = {}) {
16395
- const configPath = path50.join(dir, APITOGO_CONFIG_FILENAME);
16693
+ const configPath = path51.join(dir, APITOGO_CONFIG_FILENAME);
16396
16694
  let report;
16397
16695
  try {
16398
16696
  const { config: config2, resolved } = loadConfigV2(dir);
@@ -16502,7 +16800,7 @@ async function checkV2(dir, args, deps = platformDependencies(args)) {
16502
16800
  init_package_json();
16503
16801
 
16504
16802
  // src/cli/init/v2-handler.ts
16505
- import path51 from "node:path";
16803
+ import path52 from "node:path";
16506
16804
  import { createInterface } from "node:readline/promises";
16507
16805
  function parseDocsFlag(value) {
16508
16806
  if (value === void 0) return void 0;
@@ -16524,15 +16822,15 @@ async function ask(question) {
16524
16822
  }
16525
16823
  function describePlan(plan2) {
16526
16824
  return {
16527
- willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path51.relative(plan2.dir, entry.path)),
16528
- willKeep: plan2.keep.map((target) => path51.relative(plan2.dir, target)),
16825
+ willCreate: plan2.entries.filter((entry) => !plan2.keep.includes(entry.path)).map((entry) => path52.relative(plan2.dir, entry.path)),
16826
+ willKeep: plan2.keep.map((target) => path52.relative(plan2.dir, target)),
16529
16827
  replacesConfig: plan2.replacesConfig
16530
16828
  };
16531
16829
  }
16532
16830
  async function initV2(argv) {
16533
16831
  const json = argv.json === true;
16534
16832
  try {
16535
- const dir = path51.resolve(process.cwd(), argv.dir ?? ".");
16833
+ const dir = path52.resolve(process.cwd(), argv.dir ?? ".");
16536
16834
  const plan2 = await planInit({
16537
16835
  dir,
16538
16836
  name: argv.name,
@@ -16573,8 +16871,8 @@ async function initV2(argv) {
16573
16871
  status: "created",
16574
16872
  dir,
16575
16873
  ...describePlan(plan2),
16576
- created: result.created.map((p) => path51.relative(dir, p)),
16577
- kept: result.kept.map((p) => path51.relative(dir, p))
16874
+ created: result.created.map((p) => path52.relative(dir, p)),
16875
+ kept: result.kept.map((p) => path52.relative(dir, p))
16578
16876
  },
16579
16877
  null,
16580
16878
  2
@@ -16583,7 +16881,7 @@ async function initV2(argv) {
16583
16881
  return;
16584
16882
  }
16585
16883
  printResultToConsole(
16586
- `Created ${path51.basename(plan2.configPath)} (config version 2) in ${dir}.`
16884
+ `Created ${path52.basename(plan2.configPath)} (config version 2) in ${dir}.`
16587
16885
  );
16588
16886
  printHintToConsole(
16589
16887
  "Next: add a service with `apitogo add <portal|gateway|identity|plans>`, or run `apitogo create` for a guided setup."
@@ -16609,8 +16907,8 @@ async function initV2(argv) {
16609
16907
  }
16610
16908
 
16611
16909
  // src/cli/services/packages.ts
16612
- import { existsSync as existsSync6 } from "node:fs";
16613
- import path52 from "node:path";
16910
+ import { existsSync as existsSync7 } from "node:fs";
16911
+ import path53 from "node:path";
16614
16912
 
16615
16913
  // src/cli/common/spawn-command.ts
16616
16914
  import {
@@ -16632,7 +16930,7 @@ var LANDING_PACKAGE = "@lukoweb/apitogo-module-landing";
16632
16930
  function packagesForConfig(config2) {
16633
16931
  const portal = config2.portal;
16634
16932
  if (!portal || portal.enabled === false) return [];
16635
- const landing = portal.landing ? [LANDING_PACKAGE] : [];
16933
+ const landing = portal.landing || portal.footer ? [LANDING_PACKAGE] : [];
16636
16934
  if (portal.console?.enabled === false) return landing;
16637
16935
  const identity = config2.identity && config2.identity.enabled !== false;
16638
16936
  const plans = config2.plans && config2.plans.enabled !== false;
@@ -16643,18 +16941,18 @@ function packagesForConfig(config2) {
16643
16941
  ];
16644
16942
  }
16645
16943
  function isInstalled(dir, name) {
16646
- for (let current = path52.resolve(dir); ; current = path52.dirname(current)) {
16647
- if (existsSync6(path52.join(current, "node_modules", name, "package.json")))
16944
+ for (let current = path53.resolve(dir); ; current = path53.dirname(current)) {
16945
+ if (existsSync7(path53.join(current, "node_modules", name, "package.json")))
16648
16946
  return true;
16649
- if (path52.dirname(current) === current) return false;
16947
+ if (path53.dirname(current) === current) return false;
16650
16948
  }
16651
16949
  }
16652
16950
  function missingPackages(dir, names) {
16653
16951
  return names.filter((name) => !isInstalled(dir, name));
16654
16952
  }
16655
16953
  function detectPackageManager(dir) {
16656
- if (existsSync6(path52.join(dir, "pnpm-lock.yaml"))) return "pnpm";
16657
- if (existsSync6(path52.join(dir, "yarn.lock"))) return "yarn";
16954
+ if (existsSync7(path53.join(dir, "pnpm-lock.yaml"))) return "pnpm";
16955
+ if (existsSync7(path53.join(dir, "yarn.lock"))) return "yarn";
16658
16956
  return "npm";
16659
16957
  }
16660
16958
  function installSpecsCommand(manager, specs) {
@@ -16712,7 +17010,7 @@ async function installMissingPackages(dir, config2, version, options) {
16712
17010
  reason: "--no-install was given",
16713
17011
  command: shown
16714
17012
  };
16715
- await ensurePackageJson(dir, config2.project?.name ?? path52.basename(dir));
17013
+ await ensurePackageJson(dir, config2.project?.name ?? path53.basename(dir));
16716
17014
  await (options.installer ?? runInstaller)(dir, command2);
16717
17015
  return { status: "installed", packages: missing, command: shown };
16718
17016
  }
@@ -16775,7 +17073,7 @@ var list = (value) => {
16775
17073
  return items.length > 0 ? items : void 0;
16776
17074
  };
16777
17075
  function addOptionsFromArguments(service, argv) {
16778
- const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
17076
+ const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
16779
17077
  const issuers = list(argv.issuer);
16780
17078
  return {
16781
17079
  dir,
@@ -16829,8 +17127,8 @@ function describe(edit, dir) {
16829
17127
  return {
16830
17128
  service: edit.service,
16831
17129
  action: edit.action,
16832
- willCreate: edit.files.map((file) => path53.relative(dir, file.path)),
16833
- keep: edit.keep.map((file) => path53.relative(dir, file)),
17130
+ willCreate: edit.files.map((file) => path54.relative(dir, file.path)),
17131
+ keep: edit.keep.map((file) => path54.relative(dir, file)),
16834
17132
  notes: edit.notes
16835
17133
  };
16836
17134
  }
@@ -16844,7 +17142,7 @@ async function run(action, argv, plan2, deps = {}) {
16844
17142
  `Choose one of ${SERVICES.join(", ")}.`
16845
17143
  );
16846
17144
  }
16847
- const dir = path53.resolve(process.cwd(), argv.dir ?? ".");
17145
+ const dir = path54.resolve(process.cwd(), argv.dir ?? ".");
16848
17146
  if (!await isConfigV2(dir)) {
16849
17147
  throw new ServiceEditError(
16850
17148
  `${action} works with a config version 2 project, and there is none in ${dir}.`,
@@ -16912,7 +17210,7 @@ async function run(action, argv, plan2, deps = {}) {
16912
17210
  {
16913
17211
  status: action === "add" ? "added" : "removed",
16914
17212
  ...describe(edit, dir),
16915
- created: created.map((file) => path53.relative(dir, file)),
17213
+ created: created.map((file) => path54.relative(dir, file)),
16916
17214
  ...install ? { packages: install } : {}
16917
17215
  },
16918
17216
  null,
@@ -16922,7 +17220,7 @@ async function run(action, argv, plan2, deps = {}) {
16922
17220
  return;
16923
17221
  }
16924
17222
  printResultToConsole(
16925
- `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path53.basename(edit.configPath)}.`
17223
+ `${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path54.basename(edit.configPath)}.`
16926
17224
  );
16927
17225
  } catch (error) {
16928
17226
  const message = error instanceof Error ? error.message : String(error);
@@ -16983,7 +17281,7 @@ var removeService = (argv) => run(
16983
17281
  "remove",
16984
17282
  argv,
16985
17283
  (service) => planRemoveService(service, {
16986
- dir: path53.resolve(process.cwd(), argv.dir ?? "."),
17284
+ dir: path54.resolve(process.cwd(), argv.dir ?? "."),
16987
17285
  deleteFiles: argv.deleteFiles === true
16988
17286
  })
16989
17287
  );
@@ -17135,16 +17433,16 @@ var add_default = {
17135
17433
  };
17136
17434
 
17137
17435
  // src/cli/build/handler.ts
17138
- import path55 from "node:path";
17436
+ import path56 from "node:path";
17139
17437
  init_logger();
17140
17438
  init_package_json();
17141
17439
 
17142
17440
  // src/cli/preview/handler.ts
17143
- import path54 from "node:path";
17441
+ import path55 from "node:path";
17144
17442
  import { preview as vitePreview } from "vite";
17145
17443
  var DEFAULT_PREVIEW_PORT = 4e3;
17146
17444
  async function preview(argv) {
17147
- const dir = path54.resolve(process.cwd(), argv.dir);
17445
+ const dir = path55.resolve(process.cwd(), argv.dir);
17148
17446
  const viteConfig = await getViteConfig(dir, {
17149
17447
  command: "serve",
17150
17448
  mode: "production",
@@ -17187,7 +17485,7 @@ async function build(argv) {
17187
17485
  printDiagnosticsToConsole("");
17188
17486
  printDiagnosticsToConsole("");
17189
17487
  }
17190
- const dir = path55.resolve(process.cwd(), argv.dir);
17488
+ const dir = path56.resolve(process.cwd(), argv.dir);
17191
17489
  try {
17192
17490
  if (await isConfigV2(dir)) {
17193
17491
  const result = await buildV2({ dir, jsonOnly });
@@ -17252,10 +17550,10 @@ var build_default = {
17252
17550
  };
17253
17551
 
17254
17552
  // src/cli/check/handler.ts
17255
- import path61 from "node:path";
17553
+ import path62 from "node:path";
17256
17554
 
17257
17555
  // src/cli/platform/tools/check-publish-readiness.ts
17258
- import path60 from "node:path";
17556
+ import path61 from "node:path";
17259
17557
 
17260
17558
  // src/cli/platform/helpers/dev-portal-backend.ts
17261
17559
  function deriveProvisioningState(backend) {
@@ -17338,9 +17636,9 @@ function formatDevPortalBackendResponse(projectId, backend) {
17338
17636
 
17339
17637
  // src/cli/platform/helpers/dev-portal-manifest.ts
17340
17638
  init_gateway_limits();
17341
- import { existsSync as existsSync7 } from "node:fs";
17639
+ import { existsSync as existsSync8 } from "node:fs";
17342
17640
  import { access as access5, mkdir as mkdir13, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
17343
- import path56 from "node:path";
17641
+ import path57 from "node:path";
17344
17642
 
17345
17643
  // src/cli/make-config/header-pricing-nav.ts
17346
17644
  var HEADER_PRICING_PATH = "/pricing";
@@ -17462,7 +17760,7 @@ function isPlanFree(plan2) {
17462
17760
  return (plan2.priceAmount ?? 0) <= 0;
17463
17761
  }
17464
17762
  function manifestFilePath(siteDirectory) {
17465
- return path56.join(path56.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17763
+ return path57.join(path57.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17466
17764
  }
17467
17765
  var STORED_MANIFEST_KEYS = [
17468
17766
  "authentication",
@@ -17886,24 +18184,24 @@ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
17886
18184
  if (!trimmed) {
17887
18185
  return void 0;
17888
18186
  }
17889
- return path56.resolve(siteDirectory, trimmed);
18187
+ return path57.resolve(siteDirectory, trimmed);
17890
18188
  }
17891
18189
  function resolveBackendOpenApiPath(openApiPath, options) {
17892
18190
  const trimmed = openApiPath?.trim();
17893
18191
  if (!trimmed) {
17894
18192
  return void 0;
17895
18193
  }
17896
- if (path56.isAbsolute(trimmed)) {
18194
+ if (path57.isAbsolute(trimmed)) {
17897
18195
  return trimmed;
17898
18196
  }
17899
- const siteResolved = path56.resolve(options.siteDirectory, trimmed);
17900
- if (existsSync7(siteResolved)) {
18197
+ const siteResolved = path57.resolve(options.siteDirectory, trimmed);
18198
+ if (existsSync8(siteResolved)) {
17901
18199
  return siteResolved;
17902
18200
  }
17903
18201
  const backendDir = options.backendSourceDirectory?.trim();
17904
18202
  if (backendDir) {
17905
- const backendResolved = path56.resolve(backendDir, trimmed);
17906
- if (existsSync7(backendResolved)) {
18203
+ const backendResolved = path57.resolve(backendDir, trimmed);
18204
+ if (existsSync8(backendResolved)) {
17907
18205
  return backendResolved;
17908
18206
  }
17909
18207
  }
@@ -17927,7 +18225,7 @@ async function readManifest2(siteDirectory) {
17927
18225
  }
17928
18226
  async function writeManifest2(siteDirectory, manifest) {
17929
18227
  const filePath = manifestFilePath(siteDirectory);
17930
- await mkdir13(path56.dirname(filePath), { recursive: true });
18228
+ await mkdir13(path57.dirname(filePath), { recursive: true });
17931
18229
  let config2 = {};
17932
18230
  if (await pathExists(filePath)) {
17933
18231
  const raw = await readFile19(filePath, "utf8");
@@ -17953,7 +18251,7 @@ import {
17953
18251
  unlink,
17954
18252
  writeFile as writeFile17
17955
18253
  } from "node:fs/promises";
17956
- import path57 from "node:path";
18254
+ import path58 from "node:path";
17957
18255
  var ENV_FILENAME = ".env";
17958
18256
  var COLLAPSED_UPPERCASE_ALIASES2 = {
17959
18257
  SECRETKEY: "secretKey",
@@ -18072,12 +18370,12 @@ async function readEnvFile(filePath) {
18072
18370
  }
18073
18371
  }
18074
18372
  async function loadSiteEnv(siteDirectory) {
18075
- const resolvedDirectory = path57.resolve(siteDirectory);
18373
+ const resolvedDirectory = path58.resolve(siteDirectory);
18076
18374
  const merged = { ...process.env };
18077
18375
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18078
18376
  Object.assign(
18079
18377
  merged,
18080
- await readEnvFile(path57.join(resolvedDirectory, fileName))
18378
+ await readEnvFile(path58.join(resolvedDirectory, fileName))
18081
18379
  );
18082
18380
  }
18083
18381
  return merged;
@@ -18114,7 +18412,7 @@ function buildAuthSecretsJson(secrets2, options) {
18114
18412
  return payload;
18115
18413
  }
18116
18414
  function envFilePath(siteDirectory) {
18117
- return path57.join(path57.resolve(siteDirectory), ENV_FILENAME);
18415
+ return path58.join(path58.resolve(siteDirectory), ENV_FILENAME);
18118
18416
  }
18119
18417
  function getSecretsStatus(siteDirectory, manifest, secrets2, envExists) {
18120
18418
  const providerSecretStatus = {};
@@ -18150,7 +18448,7 @@ function validateSecretsForPublish(manifest, secrets2, emailOptions) {
18150
18448
  }
18151
18449
  async function siteEnvFileHasEntries(siteDirectory) {
18152
18450
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
18153
- const loaded = await readEnvFile(path57.join(siteDirectory, fileName));
18451
+ const loaded = await readEnvFile(path58.join(siteDirectory, fileName));
18154
18452
  if (Object.keys(loaded).length > 0) {
18155
18453
  return true;
18156
18454
  }
@@ -18158,7 +18456,7 @@ async function siteEnvFileHasEntries(siteDirectory) {
18158
18456
  return false;
18159
18457
  }
18160
18458
  async function resolvePublishConfig(siteDirectory, emailOptions) {
18161
- const resolvedDirectory = path57.resolve(siteDirectory);
18459
+ const resolvedDirectory = path58.resolve(siteDirectory);
18162
18460
  const manifestRaw = await readManifest2(resolvedDirectory);
18163
18461
  if (!manifestRaw) {
18164
18462
  throw new Error("apitogo.config.json not found in sourceDirectory.");
@@ -18190,11 +18488,11 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
18190
18488
  init_gateway_limits();
18191
18489
  import { readFile as readFile21 } from "node:fs/promises";
18192
18490
  import { createRequire as createRequire2 } from "node:module";
18193
- import path58 from "node:path";
18491
+ import path59 from "node:path";
18194
18492
  var cachedFallbackTs;
18195
18493
  async function loadTypeScriptForSite(sourceDirectory) {
18196
18494
  const require2 = createRequire2(
18197
- path58.join(path58.resolve(sourceDirectory), "package.json")
18495
+ path59.join(path59.resolve(sourceDirectory), "package.json")
18198
18496
  );
18199
18497
  try {
18200
18498
  return require2("typescript");
@@ -18241,7 +18539,7 @@ async function referencedFileExists(sourceDirectory, reference) {
18241
18539
  return true;
18242
18540
  }
18243
18541
  return pathExists(
18244
- path58.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18542
+ path59.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
18245
18543
  );
18246
18544
  }
18247
18545
  async function validateSiteConfigStructure(config2, sourceDirectory) {
@@ -18356,11 +18654,11 @@ var DEFAULT_TSX_CANDIDATES = [
18356
18654
  "landing/LandingPage.tsx"
18357
18655
  ];
18358
18656
  async function validateDevPortalSiteConfig(sourceDirectory, options) {
18359
- const resolvedDir = path58.resolve(sourceDirectory);
18657
+ const resolvedDir = path59.resolve(sourceDirectory);
18360
18658
  const issues = [];
18361
18659
  const checkedFiles = [];
18362
18660
  const skippedChecks = [];
18363
- const jsonPath = path58.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18661
+ const jsonPath = path59.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
18364
18662
  if (await pathExists(jsonPath)) {
18365
18663
  checkedFiles.push(APITOGO_CONFIG_FILENAME2);
18366
18664
  let parsed;
@@ -18392,7 +18690,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18392
18690
  );
18393
18691
  const existingTsxFiles = [];
18394
18692
  for (const relativeFile of tsxFiles) {
18395
- if (await pathExists(path58.join(resolvedDir, relativeFile))) {
18693
+ if (await pathExists(path59.join(resolvedDir, relativeFile))) {
18396
18694
  existingTsxFiles.push(relativeFile);
18397
18695
  }
18398
18696
  }
@@ -18406,7 +18704,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18406
18704
  for (const relativeFile of existingTsxFiles) {
18407
18705
  checkedFiles.push(relativeFile);
18408
18706
  const content = await readFile21(
18409
- path58.join(resolvedDir, relativeFile),
18707
+ path59.join(resolvedDir, relativeFile),
18410
18708
  "utf8"
18411
18709
  );
18412
18710
  issues.push(
@@ -18428,7 +18726,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18428
18726
 
18429
18727
  // src/cli/platform/helpers/npm-build.ts
18430
18728
  import { access as access7, readFile as readFile22 } from "node:fs/promises";
18431
- import path59 from "node:path";
18729
+ import path60 from "node:path";
18432
18730
  var BUILD_LOG_TAIL_BYTES = 8192;
18433
18731
  var MIN_NODE_MAJOR = 20;
18434
18732
  var NpmBuildError = class extends Error {
@@ -18494,13 +18792,13 @@ function runCommand(command2, args, cwd, env) {
18494
18792
  }
18495
18793
  async function runNpmInstall(sourceDirectory, options) {
18496
18794
  assertNodeVersion(options?.nodeVersion);
18497
- const cwd = path59.resolve(sourceDirectory);
18498
- const packageJsonPath = path59.join(cwd, "package.json");
18795
+ const cwd = path60.resolve(sourceDirectory);
18796
+ const packageJsonPath = path60.join(cwd, "package.json");
18499
18797
  if (!await pathExists2(packageJsonPath)) {
18500
18798
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18501
18799
  }
18502
18800
  const startedAt = Date.now();
18503
- const lockPath = path59.join(cwd, "package-lock.json");
18801
+ const lockPath = path60.join(cwd, "package-lock.json");
18504
18802
  const installArgs = await pathExists2(lockPath) ? ["ci"] : ["install"];
18505
18803
  const install = await runCommand("npm", installArgs, cwd);
18506
18804
  const installLog = install.output;
@@ -18518,8 +18816,8 @@ async function runNpmInstall(sourceDirectory, options) {
18518
18816
  }
18519
18817
  async function runNpmBuild(sourceDirectory, options) {
18520
18818
  assertNodeVersion(options?.nodeVersion);
18521
- const cwd = path59.resolve(sourceDirectory);
18522
- const packageJsonPath = path59.join(cwd, "package.json");
18819
+ const cwd = path60.resolve(sourceDirectory);
18820
+ const packageJsonPath = path60.join(cwd, "package.json");
18523
18821
  if (!await pathExists2(packageJsonPath)) {
18524
18822
  throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
18525
18823
  }
@@ -18536,8 +18834,8 @@ async function runNpmBuild(sourceDirectory, options) {
18536
18834
  build2.exitCode
18537
18835
  );
18538
18836
  }
18539
- const distPath = path59.join(cwd, "dist");
18540
- const indexPath = path59.join(distPath, "index.html");
18837
+ const distPath = path60.join(cwd, "dist");
18838
+ const indexPath = path60.join(distPath, "index.html");
18541
18839
  if (!await pathExists2(indexPath)) {
18542
18840
  throw new NpmBuildError(
18543
18841
  `Build completed but dist/index.html was not found at ${indexPath}`,
@@ -18921,7 +19219,7 @@ function validateManifestForPlatformPublish(manifest) {
18921
19219
 
18922
19220
  // src/cli/platform/tools/check-publish-readiness.ts
18923
19221
  async function checkPublishReadiness(client, _config, args) {
18924
- const sourceDirectory = path60.resolve(args.sourceDirectory);
19222
+ const sourceDirectory = path61.resolve(args.sourceDirectory);
18925
19223
  const manifest = await readManifest2(sourceDirectory) ?? createScaffoldManifest();
18926
19224
  const emailOptions = await resolveEmailOptionsForPublish(
18927
19225
  client,
@@ -18999,7 +19297,7 @@ async function checkPublishReadiness(client, _config, args) {
18999
19297
 
19000
19298
  // src/cli/check/handler.ts
19001
19299
  async function check(argv) {
19002
- const sourceDirectory = path61.resolve(process.cwd(), argv.dir);
19300
+ const sourceDirectory = path62.resolve(process.cwd(), argv.dir);
19003
19301
  if (await isConfigV2(sourceDirectory)) {
19004
19302
  await checkV2(sourceDirectory, argv);
19005
19303
  return;
@@ -19063,7 +19361,7 @@ var check_default = {
19063
19361
 
19064
19362
  // src/cli/make-config/handler.ts
19065
19363
  init_invariant();
19066
- import path63 from "node:path";
19364
+ import path64 from "node:path";
19067
19365
  import { stdin as input2 } from "node:process";
19068
19366
 
19069
19367
  // src/cli/make-config/interactive.ts
@@ -19073,7 +19371,7 @@ import { createInterface as createInterface3 } from "node:readline/promises";
19073
19371
  // src/cli/make-config/v2-edits.ts
19074
19372
  init_apitogo_config_core();
19075
19373
  import { access as access8, mkdir as mkdir15, readFile as readFile23, writeFile as writeFile18 } from "node:fs/promises";
19076
- import path62 from "node:path";
19374
+ import path63 from "node:path";
19077
19375
  import { glob as glob8 } from "glob";
19078
19376
  init_invariant();
19079
19377
  var ConfigEditError = class extends ApitogoError {
@@ -19086,10 +19384,10 @@ var exists4 = (target) => access8(target).then(
19086
19384
  () => true,
19087
19385
  () => false
19088
19386
  );
19089
- var toPosix2 = (value) => value.split(path62.sep).join("/");
19090
- var relativeToConfig = (project, target) => toPosix2(path62.relative(project.resolved.configDir, target)) || ".";
19387
+ var toPosix2 = (value) => value.split(path63.sep).join("/");
19388
+ var relativeToConfig = (project, target) => toPosix2(path63.relative(project.resolved.configDir, target)) || ".";
19091
19389
  async function openConfigProject(dir) {
19092
- const configPath = path62.join(dir, APITOGO_CONFIG_FILENAME);
19390
+ const configPath = path63.join(dir, APITOGO_CONFIG_FILENAME);
19093
19391
  if (!await exists4(configPath)) {
19094
19392
  throw new ConfigEditError(
19095
19393
  `There is no ${APITOGO_CONFIG_FILENAME} in ${dir}.`,
@@ -19154,7 +19452,7 @@ function setPortalName(project, name) {
19154
19452
  }
19155
19453
  async function addGatewaySpec(project, specPath) {
19156
19454
  const gateway = requireGateway(project);
19157
- const absolute = path62.resolve(project.dir, specPath);
19455
+ const absolute = path63.resolve(project.dir, specPath);
19158
19456
  if (!await exists4(absolute)) {
19159
19457
  throw new ConfigEditError(`The OpenAPI file ${specPath} does not exist.`);
19160
19458
  }
@@ -19198,7 +19496,7 @@ function parseBackendInput(input5) {
19198
19496
  }
19199
19497
  async function setGatewayOrigin(project, backend) {
19200
19498
  const gateway = requireGateway(project);
19201
- const source = path62.resolve(project.dir, backend.source);
19499
+ const source = path63.resolve(project.dir, backend.source);
19202
19500
  if (!await exists4(source)) {
19203
19501
  throw new ConfigEditError(
19204
19502
  `The API source folder ${backend.source} does not exist.`
@@ -19299,13 +19597,13 @@ async function writeDocsPage(project, input5) {
19299
19597
  "For example --page-order 1 puts the page first in the sidebar."
19300
19598
  );
19301
19599
  }
19302
- const base = path62.join(portal.dir, "pages", ...slug.split("/"));
19600
+ const base = path63.join(portal.dir, "pages", ...slug.split("/"));
19303
19601
  const existing = (await Promise.all(
19304
19602
  [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
19305
19603
  async (file2) => await exists4(file2) ? file2 : null
19306
19604
  )
19307
19605
  )).find((file2) => file2 !== null);
19308
- const relative = (file2) => toPosix2(path62.relative(project.dir, file2));
19606
+ const relative = (file2) => toPosix2(path63.relative(project.dir, file2));
19309
19607
  const write = (file2, title, order = input5.order) => writeFile18(
19310
19608
  file2,
19311
19609
  file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content, order) : buildDocsPage(title, input5.content, order),
@@ -19339,17 +19637,17 @@ async function writeDocsPage(project, input5) {
19339
19637
  }
19340
19638
  file = `${base}.${input5.format ?? "tsx"}`;
19341
19639
  action = "added";
19342
- await mkdir15(path62.dirname(file), { recursive: true });
19640
+ await mkdir15(path63.dirname(file), { recursive: true });
19343
19641
  await write(file, title);
19344
19642
  }
19345
- const same = (match) => path62.resolve(match) === path62.resolve(file);
19643
+ const same = (match) => path63.resolve(match) === path63.resolve(file);
19346
19644
  const inPortal = file.endsWith(".tsx") ? (await findTsxPages(portal.docs.content)).some((page) => same(page.file)) : (await glob8(portal.docs.content, { absolute: true })).some(same);
19347
19645
  return {
19348
19646
  action,
19349
19647
  slug,
19350
19648
  route: `/${slug}`,
19351
19649
  file: relative(file),
19352
- format: path62.extname(file).slice(1),
19650
+ format: path63.extname(file).slice(1),
19353
19651
  inPortal
19354
19652
  };
19355
19653
  }
@@ -19428,7 +19726,7 @@ async function readPlanCatalog(project) {
19428
19726
  }
19429
19727
  var skuOf = (item) => isPlainObject3(item) && typeof item.sku === "string" ? item.sku.trim().toLowerCase() : void 0;
19430
19728
  async function writePlanCatalog(file, catalog, plans) {
19431
- await mkdir15(path62.dirname(file), { recursive: true });
19729
+ await mkdir15(path63.dirname(file), { recursive: true });
19432
19730
  await writeFile18(
19433
19731
  file,
19434
19732
  `${JSON.stringify({ ...catalog, plans }, null, 2)}
@@ -19451,7 +19749,7 @@ async function upsertPlan(project, plan2) {
19451
19749
  return {
19452
19750
  action: index === -1 ? "added" : "updated",
19453
19751
  sku: plan2.sku,
19454
- file: toPosix2(path62.relative(project.dir, file)),
19752
+ file: toPosix2(path63.relative(project.dir, file)),
19455
19753
  paid,
19456
19754
  needsBilling: paid && billing.provider !== "stripe"
19457
19755
  };
@@ -19541,12 +19839,12 @@ async function removePlan(project, rawSku) {
19541
19839
  if (next.length === catalog.plans.length) {
19542
19840
  const known = catalog.plans.map(skuOf).filter(Boolean).join(", ");
19543
19841
  throw new ConfigEditError(
19544
- `There is no plan "${sku}" in ${toPosix2(path62.relative(project.dir, file))}.`,
19842
+ `There is no plan "${sku}" in ${toPosix2(path63.relative(project.dir, file))}.`,
19545
19843
  known ? `The plans are: ${known}.` : "The plans file has no plans."
19546
19844
  );
19547
19845
  }
19548
19846
  await writePlanCatalog(file, catalog, next);
19549
- return { sku, file: toPosix2(path62.relative(project.dir, file)) };
19847
+ return { sku, file: toPosix2(path63.relative(project.dir, file)) };
19550
19848
  }
19551
19849
 
19552
19850
  // src/cli/make-config/wrap-up.ts
@@ -19827,7 +20125,7 @@ async function applyConfigFlags(project, argv, changes) {
19827
20125
  return { project: next, openApi };
19828
20126
  }
19829
20127
  async function makeConfig(argv) {
19830
- const dir = path63.resolve(process.cwd(), argv.dir);
20128
+ const dir = path64.resolve(process.cwd(), argv.dir);
19831
20129
  const interactive = shouldRunInteractive(argv);
19832
20130
  const json = argv.json === true;
19833
20131
  const changes = [];
@@ -20078,27 +20376,27 @@ var config_default = {
20078
20376
  init_apitogo_config_core();
20079
20377
  import { execFile } from "node:child_process";
20080
20378
  import { readFile as readFile24 } from "node:fs/promises";
20081
- import path65 from "node:path";
20379
+ import path66 from "node:path";
20082
20380
  import { promisify } from "node:util";
20083
20381
 
20084
20382
  // src/cli/create/dry-run.ts
20085
20383
  import { mkdir as mkdir16, mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
20086
20384
  import { tmpdir } from "node:os";
20087
- import path64 from "node:path";
20385
+ import path65 from "node:path";
20088
20386
  async function listFiles(root, dir = root) {
20089
20387
  const entries = await readdir4(dir, { withFileTypes: true });
20090
20388
  const nested = await Promise.all(
20091
20389
  entries.map(async (entry) => {
20092
- const full = path64.join(dir, entry.name);
20390
+ const full = path65.join(dir, entry.name);
20093
20391
  if (entry.isDirectory()) return listFiles(root, full);
20094
- return [path64.relative(root, full).split(path64.sep).join("/")];
20392
+ return [path65.relative(root, full).split(path65.sep).join("/")];
20095
20393
  })
20096
20394
  );
20097
20395
  return nested.flat();
20098
20396
  }
20099
20397
  async function previewCreate(input5) {
20100
- const scratch = await mkdtemp(path64.join(tmpdir(), "apitogo-create-"));
20101
- const dir = path64.join(scratch, "project");
20398
+ const scratch = await mkdtemp(path65.join(tmpdir(), "apitogo-create-"));
20399
+ const dir = path65.join(scratch, "project");
20102
20400
  try {
20103
20401
  await mkdir16(dir);
20104
20402
  await applyInitPlan(await planInit({ dir, name: input5.name }));
@@ -20131,9 +20429,9 @@ async function previewCreate(input5) {
20131
20429
  var execFileAsync = promisify(execFile);
20132
20430
  function nextStepHint(dir) {
20133
20431
  const linked = loadConfigV2(dir).resolved.project.id;
20134
- const cd = path65.resolve(dir) === path65.resolve(process.cwd());
20432
+ const cd = path66.resolve(dir) === path66.resolve(process.cwd());
20135
20433
  const steps = linked ? "apitogo deploy" : "apitogo project create, then apitogo deploy";
20136
- return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path65.relative(process.cwd(), dir))}, then ${steps}`;
20434
+ return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path66.relative(process.cwd(), dir))}, then ${steps}`;
20137
20435
  }
20138
20436
  function projectFolderName(name) {
20139
20437
  return name.trim().replace(/[<>:"/\\|?*\s]+/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "apitogo-project";
@@ -20159,13 +20457,13 @@ async function gitHasUncommittedChanges(dir) {
20159
20457
  async function defaultName(dir) {
20160
20458
  try {
20161
20459
  const parsed = JSON.parse(
20162
- await readFile24(path65.join(dir, "package.json"), "utf8")
20460
+ await readFile24(path66.join(dir, "package.json"), "utf8")
20163
20461
  );
20164
20462
  if (typeof parsed.name === "string" && parsed.name.trim())
20165
20463
  return parsed.name.replace(/^@[^/]+\//, "");
20166
20464
  } catch {
20167
20465
  }
20168
- return path65.basename(dir);
20466
+ return path66.basename(dir);
20169
20467
  }
20170
20468
  function parseServicesFlag(value) {
20171
20469
  const names = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
@@ -20191,7 +20489,7 @@ async function createProject(argv, deps = {}) {
20191
20489
  if (!json) printDiagnosticsToConsole(line2);
20192
20490
  };
20193
20491
  try {
20194
- const base = path65.resolve(process.cwd(), argv.dir ?? ".");
20492
+ const base = path66.resolve(process.cwd(), argv.dir ?? ".");
20195
20493
  let dir = base;
20196
20494
  let requested = parseServicesFlag(argv.services);
20197
20495
  let template = argv.template;
@@ -20214,8 +20512,8 @@ async function createProject(argv, deps = {}) {
20214
20512
  default: await defaultName(base)
20215
20513
  }) : await defaultName(base));
20216
20514
  const folder = projectFolderName(name);
20217
- if (argv.dir === void 0 && folder !== path65.basename(base))
20218
- dir = path65.join(base, folder);
20515
+ if (argv.dir === void 0 && folder !== path66.basename(base))
20516
+ dir = path66.join(base, folder);
20219
20517
  }
20220
20518
  if (await (deps.hasUncommittedChanges ?? gitHasUncommittedChanges)(dir))
20221
20519
  say(
@@ -20228,7 +20526,7 @@ async function createProject(argv, deps = {}) {
20228
20526
  if (argv.dryRun) {
20229
20527
  const services = requested.length > 0 ? requested : templateServices ? STARTER_SERVICES : [];
20230
20528
  const preview2 = services.length > 0 ? await previewCreate({ name, template, services }) : void 0;
20231
- const folder = path65.relative(process.cwd(), dir) || ".";
20529
+ const folder = path66.relative(process.cwd(), dir) || ".";
20232
20530
  if (json)
20233
20531
  console.log(
20234
20532
  JSON.stringify({
@@ -20261,7 +20559,7 @@ async function createProject(argv, deps = {}) {
20261
20559
  }
20262
20560
  await applyInitPlan(plan2);
20263
20561
  say(
20264
- dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path65.relative(base, dir)}${path65.sep}${APITOGO_CONFIG_FILENAME}.`
20562
+ dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path66.relative(base, dir)}${path66.sep}${APITOGO_CONFIG_FILENAME}.`
20265
20563
  );
20266
20564
  } else {
20267
20565
  say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
@@ -20403,10 +20701,10 @@ var create_default = {
20403
20701
  };
20404
20702
 
20405
20703
  // src/cli/cmds/deploy.ts
20406
- import path84 from "node:path";
20704
+ import path85 from "node:path";
20407
20705
 
20408
20706
  // src/cli/apply/handler.ts
20409
- import path77 from "node:path";
20707
+ import path78 from "node:path";
20410
20708
 
20411
20709
  // src/cli/platform/helpers/gateway-plan-validation.ts
20412
20710
  init_gateway_limits();
@@ -20625,11 +20923,11 @@ init_gateway_limits();
20625
20923
  // src/cli/platform/helpers/deploy-artifact-hash.ts
20626
20924
  import { createHash as createHash3 } from "node:crypto";
20627
20925
  import { createReadStream, promises as fs6 } from "node:fs";
20628
- import path67 from "node:path";
20926
+ import path68 from "node:path";
20629
20927
 
20630
20928
  // src/cli/platform/helpers/package-files.ts
20631
20929
  import { promises as fs5 } from "node:fs";
20632
- import path66 from "node:path";
20930
+ import path67 from "node:path";
20633
20931
  import ignore from "ignore";
20634
20932
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
20635
20933
  "node_modules",
@@ -20643,7 +20941,7 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20643
20941
  const ig = ignore();
20644
20942
  ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
20645
20943
  if (extraExcludes.length > 0) ig.add([...extraExcludes]);
20646
- const gitignorePath = path66.join(rootDir, ".gitignore");
20944
+ const gitignorePath = path67.join(rootDir, ".gitignore");
20647
20945
  try {
20648
20946
  const content = await fs5.readFile(gitignorePath, "utf8");
20649
20947
  ig.add(content);
@@ -20652,11 +20950,11 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20652
20950
  return ig;
20653
20951
  }
20654
20952
  async function collectFiles(rootDir, relativeDir, ig, files) {
20655
- const absDir = path66.join(rootDir, relativeDir);
20953
+ const absDir = path67.join(rootDir, relativeDir);
20656
20954
  const entries = await fs5.readdir(absDir, { withFileTypes: true });
20657
20955
  for (const entry of entries) {
20658
- const rel2 = relativeDir ? path66.join(relativeDir, entry.name) : entry.name;
20659
- const relPosix = rel2.split(path66.sep).join("/");
20956
+ const rel2 = relativeDir ? path67.join(relativeDir, entry.name) : entry.name;
20957
+ const relPosix = rel2.split(path67.sep).join("/");
20660
20958
  if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
20661
20959
  continue;
20662
20960
  }
@@ -20671,7 +20969,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
20671
20969
  }
20672
20970
  }
20673
20971
  async function collectPackageableFiles(sourceDirectory, extraExcludes = []) {
20674
- const resolved = path66.resolve(sourceDirectory);
20972
+ const resolved = path67.resolve(sourceDirectory);
20675
20973
  const stat7 = await fs5.stat(resolved).catch(() => null);
20676
20974
  if (!stat7?.isDirectory()) {
20677
20975
  throw new Error(`sourceDirectory is not a directory: ${resolved}`);
@@ -20697,7 +20995,7 @@ async function sha256File(filePath) {
20697
20995
  return hash2.digest("hex");
20698
20996
  }
20699
20997
  async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20700
- const resolved = path67.resolve(rootDirectory);
20998
+ const resolved = path68.resolve(rootDirectory);
20701
20999
  const files = (await collectPackageableFiles(resolved, extraExcludes)).sort(
20702
21000
  (a, b) => a.localeCompare(b)
20703
21001
  );
@@ -20705,13 +21003,13 @@ async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20705
21003
  for (const rel2 of files) {
20706
21004
  hash2.update(rel2);
20707
21005
  hash2.update("\0");
20708
- hash2.update(await fs6.readFile(path67.join(resolved, rel2)));
21006
+ hash2.update(await fs6.readFile(path68.join(resolved, rel2)));
20709
21007
  hash2.update("\0");
20710
21008
  }
20711
21009
  return hash2.digest("hex");
20712
21010
  }
20713
21011
  async function hashDevPortalSiteSources(siteDirectory, options) {
20714
- const resolved = path67.resolve(siteDirectory);
21012
+ const resolved = path68.resolve(siteDirectory);
20715
21013
  const hash2 = createHash3("sha256");
20716
21014
  const candidates = [
20717
21015
  "apitogo.config.json",
@@ -20721,7 +21019,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20721
21019
  "zudoku.config.tsx"
20722
21020
  ];
20723
21021
  for (const name of candidates) {
20724
- const full = path67.join(resolved, name);
21022
+ const full = path68.join(resolved, name);
20725
21023
  try {
20726
21024
  hash2.update(name);
20727
21025
  hash2.update("\0");
@@ -20731,7 +21029,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20731
21029
  }
20732
21030
  }
20733
21031
  for (const dir of ["pages", "landing", "src"]) {
20734
- const full = path67.join(resolved, dir);
21032
+ const full = path68.join(resolved, dir);
20735
21033
  try {
20736
21034
  const st = await fs6.stat(full);
20737
21035
  if (!st.isDirectory()) continue;
@@ -20960,8 +21258,8 @@ function normalizeActionKey(actionKey2) {
20960
21258
  const trimmed = actionKey2.trim();
20961
21259
  if (trimmed.includes(":")) {
20962
21260
  const [method2, ...pathParts] = trimmed.split(":");
20963
- const path99 = pathParts.join(":").trim();
20964
- return `${method2?.toLowerCase()}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21261
+ const path100 = pathParts.join(":").trim();
21262
+ return `${method2?.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
20965
21263
  }
20966
21264
  const spaceIndex = trimmed.indexOf(" ");
20967
21265
  if (spaceIndex <= 0) {
@@ -20970,8 +21268,8 @@ function normalizeActionKey(actionKey2) {
20970
21268
  );
20971
21269
  }
20972
21270
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
20973
- const path98 = trimmed.slice(spaceIndex + 1).trim();
20974
- return `${method}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
21271
+ const path99 = trimmed.slice(spaceIndex + 1).trim();
21272
+ return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
20975
21273
  }
20976
21274
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
20977
21275
  const applied = [];
@@ -21450,8 +21748,8 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
21450
21748
 
21451
21749
  // src/cli/platform/helpers/openapi-gateway-servers.ts
21452
21750
  import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile19 } from "node:fs/promises";
21453
- import path68 from "node:path";
21454
- import YAML from "yaml";
21751
+ import path69 from "node:path";
21752
+ import YAML2 from "yaml";
21455
21753
  var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
21456
21754
  var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
21457
21755
  function normalizeGatewayUrl(gatewayUrl) {
@@ -21482,21 +21780,21 @@ async function resolveProjectGatewayUrl(client, projectId) {
21482
21780
  }
21483
21781
  async function readOpenApiDocument(filePath) {
21484
21782
  const content = await readFile25(filePath, "utf8");
21485
- const ext = path68.extname(filePath).toLowerCase();
21486
- const parsed = ext === ".json" ? JSON.parse(content) : YAML.parse(content);
21783
+ const ext = path69.extname(filePath).toLowerCase();
21784
+ const parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
21487
21785
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21488
21786
  throw new Error(`OpenAPI document must be an object: ${filePath}`);
21489
21787
  }
21490
21788
  return parsed;
21491
21789
  }
21492
21790
  async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
21493
- const site = path68.resolve(siteDirectory);
21494
- const source = path68.resolve(sourceOpenApiPath);
21791
+ const site = path69.resolve(siteDirectory);
21792
+ const source = path69.resolve(sourceOpenApiPath);
21495
21793
  const relativeInput = PUBLISH_OPENAPI_RELATIVE;
21496
- const outputPath = path68.join(site, ...relativeInput.split("/"));
21794
+ const outputPath = path69.join(site, ...relativeInput.split("/"));
21497
21795
  const doc = await readOpenApiDocument(source);
21498
21796
  const rewritten = withGatewayServers(doc, gatewayUrl);
21499
- await mkdir17(path68.dirname(outputPath), { recursive: true });
21797
+ await mkdir17(path69.dirname(outputPath), { recursive: true });
21500
21798
  await writeFile19(
21501
21799
  outputPath,
21502
21800
  `${JSON.stringify(rewritten, null, 2)}
@@ -21506,8 +21804,8 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
21506
21804
  return { outputPath, relativeInput };
21507
21805
  }
21508
21806
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21509
- const configPath = path68.join(
21510
- path68.resolve(siteDirectory),
21807
+ const configPath = path69.join(
21808
+ path69.resolve(siteDirectory),
21511
21809
  APITOGO_CONFIG_FILENAME2
21512
21810
  );
21513
21811
  const raw = await readFile25(configPath, "utf8");
@@ -21549,7 +21847,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21549
21847
  };
21550
21848
  }
21551
21849
  async function prepareOpenApiServersForGatewayBuild(options) {
21552
- const siteDirectory = path68.resolve(options.siteDirectory);
21850
+ const siteDirectory = path69.resolve(options.siteDirectory);
21553
21851
  let openApiPath = resolveBackendOpenApiPath(
21554
21852
  options.manifest?.backend?.openApiPath,
21555
21853
  {
@@ -21563,7 +21861,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
21563
21861
  if (!openApiPath) {
21564
21862
  try {
21565
21863
  const raw = await readFile25(
21566
- path68.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21864
+ path69.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21567
21865
  "utf8"
21568
21866
  );
21569
21867
  const config2 = JSON.parse(raw);
@@ -21592,8 +21890,8 @@ function normalizeActionKeyFormat(actionKey2) {
21592
21890
  const trimmed = actionKey2.trim();
21593
21891
  if (trimmed.includes(":")) {
21594
21892
  const [method2, ...pathParts] = trimmed.split(":");
21595
- const path99 = pathParts.join(":").trim();
21596
- return `${method2.toLowerCase()}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21893
+ const path100 = pathParts.join(":").trim();
21894
+ return `${method2.toLowerCase()}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
21597
21895
  }
21598
21896
  const spaceIndex = trimmed.indexOf(" ");
21599
21897
  if (spaceIndex <= 0) {
@@ -21602,8 +21900,8 @@ function normalizeActionKeyFormat(actionKey2) {
21602
21900
  );
21603
21901
  }
21604
21902
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21605
- const path98 = trimmed.slice(spaceIndex + 1).trim();
21606
- return `${method}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
21903
+ const path99 = trimmed.slice(spaceIndex + 1).trim();
21904
+ return `${method}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21607
21905
  }
21608
21906
  function normalizePublicRoute(route) {
21609
21907
  const trimmed = route.trim();
@@ -21637,10 +21935,10 @@ function collectManifestPublicActionKeys(gateway) {
21637
21935
 
21638
21936
  // src/cli/platform/helpers/publish-ledger.ts
21639
21937
  import { mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
21640
- import path69 from "node:path";
21938
+ import path70 from "node:path";
21641
21939
  var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
21642
21940
  function publishLedgerPath(siteDirectory) {
21643
- return path69.join(path69.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21941
+ return path70.join(path70.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21644
21942
  }
21645
21943
  async function readPublishLedger(siteDirectory) {
21646
21944
  const filePath = publishLedgerPath(siteDirectory);
@@ -21657,7 +21955,7 @@ async function readPublishLedger(siteDirectory) {
21657
21955
  }
21658
21956
  async function writePublishLedger(siteDirectory, ledger) {
21659
21957
  const filePath = publishLedgerPath(siteDirectory);
21660
- await mkdir18(path69.dirname(filePath), { recursive: true });
21958
+ await mkdir18(path70.dirname(filePath), { recursive: true });
21661
21959
  const next = {
21662
21960
  ...ledger,
21663
21961
  version: 1,
@@ -22178,7 +22476,7 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
22178
22476
  // src/cli/platform/helpers/backend-packaging.ts
22179
22477
  import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10 } from "node:fs/promises";
22180
22478
  import { tmpdir as tmpdir2 } from "node:os";
22181
- import path70 from "node:path";
22479
+ import path71 from "node:path";
22182
22480
  async function directoryHasRuntimeConfig(dir) {
22183
22481
  if (!await pathExists(dir)) {
22184
22482
  return false;
@@ -22220,20 +22518,20 @@ function runDotnetPublish(projectDir, outputDir) {
22220
22518
  async function findCsproj(projectDir) {
22221
22519
  const entries = await readdir5(projectDir);
22222
22520
  const csproj = entries.find((name) => name.endsWith(".csproj"));
22223
- return csproj ? path70.join(projectDir, csproj) : null;
22521
+ return csproj ? path71.join(projectDir, csproj) : null;
22224
22522
  }
22225
22523
  async function prepareBackendPackagingDirectory(args) {
22226
- const sourceDirectory = path70.resolve(args.sourceDirectory);
22524
+ const sourceDirectory = path71.resolve(args.sourceDirectory);
22227
22525
  if (args.publishDirectory?.trim()) {
22228
22526
  return {
22229
- directory: path70.resolve(args.publishDirectory),
22527
+ directory: path71.resolve(args.publishDirectory),
22230
22528
  packagingMode: "published"
22231
22529
  };
22232
22530
  }
22233
22531
  if (await directoryHasRuntimeConfig(sourceDirectory)) {
22234
22532
  return { directory: sourceDirectory, packagingMode: "published" };
22235
22533
  }
22236
- const publishSubdir = path70.join(sourceDirectory, "publish");
22534
+ const publishSubdir = path71.join(sourceDirectory, "publish");
22237
22535
  if (await directoryHasRuntimeConfig(publishSubdir)) {
22238
22536
  return { directory: publishSubdir, packagingMode: "published" };
22239
22537
  }
@@ -22245,9 +22543,9 @@ async function prepareBackendPackagingDirectory(args) {
22245
22543
  return { directory: sourceDirectory, packagingMode: "source" };
22246
22544
  }
22247
22545
  const tempRoot = await mkdtemp2(
22248
- path70.join(tmpdir2(), "apitogo-dotnet-publish-")
22546
+ path71.join(tmpdir2(), "apitogo-dotnet-publish-")
22249
22547
  );
22250
- const outputDir = path70.join(tempRoot, "publish");
22548
+ const outputDir = path71.join(tempRoot, "publish");
22251
22549
  const result = await runDotnetPublish(sourceDirectory, outputDir);
22252
22550
  if (result.exitCode !== 0) {
22253
22551
  await rm10(tempRoot, { recursive: true, force: true });
@@ -22292,16 +22590,16 @@ function formatJobStatusResponse(options) {
22292
22590
 
22293
22591
  // src/cli/platform/helpers/openapi.ts
22294
22592
  import { promises as fs7 } from "node:fs";
22295
- import path71 from "node:path";
22593
+ import path72 from "node:path";
22296
22594
  import SwaggerParser from "@apidevtools/swagger-parser";
22297
- import YAML2 from "yaml";
22595
+ import YAML3 from "yaml";
22298
22596
  async function validateOpenApiFile(filePath) {
22299
- const resolved = path71.resolve(filePath);
22597
+ const resolved = path72.resolve(filePath);
22300
22598
  const content = await fs7.readFile(resolved, "utf8");
22301
- const ext = path71.extname(resolved).toLowerCase();
22599
+ const ext = path72.extname(resolved).toLowerCase();
22302
22600
  let parsed;
22303
22601
  try {
22304
- parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
22602
+ parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
22305
22603
  } catch (err) {
22306
22604
  return {
22307
22605
  valid: false,
@@ -22342,12 +22640,12 @@ async function validateOpenApiFile(filePath) {
22342
22640
  // src/cli/platform/helpers/zip.ts
22343
22641
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs8 } from "node:fs";
22344
22642
  import { tmpdir as tmpdir3 } from "node:os";
22345
- import path72 from "node:path";
22643
+ import path73 from "node:path";
22346
22644
  import { ZipArchive } from "archiver";
22347
22645
  async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22348
- const resolved = path72.resolve(sourceDirectory);
22646
+ const resolved = path73.resolve(sourceDirectory);
22349
22647
  const files = await collectPackageableFiles(resolved, extraExcludes);
22350
- const zipPath = path72.join(
22648
+ const zipPath = path73.join(
22351
22649
  tmpdir3(),
22352
22650
  `apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
22353
22651
  );
@@ -22359,8 +22657,8 @@ async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
22359
22657
  output4.on("error", reject);
22360
22658
  archive.pipe(output4);
22361
22659
  for (const rel2 of files) {
22362
- archive.append(createReadStream2(path72.join(resolved, rel2)), {
22363
- name: rel2.split(path72.sep).join("/")
22660
+ archive.append(createReadStream2(path73.join(resolved, rel2)), {
22661
+ name: rel2.split(path73.sep).join("/")
22364
22662
  });
22365
22663
  }
22366
22664
  void archive.finalize();
@@ -23930,11 +24228,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
23930
24228
  }
23931
24229
 
23932
24230
  // src/cli/platform/tools/dev-portal-site-build.ts
23933
- import path76 from "node:path";
24231
+ import path77 from "node:path";
23934
24232
 
23935
24233
  // src/cli/platform/helpers/dev-portal-site-api-url.ts
23936
24234
  import { readdir as readdir6, readFile as readFile27, writeFile as writeFile21 } from "node:fs/promises";
23937
- import path73 from "node:path";
24235
+ import path74 from "node:path";
23938
24236
  var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
23939
24237
  var PLACEHOLDER_HOST_MARKERS = [
23940
24238
  "dp-example.azurecontainerapps.io",
@@ -23955,10 +24253,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
23955
24253
  return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
23956
24254
  }
23957
24255
  async function readSiteApiBaseUrl(sourceDirectory) {
23958
- const resolved = path73.resolve(sourceDirectory);
24256
+ const resolved = path74.resolve(sourceDirectory);
23959
24257
  for (const envFile of [".env", ".env.production"]) {
23960
24258
  try {
23961
- const content = await readFile27(path73.join(resolved, envFile), "utf8");
24259
+ const content = await readFile27(path74.join(resolved, envFile), "utf8");
23962
24260
  const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
23963
24261
  if (lines.length === 0) {
23964
24262
  continue;
@@ -23983,11 +24281,11 @@ async function readSiteApiBaseUrl(sourceDirectory) {
23983
24281
  return null;
23984
24282
  }
23985
24283
  async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
23986
- const resolved = path73.resolve(sourceDirectory);
24284
+ const resolved = path74.resolve(sourceDirectory);
23987
24285
  const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
23988
24286
  let patchedExisting = false;
23989
24287
  for (const envFile of [".env.production", ".env"]) {
23990
- const envPath = path73.join(resolved, envFile);
24288
+ const envPath = path74.join(resolved, envFile);
23991
24289
  try {
23992
24290
  const content = await readFile27(envPath, "utf8");
23993
24291
  const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
@@ -24001,7 +24299,7 @@ ${line2}
24001
24299
  }
24002
24300
  }
24003
24301
  if (!patchedExisting) {
24004
- await writeFile21(path73.join(resolved, ".env"), `${line2}
24302
+ await writeFile21(path74.join(resolved, ".env"), `${line2}
24005
24303
  `);
24006
24304
  }
24007
24305
  }
@@ -24009,7 +24307,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
24009
24307
  return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
24010
24308
  }
24011
24309
  async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
24012
- const sourceDirectory = path73.resolve(args.sourceDirectory);
24310
+ const sourceDirectory = path74.resolve(args.sourceDirectory);
24013
24311
  const explicit = args.apiBaseUrl?.trim();
24014
24312
  if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
24015
24313
  await patchSiteApiBaseUrl(sourceDirectory, explicit);
@@ -24045,7 +24343,7 @@ async function* walkFiles(root) {
24045
24343
  return;
24046
24344
  }
24047
24345
  for (const entry of entries) {
24048
- const full = path73.join(root, entry.name);
24346
+ const full = path74.join(root, entry.name);
24049
24347
  if (entry.isDirectory()) {
24050
24348
  if (entry.name === "node_modules" || entry.name === ".git") {
24051
24349
  continue;
@@ -24057,8 +24355,8 @@ async function* walkFiles(root) {
24057
24355
  }
24058
24356
  }
24059
24357
  async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
24060
- const dist = path73.resolve(distDirectory);
24061
- const siteRoot = path73.dirname(dist);
24358
+ const dist = path74.resolve(distDirectory);
24359
+ const siteRoot = path74.dirname(dist);
24062
24360
  const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
24063
24361
  if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
24064
24362
  throw new Error(
@@ -24094,7 +24392,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
24094
24392
 
24095
24393
  // src/cli/platform/tools/dev-portal.ts
24096
24394
  import { promises as fs10 } from "node:fs";
24097
- import path75 from "node:path";
24395
+ import path76 from "node:path";
24098
24396
 
24099
24397
  // src/cli/platform/helpers/dev-portal-backend-job.ts
24100
24398
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
@@ -24204,7 +24502,7 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
24204
24502
  // src/cli/platform/helpers/tarball.ts
24205
24503
  import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs9 } from "node:fs";
24206
24504
  import { tmpdir as tmpdir4 } from "node:os";
24207
- import path74 from "node:path";
24505
+ import path75 from "node:path";
24208
24506
  import { TarArchive } from "archiver";
24209
24507
  var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
24210
24508
  function normalizeArchiveRoot(archiveRoot) {
@@ -24215,10 +24513,10 @@ function normalizeArchiveRoot(archiveRoot) {
24215
24513
  return `${trimmed}/`;
24216
24514
  }
24217
24515
  async function packageDirectoryAsTarGz(sourceDirectory, options) {
24218
- const resolved = path74.resolve(sourceDirectory);
24516
+ const resolved = path75.resolve(sourceDirectory);
24219
24517
  const files = await collectPackageableFiles(resolved);
24220
24518
  const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
24221
- const archivePath = path74.join(
24519
+ const archivePath = path75.join(
24222
24520
  tmpdir4(),
24223
24521
  `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
24224
24522
  );
@@ -24230,8 +24528,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
24230
24528
  output4.on("error", reject);
24231
24529
  archive.pipe(output4);
24232
24530
  for (const rel2 of files) {
24233
- const entryName = entryPrefix + rel2.split(path74.sep).join("/");
24234
- archive.append(createReadStream3(path74.join(resolved, rel2)), {
24531
+ const entryName = entryPrefix + rel2.split(path75.sep).join("/");
24532
+ archive.append(createReadStream3(path75.join(resolved, rel2)), {
24235
24533
  name: entryName
24236
24534
  });
24237
24535
  }
@@ -24473,7 +24771,7 @@ function validateDeployArchiveInput(args) {
24473
24771
  async function resolveDevPortalArchive(args) {
24474
24772
  validateDeployArchiveInput(args);
24475
24773
  if (args.archivePath?.trim()) {
24476
- const archivePath = path75.resolve(args.archivePath.trim());
24774
+ const archivePath = path76.resolve(args.archivePath.trim());
24477
24775
  const stat7 = await fs10.stat(archivePath).catch(() => null);
24478
24776
  if (!stat7?.isFile()) {
24479
24777
  throw new Error(`archivePath is not a file: ${archivePath}`);
@@ -24538,7 +24836,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
24538
24836
  // src/cli/platform/tools/dev-portal-site-build.ts
24539
24837
  async function handleBuildDevPortalSite(client, config2, args, progress) {
24540
24838
  const buildProgress = progress?.child("Build dev portal site");
24541
- const sourceDirectory = path76.resolve(args.sourceDirectory);
24839
+ const sourceDirectory = path77.resolve(args.sourceDirectory);
24542
24840
  await buildProgress?.onStep("Ensuring live portal API URL");
24543
24841
  const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
24544
24842
  client,
@@ -26044,7 +26342,7 @@ function resultLooksFailed(payload) {
26044
26342
  return parsed.status === "failed" || parsed.status === "blocked";
26045
26343
  }
26046
26344
  async function apply(argv) {
26047
- const sourceDirectory = path77.resolve(process.cwd(), argv.dir);
26345
+ const sourceDirectory = path78.resolve(process.cwd(), argv.dir);
26048
26346
  let config2;
26049
26347
  try {
26050
26348
  config2 = loadConfig();
@@ -26114,7 +26412,7 @@ async function apply(argv) {
26114
26412
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
26115
26413
  const resolution = await resolveProjectNotFoundInteractively(
26116
26414
  parsed,
26117
- path77.basename(sourceDirectory)
26415
+ path78.basename(sourceDirectory)
26118
26416
  );
26119
26417
  if (resolution) {
26120
26418
  effectiveProjectId = void 0;
@@ -26134,7 +26432,7 @@ async function apply(argv) {
26134
26432
  while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
26135
26433
  const resolution = await resolveProjectNameExistsInteractively(
26136
26434
  parsed,
26137
- effectiveProjectName ?? path77.basename(sourceDirectory)
26435
+ effectiveProjectName ?? path78.basename(sourceDirectory)
26138
26436
  );
26139
26437
  if (!resolution) {
26140
26438
  break;
@@ -26205,11 +26503,11 @@ async function apply(argv) {
26205
26503
  }
26206
26504
 
26207
26505
  // src/cli/deploy/command.ts
26208
- import path83 from "node:path";
26506
+ import path84 from "node:path";
26209
26507
  init_invariant();
26210
26508
 
26211
26509
  // src/cli/project/create-handler.ts
26212
- import path78 from "node:path";
26510
+ import path79 from "node:path";
26213
26511
  import open2 from "open";
26214
26512
  init_invariant();
26215
26513
 
@@ -26523,7 +26821,7 @@ async function createLinkedProject(argv, dir, options = {}) {
26523
26821
  }
26524
26822
  }
26525
26823
  async function createProject2(argv) {
26526
- const dir = path78.resolve(process.cwd(), argv.dir ?? ".");
26824
+ const dir = path79.resolve(process.cwd(), argv.dir ?? ".");
26527
26825
  if (await isConfigV2(dir)) {
26528
26826
  await createLinkedProject(argv, dir);
26529
26827
  return;
@@ -26583,7 +26881,7 @@ async function createProject2(argv) {
26583
26881
  }
26584
26882
 
26585
26883
  // src/cli/secrets/handler.ts
26586
- import path79 from "node:path";
26884
+ import path80 from "node:path";
26587
26885
  init_invariant();
26588
26886
 
26589
26887
  // src/cli/secrets/input.ts
@@ -26648,7 +26946,7 @@ var SecretsError = class extends ApitogoError {
26648
26946
  async function resolveProjectId2(argv, deps) {
26649
26947
  const explicit = argv.projectId?.trim();
26650
26948
  if (explicit) return explicit;
26651
- const dir = path79.resolve(process.cwd(), argv.dir ?? ".");
26949
+ const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
26652
26950
  if (await isConfigV2(dir)) {
26653
26951
  const id = loadConfigV2(dir).resolved.project.id;
26654
26952
  if (id) return id;
@@ -26733,7 +27031,7 @@ var KNOWN_SECRET_PLACES = {
26733
27031
  async function wireKnownSecret(argv, name) {
26734
27032
  const place = KNOWN_SECRET_PLACES[name.toUpperCase()];
26735
27033
  if (!place) return void 0;
26736
- const dir = path79.resolve(process.cwd(), argv.dir ?? ".");
27034
+ const dir = path80.resolve(process.cwd(), argv.dir ?? ".");
26737
27035
  if (!await isConfigV2(dir)) return void 0;
26738
27036
  const project = await openConfigProject(dir);
26739
27037
  const plans = project.resolved.plans;
@@ -26794,7 +27092,7 @@ async function secrets(argv) {
26794
27092
  }
26795
27093
 
26796
27094
  // src/cli/deploy/stripe-key.ts
26797
- import path80 from "node:path";
27095
+ import path81 from "node:path";
26798
27096
  init_invariant();
26799
27097
  var StripeKeyError = class extends ApitogoError {
26800
27098
  constructor(message, developerHint) {
@@ -26812,7 +27110,7 @@ async function ensureStripeKey(argv, deps) {
26812
27110
  if (!wantsPlans(argv.only)) return { status: "not-needed" };
26813
27111
  let resolved;
26814
27112
  try {
26815
- resolved = loadConfigV2(path80.resolve(process.cwd(), argv.dir)).resolved;
27113
+ resolved = loadConfigV2(path81.resolve(process.cwd(), argv.dir)).resolved;
26816
27114
  } catch {
26817
27115
  return { status: "not-needed" };
26818
27116
  }
@@ -26847,7 +27145,7 @@ async function ensureStripeKey(argv, deps) {
26847
27145
  if (!STRIPE_KEY.test(key))
26848
27146
  throw new StripeKeyError(
26849
27147
  "That is not a Stripe secret key. Nothing was deployed.",
26850
- "A Stripe secret key starts with sk_test_, sk_live_, rk_test_ or rk_live_. Find it in Stripe under Developers > API keys."
27148
+ "A Stripe secret key starts with sk_test_, sk_live_, rk_test_ or rk_live_. Find it in Stripe under Developers > API keys (https://docs.apitogo.com/docs/guides/stripe-setup)."
26851
27149
  );
26852
27150
  await client.setSecret(projectId, name, key);
26853
27151
  deps.print(`Stripe key saved as the project secret ${name}.`);
@@ -27688,11 +27986,11 @@ async function offerStripeWebhookSecret(output4, deps) {
27688
27986
  "Payments need one more step: Stripe must be able to tell your plans backend that a payment went through."
27689
27987
  );
27690
27988
  deps.print(
27691
- `1. In Stripe, open Developers > Webhooks and add an endpoint with the URL ${webhook.url}`
27989
+ `1. In Stripe, open Workbench > Webhooks, choose Add destination and send the events to a webhook endpoint with the URL ${webhook.url}`
27692
27990
  );
27693
27991
  deps.print(`2. Select the events: ${STRIPE_WEBHOOK_EVENTS2.join(", ")}`);
27694
27992
  deps.print(
27695
- "3. Open the endpoint, reveal its signing secret (starts with whsec_) and paste it here."
27993
+ "3. Open the destination, reveal its signing secret (starts with whsec_) and paste it here. Step by step, with pictures: https://docs.apitogo.com/docs/guides/stripe-setup"
27696
27994
  );
27697
27995
  const secret = (await deps.askHidden(
27698
27996
  "Stripe webhook signing secret (hidden, Enter to skip): "
@@ -27722,7 +28020,7 @@ init_invariant();
27722
28020
 
27723
28021
  // src/cli/portal/report.ts
27724
28022
  import { access as access9 } from "node:fs/promises";
27725
- import path81 from "node:path";
28023
+ import path82 from "node:path";
27726
28024
 
27727
28025
  // src/cli/deploy/identity.ts
27728
28026
  init_invariant();
@@ -27809,6 +28107,17 @@ function identityRequestBody(resolved, portalUrl) {
27809
28107
  `${portalUrl.replace(/\/+$/, "")}${PORTAL_CALLBACK_PATH}`
27810
28108
  );
27811
28109
  }
28110
+ function identityBrandingBody(resolved) {
28111
+ const branding = resolved.identity?.providerOptions.branding;
28112
+ const name = resolved.portal?.name ?? resolved.project.name;
28113
+ const logo = branding?.logo?.trim();
28114
+ return {
28115
+ ...name ? { name } : {},
28116
+ ...logo && /^https:\/\//i.test(logo) ? { logoUrl: logo } : {},
28117
+ ...branding?.color ? { color: branding.color.toLowerCase() } : {}
28118
+ };
28119
+ }
28120
+ var IDENTITY_BRANDING_STEP = "branding";
27812
28121
  function identityInputsHash(projectId, body) {
27813
28122
  return stableJson({ projectId, body });
27814
28123
  }
@@ -27950,8 +28259,30 @@ async function deployIdentity(resolved, projectId, deps, options = {}) {
27950
28259
  },
27951
28260
  stillValid: async () => (await client.getProjectIdentity(projectId))?.status === "Active"
27952
28261
  };
27953
- const wanted = normalizedConnections(resolved);
27954
28262
  const steps = [requestStep, activateStep];
28263
+ if (client.putProjectIdentityBranding) {
28264
+ const putBranding = client.putProjectIdentityBranding.bind(client);
28265
+ const branding = identityBrandingBody(resolved);
28266
+ steps.push({
28267
+ name: IDENTITY_BRANDING_STEP,
28268
+ hash: () => stableJson({ projectId, branding }),
28269
+ run: async () => {
28270
+ try {
28271
+ await putBranding(projectId, branding);
28272
+ } catch (error) {
28273
+ if (error instanceof PlatformApiError && error.status === 404)
28274
+ return { branding: "not supported by the platform yet" };
28275
+ throw error;
28276
+ }
28277
+ return {
28278
+ name: branding.name ?? "",
28279
+ logo: branding.logoUrl ? "set" : "none",
28280
+ color: branding.color ?? ""
28281
+ };
28282
+ }
28283
+ });
28284
+ }
28285
+ const wanted = normalizedConnections(resolved);
27955
28286
  if (wanted.length > 0 || (await recordedConnectionIds(resolved)).length > 0)
27956
28287
  steps.push(connectionsStep(resolved, projectId, client, wanted));
27957
28288
  const outcomes = await runPipeline(
@@ -27983,8 +28314,11 @@ async function deployIdentity(resolved, projectId, deps, options = {}) {
27983
28314
  const connectionsRan = outcomes.some(
27984
28315
  (outcome) => outcome.step === IDENTITY_CONNECTIONS_STEP && !outcome.skipped
27985
28316
  );
28317
+ const brandingRan = outcomes.some(
28318
+ (outcome) => outcome.step === IDENTITY_BRANDING_STEP && !outcome.skipped
28319
+ );
27986
28320
  return {
27987
- status: ran || connectionsRan ? "deployed" : "unchanged",
28321
+ status: ran || connectionsRan || brandingRan ? "deployed" : "unchanged",
27988
28322
  steps: outcomes.map((outcome) => ({
27989
28323
  step: outcome.step,
27990
28324
  skipped: outcome.skipped
@@ -28423,7 +28757,7 @@ async function planPortalService(resolved, ledger) {
28423
28757
  const hash2 = await portalBuildHash(resolved, prepared);
28424
28758
  const previous = stepOf(ledger, "portal", "build");
28425
28759
  const built = await exists5(
28426
- path81.join(resolved.state.buildDir, "index.html")
28760
+ path82.join(resolved.state.buildDir, "index.html")
28427
28761
  );
28428
28762
  if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
28429
28763
  return {
@@ -28809,7 +29143,7 @@ async function statusV2(dir, only, options = {}) {
28809
29143
  if (service === "portal") {
28810
29144
  const build2 = stepOf(ledger, "portal", "build");
28811
29145
  const built = await exists5(
28812
- path81.join(resolved.state.buildDir, "index.html")
29146
+ path82.join(resolved.state.buildDir, "index.html")
28813
29147
  );
28814
29148
  const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
28815
29149
  services.push({
@@ -28820,7 +29154,7 @@ async function statusV2(dir, only, options = {}) {
28820
29154
  ...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
28821
29155
  ...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
28822
29156
  ...state === "built" ? [
28823
- `output: ${path81.relative(resolved.configDir, resolved.state.buildDir)}`
29157
+ `output: ${path82.relative(resolved.configDir, resolved.state.buildDir)}`
28824
29158
  ] : [],
28825
29159
  ...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"],
28826
29160
  ...await hostingLine(resolved, manifest, options.hostingRemote)
@@ -28887,7 +29221,7 @@ function formatStatus(output4) {
28887
29221
 
28888
29222
  // src/cli/deploy/static-web-config.ts
28889
29223
  import { access as access10, writeFile as writeFile22 } from "node:fs/promises";
28890
- import path82 from "node:path";
29224
+ import path83 from "node:path";
28891
29225
  var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
28892
29226
  var STATIC_WEB_CONFIG_VERSION = 3;
28893
29227
  var CLIENT_ONLY_ROUTES = [
@@ -28905,7 +29239,7 @@ var DEFAULT_STATIC_WEB_CONFIG = {
28905
29239
  }
28906
29240
  };
28907
29241
  async function ensureStaticWebConfig(buildDir) {
28908
- const file = path82.join(buildDir, STATIC_WEB_CONFIG_FILE);
29242
+ const file = path83.join(buildDir, STATIC_WEB_CONFIG_FILE);
28909
29243
  try {
28910
29244
  await access10(file);
28911
29245
  return false;
@@ -29260,7 +29594,7 @@ function formatDeploy(output4) {
29260
29594
  // src/cli/deploy/command.ts
29261
29595
  async function ensureLinkedProject(argv, deps) {
29262
29596
  if (argv.projectId?.trim() || deps.envProjectId?.trim()) return true;
29263
- const dir = path83.resolve(process.cwd(), argv.dir);
29597
+ const dir = path84.resolve(process.cwd(), argv.dir);
29264
29598
  let linked;
29265
29599
  try {
29266
29600
  linked = Boolean(loadConfigV2(dir).resolved.project.id);
@@ -29311,7 +29645,7 @@ async function deployProject(argv) {
29311
29645
  try {
29312
29646
  const config2 = loadConfig();
29313
29647
  const client = new PlatformApiClient(config2);
29314
- const dir = path83.resolve(process.cwd(), argv.dir);
29648
+ const dir = path84.resolve(process.cwd(), argv.dir);
29315
29649
  const deploy = (only) => deployV2(
29316
29650
  {
29317
29651
  dir,
@@ -29419,7 +29753,7 @@ var deploy_default = {
29419
29753
  }),
29420
29754
  handler: async (argv) => {
29421
29755
  await captureEvent({ argv, event: "apitogo deploy" });
29422
- if (await isConfigV2(path84.resolve(process.cwd(), argv.dir))) {
29756
+ if (await isConfigV2(path85.resolve(process.cwd(), argv.dir))) {
29423
29757
  await deployProject(argv);
29424
29758
  return;
29425
29759
  }
@@ -29429,7 +29763,7 @@ var deploy_default = {
29429
29763
 
29430
29764
  // src/cli/export/handler.ts
29431
29765
  import { mkdir as mkdir19, writeFile as writeFile23 } from "node:fs/promises";
29432
- import path85 from "node:path";
29766
+ import path86 from "node:path";
29433
29767
  init_invariant();
29434
29768
  var ExportError = class extends ApitogoError {
29435
29769
  constructor(message, developerHint) {
@@ -29443,7 +29777,7 @@ async function exportOpenApi(args, deps) {
29443
29777
  `Cannot export "${args.target}".`,
29444
29778
  "Only `apitogo export openapi` exists."
29445
29779
  );
29446
- const dir = path85.resolve(process.cwd(), args.dir ?? ".");
29780
+ const dir = path86.resolve(process.cwd(), args.dir ?? ".");
29447
29781
  if (!await isConfigV2(dir))
29448
29782
  throw new ExportError(
29449
29783
  "This command needs a config version 2 project.",
@@ -29468,8 +29802,8 @@ async function exportOpenApi(args, deps) {
29468
29802
  throw error;
29469
29803
  }
29470
29804
  if (!args.out) return { text: text2 };
29471
- const file = path85.resolve(process.cwd(), args.out);
29472
- await mkdir19(path85.dirname(file), { recursive: true });
29805
+ const file = path86.resolve(process.cwd(), args.out);
29806
+ await mkdir19(path86.dirname(file), { recursive: true });
29473
29807
  await writeFile23(file, text2);
29474
29808
  return { text: text2, file };
29475
29809
  }
@@ -29562,8 +29896,8 @@ import open3 from "open";
29562
29896
  import colors14 from "picocolors";
29563
29897
 
29564
29898
  // src/cli/platform/device-auth.ts
29565
- function url(apiUrl, path98) {
29566
- return `${apiUrl}${path98.startsWith("/") ? path98 : `/${path98}`}`;
29899
+ function url(apiUrl, path99) {
29900
+ return `${apiUrl}${path99.startsWith("/") ? path99 : `/${path99}`}`;
29567
29901
  }
29568
29902
  async function requestDeviceCode(apiUrl) {
29569
29903
  const response = await fetchWithRetry(
@@ -29745,7 +30079,7 @@ var logout_default = {
29745
30079
  };
29746
30080
 
29747
30081
  // src/cli/portal/commands.ts
29748
- import path86 from "node:path";
30082
+ import path87 from "node:path";
29749
30083
  init_invariant();
29750
30084
  function parseOnly(value) {
29751
30085
  if (value === void 0) return void 0;
@@ -29762,7 +30096,7 @@ function parseOnly(value) {
29762
30096
  async function run2(command2, argv, compute, format) {
29763
30097
  const json = argv.json === true;
29764
30098
  try {
29765
- const dir = path86.resolve(process.cwd(), argv.dir ?? ".");
30099
+ const dir = path87.resolve(process.cwd(), argv.dir ?? ".");
29766
30100
  if (!await isConfigV2(dir)) {
29767
30101
  throw new ServiceEditError(
29768
30102
  `${command2} works with a config version 2 project, and there is none in ${dir}.`,
@@ -29897,7 +30231,7 @@ var plan_default = {
29897
30231
  // src/cli/cmds/project-create.ts
29898
30232
  var project_create_default = {
29899
30233
  desc: "Create the API ToGo project for this folder: hosting for its services on the plan you choose, and project.id written into apitogo.config.json",
29900
- command: "project create",
30234
+ command: "create",
29901
30235
  builder: (yargs2) => yargs2.option("name", {
29902
30236
  type: "string",
29903
30237
  describe: "Project display name (default: project.name in apitogo.config.json)"
@@ -29947,6 +30281,143 @@ var project_create_default = {
29947
30281
  }
29948
30282
  };
29949
30283
 
30284
+ // src/cli/project/use.ts
30285
+ import { rm as rm12 } from "node:fs/promises";
30286
+ init_invariant();
30287
+ var ProjectUseError = class extends ApitogoError {
30288
+ constructor(code, message, developerHint) {
30289
+ super(message, { developerHint, title: "Cannot switch the project" });
30290
+ this.code = code;
30291
+ this.name = "ProjectUseError";
30292
+ }
30293
+ };
30294
+ var PROJECT_ID = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
30295
+ function normalizeProjectId(raw) {
30296
+ const value = raw.trim();
30297
+ if (!PROJECT_ID.test(value)) return void 0;
30298
+ const hex = value.replaceAll("-", "").toLowerCase();
30299
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
30300
+ }
30301
+ async function switchProject(options, deps) {
30302
+ const projectId = normalizeProjectId(options.projectId);
30303
+ if (!projectId)
30304
+ throw new ProjectUseError(
30305
+ "PROJECT_ID_INVALID",
30306
+ `"${options.projectId}" is not a project id.`,
30307
+ "A project id looks like fe3a12b0-fbaf-4315-b37f-b731d930ee57. The dashboard shows it on the project's page."
30308
+ );
30309
+ const { resolved } = loadConfigV2(options.dir);
30310
+ const current = resolved.project.id;
30311
+ let project;
30312
+ try {
30313
+ project = await deps.getProject(projectId);
30314
+ } catch (error) {
30315
+ if (error instanceof PlatformApiError && (error.status === 404 || error.status === 403))
30316
+ throw new ProjectUseError(
30317
+ "PROJECT_NOT_FOUND",
30318
+ `No project ${projectId} is yours.`,
30319
+ "Check the id on the dashboard, and that you are signed in to the right account (`apitogo login`)."
30320
+ );
30321
+ throw error;
30322
+ }
30323
+ if (current?.toLowerCase() === projectId)
30324
+ return { status: "unchanged", projectId, projectName: project.name };
30325
+ if (current && !options.yes) {
30326
+ const prompt = `This folder deploys to project ${current}. Move it to ${project.name} (${projectId})? The old project is left as it is.`;
30327
+ if (!deps.confirm)
30328
+ throw new ProjectUseError(
30329
+ "PROJECT_USE_NOT_CONFIRMED",
30330
+ `This folder deploys to project ${current}.`,
30331
+ "Add --yes to move it to the other project."
30332
+ );
30333
+ if (!await deps.confirm(prompt))
30334
+ throw new ProjectUseError(
30335
+ "PROJECT_USE_NOT_CONFIRMED",
30336
+ "Nothing changed: the folder still deploys to its project."
30337
+ );
30338
+ }
30339
+ const previousProjectId = await replaceProjectId(options.dir, projectId);
30340
+ await Promise.all([
30341
+ rm12(resolved.state.file, { force: true }),
30342
+ rm12(manifestPathFor(resolved.state), { force: true })
30343
+ ]);
30344
+ return {
30345
+ status: "switched",
30346
+ projectId,
30347
+ projectName: project.name,
30348
+ ...previousProjectId ? { previousProjectId } : {}
30349
+ };
30350
+ }
30351
+
30352
+ // src/cli/cmds/project-use.ts
30353
+ var project_use_default = {
30354
+ desc: "Move this folder to another existing project for good: the next deploy goes there",
30355
+ command: "use <id>",
30356
+ builder: (yargs2) => yargs2.positional("id", {
30357
+ type: "string",
30358
+ describe: "The id of the project, as the dashboard shows it",
30359
+ demandOption: true
30360
+ }).option("dir", {
30361
+ type: "string",
30362
+ describe: "Project directory",
30363
+ default: ".",
30364
+ normalize: true,
30365
+ hidden: true
30366
+ }).option("yes", {
30367
+ alias: "y",
30368
+ type: "boolean",
30369
+ describe: "Move the folder without asking, even when it deploys to another project",
30370
+ default: false
30371
+ }).option("json", {
30372
+ type: "boolean",
30373
+ describe: "Print raw JSON only",
30374
+ default: false
30375
+ }),
30376
+ handler: async (argv) => {
30377
+ await captureEvent({ argv, event: "apitogo project use" });
30378
+ const json = argv.json === true;
30379
+ const interactive = !json && process.stdin.isTTY && process.stdout.isTTY;
30380
+ try {
30381
+ const client = new PlatformApiClient(loadConfig());
30382
+ const asker = interactive ? createAsker() : void 0;
30383
+ const result = await switchProject(
30384
+ { dir: argv.dir ?? ".", projectId: argv.id, yes: argv.yes },
30385
+ {
30386
+ getProject: (projectId) => client.getProject(projectId),
30387
+ ...asker ? { confirm: (prompt) => asker.confirm(prompt, false) } : {}
30388
+ }
30389
+ );
30390
+ if (json) {
30391
+ console.log(JSON.stringify(result, null, 2));
30392
+ return;
30393
+ }
30394
+ if (result.status === "unchanged") {
30395
+ printResultToConsole(
30396
+ `This folder already deploys to ${result.projectName} (${result.projectId}).`
30397
+ );
30398
+ return;
30399
+ }
30400
+ printDiagnosticsToConsole(
30401
+ "Secrets (the Stripe keys, for example) and the people who signed up belong to each project: set the secrets again in this one with `apitogo secrets set`."
30402
+ );
30403
+ printResultToConsole(
30404
+ `This folder now deploys to ${result.projectName} (${result.projectId}). Run \`apitogo deploy\` to set up its services there.`
30405
+ );
30406
+ } catch (error) {
30407
+ printCriticalFailureToConsoleAndExit(error);
30408
+ }
30409
+ }
30410
+ };
30411
+
30412
+ // src/cli/cmds/project.ts
30413
+ var project_default = {
30414
+ desc: "Create the project for this folder, or move the folder to another project",
30415
+ command: "project <command>",
30416
+ builder: (yargs2) => yargs2.command(project_create_default).command(project_use_default).demandCommand(),
30417
+ handler: () => {
30418
+ }
30419
+ };
30420
+
29950
30421
  // src/cli/cmds/remove.ts
29951
30422
  var remove_default = {
29952
30423
  desc: "Remove a service from the project (its files are kept unless --delete-files)",
@@ -29987,14 +30458,14 @@ var remove_default = {
29987
30458
 
29988
30459
  // src/cli/dev/handler.ts
29989
30460
  init_joinUrl();
29990
- import path89 from "node:path";
30461
+ import path90 from "node:path";
29991
30462
 
29992
30463
  // src/vite/dev-server.ts
29993
30464
  init_logger();
29994
30465
  import fs11 from "node:fs/promises";
29995
30466
  import http from "node:http";
29996
30467
  import https from "node:https";
29997
- import path88 from "node:path";
30468
+ import path89 from "node:path";
29998
30469
  import { createHttpTerminator } from "http-terminator";
29999
30470
  import {
30000
30471
  createServer as createViteServer,
@@ -30024,10 +30495,22 @@ async function findAvailablePort(startPort) {
30024
30495
  // src/vite/dev-server.ts
30025
30496
  init_loader();
30026
30497
 
30498
+ // src/vite/dev-ssr-config.ts
30499
+ var SSR_CONFIG_MODULE_ID = "virtual:zudoku-config";
30500
+ function withSsrGraphParts(config2, ssrConfig) {
30501
+ if (!ssrConfig) return config2;
30502
+ return {
30503
+ ...config2,
30504
+ ...ssrConfig.plugins?.length ? { plugins: ssrConfig.plugins } : {},
30505
+ ...ssrConfig.navigation ? { navigation: ssrConfig.navigation } : {},
30506
+ ...ssrConfig.slots ? { slots: ssrConfig.slots } : {}
30507
+ };
30508
+ }
30509
+
30027
30510
  // src/vite/pagefind-dev-index.ts
30028
30511
  init_invariant();
30029
30512
  init_joinUrl();
30030
- import path87 from "node:path";
30513
+ import path88 from "node:path";
30031
30514
  import { createIndex as createIndex2 } from "pagefind";
30032
30515
  import { isRunnableDevEnvironment } from "vite";
30033
30516
  async function* buildPagefindDevIndex(vite, config2) {
@@ -30080,7 +30563,7 @@ async function* buildPagefindDevIndex(vite, config2) {
30080
30563
  path: urlPath
30081
30564
  };
30082
30565
  }
30083
- const outputPath = path87.join(vite.config.publicDir, "pagefind");
30566
+ const outputPath = path88.join(vite.config.publicDir, "pagefind");
30084
30567
  await pagefindIndex.writeFiles({ outputPath });
30085
30568
  yield { type: "complete", success: true, indexed };
30086
30569
  }
@@ -30099,9 +30582,9 @@ var DevServer = class {
30099
30582
  this.protocol = "https";
30100
30583
  const { dir } = this.options;
30101
30584
  const [key, cert, ca] = await Promise.all([
30102
- fs11.readFile(path88.resolve(dir, config2.https.key)),
30103
- fs11.readFile(path88.resolve(dir, config2.https.cert)),
30104
- config2.https.ca ? fs11.readFile(path88.resolve(dir, config2.https.ca)) : void 0
30585
+ fs11.readFile(path89.resolve(dir, config2.https.key)),
30586
+ fs11.readFile(path89.resolve(dir, config2.https.cert)),
30587
+ config2.https.ca ? fs11.readFile(path89.resolve(dir, config2.https.ca)) : void 0
30105
30588
  ]);
30106
30589
  return https.createServer({ key, cert, ca });
30107
30590
  }
@@ -30118,7 +30601,7 @@ var DevServer = class {
30118
30601
  );
30119
30602
  const server = await this.createNodeServer(config2);
30120
30603
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
30121
- const publicRoot = path88.resolve(
30604
+ const publicRoot = path89.resolve(
30122
30605
  this.options.dir,
30123
30606
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
30124
30607
  );
@@ -30135,7 +30618,7 @@ var DevServer = class {
30135
30618
  // built-in transform middleware which would treat the path as a static asset.
30136
30619
  name: "apitogo:entry-client",
30137
30620
  configureServer(server2) {
30138
- const entryPath = path88.posix.join(
30621
+ const entryPath = path89.posix.join(
30139
30622
  server2.config.base,
30140
30623
  "/__z/entry.client.tsx"
30141
30624
  );
@@ -30232,6 +30715,7 @@ var DevServer = class {
30232
30715
  const entryServer = await ssrEnvironment.runner.import(
30233
30716
  getAppServerEntryPath()
30234
30717
  );
30718
+ const ssrConfig = await ssrEnvironment.runner.import(SSR_CONFIG_MODULE_ID);
30235
30719
  const hasBody = req.method !== "GET" && req.method !== "HEAD";
30236
30720
  const request = new Request(
30237
30721
  `${this.protocol}://${req.headers.host}${url2}`,
@@ -30247,7 +30731,9 @@ var DevServer = class {
30247
30731
  const response = await entryServer.handleRequest({
30248
30732
  template,
30249
30733
  request,
30250
- routes: entryServer.getRoutesByConfig(currentConfig),
30734
+ routes: entryServer.getRoutesByConfig(
30735
+ withSsrGraphParts(currentConfig, ssrConfig.default)
30736
+ ),
30251
30737
  basePath: currentConfig.basePath
30252
30738
  });
30253
30739
  for (const [key, value] of response.headers) {
@@ -30300,7 +30786,7 @@ init_package_json();
30300
30786
  async function dev(argv) {
30301
30787
  const packageJson3 = getZudokuPackageJson();
30302
30788
  process.env.NODE_ENV = "development";
30303
- let dir = path89.resolve(process.cwd(), argv.dir);
30789
+ let dir = path90.resolve(process.cwd(), argv.dir);
30304
30790
  if (await isConfigV2(dir)) {
30305
30791
  const { root, prepared } = await preparePortalWorkspace(dir, "development");
30306
30792
  dir = root;
@@ -30435,12 +30921,12 @@ var secrets_default = {
30435
30921
  };
30436
30922
 
30437
30923
  // src/cli/cmds/status.ts
30438
- import path91 from "node:path";
30924
+ import path92 from "node:path";
30439
30925
 
30440
30926
  // src/cli/status/handler.ts
30441
- import path90 from "node:path";
30927
+ import path91 from "node:path";
30442
30928
  async function status(argv) {
30443
- const sourceDirectory = path90.resolve(process.cwd(), argv.dir);
30929
+ const sourceDirectory = path91.resolve(process.cwd(), argv.dir);
30444
30930
  let config2;
30445
30931
  try {
30446
30932
  config2 = loadConfig();
@@ -30566,7 +31052,7 @@ var status_default = {
30566
31052
  }),
30567
31053
  handler: async (argv) => {
30568
31054
  await captureEvent({ argv, event: "apitogo status" });
30569
- if (await isConfigV2(path91.resolve(process.cwd(), argv.dir))) {
31055
+ if (await isConfigV2(path92.resolve(process.cwd(), argv.dir))) {
30570
31056
  await statusOfProject(argv);
30571
31057
  return;
30572
31058
  }
@@ -30575,11 +31061,11 @@ var status_default = {
30575
31061
  };
30576
31062
 
30577
31063
  // src/cli/cmds/teardown.ts
30578
- import path94 from "node:path";
31064
+ import path95 from "node:path";
30579
31065
 
30580
31066
  // src/cli/teardown/command.ts
30581
31067
  init_invariant();
30582
- import path92 from "node:path";
31068
+ import path93 from "node:path";
30583
31069
  import { createInterface as createInterface7 } from "node:readline/promises";
30584
31070
 
30585
31071
  // src/cli/teardown/v2.ts
@@ -30940,7 +31426,7 @@ async function teardownProject(argv) {
30940
31426
  const progress = createConsoleProgressReporter();
30941
31427
  const result = await teardownV2(
30942
31428
  {
30943
- dir: path92.resolve(process.cwd(), argv.dir),
31429
+ dir: path93.resolve(process.cwd(), argv.dir),
30944
31430
  projectId: argv.projectId,
30945
31431
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
30946
31432
  yes: argv.yes === true,
@@ -30999,7 +31485,7 @@ async function teardownProject(argv) {
30999
31485
  }
31000
31486
 
31001
31487
  // src/cli/teardown/handler.ts
31002
- import path93 from "node:path";
31488
+ import path94 from "node:path";
31003
31489
  function resultLooksFailed3(payload) {
31004
31490
  try {
31005
31491
  const parsed = JSON.parse(payload);
@@ -31015,7 +31501,7 @@ async function teardown(argv) {
31015
31501
  );
31016
31502
  return;
31017
31503
  }
31018
- const sourceDirectory = path93.resolve(process.cwd(), argv.dir);
31504
+ const sourceDirectory = path94.resolve(process.cwd(), argv.dir);
31019
31505
  let config2;
31020
31506
  try {
31021
31507
  config2 = loadConfig();
@@ -31093,7 +31579,7 @@ var teardown_default = {
31093
31579
  }),
31094
31580
  handler: async (argv) => {
31095
31581
  await captureEvent({ argv, event: "apitogo teardown" });
31096
- if (await isConfigV2(path94.resolve(process.cwd(), argv.dir))) {
31582
+ if (await isConfigV2(path95.resolve(process.cwd(), argv.dir))) {
31097
31583
  await teardownProject(argv);
31098
31584
  return;
31099
31585
  }
@@ -31103,7 +31589,7 @@ var teardown_default = {
31103
31589
 
31104
31590
  // src/cli/teardown/reset-command.ts
31105
31591
  init_invariant();
31106
- import path95 from "node:path";
31592
+ import path96 from "node:path";
31107
31593
 
31108
31594
  // src/cli/teardown/reset.ts
31109
31595
  init_invariant();
@@ -31226,7 +31712,7 @@ async function resetProject(argv) {
31226
31712
  const config2 = loadConfig();
31227
31713
  const result = await resetProjectV2(
31228
31714
  {
31229
- dir: path95.resolve(process.cwd(), argv.dir),
31715
+ dir: path96.resolve(process.cwd(), argv.dir),
31230
31716
  projectId: argv.projectId,
31231
31717
  yes: argv.yes === true
31232
31718
  },
@@ -31290,7 +31776,7 @@ var test_reset_default = {
31290
31776
  };
31291
31777
 
31292
31778
  // src/cli/test-sign-in/handler.ts
31293
- import path96 from "node:path";
31779
+ import path97 from "node:path";
31294
31780
  init_invariant();
31295
31781
  var TestSignInError = class extends ApitogoError {
31296
31782
  constructor(message, developerHint) {
@@ -31301,7 +31787,7 @@ var TestSignInError = class extends ApitogoError {
31301
31787
  async function resolveProjectId3(argv) {
31302
31788
  const explicit = argv.projectId?.trim();
31303
31789
  if (explicit) return explicit;
31304
- const dir = path96.resolve(process.cwd(), argv.dir ?? ".");
31790
+ const dir = path97.resolve(process.cwd(), argv.dir ?? ".");
31305
31791
  if (await isConfigV2(dir)) {
31306
31792
  const id = loadConfigV2(dir).resolved.project.id;
31307
31793
  if (id) return id;
@@ -31433,44 +31919,44 @@ To fix, run:
31433
31919
  var packageJson = getZudokuPackageJson();
31434
31920
  var command = (module) => module;
31435
31921
  function createApitogoCli(argv) {
31436
- return yargs(argv).scriptName("apitogo").middleware(warnPackageVersionMismatch).command(command(init_default)).command(command(create_default)).command(command(add_default)).command(command(remove_default)).command(command(config_default)).command(command(run_default)).command(command(build_default)).command(command(check_default)).command(command(plan_default)).command(command(secrets_default)).command(command(test_sign_in_default)).command(command(project_create_default)).command(command(test_reset_default)).command(command(deploy_default)).command(command(export_default)).command(command(status_default)).command(command(teardown_default)).command(command(login_default)).command(command(logout_default)).demandCommand().strictCommands().version(packageJson?.version).fail((message, error) => {
31922
+ return yargs(argv).scriptName("apitogo").middleware(warnPackageVersionMismatch).command(command(init_default)).command(command(create_default)).command(command(add_default)).command(command(remove_default)).command(command(config_default)).command(command(run_default)).command(command(build_default)).command(command(check_default)).command(command(plan_default)).command(command(secrets_default)).command(command(test_sign_in_default)).command(command(project_default)).command(command(test_reset_default)).command(command(deploy_default)).command(command(export_default)).command(command(status_default)).command(command(teardown_default)).command(command(login_default)).command(command(logout_default)).demandCommand().strictCommands().version(packageJson?.version).fail((message, error) => {
31437
31923
  throw error ?? new UsageError(message);
31438
31924
  }).help();
31439
31925
  }
31440
31926
 
31441
31927
  // src/cli/local-handoff.ts
31442
31928
  import { spawn as spawn2 } from "node:child_process";
31443
- import { existsSync as existsSync8, realpathSync as realpathSync2 } from "node:fs";
31444
- import path97 from "node:path";
31929
+ import { existsSync as existsSync9, realpathSync as realpathSync2 } from "node:fs";
31930
+ import path98 from "node:path";
31445
31931
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
31446
31932
  var PACKAGE_NAME = "@lukoweb/apitogo";
31447
31933
  var resolveProjectDir = (argv, cwd) => {
31448
31934
  for (let i = 0; i < argv.length; i++) {
31449
31935
  const arg = argv[i];
31450
31936
  const next = argv[i + 1];
31451
- if (arg === "--dir" && next) return path97.resolve(cwd, next);
31937
+ if (arg === "--dir" && next) return path98.resolve(cwd, next);
31452
31938
  if (arg?.startsWith("--dir=")) {
31453
- return path97.resolve(cwd, arg.slice("--dir=".length));
31939
+ return path98.resolve(cwd, arg.slice("--dir=".length));
31454
31940
  }
31455
31941
  }
31456
31942
  return cwd;
31457
31943
  };
31458
31944
  var findLocalCli = (projectDir, runningRoot) => {
31459
- let dir = path97.resolve(projectDir);
31945
+ let dir = path98.resolve(projectDir);
31460
31946
  let localPackageRoot;
31461
31947
  while (!localPackageRoot) {
31462
- const candidate = path97.join(dir, "node_modules", PACKAGE_NAME);
31463
- if (existsSync8(path97.join(candidate, "package.json"))) {
31948
+ const candidate = path98.join(dir, "node_modules", PACKAGE_NAME);
31949
+ if (existsSync9(path98.join(candidate, "package.json"))) {
31464
31950
  localPackageRoot = candidate;
31465
- } else if (dir === path97.dirname(dir)) {
31951
+ } else if (dir === path98.dirname(dir)) {
31466
31952
  return void 0;
31467
31953
  } else {
31468
- dir = path97.dirname(dir);
31954
+ dir = path98.dirname(dir);
31469
31955
  }
31470
31956
  }
31471
31957
  const localRoot = realpathSync2(localPackageRoot);
31472
31958
  if (localRoot === realpathSync2(runningRoot)) return void 0;
31473
- return path97.join(localRoot, "cli.js");
31959
+ return path98.join(localRoot, "cli.js");
31474
31960
  };
31475
31961
  var handoffToLocalCli = async (argv, runningRoot) => {
31476
31962
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -31482,7 +31968,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
31482
31968
  );
31483
31969
  if (!localCli) return void 0;
31484
31970
  printDiagnosticsToConsole(
31485
- `Using the project's local ${PACKAGE_NAME} (${path97.dirname(localCli)})`
31971
+ `Using the project's local ${PACKAGE_NAME} (${path98.dirname(localCli)})`
31486
31972
  );
31487
31973
  const child = spawn2(process.execPath, [localCli, ...argv], {
31488
31974
  stdio: "inherit",