@lukoweb/apitogo 0.1.121 → 0.1.127

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 (53) hide show
  1. package/dist/cli/cli.js +659 -402
  2. package/dist/declarations/cli/platform/client/platform-api.d.ts +6 -0
  3. package/dist/declarations/cli/platform/helpers/dev-portal-manifest.d.ts +2 -0
  4. package/dist/declarations/cli/platform/helpers/publish-preflight.d.ts +2 -0
  5. package/dist/declarations/cli/platform/tools/gateway.d.ts +6 -0
  6. package/dist/declarations/config/local-manifest.d.ts +21 -1
  7. package/dist/declarations/config/validators/ModulesSchema.d.ts +20 -0
  8. package/dist/declarations/config/validators/ZudokuConfig.d.ts +5 -0
  9. package/dist/declarations/lib/authentication/auth-header-nav.d.ts +5 -1
  10. package/dist/declarations/lib/authentication/hook.d.ts +25 -3
  11. package/dist/declarations/lib/authentication/state.d.ts +39 -3
  12. package/dist/declarations/lib/plugins/openapi/processors/injectApitogoGatewayServer.d.ts +1 -1
  13. package/dist/flat-config.d.ts +32 -24
  14. package/docs/cli/go-live.md +21 -20
  15. package/docs/components/slot.mdx +2 -2
  16. package/docs/components.mdx +34 -34
  17. package/docs/concepts/auth-provider-api-identities.md +2 -2
  18. package/docs/configuration/authentication.md +16 -14
  19. package/docs/configuration/build-configuration.mdx +2 -2
  20. package/docs/configuration/make-config.mdx +5 -5
  21. package/docs/configuration/manifest.md +8 -14
  22. package/docs/configuration/navigation.mdx +4 -4
  23. package/docs/configuration/overview.md +6 -7
  24. package/docs/configuration/plans.md +45 -16
  25. package/docs/deploy/vercel.md +3 -4
  26. package/docs/deployment.md +5 -4
  27. package/docs/guides/custom-pages.md +2 -2
  28. package/docs/guides/environment-variables.md +19 -17
  29. package/docs/guides/managing-api-keys-and-identities.md +10 -18
  30. package/docs/mcp/go-live.md +12 -12
  31. package/docs/mcp/overview.md +10 -10
  32. package/docs/mcp/setup.md +9 -9
  33. package/docs/mcp/shape.md +4 -3
  34. package/docs/mcp/tools.md +61 -61
  35. package/docs/plugins.md +2 -2
  36. package/docs/quickstart.md +5 -6
  37. package/package.json +1 -1
  38. package/src/config/apitogo-config-core.ts +7 -1
  39. package/src/config/gateway-limits.ts +7 -2
  40. package/src/config/loader.ts +5 -7
  41. package/src/config/local-manifest.ts +24 -7
  42. package/src/config/resolve-modules.ts +5 -7
  43. package/src/config/validators/ModulesSchema.ts +11 -0
  44. package/src/lib/authentication/auth-header-nav.ts +3 -3
  45. package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +2 -5
  46. package/src/lib/authentication/state.ts +1 -1
  47. package/src/lib/components/Layout.tsx +1 -4
  48. package/src/lib/components/SiteTitle.tsx +3 -1
  49. package/src/lib/plugins/openapi/processors/injectApitogoGatewayServer.ts +9 -3
  50. package/src/vite/config.ts +0 -21
  51. package/src/vite/plugin-api.ts +1 -1
  52. package/src/vite/plugin-modules.ts +1 -2
  53. package/src/vite/plugin-offroot-resolve.ts +2 -6
package/dist/cli/cli.js CHANGED
@@ -212,7 +212,9 @@ function mergeLimitsSource(prior, patch) {
212
212
  return planForConfig(merged);
213
213
  }
214
214
  function planLimitsForPlatform(plan) {
215
- return stringifyGatewayLimits(coerceGatewayLimits(plan));
215
+ return stringifyGatewayLimits(
216
+ coerceGatewayLimits(plan)
217
+ );
216
218
  }
217
219
  function validateLimitsField(value, field) {
218
220
  if (value == null || value === "") {
@@ -734,6 +736,8 @@ var init_apitogo_config_core = __esm({
734
736
  DEMOMODE: "demoMode",
735
737
  DISPLAYNAME: "displayName",
736
738
  PRICEAMOUNT: "priceAmount",
739
+ PRICINGMODEL: "pricingModel",
740
+ PRICELABEL: "priceLabel",
737
741
  PRICECURRENCY: "priceCurrency",
738
742
  BILLINGPERIOD: "billingPeriod",
739
743
  LIMITSJSON: "limitsJson",
@@ -1018,6 +1022,10 @@ var init_ModulesSchema = __esm({
1018
1022
  sku: z3.string(),
1019
1023
  displayName: z3.string(),
1020
1024
  priceAmount: z3.number().default(0),
1025
+ pricingModel: z3.enum(["fixed", "custom"]).optional().describe(
1026
+ '"custom" shows a contact-sales form instead of checkout; priceAmount must be 0.'
1027
+ ),
1028
+ priceLabel: z3.string().max(64).optional().describe('Text shown instead of the price, e.g. "Custom".'),
1021
1029
  priceCurrency: z3.string().trim().toLowerCase().regex(
1022
1030
  /^[a-z]{3}$/,
1023
1031
  "priceCurrency must be a 3-letter ISO 4217 code (lowercase)"
@@ -1125,6 +1133,8 @@ function plansToPreviewCatalog(plans) {
1125
1133
  sku: plan.sku,
1126
1134
  displayName: plan.displayName,
1127
1135
  priceAmount: plan.priceAmount ?? 0,
1136
+ pricingModel: plan.pricingModel ?? "fixed",
1137
+ ...plan.priceLabel ? { priceLabel: plan.priceLabel } : {},
1128
1138
  priceCurrency: plan.priceCurrency ?? "usd",
1129
1139
  billingPeriod: normalizeBillingPeriod(plan.billingPeriod),
1130
1140
  limits: coerceGatewayLimits(plan) ?? null,
@@ -1173,13 +1183,14 @@ async function readEffectiveManifest(rootDir, _env) {
1173
1183
  return null;
1174
1184
  }
1175
1185
  }
1176
- var BillingPeriodSchema, GatewayLimitsSchema, PriceCurrencySchema, ALLOWED_RUNTIME_VERSIONS, ManifestPlanInputSchema, DevPortalLocalManifestSchema;
1186
+ var BillingPeriodSchema, PlanPricingModelSchema, GatewayLimitsSchema, PriceCurrencySchema, ALLOWED_RUNTIME_VERSIONS, ManifestPlanInputSchema, DevPortalLocalManifestSchema;
1177
1187
  var init_local_manifest = __esm({
1178
1188
  "src/config/local-manifest.ts"() {
1179
- init_gateway_limits();
1180
1189
  init_apitogo_config_loader();
1190
+ init_gateway_limits();
1181
1191
  init_landing_manifest();
1182
1192
  BillingPeriodSchema = z4.enum(["month", "year", "monthly", "annual"]);
1193
+ PlanPricingModelSchema = z4.enum(["fixed", "custom"]);
1183
1194
  GatewayLimitsSchema = z4.object({
1184
1195
  rateLimit: z4.object({
1185
1196
  calls: z4.number(),
@@ -1210,6 +1221,9 @@ var init_local_manifest = __esm({
1210
1221
  sku: z4.string().min(1),
1211
1222
  displayName: z4.string().min(1),
1212
1223
  priceAmount: z4.number().default(0),
1224
+ /** `custom` plans show a contact-sales form instead of checkout. */
1225
+ pricingModel: PlanPricingModelSchema.optional(),
1226
+ priceLabel: z4.string().max(64).optional(),
1213
1227
  priceCurrency: PriceCurrencySchema.optional(),
1214
1228
  billingPeriod: BillingPeriodSchema.optional(),
1215
1229
  limits: GatewayLimitsSchema.optional(),
@@ -1503,10 +1517,7 @@ var init_resolve_modules = __esm({
1503
1517
  codeExample: content?.hero?.codeExample ?? defaults.hero?.codeExample,
1504
1518
  titleAccent: content?.hero?.titleAccent ?? (content?.hero?.title ? void 0 : defaults.hero?.titleAccent)
1505
1519
  },
1506
- featuresSection: mergeLandingSection(
1507
- defaults.featuresSection,
1508
- content?.featuresSection
1509
- ),
1520
+ featuresSection: defaults.featuresSection || content?.featuresSection ? { ...defaults.featuresSection, ...content?.featuresSection } : void 0,
1510
1521
  features: content?.features ?? defaults.features,
1511
1522
  primaryAction: content?.primaryAction ?? defaults.primaryAction,
1512
1523
  secondaryAction: content?.secondaryAction ?? defaults.secondaryAction,
@@ -1534,7 +1545,7 @@ var init_resolve_modules = __esm({
1534
1545
  ...content.hero,
1535
1546
  title: content.hero?.title ?? siteTitle
1536
1547
  },
1537
- featuresSection: content.featuresSection ? mergeLandingSection(void 0, content.featuresSection) : void 0,
1548
+ featuresSection: content.featuresSection,
1538
1549
  features: content.features,
1539
1550
  primaryAction: content.primaryAction,
1540
1551
  secondaryAction: content.secondaryAction,
@@ -5063,7 +5074,7 @@ async function loadZudokuConfigWithMeta(rootDir) {
5063
5074
  dependencies,
5064
5075
  configPath
5065
5076
  },
5066
- __resolvedModules: resolveModulesConfig(config2, { rootDir })
5077
+ __resolvedModules: resolveModulesConfig(config2)
5067
5078
  };
5068
5079
  return configWithMetadata;
5069
5080
  }
@@ -5094,9 +5105,7 @@ function isWatchableConfigDependency(depPath) {
5094
5105
  return !depPath.startsWith("\0virtual:") && !depPath.startsWith("virtual:");
5095
5106
  }
5096
5107
  function getWatchableConfigDependencies(config2) {
5097
- const manifestEnv = resolveManifestEnv(
5098
- config2.__meta.mode === "production" ? "production" : "development"
5099
- );
5108
+ const manifestEnv = resolveManifestEnv("development");
5100
5109
  const { watchPaths } = manifestPathsForEnv(
5101
5110
  config2.__meta.rootDir,
5102
5111
  manifestEnv
@@ -5175,9 +5184,7 @@ async function loadZudokuConfig(configEnv, rootDir) {
5175
5184
  ...transformedConfig.__meta,
5176
5185
  ...configWithManifestMeta.__meta
5177
5186
  },
5178
- __resolvedModules: resolveModulesConfig(transformedConfig, {
5179
- rootDir: transformedConfig.__meta.rootDir
5180
- })
5187
+ __resolvedModules: resolveModulesConfig(transformedConfig)
5181
5188
  };
5182
5189
  if (!process.env.APITOGO_JSON_ONLY) {
5183
5190
  logger.info(
@@ -5802,7 +5809,7 @@ import {
5802
5809
  // package.json
5803
5810
  var package_default = {
5804
5811
  name: "@lukoweb/apitogo",
5805
- version: "0.1.121",
5812
+ version: "0.1.127",
5806
5813
  type: "module",
5807
5814
  sideEffects: [
5808
5815
  "**/*.css",
@@ -9864,10 +9871,6 @@ function vitePlugin() {
9864
9871
  }
9865
9872
 
9866
9873
  // src/vite/config.ts
9867
- var browserConfigLoaderPath2 = path26.join(
9868
- getZudokuRootDir(),
9869
- "src/config/apitogo-config-loader.browser.ts"
9870
- );
9871
9874
  var resolveMergedPublicDir = (rootDir, merged) => {
9872
9875
  if (merged.publicDir === false) return void 0;
9873
9876
  const rel = typeof merged.publicDir === "string" ? merged.publicDir : "public";
@@ -9925,22 +9928,6 @@ async function getViteConfig(dir, configEnv) {
9925
9928
  "@mdx-js/react": import.meta.resolve("@mdx-js/react")
9926
9929
  }
9927
9930
  },
9928
- environments: {
9929
- client: {
9930
- resolve: {
9931
- alias: {
9932
- [path26.join(
9933
- getZudokuRootDir(),
9934
- "src/config/apitogo-config-loader.node.ts"
9935
- )]: browserConfigLoaderPath2,
9936
- [path26.join(
9937
- getZudokuRootDir(),
9938
- "src/config/apitogo-config-loader.node.js"
9939
- )]: browserConfigLoaderPath2
9940
- }
9941
- }
9942
- }
9943
- },
9944
9931
  define: {
9945
9932
  "process.env.ZUDOKU_VERSION": JSON.stringify(package_default.version),
9946
9933
  "process.env.IS_ZUPLO": ZuploEnv.isZuplo,
@@ -10689,7 +10676,9 @@ async function build(argv) {
10689
10676
  const packageJson3 = getZudokuPackageJson();
10690
10677
  const jsonOnly = argv.jsonOnly === true || process.env.APITOGO_JSON_ONLY === "1";
10691
10678
  if (!jsonOnly) {
10692
- printDiagnosticsToConsole(`Starting API ToGo build v${packageJson3.version}`);
10679
+ printDiagnosticsToConsole(
10680
+ `Starting API ToGo build v${packageJson3.version}`
10681
+ );
10693
10682
  printDiagnosticsToConsole("");
10694
10683
  printDiagnosticsToConsole("");
10695
10684
  }
@@ -11795,11 +11784,16 @@ function applyManifestToConfig(config2, manifest) {
11795
11784
  const site = next.site ?? {};
11796
11785
  site.title = title;
11797
11786
  if (manifest.branding?.logoUrl) {
11787
+ const existingLogo = site.logo ?? {};
11788
+ const existingSrc = existingLogo.src ?? {};
11798
11789
  site.logo = {
11799
- ...site.logo ?? {},
11790
+ ...existingLogo,
11791
+ // Platform branding only stores a single logo — only override the
11792
+ // light slot so an existing dark-mode asset (e.g. logo-dark.svg)
11793
+ // isn't clobbered.
11800
11794
  src: {
11801
- light: manifest.branding.logoUrl,
11802
- dark: manifest.branding.logoUrl
11795
+ ...existingSrc,
11796
+ light: manifest.branding.logoUrl
11803
11797
  }
11804
11798
  };
11805
11799
  }
@@ -12176,7 +12170,14 @@ async function writeManifest(siteDirectory, manifest) {
12176
12170
  }
12177
12171
 
12178
12172
  // src/cli/platform/helpers/dev-portal-secrets.ts
12179
- import { access as access2, appendFile, mkdir as mkdir7, readFile as readFile7, unlink, writeFile as writeFile8 } from "node:fs/promises";
12173
+ import {
12174
+ access as access2,
12175
+ appendFile,
12176
+ mkdir as mkdir7,
12177
+ readFile as readFile7,
12178
+ unlink,
12179
+ writeFile as writeFile8
12180
+ } from "node:fs/promises";
12180
12181
  import path37 from "node:path";
12181
12182
  var ENV_FILENAME = ".env";
12182
12183
  var COLLAPSED_UPPERCASE_ALIASES2 = {
@@ -12236,7 +12237,7 @@ function parseEnvValue2(raw) {
12236
12237
  return trimmed;
12237
12238
  }
12238
12239
  function resolveExistingKey2(target, key) {
12239
- if (Object.prototype.hasOwnProperty.call(target, key)) {
12240
+ if (Object.hasOwn(target, key)) {
12240
12241
  return key;
12241
12242
  }
12242
12243
  const lower = key.toLowerCase();
@@ -12299,7 +12300,10 @@ async function loadSiteEnv(siteDirectory) {
12299
12300
  const resolvedDirectory = path37.resolve(siteDirectory);
12300
12301
  const merged = { ...process.env };
12301
12302
  for (const fileName of [ENV_FILENAME, ".env.local"]) {
12302
- Object.assign(merged, await readEnvFile(path37.join(resolvedDirectory, fileName)));
12303
+ Object.assign(
12304
+ merged,
12305
+ await readEnvFile(path37.join(resolvedDirectory, fileName))
12306
+ );
12303
12307
  }
12304
12308
  return merged;
12305
12309
  }
@@ -12385,7 +12389,11 @@ function getSecretsStatus(siteDirectory, manifest, secrets, envExists) {
12385
12389
  };
12386
12390
  }
12387
12391
  function validateSecretsForPublish(manifest, secrets, emailOptions) {
12388
- const prerequisites = getPublishPrerequisites(manifest, secrets, emailOptions);
12392
+ const prerequisites = getPublishPrerequisites(
12393
+ manifest,
12394
+ secrets,
12395
+ emailOptions
12396
+ );
12389
12397
  if (prerequisites.readyToPublish) {
12390
12398
  return;
12391
12399
  }
@@ -12411,7 +12419,11 @@ async function resolvePublishConfig(siteDirectory, emailOptions) {
12411
12419
  const env = await loadSiteEnv(resolvedDirectory);
12412
12420
  const secrets = readAuthenticationSecretsFromEnv(env);
12413
12421
  const envExists = await siteEnvFileHasEntries(resolvedDirectory);
12414
- const publishPrerequisites = getPublishPrerequisites(manifestRaw, secrets, emailOptions);
12422
+ const publishPrerequisites = getPublishPrerequisites(
12423
+ manifestRaw,
12424
+ secrets,
12425
+ emailOptions
12426
+ );
12415
12427
  const secretsStatus = getSecretsStatus(
12416
12428
  resolvedDirectory,
12417
12429
  manifestRaw,
@@ -12487,13 +12499,15 @@ var writeScaffoldSecretsArtifacts = writeScaffoldEnv;
12487
12499
  var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apitogo.config.json \u2014 ask the user what plans they want before calling update_dev_portal_manifest; never add Free+Pro defaults unless they explicitly request them. The user must save auth secrets (OIDC provider secrets, and Stripe if paid plans) in .env using APITOGO_CONFIG_AUTHENTICATION__* keys. Native auth email is provisioned by the platform \u2014 do not ask for an ACS connection string. Share .env and required fields from publishPrerequisites.missingFields. Never ask for or accept secret values in chat. When the user confirms secrets are saved locally, call check_dev_portal_publish_readiness, then publish_dev_portal.";
12488
12500
 
12489
12501
  // src/cli/platform/helpers/dev-portal-site-validation.ts
12502
+ init_gateway_limits();
12490
12503
  import { readFile as readFile8 } from "node:fs/promises";
12491
12504
  import { createRequire as createRequire2 } from "node:module";
12492
12505
  import path38 from "node:path";
12493
- init_gateway_limits();
12494
12506
  var cachedFallbackTs;
12495
12507
  async function loadTypeScriptForSite(sourceDirectory) {
12496
- const require2 = createRequire2(path38.join(path38.resolve(sourceDirectory), "package.json"));
12508
+ const require2 = createRequire2(
12509
+ path38.join(path38.resolve(sourceDirectory), "package.json")
12510
+ );
12497
12511
  try {
12498
12512
  return require2("typescript");
12499
12513
  } catch {
@@ -12520,11 +12534,12 @@ function checkTsxSyntax(ts, fileName, content) {
12520
12534
  }
12521
12535
  });
12522
12536
  return (result.diagnostics ?? []).filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error).map((diagnostic) => {
12523
- const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, " ");
12537
+ const message = ts.flattenDiagnosticMessageText(
12538
+ diagnostic.messageText,
12539
+ " "
12540
+ );
12524
12541
  if (diagnostic.file && diagnostic.start !== void 0) {
12525
- const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
12526
- diagnostic.start
12527
- );
12542
+ const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
12528
12543
  return `${message} (line ${line + 1}, column ${character + 1})`;
12529
12544
  }
12530
12545
  return message;
@@ -12537,7 +12552,9 @@ async function referencedFileExists(sourceDirectory, reference) {
12537
12552
  if (/^https?:\/\//i.test(reference)) {
12538
12553
  return true;
12539
12554
  }
12540
- return pathExists(path38.resolve(sourceDirectory, reference.replace(/^\.\//, "")));
12555
+ return pathExists(
12556
+ path38.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
12557
+ );
12541
12558
  }
12542
12559
  async function validateSiteConfigStructure(config2, sourceDirectory) {
12543
12560
  const errors = [];
@@ -12561,7 +12578,9 @@ async function validateSiteConfigStructure(config2, sourceDirectory) {
12561
12578
  if (!item) {
12562
12579
  errors.push(`"navigation[${index}]" must be an object.`);
12563
12580
  } else if (typeof item.type !== "string" || !item.type.trim()) {
12564
- errors.push(`"navigation[${index}].type" must be a non-empty string.`);
12581
+ errors.push(
12582
+ `"navigation[${index}].type" must be a non-empty string.`
12583
+ );
12565
12584
  }
12566
12585
  });
12567
12586
  }
@@ -12580,7 +12599,9 @@ async function validateSiteConfigStructure(config2, sourceDirectory) {
12580
12599
  if (typeof api.input !== "string" || !api.input.trim()) {
12581
12600
  errors.push(`"apis[${index}].input" must be a non-empty string.`);
12582
12601
  } else if (api.type === "file" && !await referencedFileExists(sourceDirectory, api.input)) {
12583
- errors.push(`"apis[${index}].input" points to a missing file: ${api.input}`);
12602
+ errors.push(
12603
+ `"apis[${index}].input" points to a missing file: ${api.input}`
12604
+ );
12584
12605
  }
12585
12606
  if (typeof api.path !== "string" || !api.path.trim()) {
12586
12607
  errors.push(`"apis[${index}].path" must be a non-empty string.`);
@@ -12593,7 +12614,9 @@ async function validateSiteConfigStructure(config2, sourceDirectory) {
12593
12614
  const modules = asRecord2(root.modules);
12594
12615
  const landing = asRecord2(modules?.landing);
12595
12616
  if (typeof landing?.page === "string" && landing.page.trim() && !await referencedFileExists(sourceDirectory, landing.page)) {
12596
- errors.push(`"modules.landing.page" points to a missing file: ${landing.page}`);
12617
+ errors.push(
12618
+ `"modules.landing.page" points to a missing file: ${landing.page}`
12619
+ );
12597
12620
  }
12598
12621
  const plansModule = asRecord2(asRecord2(modules?.userPanel)?.plans);
12599
12622
  if (plansModule?.items !== void 0) {
@@ -12603,7 +12626,9 @@ async function validateSiteConfigStructure(config2, sourceDirectory) {
12603
12626
  plansModule.items.forEach((entry, index) => {
12604
12627
  const plan = asRecord2(entry);
12605
12628
  if (!plan) {
12606
- errors.push(`"modules.userPanel.plans.items[${index}]" must be an object.`);
12629
+ errors.push(
12630
+ `"modules.userPanel.plans.items[${index}]" must be an object.`
12631
+ );
12607
12632
  return;
12608
12633
  }
12609
12634
  if (typeof plan.sku !== "string" || !plan.sku.trim()) {
@@ -12662,7 +12687,10 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
12662
12687
  });
12663
12688
  }
12664
12689
  if (!parseFailed) {
12665
- const structureErrors = await validateSiteConfigStructure(parsed, resolvedDir);
12690
+ const structureErrors = await validateSiteConfigStructure(
12691
+ parsed,
12692
+ resolvedDir
12693
+ );
12666
12694
  issues.push(
12667
12695
  ...structureErrors.map((message) => ({
12668
12696
  file: APITOGO_CONFIG_FILENAME2,
@@ -12689,7 +12717,10 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
12689
12717
  } else {
12690
12718
  for (const relativeFile of existingTsxFiles) {
12691
12719
  checkedFiles.push(relativeFile);
12692
- const content = await readFile8(path38.join(resolvedDir, relativeFile), "utf8");
12720
+ const content = await readFile8(
12721
+ path38.join(resolvedDir, relativeFile),
12722
+ "utf8"
12723
+ );
12693
12724
  issues.push(
12694
12725
  ...checkTsxSyntax(ts, relativeFile, content).map((message) => ({
12695
12726
  file: relativeFile,
@@ -12745,7 +12776,9 @@ function parseNodeMajor(version) {
12745
12776
  function assertNodeVersion(required) {
12746
12777
  const major = parseNodeMajor(process.version);
12747
12778
  if (Number.isNaN(major) || major < MIN_NODE_MAJOR) {
12748
- throw new Error(`Node.js ${MIN_NODE_MAJOR}+ is required (current: ${process.version}).`);
12779
+ throw new Error(
12780
+ `Node.js ${MIN_NODE_MAJOR}+ is required (current: ${process.version}).`
12781
+ );
12749
12782
  }
12750
12783
  if (required) {
12751
12784
  const requiredMajor = parseNodeMajor(required);
@@ -13011,7 +13044,9 @@ function normalizeRuntime(runtime) {
13011
13044
  if (raw === "dotnet" || raw === ".net" || raw === "net") return "dotnet";
13012
13045
  if (raw === "nodejs" || raw === "node") return "nodejs";
13013
13046
  if (raw === "python") return "python";
13014
- throw new Error(`runtime must be one of: dotnet, nodejs, python; got '${runtime}'.`);
13047
+ throw new Error(
13048
+ `runtime must be one of: dotnet, nodejs, python; got '${runtime}'.`
13049
+ );
13015
13050
  }
13016
13051
  function normalizePlanForGatewayApi(plan) {
13017
13052
  return {
@@ -13069,7 +13104,10 @@ function normalizeManifestForPlatform(manifest) {
13069
13104
  }
13070
13105
  }
13071
13106
  if (backend.runtimeVersion) {
13072
- const slug = normalizeRuntimeVersionSlug(backend.runtime, backend.runtimeVersion);
13107
+ const slug = normalizeRuntimeVersionSlug(
13108
+ backend.runtime,
13109
+ backend.runtimeVersion
13110
+ );
13073
13111
  if (slug !== backend.runtimeVersion) {
13074
13112
  corrections.push({
13075
13113
  field: "backend.runtimeVersion",
@@ -13094,7 +13132,10 @@ function validateManifestForPlatformPublish(manifest) {
13094
13132
  } else {
13095
13133
  for (const [index, plan] of manifest.plans.entries()) {
13096
13134
  if (!plan.sku?.trim()) {
13097
- issues.push({ field: `plans[${index}].sku`, message: "sku is required." });
13135
+ issues.push({
13136
+ field: `plans[${index}].sku`,
13137
+ message: "sku is required."
13138
+ });
13098
13139
  }
13099
13140
  if (!plan.displayName?.trim()) {
13100
13141
  issues.push({
@@ -13105,12 +13146,33 @@ function validateManifestForPlatformPublish(manifest) {
13105
13146
  if (plan.priceAmount !== void 0 && plan.priceAmount < 0) {
13106
13147
  issues.push({
13107
13148
  field: `plans[${index}].priceAmount`,
13108
- message: "priceAmount must be >= 0."
13149
+ message: 'priceAmount must be >= 0. For negotiated pricing use pricingModel: "custom".'
13150
+ });
13151
+ }
13152
+ if (plan.pricingModel !== void 0 && plan.pricingModel !== "fixed" && plan.pricingModel !== "custom") {
13153
+ issues.push({
13154
+ field: `plans[${index}].pricingModel`,
13155
+ message: "pricingModel must be 'fixed' or 'custom'."
13156
+ });
13157
+ }
13158
+ if (plan.pricingModel === "custom" && (plan.priceAmount ?? 0) !== 0) {
13159
+ issues.push({
13160
+ field: `plans[${index}].priceAmount`,
13161
+ message: "priceAmount must be 0 (or omitted) for custom-priced plans."
13162
+ });
13163
+ }
13164
+ if (plan.priceLabel !== void 0 && plan.priceLabel.length > 64) {
13165
+ issues.push({
13166
+ field: `plans[${index}].priceLabel`,
13167
+ message: "priceLabel must be at most 64 characters."
13109
13168
  });
13110
13169
  }
13111
13170
  const currencyResult = tryNormalizePriceCurrency(plan.priceCurrency);
13112
13171
  if (!currencyResult.ok) {
13113
- issues.push({ field: `plans[${index}].priceCurrency`, message: currencyResult.message });
13172
+ issues.push({
13173
+ field: `plans[${index}].priceCurrency`,
13174
+ message: currencyResult.message
13175
+ });
13114
13176
  } else if ((plan.priceCurrency ?? "usd") !== currencyResult.value) {
13115
13177
  corrections.push({
13116
13178
  field: `plans[${index}].priceCurrency`,
@@ -13152,7 +13214,10 @@ function validateManifestForPlatformPublish(manifest) {
13152
13214
  manifest.backend.runtimeVersion
13153
13215
  );
13154
13216
  if (!runtimeResult.ok) {
13155
- issues.push({ field: "backend.runtimeVersion", message: runtimeResult.message });
13217
+ issues.push({
13218
+ field: "backend.runtimeVersion",
13219
+ message: runtimeResult.message
13220
+ });
13156
13221
  } else if (runtimeResult.value !== manifest.backend.runtimeVersion) {
13157
13222
  corrections.push({
13158
13223
  field: "backend.runtimeVersion",
@@ -13320,9 +13385,143 @@ import path52 from "node:path";
13320
13385
  import { stdin as input2 } from "node:process";
13321
13386
  import { glob as glob5 } from "glob";
13322
13387
 
13388
+ // src/cli/platform/helpers/openapi-action-keys.ts
13389
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
13390
+ import path42 from "node:path";
13391
+ import YAML from "yaml";
13392
+ var HTTP_METHODS = [
13393
+ "get",
13394
+ "post",
13395
+ "put",
13396
+ "patch",
13397
+ "delete",
13398
+ "head",
13399
+ "options"
13400
+ ];
13401
+ function normalizeOpenApiPathTemplate(pathTemplate) {
13402
+ if (!pathTemplate.trim()) {
13403
+ return "/";
13404
+ }
13405
+ let value = pathTemplate.trim();
13406
+ if (!value.startsWith("/")) {
13407
+ value = `/${value}`;
13408
+ }
13409
+ if (value.length > 1 && value.endsWith("/")) {
13410
+ value = value.replace(/\/+$/, "");
13411
+ }
13412
+ return value;
13413
+ }
13414
+ function resolveRelativeServerBasePath(document) {
13415
+ const serverUrl = document.servers?.[0]?.url;
13416
+ if (!serverUrl || serverUrl === "/") {
13417
+ return null;
13418
+ }
13419
+ return serverUrl.startsWith("/") ? serverUrl.replace(/\/+$/, "") : null;
13420
+ }
13421
+ function combineBasePath(basePath, pathKey) {
13422
+ const normalizedPath = normalizeOpenApiPathTemplate(pathKey);
13423
+ if (!basePath || basePath === "/") {
13424
+ return normalizedPath;
13425
+ }
13426
+ const baseNorm = normalizeOpenApiPathTemplate(basePath);
13427
+ if (normalizedPath === "/") {
13428
+ return baseNorm;
13429
+ }
13430
+ return `${baseNorm.replace(/\/+$/, "")}${normalizedPath}`;
13431
+ }
13432
+ function extractGatewayActionKeysFromOpenApiDocument(document) {
13433
+ if (!document || typeof document !== "object") {
13434
+ return [];
13435
+ }
13436
+ const spec = document;
13437
+ if (!spec.paths || typeof spec.paths !== "object") {
13438
+ return [];
13439
+ }
13440
+ const basePath = resolveRelativeServerBasePath(spec);
13441
+ const keys = /* @__PURE__ */ new Set();
13442
+ for (const [pathKey, pathItem] of Object.entries(spec.paths)) {
13443
+ if (!pathItem || typeof pathItem !== "object") {
13444
+ continue;
13445
+ }
13446
+ const pathTemplate = combineBasePath(basePath, pathKey);
13447
+ for (const method of HTTP_METHODS) {
13448
+ if (!pathItem[method] || typeof pathItem[method] !== "object") {
13449
+ continue;
13450
+ }
13451
+ keys.add(`${method}:${pathTemplate}`);
13452
+ }
13453
+ }
13454
+ return [...keys].sort((a, b) => a.localeCompare(b));
13455
+ }
13456
+ function extractGatewayActionKeysFromOpenApiFile(filePath) {
13457
+ try {
13458
+ const raw = readFileSync3(filePath, "utf8");
13459
+ const ext = path42.extname(filePath).toLowerCase();
13460
+ const parsed = ext === ".json" ? JSON.parse(raw) : YAML.parse(raw);
13461
+ return extractGatewayActionKeysFromOpenApiDocument(parsed);
13462
+ } catch {
13463
+ return [];
13464
+ }
13465
+ }
13466
+ function resolveConfigOpenApiFilePath(siteDirectory, config2) {
13467
+ const backend = config2.backend;
13468
+ const candidates = [];
13469
+ if (typeof backend?.openApiPath === "string" && backend.openApiPath.trim()) {
13470
+ candidates.push(backend.openApiPath.trim());
13471
+ }
13472
+ const apis = config2.apis;
13473
+ const entries = Array.isArray(apis) ? apis : apis ? [apis] : [];
13474
+ for (const entry of entries) {
13475
+ if (entry && typeof entry === "object" && entry.type === "file" && typeof entry.input === "string") {
13476
+ candidates.push(entry.input);
13477
+ }
13478
+ }
13479
+ for (const candidate of candidates) {
13480
+ const resolved = path42.isAbsolute(candidate) ? candidate : path42.resolve(siteDirectory, candidate);
13481
+ if (existsSync5(resolved)) {
13482
+ return resolved;
13483
+ }
13484
+ }
13485
+ return void 0;
13486
+ }
13487
+ function loadOpenApiActionKeysForSite(siteDirectory, config2) {
13488
+ const filePath = resolveConfigOpenApiFilePath(siteDirectory, config2);
13489
+ if (!filePath) {
13490
+ return [];
13491
+ }
13492
+ return extractGatewayActionKeysFromOpenApiFile(filePath);
13493
+ }
13494
+ function loadOpenApiActionKeysFromSiteDirectory(siteDirectory) {
13495
+ const configPath = path42.join(siteDirectory, "apitogo.config.json");
13496
+ if (!existsSync5(configPath)) {
13497
+ return [];
13498
+ }
13499
+ try {
13500
+ const parsed = JSON.parse(readFileSync3(configPath, "utf8"));
13501
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
13502
+ return [];
13503
+ }
13504
+ return loadOpenApiActionKeysForSite(
13505
+ siteDirectory,
13506
+ parsed
13507
+ );
13508
+ } catch {
13509
+ return [];
13510
+ }
13511
+ }
13512
+ function withDefaultIncludedActionKeys(plan, actionKeys) {
13513
+ if (plan.includedActionKeys && plan.includedActionKeys.length > 0) {
13514
+ return plan;
13515
+ }
13516
+ if (actionKeys.length === 0) {
13517
+ return plan;
13518
+ }
13519
+ return { ...plan, includedActionKeys: actionKeys };
13520
+ }
13521
+
13323
13522
  // src/cli/scaffold/dev-portal-config-pages.ts
13324
13523
  import { access as access4, readFile as readFile10, rename as rename2, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
13325
- import path42 from "node:path";
13524
+ import path43 from "node:path";
13326
13525
  async function writeConfigAtomically(filePath, content) {
13327
13526
  const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
13328
13527
  try {
@@ -13335,7 +13534,7 @@ async function writeConfigAtomically(filePath, content) {
13335
13534
  }
13336
13535
  async function resolveApitogoConfigPath(sourceDirectory) {
13337
13536
  for (const filename of ["apitogo.config.tsx", "apitogo.config.jsx"]) {
13338
- const configPath = path42.join(sourceDirectory, filename);
13537
+ const configPath = path43.join(sourceDirectory, filename);
13339
13538
  try {
13340
13539
  await access4(configPath);
13341
13540
  return configPath;
@@ -13391,7 +13590,7 @@ function inlineMarkdownToJsx(text) {
13391
13590
  const link = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(token);
13392
13591
  if (link) {
13393
13592
  parts.push(
13394
- `<a href={${JSON.stringify(link[2])}} className="text-primary underline">${jsxTextExpr(link[1])}</a>`
13593
+ `<a href={${JSON.stringify(link[2])}} className="text-primary underline">${jsxTextExpr(link[1] ?? "")}</a>`
13395
13594
  );
13396
13595
  } else {
13397
13596
  parts.push(jsxTextExpr(token));
@@ -13476,9 +13675,9 @@ ${indent}</Callout>`
13476
13675
  }
13477
13676
  const tipQuote = /^>\s*\*\*(Tip|Note|Info|Caution|Danger):\*\*\s*(.*)$/i.exec(line.trim());
13478
13677
  if (tipQuote) {
13479
- const rawType = tipQuote[1].toLowerCase();
13678
+ const rawType = (tipQuote[1] ?? "").toLowerCase();
13480
13679
  const type = CALLOUT_TYPES.has(rawType) ? rawType : "tip";
13481
- const rest = tipQuote[2].trim();
13680
+ const rest = (tipQuote[2] ?? "").trim();
13482
13681
  i += 1;
13483
13682
  const more = rest ? [rest] : [];
13484
13683
  while (i < lines.length && /^>\s*/.test(lines[i] ?? "")) {
@@ -13514,10 +13713,10 @@ ${indent}</pre>`
13514
13713
  }
13515
13714
  const heading = /^(#{1,3})\s+(.+)$/.exec(line);
13516
13715
  if (heading) {
13517
- const level = heading[1].length;
13716
+ const level = (heading[1] ?? "").length;
13518
13717
  const className = level === 1 ? "text-foreground" : level === 2 ? "text-foreground" : "text-foreground";
13519
13718
  blocks.push(
13520
- `${indent}<Heading level={${level}} className="${className}">${inlineMarkdownToJsx(heading[2].trim())}</Heading>`
13719
+ `${indent}<Heading level={${level}} className="${className}">${inlineMarkdownToJsx((heading[2] ?? "").trim())}</Heading>`
13521
13720
  );
13522
13721
  i += 1;
13523
13722
  continue;
@@ -13619,7 +13818,7 @@ function splitMarkdownByH2(content) {
13619
13818
  if (current) {
13620
13819
  sections.push(current);
13621
13820
  }
13622
- current = { title: h2[1].trim(), body: "" };
13821
+ current = { title: (h2[1] ?? "").trim(), body: "" };
13623
13822
  continue;
13624
13823
  }
13625
13824
  if (current) {
@@ -13901,7 +14100,10 @@ function appendEntryToArrayRange(content, range, entry) {
13901
14100
  const after = content.slice(range.close);
13902
14101
  const trimmedBefore = before.trimEnd();
13903
14102
  const needsComma = !trimmedBefore.endsWith("[") && !trimmedBefore.endsWith(",");
13904
- const closeIndent = range.indent.slice(0, Math.max(0, range.indent.length - 2));
14103
+ const closeIndent = range.indent.slice(
14104
+ 0,
14105
+ Math.max(0, range.indent.length - 2)
14106
+ );
13905
14107
  return `${trimmedBefore}${needsComma ? "," : ""}
13906
14108
  ${entry}
13907
14109
  ${closeIndent}${after}`;
@@ -13909,7 +14111,10 @@ ${closeIndent}${after}`;
13909
14111
  function prependEntryToArrayRange(content, range, entry) {
13910
14112
  const inner = content.slice(range.open + 1, range.close);
13911
14113
  const trimmedInner = inner.trim();
13912
- const closeIndent = range.indent.slice(0, Math.max(0, range.indent.length - 2));
14114
+ const closeIndent = range.indent.slice(
14115
+ 0,
14116
+ Math.max(0, range.indent.length - 2)
14117
+ );
13913
14118
  const normalizedEntry = entry.trimEnd().endsWith(",") ? entry.trimEnd() : `${entry.trimEnd()},`;
13914
14119
  if (!trimmedInner) {
13915
14120
  return `${content.slice(0, range.open + 1)}
@@ -14027,7 +14232,11 @@ function appendCustomPageToNavigation(content, slug, label, _options) {
14027
14232
  if (content.includes(`path: ${pagePath}`)) {
14028
14233
  return content;
14029
14234
  }
14030
- const rewrittenBuildExport = appendCustomPageToDirectBuildExport(content, slug, label);
14235
+ const rewrittenBuildExport = appendCustomPageToDirectBuildExport(
14236
+ content,
14237
+ slug,
14238
+ label
14239
+ );
14031
14240
  if (rewrittenBuildExport) {
14032
14241
  return rewrittenBuildExport;
14033
14242
  }
@@ -14035,12 +14244,24 @@ function appendCustomPageToNavigation(content, slug, label, _options) {
14035
14244
  if (!range) {
14036
14245
  return content;
14037
14246
  }
14038
- const docsItems = findDocumentationCategoryItemsRange(content, range.open, range.close);
14247
+ const docsItems = findDocumentationCategoryItemsRange(
14248
+ content,
14249
+ range.open,
14250
+ range.close
14251
+ );
14039
14252
  if (docsItems) {
14040
- const entry = formatCustomPageNavigationEntry(slug, label, docsItems.indent);
14253
+ const entry = formatCustomPageNavigationEntry(
14254
+ slug,
14255
+ label,
14256
+ docsItems.indent
14257
+ );
14041
14258
  return appendEntryToArrayRange(content, docsItems, entry);
14042
14259
  }
14043
- const category = formatDocumentationCategoryWithPage(slug, label, `${range.indent} `);
14260
+ const category = formatDocumentationCategoryWithPage(
14261
+ slug,
14262
+ label,
14263
+ `${range.indent} `
14264
+ );
14044
14265
  return prependEntryToArrayRange(
14045
14266
  content,
14046
14267
  { open: range.open, close: range.close, indent: `${range.indent} ` },
@@ -14048,7 +14269,12 @@ function appendCustomPageToNavigation(content, slug, label, _options) {
14048
14269
  );
14049
14270
  }
14050
14271
  function patchApitogoConfigForNewPage(content, slug, label, options) {
14051
- const withNavigation = appendCustomPageToNavigation(content, slug, label, options);
14272
+ const withNavigation = appendCustomPageToNavigation(
14273
+ content,
14274
+ slug,
14275
+ label,
14276
+ options
14277
+ );
14052
14278
  if (withNavigation === content) {
14053
14279
  return content;
14054
14280
  }
@@ -14072,17 +14298,17 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label) {
14072
14298
 
14073
14299
  // src/cli/scaffold/wire-openapi.ts
14074
14300
  import { access as access6, readFile as readFile12, writeFile as writeFile11 } from "node:fs/promises";
14075
- import path47 from "node:path";
14301
+ import path48 from "node:path";
14076
14302
 
14077
14303
  // src/cli/platform/helpers/openapi.ts
14078
14304
  import { promises as fs6 } from "node:fs";
14079
- import path44 from "node:path";
14305
+ import path45 from "node:path";
14080
14306
  import SwaggerParser from "@apidevtools/swagger-parser";
14081
- import YAML from "yaml";
14307
+ import YAML2 from "yaml";
14082
14308
 
14083
14309
  // src/cli/scaffold/discover-openapi-files.ts
14084
14310
  import { promises as fs5 } from "node:fs";
14085
- import path43 from "node:path";
14311
+ import path44 from "node:path";
14086
14312
  var OPENAPI_BASENAMES = [
14087
14313
  "openapi.yaml",
14088
14314
  "openapi.yml",
@@ -14104,13 +14330,13 @@ var SKIP_OPENAPI_SEARCH_DIRS = /* @__PURE__ */ new Set([
14104
14330
  ".apitogo"
14105
14331
  ]);
14106
14332
  async function collectFromDir(dir, searchedDirectories, candidates) {
14107
- const resolved = path43.resolve(dir);
14333
+ const resolved = path44.resolve(dir);
14108
14334
  if (searchedDirectories.includes(resolved)) {
14109
14335
  return;
14110
14336
  }
14111
14337
  searchedDirectories.push(resolved);
14112
14338
  for (const name of OPENAPI_BASENAMES) {
14113
- const candidate = path43.join(resolved, name);
14339
+ const candidate = path44.join(resolved, name);
14114
14340
  try {
14115
14341
  await fs5.access(candidate);
14116
14342
  candidates.push(candidate);
@@ -14125,8 +14351,8 @@ async function collectSiteOpenApiFiles(siteDirectory) {
14125
14351
  return { candidates: [...new Set(candidates)], searchedDirectories };
14126
14352
  }
14127
14353
  async function collectNearbyOpenApiFiles(siteDirectory) {
14128
- const site = path43.resolve(siteDirectory);
14129
- const parent = path43.dirname(site);
14354
+ const site = path44.resolve(siteDirectory);
14355
+ const parent = path44.dirname(site);
14130
14356
  const searchedDirectories = [];
14131
14357
  const candidates = [];
14132
14358
  if (parent && parent !== site) {
@@ -14137,8 +14363,8 @@ async function collectNearbyOpenApiFiles(siteDirectory) {
14137
14363
  if (!entry.isDirectory() || SKIP_OPENAPI_SEARCH_DIRS.has(entry.name)) {
14138
14364
  continue;
14139
14365
  }
14140
- const sibling = path43.join(parent, entry.name);
14141
- if (path43.resolve(sibling) === site) {
14366
+ const sibling = path44.join(parent, entry.name);
14367
+ if (path44.resolve(sibling) === site) {
14142
14368
  continue;
14143
14369
  }
14144
14370
  await collectFromDir(sibling, searchedDirectories, candidates);
@@ -14166,7 +14392,7 @@ function parseOpenApiObject(doc) {
14166
14392
  };
14167
14393
  }
14168
14394
  function liteParseOpenApi(content, filePath) {
14169
- const ext = path43.extname(filePath).toLowerCase();
14395
+ const ext = path44.extname(filePath).toLowerCase();
14170
14396
  if (ext === ".json") {
14171
14397
  try {
14172
14398
  const parsed = JSON.parse(content);
@@ -14231,12 +14457,12 @@ async function discoverNearbyOpenApi(siteDirectory) {
14231
14457
  };
14232
14458
  }
14233
14459
  async function validateOpenApiFile(filePath) {
14234
- const resolved = path44.resolve(filePath);
14460
+ const resolved = path45.resolve(filePath);
14235
14461
  const content = await fs6.readFile(resolved, "utf8");
14236
- const ext = path44.extname(resolved).toLowerCase();
14462
+ const ext = path45.extname(resolved).toLowerCase();
14237
14463
  let parsed;
14238
14464
  try {
14239
- parsed = ext === ".json" ? JSON.parse(content) : YAML.parse(content);
14465
+ parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
14240
14466
  } catch (err) {
14241
14467
  return {
14242
14468
  valid: false,
@@ -14276,12 +14502,7 @@ async function validateOpenApiFile(filePath) {
14276
14502
 
14277
14503
  // src/cli/scaffold/scaffold-dev-portal-site.ts
14278
14504
  import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile10 } from "node:fs/promises";
14279
- import path46 from "node:path";
14280
-
14281
- // src/cli/scaffold/create-apitogo-cli.ts
14282
- import { spawn as spawn2 } from "node:child_process";
14283
- import { access as access5, rm as rm4 } from "node:fs/promises";
14284
- import path45 from "node:path";
14505
+ import path47 from "node:path";
14285
14506
 
14286
14507
  // src/cli/scaffold/apitogo-npm-packages.ts
14287
14508
  var DEFAULT_CREATE_APITOGO_PACKAGE2 = "@lukoweb/create-apitogo@latest";
@@ -14367,6 +14588,9 @@ var DOC_TOOL_LOCAL_PACKAGE_PATHS = {
14367
14588
  };
14368
14589
 
14369
14590
  // src/cli/scaffold/create-apitogo-cli.ts
14591
+ import { spawn as spawn2 } from "node:child_process";
14592
+ import { access as access5, rm as rm4 } from "node:fs/promises";
14593
+ import path46 from "node:path";
14370
14594
  var CreateApitogoCliError = class extends Error {
14371
14595
  exitCode;
14372
14596
  log;
@@ -14382,9 +14606,9 @@ function resolveDocToolPath(explicit) {
14382
14606
  return value || void 0;
14383
14607
  }
14384
14608
  function splitOutputDirectory(outputDirectory) {
14385
- const resolvedOutputDirectory = path45.resolve(outputDirectory);
14386
- const projectName = path45.basename(resolvedOutputDirectory);
14387
- const parentDirectory = path45.dirname(resolvedOutputDirectory);
14609
+ const resolvedOutputDirectory = path46.resolve(outputDirectory);
14610
+ const projectName = path46.basename(resolvedOutputDirectory);
14611
+ const parentDirectory = path46.dirname(resolvedOutputDirectory);
14388
14612
  if (!projectName || projectName === "." || projectName === "..") {
14389
14613
  throw new Error(
14390
14614
  "outputDirectory must be a named folder (e.g. ./dev-portal-site), not the current directory."
@@ -14410,7 +14634,7 @@ async function directoryHasEntries(dir) {
14410
14634
  }
14411
14635
  }
14412
14636
  function resolveLocalCreateApitogoEntry(docToolPath) {
14413
- return path45.resolve(docToolPath, "packages/create-zudoku/dist/index.js");
14637
+ return path46.resolve(docToolPath, "packages/create-zudoku/dist/index.js");
14414
14638
  }
14415
14639
  async function resolveCreateApitogoInvocation(options) {
14416
14640
  const docToolPath = resolveDocToolPath(options.docToolPath);
@@ -14492,7 +14716,7 @@ async function runCreateApitogoDevPortal(options) {
14492
14716
  result.exitCode
14493
14717
  );
14494
14718
  }
14495
- if (!await pathExists3(path45.join(resolvedOutputDirectory, "package.json"))) {
14719
+ if (!await pathExists3(path46.join(resolvedOutputDirectory, "package.json"))) {
14496
14720
  throw new CreateApitogoCliError(
14497
14721
  `create-apitogo completed but package.json was not found at ${resolvedOutputDirectory}`,
14498
14722
  result.output,
@@ -14541,14 +14765,17 @@ function ensureRequiredApitogoModules(pkg, options) {
14541
14765
  const moduleAliasVersion = resolveApitogoModuleVersionFromCore(coreVersion);
14542
14766
  for (const [moduleName, localPath] of [
14543
14767
  [landing, DOC_TOOL_LOCAL_PACKAGE_PATHS["@lukoweb/apitogo-module-landing"]],
14544
- [userPanel, DOC_TOOL_LOCAL_PACKAGE_PATHS["@lukoweb/apitogo-module-user-panel"]],
14768
+ [
14769
+ userPanel,
14770
+ DOC_TOOL_LOCAL_PACKAGE_PATHS["@lukoweb/apitogo-module-user-panel"]
14771
+ ],
14545
14772
  [
14546
14773
  billing,
14547
14774
  DOC_TOOL_LOCAL_PACKAGE_PATHS["@lukoweb/apitogo-plugin-dev-portal-billing"]
14548
14775
  ]
14549
14776
  ]) {
14550
14777
  if (options.docToolPath) {
14551
- deps[moduleName] = `file:${path46.join(options.docToolPath, localPath)}`;
14778
+ deps[moduleName] = `file:${path47.join(options.docToolPath, localPath)}`;
14552
14779
  continue;
14553
14780
  }
14554
14781
  deps[moduleName] = `npm:${moduleName}@${moduleAliasVersion}`;
@@ -14575,9 +14802,11 @@ function rewritePackageJson(raw, options) {
14575
14802
  for (const section of ["dependencies", "devDependencies"]) {
14576
14803
  const deps = pkg[section];
14577
14804
  if (!deps) continue;
14578
- for (const [dep, relativePath] of Object.entries(DOC_TOOL_LOCAL_PACKAGE_PATHS)) {
14805
+ for (const [dep, relativePath] of Object.entries(
14806
+ DOC_TOOL_LOCAL_PACKAGE_PATHS
14807
+ )) {
14579
14808
  if (dep in deps) {
14580
- deps[dep] = `file:${path46.join(options.docToolPath, relativePath)}`;
14809
+ deps[dep] = `file:${path47.join(options.docToolPath, relativePath)}`;
14581
14810
  linkedWorkspacePackage = true;
14582
14811
  }
14583
14812
  }
@@ -14634,7 +14863,10 @@ function patchApisConfig(content, config2) {
14634
14863
  return insertApisConfig(content, config2);
14635
14864
  }
14636
14865
  const asArray = match[0].includes("apis: [");
14637
- return content.replace(APIS_CONFIG_PATTERN, renderApisConfig(config2, asArray));
14866
+ return content.replace(
14867
+ APIS_CONFIG_PATTERN,
14868
+ renderApisConfig(config2, asArray)
14869
+ );
14638
14870
  }
14639
14871
  function patchApitogoConfigOpenApiToFile(content, input5 = "./openapi.json") {
14640
14872
  return patchApisConfig(content, {
@@ -14674,23 +14906,23 @@ function patchDashboardPageConfig(content) {
14674
14906
  );
14675
14907
  }
14676
14908
  async function ensureAccountPlansPage(outputDirectory) {
14677
- const pagePath = path46.join(outputDirectory, ACCOUNT_PLANS_PAGE_PATH);
14909
+ const pagePath = path47.join(outputDirectory, ACCOUNT_PLANS_PAGE_PATH);
14678
14910
  try {
14679
14911
  await readFile11(pagePath, "utf8");
14680
14912
  return void 0;
14681
14913
  } catch {
14682
- await mkdir8(path46.dirname(pagePath), { recursive: true });
14914
+ await mkdir8(path47.dirname(pagePath), { recursive: true });
14683
14915
  await writeFile10(pagePath, ACCOUNT_PLANS_PAGE);
14684
14916
  return ACCOUNT_PLANS_PAGE_PATH;
14685
14917
  }
14686
14918
  }
14687
14919
  async function ensureDashboardPage(outputDirectory) {
14688
- const pagePath = path46.join(outputDirectory, DASHBOARD_PAGE_PATH);
14920
+ const pagePath = path47.join(outputDirectory, DASHBOARD_PAGE_PATH);
14689
14921
  try {
14690
14922
  await readFile11(pagePath, "utf8");
14691
14923
  return void 0;
14692
14924
  } catch {
14693
- await mkdir8(path46.dirname(pagePath), { recursive: true });
14925
+ await mkdir8(path47.dirname(pagePath), { recursive: true });
14694
14926
  await writeFile10(pagePath, DASHBOARD_PAGE);
14695
14927
  return DASHBOARD_PAGE_PATH;
14696
14928
  }
@@ -14716,13 +14948,20 @@ function patchApitogoConfig(content, options) {
14716
14948
  });
14717
14949
  }
14718
14950
  if (options.logoUrl) {
14719
- if (/logo:\s*\{/.test(patched)) {
14720
- patched = patched.replace(/logo:\s*\{[\s\S]*?\},/m, `logo: {
14951
+ const logoBlockRegex = /logo:\s*\{[\s\S]*?\},/m;
14952
+ const existingBlock = patched.match(logoBlockRegex)?.[0];
14953
+ if (existingBlock) {
14954
+ const darkMatch = existingBlock.match(/dark:\s*"([^"]*)"/);
14955
+ const darkLine = darkMatch ? `
14956
+ dark: "${darkMatch[1]}",` : "";
14957
+ patched = patched.replace(
14958
+ logoBlockRegex,
14959
+ `logo: {
14721
14960
  src: {
14722
- light: "${options.logoUrl}",
14723
- dark: "${options.logoUrl}",
14961
+ light: "${options.logoUrl}",${darkLine}
14724
14962
  },
14725
- },`);
14963
+ },`
14964
+ );
14726
14965
  } else {
14727
14966
  patched = patched.replace(
14728
14967
  /site:\s*\{/,
@@ -14730,7 +14969,6 @@ function patchApitogoConfig(content, options) {
14730
14969
  logo: {
14731
14970
  src: {
14732
14971
  light: "${options.logoUrl}",
14733
- dark: "${options.logoUrl}",
14734
14972
  },
14735
14973
  },`
14736
14974
  );
@@ -14741,7 +14979,7 @@ function patchApitogoConfig(content, options) {
14741
14979
  async function patchScaffoldedSite(outputDirectory, options) {
14742
14980
  const title = options.branding?.title ?? options.siteName;
14743
14981
  const filesWritten = [];
14744
- const packageJsonPath = path46.join(outputDirectory, "package.json");
14982
+ const packageJsonPath = path47.join(outputDirectory, "package.json");
14745
14983
  if (options.patchPackageJson !== false) {
14746
14984
  const packageJson3 = await readFile11(packageJsonPath, "utf8");
14747
14985
  await writeFile10(
@@ -14754,7 +14992,7 @@ async function patchScaffoldedSite(outputDirectory, options) {
14754
14992
  );
14755
14993
  filesWritten.push("package.json");
14756
14994
  }
14757
- const configPath = path46.join(outputDirectory, "apitogo.config.tsx");
14995
+ const configPath = path47.join(outputDirectory, "apitogo.config.tsx");
14758
14996
  const configContent = await readFile11(configPath, "utf8");
14759
14997
  await writeFile10(
14760
14998
  configPath,
@@ -14849,8 +15087,8 @@ async function discoverOpenApiByMode(siteDirectory, mode) {
14849
15087
  return discoverNearbyOpenApi(siteDirectory);
14850
15088
  }
14851
15089
  async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
14852
- const resolvedSource = path47.resolve(sourceDirectory);
14853
- const resolvedOpenApi = path47.resolve(openApiPath);
15090
+ const resolvedSource = path48.resolve(sourceDirectory);
15091
+ const resolvedOpenApi = path48.resolve(openApiPath);
14854
15092
  const input5 = toSiteRelativeOpenApiInput(resolvedSource, resolvedOpenApi);
14855
15093
  const validation = await validateOpenApiFile(resolvedOpenApi);
14856
15094
  if (!validation.valid) {
@@ -14875,7 +15113,7 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
14875
15113
  "utf8"
14876
15114
  );
14877
15115
  } else {
14878
- const tsxConfigPath = path47.join(resolvedSource, "apitogo.config.tsx");
15116
+ const tsxConfigPath = path48.join(resolvedSource, "apitogo.config.tsx");
14879
15117
  const configContent = await readFile12(tsxConfigPath, "utf8");
14880
15118
  try {
14881
15119
  const patched = patchApitogoConfigOpenApiToFile(configContent, input5);
@@ -14961,12 +15199,12 @@ async function discoverAndWireOpenApi(siteDirectory, mode) {
14961
15199
  }
14962
15200
 
14963
15201
  // src/cli/make-config/interactive.ts
14964
- import { createInterface } from "node:readline/promises";
15202
+ import path50 from "node:path";
14965
15203
  import { stdin as input, stdout as output } from "node:process";
14966
- import path49 from "node:path";
15204
+ import { createInterface } from "node:readline/promises";
14967
15205
 
14968
15206
  // src/cli/make-config/json-config.ts
14969
- import path48 from "node:path";
15207
+ import path49 from "node:path";
14970
15208
 
14971
15209
  // src/cli/make-config/branding-assets.ts
14972
15210
  var LOGO_LIGHT = '<svg xmlns="http://www.w3.org/2000/svg" width="201.57" height="72.66" viewBox="-0.09 -46.99 201.57 72.66">\n <path d="M13.5 0.39Q8.03 0.39 4.76 -2.09Q1.48 -4.57 1.41 -9.61H8.03Q8.14 -7.48 9.6 -6.41Q11.05 -5.33 13.42 -5.33Q15.57 -5.33 16.8 -6.2Q18.03 -7.07 18.03 -8.46Q18.03 -9.71 16.87 -10.44Q15.7 -11.17 13.32 -11.72L10.23 -12.44Q6.46 -13.3 4.32 -15.25Q2.17 -17.21 2.17 -20.49Q2.17 -23.18 3.62 -25.21Q5.08 -27.23 7.64 -28.36Q10.2 -29.49 13.5 -29.49Q16.88 -29.49 19.36 -28.35Q21.84 -27.21 23.2 -25.17Q24.57 -23.12 24.61 -20.43H17.99Q17.85 -22.01 16.7 -22.89Q15.55 -23.77 13.48 -23.77Q11.41 -23.77 10.35 -22.97Q9.3 -22.17 9.3 -20.96Q9.3 -19.61 10.47 -18.88Q11.64 -18.14 13.59 -17.71L16.11 -17.13Q20.33 -16.19 22.67 -14.06Q25 -11.93 25 -8.44Q25 -4.3 21.95 -1.95Q18.91 0.39 13.5 0.39Z M32.97 0.39Q29.82 0.39 27.8 -1.21Q25.78 -2.81 25.78 -6.05Q25.78 -8.48 26.89 -9.9Q28 -11.33 29.85 -12.01Q31.7 -12.7 33.9 -12.89Q36.68 -13.12 37.88 -13.47Q39.08 -13.81 39.08 -14.82V-14.9Q39.08 -16.04 38.29 -16.65Q37.5 -17.27 36.11 -17.27Q34.68 -17.27 33.77 -16.66Q32.85 -16.05 32.58 -15.02L26.4 -15.82Q27.05 -18.61 29.56 -20.36Q32.07 -22.11 36.17 -22.11Q38.88 -22.11 41.08 -21.27Q43.28 -20.43 44.58 -18.8Q45.88 -17.17 45.88 -14.82V0H39.47V-3.07H39.29Q37.48 0.39 32.97 0.39ZM35.04 -4.12Q36.75 -4.12 37.95 -5.14Q39.14 -6.15 39.14 -7.77V-9.86Q38.55 -9.55 37.44 -9.34Q36.33 -9.12 35.37 -8.96Q32.22 -8.5 32.22 -6.37Q32.22 -5.27 33.01 -4.7Q33.81 -4.12 35.04 -4.12Z M48.59 0V-21.84H54.86L55.17 -17.5Q56.07 -19.96 57.78 -21.04Q59.49 -22.13 61.46 -22.13Q63.53 -22.13 64.91 -20.94Q66.28 -19.75 67.08 -17.07Q68.08 -19.77 70.02 -20.95Q71.97 -22.13 74.17 -22.13Q77.24 -22.13 79.2 -20.16Q81.17 -18.18 81.17 -14.73V0H74.37V-13.18Q74.37 -14.84 73.5 -15.7Q72.63 -16.56 71.28 -16.56Q69.84 -16.56 69 -15.62Q68.16 -14.67 68.16 -13.11V0H61.61V-13.28Q61.61 -14.79 60.77 -15.67Q59.92 -16.56 58.57 -16.56Q57.18 -16.56 56.29 -15.62Q55.4 -14.69 55.4 -12.97V0Z M83.88 8.16V-21.84H90.62V-18.09H90.85Q91.26 -19.06 92.04 -19.99Q92.82 -20.92 94.05 -21.51Q95.28 -22.11 97.02 -22.11Q99.33 -22.11 101.31 -20.9Q103.29 -19.69 104.51 -17.21Q105.73 -14.73 105.73 -10.9Q105.73 -7.19 104.55 -4.7Q103.37 -2.21 101.39 -0.95Q99.4 0.31 96.98 0.31Q94.48 0.31 92.99 -0.87Q91.49 -2.05 90.85 -3.54H90.69V8.16ZM94.66 -5Q96.63 -5 97.68 -6.62Q98.72 -8.24 98.72 -10.9Q98.72 -13.55 97.68 -15.17Q96.63 -16.78 94.66 -16.78Q92.71 -16.78 91.62 -15.2Q90.54 -13.61 90.54 -10.9Q90.54 -8.22 91.62 -6.61Q92.71 -5 94.66 -5Z M114.4 -29.1V0H107.59V-29.1Z M127.29 0.41Q122.15 0.41 119.21 -2.59Q116.27 -5.59 116.27 -10.84Q116.27 -14.2 117.59 -16.74Q118.91 -19.28 121.33 -20.69Q123.75 -22.11 127.04 -22.11Q130.1 -22.11 132.47 -20.83Q134.85 -19.55 136.21 -17.06Q137.56 -14.57 137.56 -10.98V-9.26H122.97Q123.07 -6.89 124.27 -5.71Q125.47 -4.53 127.41 -4.53Q128.77 -4.53 129.77 -5.11Q130.77 -5.68 131.18 -6.78L137.25 -5.78Q136.43 -2.95 133.84 -1.27Q131.25 0.41 127.29 0.41ZM123.01 -13.22H131.1Q130.9 -15.08 129.89 -16.12Q128.87 -17.17 127.09 -17.17Q125.3 -17.17 124.25 -16.1Q123.21 -15.04 123.01 -13.22Z" fill="#202633"/>\n <path d="M138.48 0 148.19 -29.1H157.43L167.43 0H159.69L157.72 -6.19H148.03L146.14 0ZM149.65 -11.46H156.04L155.65 -12.71Q154.89 -15.21 154.16 -17.95Q153.44 -20.68 152.74 -23.61Q152.07 -20.66 151.42 -17.93Q150.77 -15.2 150.04 -12.71Z M168.81 0V-29.1H180.72Q184 -29.1 186.39 -27.83Q188.77 -26.56 190.05 -24.3Q191.33 -22.03 191.33 -19.04Q191.33 -16.04 190.02 -13.8Q188.71 -11.56 186.29 -10.32Q183.87 -9.08 180.53 -9.08H175.7V0ZM175.7 -14.53H179.28Q181.7 -14.53 182.94 -15.77Q184.18 -17.01 184.18 -19.04Q184.18 -21.09 182.93 -22.3Q181.68 -23.52 179.26 -23.52H175.7Z M199.98 -29.1V0H193.08V-29.1Z" fill="#008A7D"/>\n</svg>\n';
@@ -15345,9 +15583,9 @@ function openapiInputForConfig(specRelativePosix) {
15345
15583
  return `./${specRelativePosix}`;
15346
15584
  }
15347
15585
  function toRelativeOpenApiInput(projectDir, inputPath) {
15348
- const resolved = path48.isAbsolute(inputPath) ? inputPath : path48.resolve(projectDir, inputPath);
15349
- const relative = path48.relative(projectDir, resolved);
15350
- const posix = relative.split(path48.sep).join(path48.posix.sep);
15586
+ const resolved = path49.isAbsolute(inputPath) ? inputPath : path49.resolve(projectDir, inputPath);
15587
+ const relative = path49.relative(projectDir, resolved);
15588
+ const posix = relative.split(path49.sep).join(path49.posix.sep);
15351
15589
  if (!posix) {
15352
15590
  throw new Error("OpenAPI path must point to a file.");
15353
15591
  }
@@ -15374,6 +15612,11 @@ function buildPlanItemFromWizard(input5) {
15374
15612
  priceAmount: input5.priceAmount,
15375
15613
  limits: buildLimits(input5.rateLimitCalls, input5.monthlyQuota)
15376
15614
  };
15615
+ if (input5.pricingModel === "custom") {
15616
+ item.priceAmount = 0;
15617
+ item.pricingModel = "custom";
15618
+ return item;
15619
+ }
15377
15620
  if (input5.priceAmount > 0) {
15378
15621
  item.priceCurrency = "usd";
15379
15622
  item.billingPeriod = "month";
@@ -15550,11 +15793,11 @@ ${customPages}
15550
15793
  // src/cli/make-config/wrap-up.ts
15551
15794
  var DOCS = "https://docs.apitogo.com";
15552
15795
  function printPageAddedHint(relativePath, routePath) {
15553
- printDiagnosticsToConsole(`Page created: ${relativePath} (route ${routePath})`);
15554
- printDiagnosticsToConsole("Edit that file to change the content.");
15555
15796
  printDiagnosticsToConsole(
15556
- `Predefined components: ${DOCS}/docs/components`
15797
+ `Page created: ${relativePath} (route ${routePath})`
15557
15798
  );
15799
+ printDiagnosticsToConsole("Edit that file to change the content.");
15800
+ printDiagnosticsToConsole(`Predefined components: ${DOCS}/docs/components`);
15558
15801
  }
15559
15802
  function printPlanAddedHint(displayName) {
15560
15803
  printDiagnosticsToConsole(
@@ -15584,9 +15827,13 @@ function printInteractiveWrapUp(hasPaidPlans) {
15584
15827
  printDiagnosticsToConsole("");
15585
15828
  printDiagnosticsToConsole("Configure more:");
15586
15829
  printDiagnosticsToConsole(`- Overview: ${DOCS}/docs/configuration/overview`);
15587
- printDiagnosticsToConsole(`- Gateway plans: ${DOCS}/docs/configuration/plans`);
15830
+ printDiagnosticsToConsole(
15831
+ `- Gateway plans: ${DOCS}/docs/configuration/plans`
15832
+ );
15588
15833
  printDiagnosticsToConsole(`- Custom pages: ${DOCS}/docs/guides/custom-pages`);
15589
- printDiagnosticsToConsole(`- API reference: ${DOCS}/docs/configuration/api-reference`);
15834
+ printDiagnosticsToConsole(
15835
+ `- API reference: ${DOCS}/docs/configuration/api-reference`
15836
+ );
15590
15837
  printDiagnosticsToConsole(`- Theme: ${DOCS}/docs/customization/colors-theme`);
15591
15838
  printDiagnosticsToConsole(`- Fonts: ${DOCS}/docs/customization/fonts`);
15592
15839
  printDiagnosticsToConsole(`- CLI shape: ${DOCS}/docs/cli/shape`);
@@ -15672,9 +15919,7 @@ async function promptMakeConfigInteractive(options) {
15672
15919
  );
15673
15920
  runtimeVersion = parseRuntimeVersion(runtime, versionRaw);
15674
15921
  if (!runtimeVersion) {
15675
- printDiagnosticsToConsole(
15676
- `Choose one of: ${versions.join(", ")}.`
15677
- );
15922
+ printDiagnosticsToConsole(`Choose one of: ${versions.join(", ")}.`);
15678
15923
  }
15679
15924
  }
15680
15925
  const backend = {
@@ -15720,7 +15965,7 @@ async function promptMakeConfigInteractive(options) {
15720
15965
  continue;
15721
15966
  }
15722
15967
  const routePath = `/${slug}`;
15723
- const relativePath = path49.posix.join(options.pagesDir, `${slug}.tsx`);
15968
+ const relativePath = path50.posix.join(options.pagesDir, `${slug}.tsx`);
15724
15969
  const page = {
15725
15970
  slug,
15726
15971
  title: pageTitle.trim(),
@@ -15736,11 +15981,13 @@ async function promptMakeConfigInteractive(options) {
15736
15981
  printDiagnosticsToConsole("Plan title is required.");
15737
15982
  continue;
15738
15983
  }
15739
- const priceAmount = await askNonNegativeNumber(
15984
+ const pricingAnswer = (await ask(
15740
15985
  rl,
15741
- "Price (USD/month, 0 for free)",
15742
- 0
15743
- );
15986
+ "Pricing: fixed price or custom (contact sales)? (fixed/custom)",
15987
+ "fixed"
15988
+ )).toLowerCase();
15989
+ const pricingModel = pricingAnswer.startsWith("c") ? "custom" : "fixed";
15990
+ const priceAmount = pricingModel === "custom" ? 0 : await askNonNegativeNumber(rl, "Price (USD/month, 0 for free)", 0);
15744
15991
  const rateLimitCalls = await askNonNegativeNumber(
15745
15992
  rl,
15746
15993
  "Rate limit (calls per minute)",
@@ -15754,6 +16001,7 @@ async function promptMakeConfigInteractive(options) {
15754
16001
  const planItem = buildPlanItemFromWizard({
15755
16002
  displayName: displayName.trim(),
15756
16003
  priceAmount,
16004
+ pricingModel,
15757
16005
  rateLimitCalls,
15758
16006
  monthlyQuota
15759
16007
  });
@@ -15813,7 +16061,7 @@ export default function ${componentName}() {
15813
16061
  init_package_json();
15814
16062
  import { constants } from "node:fs";
15815
16063
  import { access as access7, mkdir as mkdir9, readFile as readFile13, writeFile as writeFile12 } from "node:fs/promises";
15816
- import path50 from "node:path";
16064
+ import path51 from "node:path";
15817
16065
  import { glob as glob4 } from "glob";
15818
16066
 
15819
16067
  // src/cli/make-config/favicon-png.ts
@@ -15828,9 +16076,9 @@ var OPENAPI_GLOBS = [
15828
16076
  "openapi.yaml",
15829
16077
  "openapi.yml"
15830
16078
  ];
15831
- var toPosix = (p) => p.split(path50.sep).join("/");
16079
+ var toPosix = (p) => p.split(path51.sep).join("/");
15832
16080
  var sanitizePackageName = (dir) => {
15833
- const base = path50.basename(path50.resolve(dir));
16081
+ const base = path51.basename(path51.resolve(dir));
15834
16082
  const s = base.toLowerCase().replace(/[^a-z0-9-_.]/g, "-").replace(/^-+|-+$/g, "");
15835
16083
  return s || "apitogo-site";
15836
16084
  };
@@ -15840,7 +16088,7 @@ var apitogoVersionRange = () => {
15840
16088
  };
15841
16089
  async function findOpenApiSpecPath(dir) {
15842
16090
  for (const rel of OPENAPI_GLOBS) {
15843
- const full = path50.join(dir, rel);
16091
+ const full = path51.join(dir, rel);
15844
16092
  try {
15845
16093
  await access7(full, constants.R_OK);
15846
16094
  return toPosix(rel);
@@ -15855,7 +16103,7 @@ async function findOpenApiSpecPath(dir) {
15855
16103
  return extra[0] ? toPosix(extra[0]) : null;
15856
16104
  }
15857
16105
  async function ensurePackageJson(dir) {
15858
- const pkgPath = path50.join(dir, "package.json");
16106
+ const pkgPath = path51.join(dir, "package.json");
15859
16107
  const apitogoVer = apitogoVersionRange();
15860
16108
  const baseDeps = {
15861
16109
  react: ">=19.0.0",
@@ -15922,7 +16170,7 @@ async function ensurePackageJson(dir) {
15922
16170
  return false;
15923
16171
  }
15924
16172
  async function ensureTsconfigJson(dir) {
15925
- const tsPath = path50.join(dir, "tsconfig.json");
16173
+ const tsPath = path51.join(dir, "tsconfig.json");
15926
16174
  try {
15927
16175
  await access7(tsPath, constants.R_OK);
15928
16176
  return false;
@@ -15971,40 +16219,40 @@ var fileExists2 = async (filePath) => {
15971
16219
  }
15972
16220
  };
15973
16221
  async function ensurePortalStubFiles(dir) {
15974
- const landingPath = path50.join(dir, "landing", "LandingPage.tsx");
16222
+ const landingPath = path51.join(dir, "landing", "LandingPage.tsx");
15975
16223
  if (!await fileExists2(landingPath)) {
15976
- await mkdir9(path50.join(dir, "landing"), { recursive: true });
16224
+ await mkdir9(path51.join(dir, "landing"), { recursive: true });
15977
16225
  await writeFile12(landingPath, LANDING_PAGE_SOURCE, "utf8");
15978
16226
  }
15979
- const plansPath = path50.join(dir, "user-panel", "AccountPlansPage.tsx");
16227
+ const plansPath = path51.join(dir, "user-panel", "AccountPlansPage.tsx");
15980
16228
  if (!await fileExists2(plansPath)) {
15981
- await mkdir9(path50.join(dir, "user-panel"), { recursive: true });
16229
+ await mkdir9(path51.join(dir, "user-panel"), { recursive: true });
15982
16230
  await writeFile12(plansPath, ACCOUNT_PLANS_PAGE_SOURCE, "utf8");
15983
16231
  }
15984
- const dashboardPath = path50.join(dir, "user-panel", "DashboardPage.tsx");
16232
+ const dashboardPath = path51.join(dir, "user-panel", "DashboardPage.tsx");
15985
16233
  if (!await fileExists2(dashboardPath)) {
15986
- await mkdir9(path50.join(dir, "user-panel"), { recursive: true });
16234
+ await mkdir9(path51.join(dir, "user-panel"), { recursive: true });
15987
16235
  await writeFile12(dashboardPath, DASHBOARD_PAGE_SOURCE, "utf8");
15988
16236
  }
15989
16237
  await ensurePublicBrandingFiles(dir);
15990
16238
  }
15991
16239
  async function ensurePublicBrandingFiles(dir) {
15992
- const destDir = path50.join(dir, "public");
16240
+ const destDir = path51.join(dir, "public");
15993
16241
  await mkdir9(destDir, { recursive: true });
15994
16242
  for (const { name, content } of BRANDING_ASSETS) {
15995
- const dest = path50.join(destDir, name);
16243
+ const dest = path51.join(destDir, name);
15996
16244
  if (await fileExists2(dest)) {
15997
16245
  continue;
15998
16246
  }
15999
16247
  await writeFile12(dest, content, "utf8");
16000
16248
  }
16001
- const faviconDest = path50.join(destDir, "Icon.png");
16249
+ const faviconDest = path51.join(destDir, "Icon.png");
16002
16250
  if (!await fileExists2(faviconDest)) {
16003
16251
  await writeFile12(faviconDest, Buffer.from(FAVICON_PNG_BASE64, "base64"));
16004
16252
  }
16005
16253
  }
16006
16254
  async function ensureThinConfigTsx(dir) {
16007
- const tsxPath = path50.join(dir, "apitogo.config.tsx");
16255
+ const tsxPath = path51.join(dir, "apitogo.config.tsx");
16008
16256
  if (await fileExists2(tsxPath)) {
16009
16257
  return false;
16010
16258
  }
@@ -16012,137 +16260,6 @@ async function ensureThinConfigTsx(dir) {
16012
16260
  return true;
16013
16261
  }
16014
16262
 
16015
- // src/cli/platform/helpers/openapi-action-keys.ts
16016
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
16017
- import path51 from "node:path";
16018
- import YAML2 from "yaml";
16019
- var HTTP_METHODS = [
16020
- "get",
16021
- "post",
16022
- "put",
16023
- "patch",
16024
- "delete",
16025
- "head",
16026
- "options"
16027
- ];
16028
- function normalizeOpenApiPathTemplate(pathTemplate) {
16029
- if (!pathTemplate.trim()) {
16030
- return "/";
16031
- }
16032
- let value = pathTemplate.trim();
16033
- if (!value.startsWith("/")) {
16034
- value = `/${value}`;
16035
- }
16036
- if (value.length > 1 && value.endsWith("/")) {
16037
- value = value.replace(/\/+$/, "");
16038
- }
16039
- return value;
16040
- }
16041
- function resolveRelativeServerBasePath(document) {
16042
- const serverUrl = document.servers?.[0]?.url;
16043
- if (!serverUrl || serverUrl === "/") {
16044
- return null;
16045
- }
16046
- return serverUrl.startsWith("/") ? serverUrl.replace(/\/+$/, "") : null;
16047
- }
16048
- function combineBasePath(basePath, pathKey) {
16049
- const normalizedPath = normalizeOpenApiPathTemplate(pathKey);
16050
- if (!basePath || basePath === "/") {
16051
- return normalizedPath;
16052
- }
16053
- const baseNorm = normalizeOpenApiPathTemplate(basePath);
16054
- if (normalizedPath === "/") {
16055
- return baseNorm;
16056
- }
16057
- return `${baseNorm.replace(/\/+$/, "")}${normalizedPath}`;
16058
- }
16059
- function extractGatewayActionKeysFromOpenApiDocument(document) {
16060
- if (!document || typeof document !== "object") {
16061
- return [];
16062
- }
16063
- const spec = document;
16064
- if (!spec.paths || typeof spec.paths !== "object") {
16065
- return [];
16066
- }
16067
- const basePath = resolveRelativeServerBasePath(spec);
16068
- const keys = /* @__PURE__ */ new Set();
16069
- for (const [pathKey, pathItem] of Object.entries(spec.paths)) {
16070
- if (!pathItem || typeof pathItem !== "object") {
16071
- continue;
16072
- }
16073
- const pathTemplate = combineBasePath(basePath, pathKey);
16074
- for (const method of HTTP_METHODS) {
16075
- if (!pathItem[method] || typeof pathItem[method] !== "object") {
16076
- continue;
16077
- }
16078
- keys.add(`${method}:${pathTemplate}`);
16079
- }
16080
- }
16081
- return [...keys].sort((a, b) => a.localeCompare(b));
16082
- }
16083
- function extractGatewayActionKeysFromOpenApiFile(filePath) {
16084
- try {
16085
- const raw = readFileSync3(filePath, "utf8");
16086
- const ext = path51.extname(filePath).toLowerCase();
16087
- const parsed = ext === ".json" ? JSON.parse(raw) : YAML2.parse(raw);
16088
- return extractGatewayActionKeysFromOpenApiDocument(parsed);
16089
- } catch {
16090
- return [];
16091
- }
16092
- }
16093
- function resolveConfigOpenApiFilePath(siteDirectory, config2) {
16094
- const backend = config2.backend;
16095
- const candidates = [];
16096
- if (typeof backend?.openApiPath === "string" && backend.openApiPath.trim()) {
16097
- candidates.push(backend.openApiPath.trim());
16098
- }
16099
- const apis = config2.apis;
16100
- const entries = Array.isArray(apis) ? apis : apis ? [apis] : [];
16101
- for (const entry of entries) {
16102
- if (entry && typeof entry === "object" && entry.type === "file" && typeof entry.input === "string") {
16103
- candidates.push(entry.input);
16104
- }
16105
- }
16106
- for (const candidate of candidates) {
16107
- const resolved = path51.isAbsolute(candidate) ? candidate : path51.resolve(siteDirectory, candidate);
16108
- if (existsSync5(resolved)) {
16109
- return resolved;
16110
- }
16111
- }
16112
- return void 0;
16113
- }
16114
- function loadOpenApiActionKeysForSite(siteDirectory, config2) {
16115
- const filePath = resolveConfigOpenApiFilePath(siteDirectory, config2);
16116
- if (!filePath) {
16117
- return [];
16118
- }
16119
- return extractGatewayActionKeysFromOpenApiFile(filePath);
16120
- }
16121
- function loadOpenApiActionKeysFromSiteDirectory(siteDirectory) {
16122
- const configPath = path51.join(siteDirectory, "apitogo.config.json");
16123
- if (!existsSync5(configPath)) {
16124
- return [];
16125
- }
16126
- try {
16127
- const parsed = JSON.parse(readFileSync3(configPath, "utf8"));
16128
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16129
- return [];
16130
- }
16131
- return loadOpenApiActionKeysForSite(siteDirectory, parsed);
16132
- } catch {
16133
- return [];
16134
- }
16135
- }
16136
- function withDefaultIncludedActionKeys(plan, actionKeys) {
16137
- if (plan.includedActionKeys && plan.includedActionKeys.length > 0) {
16138
- return plan;
16139
- }
16140
- if (actionKeys.length === 0) {
16141
- return plan;
16142
- }
16143
- return { ...plan, includedActionKeys: actionKeys };
16144
- }
16145
-
16146
16263
  // src/cli/make-config/handler.ts
16147
16264
  var toPosixPath2 = (value) => value.split(path52.sep).join(path52.posix.sep);
16148
16265
  var fileExists3 = async (filePath) => {
@@ -16272,7 +16389,10 @@ async function makeConfig(argv) {
16272
16389
  const flagPatches = {
16273
16390
  site: parseJsonObjectFlag(argv.site, "site"),
16274
16391
  theme: parseJsonObjectFlag(argv.theme, "theme"),
16275
- authentication: parseJsonObjectFlag(argv.authentication, "authentication"),
16392
+ authentication: parseJsonObjectFlag(
16393
+ argv.authentication,
16394
+ "authentication"
16395
+ ),
16276
16396
  title: argv.title
16277
16397
  };
16278
16398
  const persistCtx = {
@@ -16747,7 +16867,9 @@ async function sha256File(filePath) {
16747
16867
  }
16748
16868
  async function hashDirectoryContents(rootDirectory) {
16749
16869
  const resolved = path54.resolve(rootDirectory);
16750
- const files = (await collectPackageableFiles(resolved)).sort((a, b) => a.localeCompare(b));
16870
+ const files = (await collectPackageableFiles(resolved)).sort(
16871
+ (a, b) => a.localeCompare(b)
16872
+ );
16751
16873
  const hash2 = createHash2("sha256");
16752
16874
  for (const rel of files) {
16753
16875
  hash2.update(rel);
@@ -16820,7 +16942,9 @@ async function syncDevPortalPlanCatalog(options) {
16820
16942
  signal: controller.signal
16821
16943
  });
16822
16944
  if (!response.ok) {
16823
- throw new Error(`GET /api/v1/plans?refresh=true&force=true returned ${response.status}.`);
16945
+ throw new Error(
16946
+ `GET /api/v1/plans?refresh=true&force=true returned ${response.status}.`
16947
+ );
16824
16948
  }
16825
16949
  const catalog = await response.json();
16826
16950
  if (options.expectedPlanCount !== void 0 && catalog.plans.length < options.expectedPlanCount) {
@@ -16954,7 +17078,10 @@ function arraysEquivalentForAuth(existing, desired) {
16954
17078
  if (isPlatformManagedAuthMode(desired.authMode)) {
16955
17079
  return true;
16956
17080
  }
16957
- return (existing.oidcMetadataUrl ?? "").trim() === (desired.oidcMetadataUrl ?? "").trim() && sameStringList(existing.oidcAllowedIssuers, desired.oidcAllowedIssuers ?? void 0) && sameStringList(existing.oidcAudiences, desired.oidcAudiences ?? void 0);
17081
+ return (existing.oidcMetadataUrl ?? "").trim() === (desired.oidcMetadataUrl ?? "").trim() && sameStringList(
17082
+ existing.oidcAllowedIssuers,
17083
+ desired.oidcAllowedIssuers ?? void 0
17084
+ ) && sameStringList(existing.oidcAudiences, desired.oidcAudiences ?? void 0);
16958
17085
  }
16959
17086
  async function ensureGatewayAuthConfigured(client, projectId, desired) {
16960
17087
  let existing = null;
@@ -17003,7 +17130,7 @@ function normalizeActionKey(actionKey) {
17003
17130
  if (trimmed.includes(":")) {
17004
17131
  const [method2, ...pathParts] = trimmed.split(":");
17005
17132
  const path73 = pathParts.join(":").trim();
17006
- return `${method2.toLowerCase()}:${path73.startsWith("/") ? path73 : `/${path73}`}`;
17133
+ return `${method2?.toLowerCase()}:${path73.startsWith("/") ? path73 : `/${path73}`}`;
17007
17134
  }
17008
17135
  const spaceIndex = trimmed.indexOf(" ");
17009
17136
  if (spaceIndex <= 0) {
@@ -17024,12 +17151,16 @@ async function applyGatewayPublicRoutes(client, projectId, routes, options) {
17024
17151
  }
17025
17152
  let knownKeys = null;
17026
17153
  if (options?.knownActions) {
17027
- knownKeys = new Set(options.knownActions.map((action) => action.actionKey.toLowerCase()));
17154
+ knownKeys = new Set(
17155
+ options.knownActions.map((action) => action.actionKey.toLowerCase())
17156
+ );
17028
17157
  } else {
17029
17158
  try {
17030
17159
  const listed = await listAllGatewayActions(client, projectId);
17031
17160
  if (listed.length > 0) {
17032
- knownKeys = new Set(listed.map((action) => action.actionKey.toLowerCase()));
17161
+ knownKeys = new Set(
17162
+ listed.map((action) => action.actionKey.toLowerCase())
17163
+ );
17033
17164
  }
17034
17165
  } catch {
17035
17166
  knownKeys = null;
@@ -17054,7 +17185,11 @@ async function applyGatewayPublicRoutes(client, projectId, routes, options) {
17054
17185
  continue;
17055
17186
  }
17056
17187
  try {
17057
- const action = await client.setGatewayActionPublic(projectId, actionKey, true);
17188
+ const action = await client.setGatewayActionPublic(
17189
+ projectId,
17190
+ actionKey,
17191
+ true
17192
+ );
17058
17193
  applied.push(action);
17059
17194
  } catch (error) {
17060
17195
  const message = error instanceof Error ? error.message : String(error);
@@ -17106,6 +17241,8 @@ async function handleCreateGatewayPlan(client, config2, args) {
17106
17241
  limitsJson: args.limitsJson ?? null,
17107
17242
  includedActionKeys,
17108
17243
  priceAmount: args.priceAmount,
17244
+ pricingModel: args.pricingModel,
17245
+ priceLabel: args.priceLabel,
17109
17246
  priceCurrency: args.priceCurrency,
17110
17247
  billingPeriod: args.billingPeriod,
17111
17248
  sortOrder: args.sortOrder
@@ -17132,6 +17269,8 @@ function buildUpdateGatewayPlanBody(existing, patch) {
17132
17269
  limitsJson: patch.limitsJson !== void 0 ? patch.limitsJson : existing.limitsJson ?? null,
17133
17270
  includedActionKeys: patch.includedActionKeys !== void 0 ? patch.includedActionKeys : existingActionKeys(existing),
17134
17271
  priceAmount: patch.priceAmount ?? existing.priceAmount,
17272
+ pricingModel: patch.pricingModel ?? existing.pricingModel,
17273
+ priceLabel: patch.priceLabel ?? existing.priceLabel ?? void 0,
17135
17274
  priceCurrency: patch.priceCurrency ?? existing.priceCurrency,
17136
17275
  billingPeriod: patch.billingPeriod ?? existing.billingPeriod,
17137
17276
  sortOrder: patch.sortOrder ?? existing.sortOrder
@@ -17170,7 +17309,11 @@ async function handleConfigureGatewayBaseline(client, config2, args) {
17170
17309
  oidcAudiences: args.oidcAudiences ?? null
17171
17310
  };
17172
17311
  try {
17173
- ensured = await ensureGatewayAuthConfigured(client, projectId, desiredAuth);
17312
+ ensured = await ensureGatewayAuthConfigured(
17313
+ client,
17314
+ projectId,
17315
+ desiredAuth
17316
+ );
17174
17317
  } catch (error) {
17175
17318
  const message = error instanceof Error ? error.message : String(error);
17176
17319
  if (/EntraGateway|not configured|Graph permissions/i.test(message)) {
@@ -17197,7 +17340,11 @@ async function handleConfigureGatewayBaseline(client, config2, args) {
17197
17340
  });
17198
17341
  auth = entraWait.auth;
17199
17342
  } else if (provisionStatus2 === "failed") {
17200
- ensured = await ensureGatewayAuthConfigured(client, projectId, desiredAuth);
17343
+ ensured = await ensureGatewayAuthConfigured(
17344
+ client,
17345
+ projectId,
17346
+ desiredAuth
17347
+ );
17201
17348
  entraWait = await waitForGatewayEntraProvision(client, projectId, {
17202
17349
  timeoutMs: args.timeoutMs,
17203
17350
  pollIntervalMs: args.pollIntervalMs
@@ -17247,13 +17394,21 @@ async function handleConfigureGatewayBaseline(client, config2, args) {
17247
17394
  if (normalizeLimits(settings.limitsJson) === normalizeLimits(limitsJson)) {
17248
17395
  skippedSettings = true;
17249
17396
  } else {
17250
- settingsUpdated = await client.updateGatewaySettings(projectId, { limitsJson });
17397
+ settingsUpdated = await client.updateGatewaySettings(projectId, {
17398
+ limitsJson
17399
+ });
17251
17400
  }
17252
17401
  const healthKey = args.publicHealthActionKey ?? "get:/health";
17253
17402
  const publicRoutes = [
17254
- ...new Set([...args.publicRoutes ?? [], healthKey].map((route) => route.trim()).filter(Boolean))
17403
+ ...new Set(
17404
+ [...args.publicRoutes ?? [], healthKey].map((route) => route.trim()).filter(Boolean)
17405
+ )
17255
17406
  ];
17256
- const publicRoutesResult = await applyGatewayPublicRoutes(client, projectId, publicRoutes);
17407
+ const publicRoutesResult = await applyGatewayPublicRoutes(
17408
+ client,
17409
+ projectId,
17410
+ publicRoutes
17411
+ );
17257
17412
  const healthAction = publicRoutesResult.applied.find(
17258
17413
  (action) => action.actionKey.toLowerCase() === normalizeActionKey(healthKey).toLowerCase()
17259
17414
  ) ?? null;
@@ -17291,7 +17446,9 @@ async function handleConfigureGatewayBaseline(client, config2, args) {
17291
17446
  // src/cli/platform/helpers/gateway-plan-sync.ts
17292
17447
  function resolveIncludedActionKeysForUpsert(manifestPlan, existingPlan) {
17293
17448
  if (manifestPlan.includedActionKeys !== void 0) {
17294
- return manifestPlan.includedActionKeys.map((key) => normalizeActionKey(key));
17449
+ return manifestPlan.includedActionKeys.map(
17450
+ (key) => normalizeActionKey(key)
17451
+ );
17295
17452
  }
17296
17453
  if (existingPlan) {
17297
17454
  return existingPlan.includedActions.map((action) => action.actionKey);
@@ -17299,7 +17456,10 @@ function resolveIncludedActionKeysForUpsert(manifestPlan, existingPlan) {
17299
17456
  return [];
17300
17457
  }
17301
17458
  async function resolveActionKeysOrAllProjectActions(manifestPlan, existingPlan, getAllKeys) {
17302
- const resolved = resolveIncludedActionKeysForUpsert(manifestPlan, existingPlan);
17459
+ const resolved = resolveIncludedActionKeysForUpsert(
17460
+ manifestPlan,
17461
+ existingPlan
17462
+ );
17303
17463
  if (resolved.length > 0) {
17304
17464
  return resolved;
17305
17465
  }
@@ -17313,7 +17473,9 @@ function buildGatewayPlanSortTargetsFromIds(manifestPlans, planIdsBySku, skuById
17313
17473
  const key = input5.sku.toLowerCase();
17314
17474
  const planId = planIdsBySku.get(key);
17315
17475
  if (!planId) {
17316
- throw new Error(`Gateway plan '${input5.sku}' was not found; upsert plans before reordering.`);
17476
+ throw new Error(
17477
+ `Gateway plan '${input5.sku}' was not found; upsert plans before reordering.`
17478
+ );
17317
17479
  }
17318
17480
  const sku = skuById?.get(key)?.sku ?? input5.sku;
17319
17481
  targets.push({ planId, sku, sortOrder: index });
@@ -17389,6 +17551,9 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17389
17551
  isActive: true,
17390
17552
  limitsJson: planLimitsForPlatform(input5),
17391
17553
  priceAmount: input5.priceAmount,
17554
+ // Config is the source of truth: dropping pricingModel/priceLabel reverts to fixed / no label.
17555
+ pricingModel: input5.pricingModel ?? "fixed",
17556
+ priceLabel: input5.priceLabel ?? "",
17392
17557
  priceCurrency: input5.priceCurrency,
17393
17558
  billingPeriod: input5.billingPeriod ? normalizeBillingPeriod2(input5.billingPeriod) : void 0
17394
17559
  };
@@ -17399,7 +17564,11 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17399
17564
  projectActionKeys
17400
17565
  );
17401
17566
  }
17402
- const patchedJson = await handlePatchGatewayPlan(client, config2, patchArgs);
17567
+ const patchedJson = await handlePatchGatewayPlan(
17568
+ client,
17569
+ config2,
17570
+ patchArgs
17571
+ );
17403
17572
  results.push(JSON.parse(patchedJson));
17404
17573
  planIdsBySku.set(input5.sku.toLowerCase(), found.id);
17405
17574
  continue;
@@ -17412,10 +17581,16 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17412
17581
  sku: input5.sku,
17413
17582
  displayName: input5.displayName,
17414
17583
  priceAmount: input5.priceAmount,
17584
+ pricingModel: input5.pricingModel,
17585
+ priceLabel: input5.priceLabel,
17415
17586
  priceCurrency: input5.priceCurrency ?? "usd",
17416
17587
  billingPeriod: normalizeBillingPeriod2(input5.billingPeriod),
17417
17588
  limitsJson: planLimitsForPlatform(input5),
17418
- includedActionKeys: options?.deferActionKeys ? [] : await resolveActionKeysOrAllProjectActions(input5, void 0, projectActionKeys),
17589
+ includedActionKeys: options?.deferActionKeys ? [] : await resolveActionKeysOrAllProjectActions(
17590
+ input5,
17591
+ void 0,
17592
+ projectActionKeys
17593
+ ),
17419
17594
  skipDefaultActions: true,
17420
17595
  sortOrder: index
17421
17596
  });
@@ -17426,8 +17601,17 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17426
17601
  results.push(created);
17427
17602
  }
17428
17603
  if (!options?.syncActionsOnly && plans.length > 0 && plans.every((plan) => planIdsBySku.has(plan.sku.toLowerCase()))) {
17429
- const sortTargets = buildGatewayPlanSortTargetsFromIds(plans, planIdsBySku, bySku);
17430
- const sortResults = await applyGatewayPlanSortOrders(client, config2, projectId, sortTargets);
17604
+ const sortTargets = buildGatewayPlanSortTargetsFromIds(
17605
+ plans,
17606
+ planIdsBySku,
17607
+ bySku
17608
+ );
17609
+ const sortResults = await applyGatewayPlanSortOrders(
17610
+ client,
17611
+ config2,
17612
+ projectId,
17613
+ sortTargets
17614
+ );
17431
17615
  results.push(...sortResults);
17432
17616
  }
17433
17617
  return results;
@@ -17482,12 +17666,19 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
17482
17666
  const doc = await readOpenApiDocument(source);
17483
17667
  const rewritten = withGatewayServers(doc, gatewayUrl);
17484
17668
  await mkdir11(path55.dirname(outputPath), { recursive: true });
17485
- await writeFile14(outputPath, `${JSON.stringify(rewritten, null, 2)}
17486
- `, "utf8");
17669
+ await writeFile14(
17670
+ outputPath,
17671
+ `${JSON.stringify(rewritten, null, 2)}
17672
+ `,
17673
+ "utf8"
17674
+ );
17487
17675
  return { outputPath, relativeInput };
17488
17676
  }
17489
17677
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
17490
- const configPath = path55.join(path55.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
17678
+ const configPath = path55.join(
17679
+ path55.resolve(siteDirectory),
17680
+ APITOGO_CONFIG_FILENAME2
17681
+ );
17491
17682
  const raw = await readFile15(configPath, "utf8");
17492
17683
  const config2 = JSON.parse(raw);
17493
17684
  const previousApis = config2.apis;
@@ -17517,20 +17708,30 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
17517
17708
  } else {
17518
17709
  current.backend = previousBackend;
17519
17710
  }
17520
- await writeFile14(configPath, `${JSON.stringify(current, null, 2)}
17521
- `, "utf8");
17711
+ await writeFile14(
17712
+ configPath,
17713
+ `${JSON.stringify(current, null, 2)}
17714
+ `,
17715
+ "utf8"
17716
+ );
17522
17717
  }
17523
17718
  };
17524
17719
  }
17525
17720
  async function prepareOpenApiServersForGatewayBuild(options) {
17526
17721
  const siteDirectory = path55.resolve(options.siteDirectory);
17527
- let openApiPath = resolveBackendOpenApiPath(options.manifest?.backend?.openApiPath, {
17528
- siteDirectory,
17529
- backendSourceDirectory: options.manifest?.backend?.sourceDirectory
17530
- });
17722
+ let openApiPath = resolveBackendOpenApiPath(
17723
+ options.manifest?.backend?.openApiPath,
17724
+ {
17725
+ siteDirectory,
17726
+ backendSourceDirectory: options.manifest?.backend?.sourceDirectory
17727
+ }
17728
+ );
17531
17729
  if (!openApiPath) {
17532
17730
  try {
17533
- const raw = await readFile15(path55.join(siteDirectory, APITOGO_CONFIG_FILENAME2), "utf8");
17731
+ const raw = await readFile15(
17732
+ path55.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
17733
+ "utf8"
17734
+ );
17534
17735
  const config2 = JSON.parse(raw);
17535
17736
  const input5 = Array.isArray(config2.apis) ? config2.apis[0]?.input?.trim() : void 0;
17536
17737
  openApiPath = resolveBackendOpenApiPath(input5, { siteDirectory });
@@ -17545,7 +17746,10 @@ async function prepareOpenApiServersForGatewayBuild(options) {
17545
17746
  openApiPath,
17546
17747
  options.gatewayUrl
17547
17748
  );
17548
- const { restore } = await pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput);
17749
+ const { restore } = await pointSiteApisAtPublishOpenApi(
17750
+ siteDirectory,
17751
+ relativeInput
17752
+ );
17549
17753
  return { relativeInput, outputPath, restore };
17550
17754
  }
17551
17755
 
@@ -17880,7 +18084,9 @@ async function validateDevPortalReadiness(client, projectId, options) {
17880
18084
  })
17881
18085
  );
17882
18086
  checks.push(checkSwaAlignment(options.project, urls.swaUrl));
17883
- checks.push(await checkGatewayPlans(client, projectId, options.postDeploy ?? false));
18087
+ checks.push(
18088
+ await checkGatewayPlans(client, projectId, options.postDeploy ?? false)
18089
+ );
17884
18090
  checks.push({
17885
18091
  name: "oidc_redirect_uri",
17886
18092
  severity: "info",
@@ -18154,10 +18360,10 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
18154
18360
  }
18155
18361
 
18156
18362
  // src/cli/platform/helpers/backend-packaging.ts
18363
+ import { spawn as spawn3 } from "node:child_process";
18157
18364
  import { mkdtemp, readdir, rm as rm5 } from "node:fs/promises";
18158
18365
  import { tmpdir } from "node:os";
18159
18366
  import path57 from "node:path";
18160
- import { spawn as spawn3 } from "node:child_process";
18161
18367
  async function directoryHasRuntimeConfig(dir) {
18162
18368
  if (!await pathExists(dir)) {
18163
18369
  return false;
@@ -18224,7 +18430,9 @@ async function prepareBackendPackagingDirectory(args) {
18224
18430
  if (!csproj) {
18225
18431
  return { directory: sourceDirectory, packagingMode: "source" };
18226
18432
  }
18227
- const tempRoot = await mkdtemp(path57.join(tmpdir(), "apitogo-dotnet-publish-"));
18433
+ const tempRoot = await mkdtemp(
18434
+ path57.join(tmpdir(), "apitogo-dotnet-publish-")
18435
+ );
18228
18436
  const outputDir = path57.join(tempRoot, "publish");
18229
18437
  const result = await runDotnetPublish(sourceDirectory, outputDir);
18230
18438
  if (result.exitCode !== 0) {
@@ -18288,7 +18496,9 @@ async function packageBackendDirectory(sourceDirectory) {
18288
18496
  output4.on("error", reject);
18289
18497
  archive.pipe(output4);
18290
18498
  for (const rel of files) {
18291
- archive.append(createReadStream2(path58.join(resolved, rel)), { name: rel.split(path58.sep).join("/") });
18499
+ archive.append(createReadStream2(path58.join(resolved, rel)), {
18500
+ name: rel.split(path58.sep).join("/")
18501
+ });
18292
18502
  }
18293
18503
  void archive.finalize();
18294
18504
  });
@@ -18494,7 +18704,10 @@ async function handleWaitForJob(client, config2, args, progress) {
18494
18704
  return client.getDevPortalDeployStatus(projectId, args.jobId);
18495
18705
  }
18496
18706
  if (args.jobId) {
18497
- return client.getAzureInfrastructureDeployStatus(projectId, args.jobId);
18707
+ return client.getAzureInfrastructureDeployStatus(
18708
+ projectId,
18709
+ args.jobId
18710
+ );
18498
18711
  }
18499
18712
  return client.getLatestAzureInfrastructureStatus(projectId);
18500
18713
  },
@@ -18531,7 +18744,10 @@ async function handleDeployBackend(client, config2, args, progress) {
18531
18744
  const projectId = resolveProjectId(args.projectId, config2);
18532
18745
  const deployProgress = progress?.child("Backend deployment");
18533
18746
  const runtime = normalizeRuntime(args.runtime);
18534
- const runtimeVersion = normalizeRuntimeVersionSlug(runtime, args.runtimeVersion);
18747
+ const runtimeVersion = normalizeRuntimeVersionSlug(
18748
+ runtime,
18749
+ args.runtimeVersion
18750
+ );
18535
18751
  const resolvedOpenApiPath = resolveDeployBackendOpenApiPath({
18536
18752
  openApiPath: args.openApiPath,
18537
18753
  sourceDirectory: args.sourceDirectory,
@@ -18541,7 +18757,9 @@ async function handleDeployBackend(client, config2, args, progress) {
18541
18757
  await deployProgress?.onStep("Validating OpenAPI");
18542
18758
  const validation = await validateOpenApiFile(resolvedOpenApiPath);
18543
18759
  if (!validation.valid) {
18544
- throw new Error(`OpenAPI validation failed: ${validation.errors.join("; ")}`);
18760
+ throw new Error(
18761
+ `OpenAPI validation failed: ${validation.errors.join("; ")}`
18762
+ );
18545
18763
  }
18546
18764
  }
18547
18765
  await deployProgress?.onStep("Packaging backend source");
@@ -18711,6 +18929,20 @@ function parseToolJsonResult(result) {
18711
18929
  }
18712
18930
  }
18713
18931
 
18932
+ // src/cli/platform/helpers/project-name.ts
18933
+ function normalizeProjectName(name) {
18934
+ return name.trim().split(/\s+/).filter(Boolean).join(" ").toLowerCase();
18935
+ }
18936
+ function findProjectByNormalizedName(projects, name) {
18937
+ const needle = normalizeProjectName(name);
18938
+ if (!needle) {
18939
+ return void 0;
18940
+ }
18941
+ return projects.find(
18942
+ (project) => normalizeProjectName(project.name) === needle
18943
+ );
18944
+ }
18945
+
18714
18946
  // src/cli/platform/helpers/platform-plan-catalog.ts
18715
18947
  var PLATFORM_PLAN_CATALOG = [
18716
18948
  {
@@ -18946,7 +19178,11 @@ async function handleEnsurePlatformPlan(client, config2, args, progress) {
18946
19178
  projectId = resolved.projectId;
18947
19179
  } else {
18948
19180
  projectId = resolveProjectId(projectId, config2);
18949
- steps.push({ step: "resolve_project", status: "succeeded", detail: { projectId } });
19181
+ steps.push({
19182
+ step: "resolve_project",
19183
+ status: "succeeded",
19184
+ detail: { projectId }
19185
+ });
18950
19186
  }
18951
19187
  const existingSubscription = await client.getSubscription(projectId);
18952
19188
  if (isPlatformPlanReady(existingSubscription)) {
@@ -19140,18 +19376,6 @@ async function assertPaidGatewayPlansAllowedForPublish(client, projectId, plans)
19140
19376
  };
19141
19377
  }
19142
19378
 
19143
- // src/cli/platform/helpers/project-name.ts
19144
- function normalizeProjectName(name) {
19145
- return name.trim().split(/\s+/).filter(Boolean).join(" ").toLowerCase();
19146
- }
19147
- function findProjectByNormalizedName(projects, name) {
19148
- const needle = normalizeProjectName(name);
19149
- if (!needle) {
19150
- return void 0;
19151
- }
19152
- return projects.find((project) => normalizeProjectName(project.name) === needle);
19153
- }
19154
-
19155
19379
  // src/cli/platform/tools/projects.ts
19156
19380
  function tenantDisplayName(tenant) {
19157
19381
  return tenant.displayName ?? tenant.name ?? tenant.userName ?? tenant.email;
@@ -19202,10 +19426,14 @@ async function handleGetProject(client, config2, args) {
19202
19426
  if (args.includeInfrastructureStatus !== false) {
19203
19427
  for (let i = 0; i < enrichedModules.length; i++) {
19204
19428
  const mod = enrichedModules[i];
19429
+ if (!mod) continue;
19205
19430
  const statusLower = mod.status.toLowerCase();
19206
19431
  if (statusLower === "pending" || statusLower === "provisioning") {
19207
19432
  try {
19208
- const infra = await client.getInfrastructureStatus(projectId, mod.type);
19433
+ const infra = await client.getInfrastructureStatus(
19434
+ projectId,
19435
+ mod.type
19436
+ );
19209
19437
  enrichedModules[i] = {
19210
19438
  ...mod,
19211
19439
  status: `${mod.status} (infra: ${infra.status})`
@@ -19317,7 +19545,11 @@ async function resolveBootstrapProjectId(client, args, steps) {
19317
19545
  });
19318
19546
  const created = JSON.parse(createdJson);
19319
19547
  const projectId = created.project.id;
19320
- steps.push({ step: "create_project", status: "succeeded", detail: { projectId } });
19548
+ steps.push({
19549
+ step: "create_project",
19550
+ status: "succeeded",
19551
+ detail: { projectId }
19552
+ });
19321
19553
  return { projectId, resumed: false };
19322
19554
  } catch (error) {
19323
19555
  if (!(error instanceof PlatformApiError) || error.status !== 409) {
@@ -19338,7 +19570,9 @@ var READY_MODULE_STATUSES = /* @__PURE__ */ new Set(["provisioned", "configured"
19338
19570
  function moduleTypesPendingActivation(project, requestedModules) {
19339
19571
  return requestedModules.filter((moduleType) => {
19340
19572
  const normalized = normalizeModuleType(moduleType);
19341
- const mod = project.modules.find((m) => normalizeModuleType(m.type) === normalized);
19573
+ const mod = project.modules.find(
19574
+ (m) => normalizeModuleType(m.type) === normalized
19575
+ );
19342
19576
  if (!mod?.status) {
19343
19577
  return true;
19344
19578
  }
@@ -19352,11 +19586,15 @@ async function handleBootstrapProject(client, config2, args, progress) {
19352
19586
  const selectFreePlan = args.selectFreePlan === true;
19353
19587
  const bootstrapProgress = progress?.child("Bootstrap project");
19354
19588
  await bootstrapProgress?.onStep("Creating project");
19355
- const { projectId, resumed } = await resolveBootstrapProjectId(client, {
19356
- name: args.name,
19357
- description: args.description,
19358
- modules
19359
- }, steps);
19589
+ const { projectId, resumed } = await resolveBootstrapProjectId(
19590
+ client,
19591
+ {
19592
+ name: args.name,
19593
+ description: args.description,
19594
+ modules
19595
+ },
19596
+ steps
19597
+ );
19360
19598
  if (selectFreePlan && !resumed) {
19361
19599
  await bootstrapProgress?.onStep("Selecting free plan");
19362
19600
  const freeResult = await handleSelectFreePlan(
@@ -19370,7 +19608,11 @@ async function handleBootstrapProject(client, config2, args, progress) {
19370
19608
  }
19371
19609
  steps.push({ step: "select_free_plan", status: "succeeded" });
19372
19610
  } else if (selectFreePlan && resumed) {
19373
- steps.push({ step: "select_free_plan", status: "skipped", detail: { reason: "project resumed" } });
19611
+ steps.push({
19612
+ step: "select_free_plan",
19613
+ status: "skipped",
19614
+ detail: { reason: "project resumed" }
19615
+ });
19374
19616
  }
19375
19617
  const projectBeforeModules = await client.getProject(projectId);
19376
19618
  const planSelected = !isPlatformPlanUnselected(projectBeforeModules.planTier);
@@ -19444,7 +19686,11 @@ async function handleBootstrapProject(client, config2, args, progress) {
19444
19686
  })
19445
19687
  );
19446
19688
  for (const { moduleType, activated } of activations) {
19447
- steps.push({ step: `activate_${moduleType}`, status: "pending", detail: activated });
19689
+ steps.push({
19690
+ step: `activate_${moduleType}`,
19691
+ status: "pending",
19692
+ detail: activated
19693
+ });
19448
19694
  }
19449
19695
  if (wait) {
19450
19696
  const waits = await Promise.all(
@@ -19468,7 +19714,11 @@ async function handleBootstrapProject(client, config2, args, progress) {
19468
19714
  })
19469
19715
  );
19470
19716
  for (const { moduleType, waited } of waits) {
19471
- steps.push({ step: `wait_${moduleType}`, status: waited.status, detail: waited });
19717
+ steps.push({
19718
+ step: `wait_${moduleType}`,
19719
+ status: waited.status,
19720
+ detail: waited
19721
+ });
19472
19722
  if (waited.status === "failed") {
19473
19723
  return JSON.stringify(
19474
19724
  {
@@ -19749,7 +19999,10 @@ var PLACEHOLDER_HOST_MARKERS = [
19749
19999
  "dp-example.azurecontainerapps.io",
19750
20000
  "your-dev-portal-api.example.com"
19751
20001
  ];
19752
- var ENV_LINE_GLOBAL_RE = new RegExp(`^#?\\s*${SITE_API_BASE_URL_ENV_KEY}=(.*)$`, "gm");
20002
+ var ENV_LINE_GLOBAL_RE = new RegExp(
20003
+ `^#?\\s*${SITE_API_BASE_URL_ENV_KEY}=(.*)$`,
20004
+ "gm"
20005
+ );
19753
20006
  function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
19754
20007
  if (!apiBaseUrl?.trim()) {
19755
20008
  return true;
@@ -19902,51 +20155,6 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
19902
20155
  import { promises as fs11 } from "node:fs";
19903
20156
  import path61 from "node:path";
19904
20157
 
19905
- // src/cli/platform/helpers/tarball.ts
19906
- import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs10 } from "node:fs";
19907
- import { tmpdir as tmpdir3 } from "node:os";
19908
- import path60 from "node:path";
19909
- import { TarArchive } from "archiver";
19910
- var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
19911
- function normalizeArchiveRoot(archiveRoot) {
19912
- const trimmed = archiveRoot.trim().replace(/^\/+|\/+$/g, "");
19913
- if (!trimmed) {
19914
- throw new Error("archiveRoot must not be empty.");
19915
- }
19916
- return `${trimmed}/`;
19917
- }
19918
- async function packageDirectoryAsTarGz(sourceDirectory, options) {
19919
- const resolved = path60.resolve(sourceDirectory);
19920
- const files = await collectPackageableFiles(resolved);
19921
- const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
19922
- const archivePath = path60.join(
19923
- tmpdir3(),
19924
- `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
19925
- );
19926
- await new Promise((resolve, reject) => {
19927
- const output4 = createWriteStream3(archivePath);
19928
- const archive = new TarArchive({ gzip: true, gzipOptions: { level: 6 } });
19929
- output4.on("close", () => resolve());
19930
- archive.on("error", reject);
19931
- output4.on("error", reject);
19932
- archive.pipe(output4);
19933
- for (const rel of files) {
19934
- const entryName = entryPrefix + rel.split(path60.sep).join("/");
19935
- archive.append(createReadStream3(path60.join(resolved, rel)), { name: entryName });
19936
- }
19937
- void archive.finalize();
19938
- });
19939
- const { size } = await fs10.stat(archivePath);
19940
- return { archivePath, byteSize: size };
19941
- }
19942
- async function cleanupArchive(archivePath) {
19943
- await fs10.unlink(archivePath).catch(() => void 0);
19944
- }
19945
- function isTarGzPath(filePath) {
19946
- const normalized = filePath.toLowerCase();
19947
- return normalized.endsWith(".tgz") || normalized.endsWith(".tar.gz");
19948
- }
19949
-
19950
20158
  // src/cli/platform/helpers/dev-portal-backend-job.ts
19951
20159
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
19952
20160
  var ACTIVE_JOB_STATUSES = /* @__PURE__ */ new Set(["Pending", "InProgress"]);
@@ -20049,16 +20257,65 @@ async function pollBackendUpdateJobStatus(client, projectId, jobId, options) {
20049
20257
  }
20050
20258
  async function snapshotBackendUpdateJobIds(client, projectId) {
20051
20259
  const result = await client.listProvisioningJobs(projectId);
20052
- return new Set(
20053
- result.jobs.filter(isBackendUpdateJob).map((job) => job.id)
20260
+ return new Set(result.jobs.filter(isBackendUpdateJob).map((job) => job.id));
20261
+ }
20262
+
20263
+ // src/cli/platform/helpers/tarball.ts
20264
+ import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs10 } from "node:fs";
20265
+ import { tmpdir as tmpdir3 } from "node:os";
20266
+ import path60 from "node:path";
20267
+ import { TarArchive } from "archiver";
20268
+ var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
20269
+ function normalizeArchiveRoot(archiveRoot) {
20270
+ const trimmed = archiveRoot.trim().replace(/^\/+|\/+$/g, "");
20271
+ if (!trimmed) {
20272
+ throw new Error("archiveRoot must not be empty.");
20273
+ }
20274
+ return `${trimmed}/`;
20275
+ }
20276
+ async function packageDirectoryAsTarGz(sourceDirectory, options) {
20277
+ const resolved = path60.resolve(sourceDirectory);
20278
+ const files = await collectPackageableFiles(resolved);
20279
+ const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
20280
+ const archivePath = path60.join(
20281
+ tmpdir3(),
20282
+ `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
20054
20283
  );
20284
+ await new Promise((resolve, reject) => {
20285
+ const output4 = createWriteStream3(archivePath);
20286
+ const archive = new TarArchive({ gzip: true, gzipOptions: { level: 6 } });
20287
+ output4.on("close", () => resolve());
20288
+ archive.on("error", reject);
20289
+ output4.on("error", reject);
20290
+ archive.pipe(output4);
20291
+ for (const rel of files) {
20292
+ const entryName = entryPrefix + rel.split(path60.sep).join("/");
20293
+ archive.append(createReadStream3(path60.join(resolved, rel)), {
20294
+ name: entryName
20295
+ });
20296
+ }
20297
+ void archive.finalize();
20298
+ });
20299
+ const { size } = await fs10.stat(archivePath);
20300
+ return { archivePath, byteSize: size };
20301
+ }
20302
+ async function cleanupArchive(archivePath) {
20303
+ await fs10.unlink(archivePath).catch(() => void 0);
20304
+ }
20305
+ function isTarGzPath(filePath) {
20306
+ const normalized = filePath.toLowerCase();
20307
+ return normalized.endsWith(".tgz") || normalized.endsWith(".tar.gz");
20055
20308
  }
20056
20309
 
20057
20310
  // src/cli/platform/tools/dev-portal.ts
20058
20311
  async function handleGetDevPortalBackend(client, config2, args) {
20059
20312
  const projectId = resolveProjectId(args.projectId, config2);
20060
20313
  const backend = await client.getDevPortalBackend(projectId);
20061
- return JSON.stringify(formatDevPortalBackendResponse(projectId, backend), null, 2);
20314
+ return JSON.stringify(
20315
+ formatDevPortalBackendResponse(projectId, backend),
20316
+ null,
20317
+ 2
20318
+ );
20062
20319
  }
20063
20320
  async function upsertDevPortalSettings(client, config2, projectId, body, waitOptions, progress) {
20064
20321
  const baselineJobIds = waitOptions.wait ? await snapshotBackendUpdateJobIds(client, projectId) : void 0;