@lukoweb/apitogo 0.2.53 → 0.2.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/cli.js CHANGED
@@ -837,7 +837,7 @@ import { stat as stat4 } from "node:fs/promises";
837
837
  var fileExists;
838
838
  var init_file_exists = __esm({
839
839
  "src/config/file-exists.ts"() {
840
- fileExists = (path97) => stat4(path97).then(() => true).catch(() => false);
840
+ fileExists = (path98) => stat4(path98).then(() => true).catch(() => false);
841
841
  }
842
842
  });
843
843
 
@@ -5523,10 +5523,10 @@ function createSpinner(initialText, { stream = process.stderr, silent = false }
5523
5523
  stream.write(`${initialText}
5524
5524
  `);
5525
5525
  return {
5526
- update(text2) {
5527
- if (text2 === last) return;
5528
- last = text2;
5529
- stream.write(`${text2}
5526
+ update(text3) {
5527
+ if (text3 === last) return;
5528
+ last = text3;
5529
+ stream.write(`${text3}
5530
5530
  `);
5531
5531
  },
5532
5532
  log: (line2) => void stream.write(`${line2}
@@ -5536,7 +5536,7 @@ function createSpinner(initialText, { stream = process.stderr, silent = false }
5536
5536
  };
5537
5537
  }
5538
5538
  const startedAt = Date.now();
5539
- let text = initialText;
5539
+ let text2 = initialText;
5540
5540
  let frame = 0;
5541
5541
  let timer;
5542
5542
  const clear = () => stream.write("\r\x1B[K");
@@ -5547,7 +5547,7 @@ function createSpinner(initialText, { stream = process.stderr, silent = false }
5547
5547
  10,
5548
5548
  (stream.columns || 80) - 3 - ` (${formatElapsed(elapsed)})`.length
5549
5549
  );
5550
- const shown = text.length > room ? `${text.slice(0, room - 1)}\u2026` : text;
5550
+ const shown = text2.length > room ? `${text2.slice(0, room - 1)}\u2026` : text2;
5551
5551
  stream.write(
5552
5552
  `\r\x1B[K${colors.cyan(FRAMES[frame++ % FRAMES.length])} ${shown}${suffix}`
5553
5553
  );
@@ -5560,7 +5560,7 @@ function createSpinner(initialText, { stream = process.stderr, silent = false }
5560
5560
  timer.unref();
5561
5561
  const spinner = {
5562
5562
  update(next) {
5563
- text = next;
5563
+ text2 = next;
5564
5564
  },
5565
5565
  log(line2) {
5566
5566
  if (!timer) {
@@ -5585,8 +5585,8 @@ function createSpinner(initialText, { stream = process.stderr, silent = false }
5585
5585
  active.add(spinner);
5586
5586
  return spinner;
5587
5587
  }
5588
- async function withSpinner(text, fn, options) {
5589
- const spinner = createSpinner(text, options);
5588
+ async function withSpinner(text2, fn, options) {
5589
+ const spinner = createSpinner(text2, options);
5590
5590
  try {
5591
5591
  return await fn(spinner);
5592
5592
  } finally {
@@ -5631,9 +5631,9 @@ function printBoxToConsole(message) {
5631
5631
  }
5632
5632
  async function printCriticalFailureToConsoleAndExit(message) {
5633
5633
  stopAllSpinners();
5634
- const text = typeof message === "string" && message.trim() === "" ? "Command failed with no further details." : message;
5634
+ const text2 = typeof message === "string" && message.trim() === "" ? "Command failed with no further details." : message;
5635
5635
  console.error(
5636
- `${colors2.bold(colors2.red(`${withSymbol(symbols.error, process.stderr.isTTY)}Error:`))} ${colors2.red(text)}`
5636
+ `${colors2.bold(colors2.red(`${withSymbol(symbols.error, process.stderr.isTTY)}Error:`))} ${colors2.red(text2)}`
5637
5637
  );
5638
5638
  await Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
5639
5639
  process.exit(1);
@@ -5872,8 +5872,8 @@ function formatConfigIssue(issue4) {
5872
5872
  const hint = issue4.hint ? ` ${issue4.hint}` : "";
5873
5873
  const rest = issue4.message.slice(issue4.path.length);
5874
5874
  const startsWithPath = issue4.path !== "" && issue4.message.startsWith(issue4.path) && (rest === "" || /^[\s:]/.test(rest));
5875
- const text = startsWithPath ? issue4.message : `${issue4.path === "" ? "(root)" : issue4.path}: ${issue4.message}`;
5876
- return `${text}${hint} [${issue4.code}]`;
5875
+ const text2 = startsWithPath ? issue4.message : `${issue4.path === "" ? "(root)" : issue4.path}: ${issue4.message}`;
5876
+ return `${text2}${hint} [${issue4.code}]`;
5877
5877
  }
5878
5878
  var ConfigError = class extends ApitogoError {
5879
5879
  issues;
@@ -6073,8 +6073,8 @@ var ConfigV2Schema = z.strictObject({
6073
6073
  var REMOVED_KEYS = {
6074
6074
  "plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
6075
6075
  };
6076
- function pathToString(path97) {
6077
- return path97.reduce(
6076
+ function pathToString(path98) {
6077
+ return path98.reduce(
6078
6078
  (acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
6079
6079
  ""
6080
6080
  );
@@ -6094,9 +6094,9 @@ function step(schema2, segment) {
6094
6094
  }
6095
6095
  return void 0;
6096
6096
  }
6097
- function knownKeysAt(path97) {
6097
+ function knownKeysAt(path98) {
6098
6098
  let current = ConfigV2Schema;
6099
- for (const segment of path97) {
6099
+ for (const segment of path98) {
6100
6100
  if (!current) return [];
6101
6101
  current = step(current, segment);
6102
6102
  }
@@ -6132,7 +6132,7 @@ function unknownKeyHint(fullPath, key, known) {
6132
6132
  return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
6133
6133
  }
6134
6134
  function mapIssue(issue4) {
6135
- const path97 = pathToString(issue4.path);
6135
+ const path98 = pathToString(issue4.path);
6136
6136
  switch (issue4.code) {
6137
6137
  case "unrecognized_keys":
6138
6138
  return issue4.keys.map((key) => {
@@ -6148,20 +6148,20 @@ function mapIssue(issue4) {
6148
6148
  return [
6149
6149
  {
6150
6150
  code: "CONFIG_INVALID_VALUE",
6151
- path: path97,
6151
+ path: path98,
6152
6152
  message: `must be one of: ${issue4.values.map((v) => JSON.stringify(v)).join(", ")}.`
6153
6153
  }
6154
6154
  ];
6155
6155
  case "custom": {
6156
6156
  const code = typeof issue4.params?.code === "string" ? issue4.params.code : "CONFIG_INVALID_VALUE";
6157
- return [{ code, path: path97, message: issue4.message ?? "is invalid." }];
6157
+ return [{ code, path: path98, message: issue4.message ?? "is invalid." }];
6158
6158
  }
6159
6159
  case "invalid_type":
6160
6160
  return [
6161
- /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path97, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path97, message: issue4.message }
6161
+ /received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path98, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path98, message: issue4.message }
6162
6162
  ];
6163
6163
  default:
6164
- return [{ code: "CONFIG_INVALID_VALUE", path: path97, message: issue4.message }];
6164
+ return [{ code: "CONFIG_INVALID_VALUE", path: path98, message: issue4.message }];
6165
6165
  }
6166
6166
  }
6167
6167
  function isRecord(value) {
@@ -6391,16 +6391,16 @@ var NOT_DOCS = /^(readme|license|licence|changelog)(\.|$)/i;
6391
6391
  var DOCS_SEARCH_DEPTH = 2;
6392
6392
  var OWN_PAGES_FOLDER = "portal/pages";
6393
6393
  async function countDocs(dir) {
6394
- let count = 0;
6394
+ let count2 = 0;
6395
6395
  for (const entry of await readEntries(dir)) {
6396
6396
  if (entry.isDirectory()) {
6397
6397
  if (!IGNORED_DIRS.has(entry.name))
6398
- count += await countDocs(path6.join(dir, entry.name));
6398
+ count2 += await countDocs(path6.join(dir, entry.name));
6399
6399
  } else if (entry.isFile() && DOCS_EXTENSIONS.has(path6.extname(entry.name))) {
6400
- count += 1;
6400
+ count2 += 1;
6401
6401
  }
6402
6402
  }
6403
- return count;
6403
+ return count2;
6404
6404
  }
6405
6405
  async function detectExistingDocs(dir) {
6406
6406
  const candidates = [];
@@ -6562,14 +6562,14 @@ function resolveConfigV2(config2, configPath) {
6562
6562
  }
6563
6563
 
6564
6564
  // src/config/v2/load.ts
6565
- function describeJsonError(error, text) {
6565
+ function describeJsonError(error, text2) {
6566
6566
  const message = error instanceof Error ? error.message : String(error);
6567
6567
  const lineColumn = /line (\d+) column (\d+)/.exec(message);
6568
6568
  if (lineColumn) return `${message}`;
6569
6569
  const position = /position (\d+)/.exec(message);
6570
6570
  if (!position) return message;
6571
6571
  const offset = Number(position[1]);
6572
- const before = text.slice(0, offset);
6572
+ const before = text2.slice(0, offset);
6573
6573
  const line2 = before.split("\n").length;
6574
6574
  const column = offset - before.lastIndexOf("\n");
6575
6575
  return `${message} (line ${line2} column ${column})`;
@@ -6587,16 +6587,16 @@ function loadConfigV2(pathOrDir) {
6587
6587
  }
6588
6588
  ]);
6589
6589
  }
6590
- const text = readFileSync2(configPath, "utf8");
6590
+ const text2 = readFileSync2(configPath, "utf8");
6591
6591
  let raw;
6592
6592
  try {
6593
- raw = JSON.parse(text);
6593
+ raw = JSON.parse(text2);
6594
6594
  } catch (error) {
6595
6595
  throw new ConfigError(path8.basename(configPath), [
6596
6596
  {
6597
6597
  code: "CONFIG_INVALID_JSON",
6598
6598
  path: "",
6599
- message: describeJsonError(error, text),
6599
+ message: describeJsonError(error, text2),
6600
6600
  hint: "Fix the JSON syntax; comments and trailing commas are not allowed."
6601
6601
  }
6602
6602
  ]);
@@ -6617,6 +6617,47 @@ import path9 from "node:path";
6617
6617
  import { glob } from "glob";
6618
6618
  import globParent from "glob-parent";
6619
6619
 
6620
+ // src/config/v2/page-order.ts
6621
+ var FIRST_PAGE_NAMES = [
6622
+ "introduction",
6623
+ "intro",
6624
+ "welcome",
6625
+ "getting-started",
6626
+ "get-started",
6627
+ "overview",
6628
+ "quickstart",
6629
+ "quick-start"
6630
+ ];
6631
+ var lastSegment = (route) => route.split("/").filter(Boolean).pop() ?? "";
6632
+ var rank = (route, orders) => {
6633
+ const name = lastSegment(route).toLowerCase();
6634
+ if (name === "index" || route === "/") return [0, 0];
6635
+ const order = orders.get(route);
6636
+ if (order !== void 0) return [1, order];
6637
+ const preferred = FIRST_PAGE_NAMES.indexOf(name);
6638
+ return [2, preferred === -1 ? FIRST_PAGE_NAMES.length : preferred];
6639
+ };
6640
+ function comparePages(a, b, orders = /* @__PURE__ */ new Map()) {
6641
+ const [groupA, valueA] = rank(a, orders);
6642
+ const [groupB, valueB] = rank(b, orders);
6643
+ return groupA - groupB || valueA - valueB || a.localeCompare(b);
6644
+ }
6645
+ function firstPage(routes, orders = /* @__PURE__ */ new Map()) {
6646
+ const depth = (route) => route.split("/").length;
6647
+ return [...routes].sort(
6648
+ (a, b) => depth(a) - depth(b) || comparePages(a, b, orders)
6649
+ )[0];
6650
+ }
6651
+ function orderValue(value) {
6652
+ const number = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : Number.NaN;
6653
+ return Number.isInteger(number) ? number : void 0;
6654
+ }
6655
+ var TSX_ORDER = /export\s+const\s+order\s*(?::\s*number\s*)?=\s*(-?\d+)/;
6656
+ function tsxPageOrder(source) {
6657
+ const match = TSX_ORDER.exec(source);
6658
+ return match?.[1] ? Number(match[1]) : void 0;
6659
+ }
6660
+
6620
6661
  // src/config/v2/docs-navigation.ts
6621
6662
  var DOCS_NAVIGATION_LABEL = "Documentation";
6622
6663
  var emptyBranch = () => ({ pages: /* @__PURE__ */ new Map(), folders: /* @__PURE__ */ new Map() });
@@ -6625,9 +6666,9 @@ function folderLabel(name) {
6625
6666
  return words.charAt(0).toUpperCase() + words.slice(1);
6626
6667
  }
6627
6668
  var byName = (a, b) => a.localeCompare(b);
6628
- var toItems = (branch, prefix) => {
6669
+ var toItems = (branch, prefix, orders) => {
6629
6670
  const names = [...branch.pages.keys()].sort(
6630
- (a, b) => a === "index" ? -1 : b === "index" ? 1 : byName(a, b)
6671
+ (a, b) => comparePages(`/${prefix}${a}`, `/${prefix}${b}`, orders)
6631
6672
  );
6632
6673
  const folders = [...branch.folders.keys()].sort(byName);
6633
6674
  const folderPath = prefix === "" ? "/" : `/${prefix.slice(0, -1)}`;
@@ -6653,7 +6694,8 @@ var toItems = (branch, prefix) => {
6653
6694
  collapsed: false,
6654
6695
  items: toItems(
6655
6696
  branch.folders.get(folder) ?? emptyBranch(),
6656
- `${prefix}${folder}/`
6697
+ `${prefix}${folder}/`,
6698
+ orders
6657
6699
  )
6658
6700
  })
6659
6701
  )
@@ -6669,7 +6711,7 @@ var branchOf = (root, route) => {
6669
6711
  }, root);
6670
6712
  return { branch, page };
6671
6713
  };
6672
- function docsNavigation(routes, customPages = []) {
6714
+ function docsNavigation(routes, customPages = [], orders = /* @__PURE__ */ new Map()) {
6673
6715
  const root = emptyBranch();
6674
6716
  for (const route of routes) {
6675
6717
  const { branch, page } = branchOf(root, route);
@@ -6680,7 +6722,7 @@ function docsNavigation(routes, customPages = []) {
6680
6722
  if (page && !branch.pages.has(page))
6681
6723
  branch.pages.set(page, { kind: "custom", ...custom });
6682
6724
  }
6683
- const items = toItems(root, "");
6725
+ const items = toItems(root, "", orders);
6684
6726
  return items.length === 0 ? [] : [{ type: "category", label: DOCS_NAVIGATION_LABEL, items }];
6685
6727
  }
6686
6728
 
@@ -6733,7 +6775,13 @@ async function findTsxPages(contentGlobs) {
6733
6775
  const route = `/${relative.replace(/\.tsx$/, "")}`;
6734
6776
  if (found.has(route)) continue;
6735
6777
  const source = await readFile3(file, "utf8").catch(() => "");
6736
- found.set(route, { route, file, label: tsxPageLabel(source, file) });
6778
+ const order = tsxPageOrder(source);
6779
+ found.set(route, {
6780
+ route,
6781
+ file,
6782
+ label: tsxPageLabel(source, file),
6783
+ ...order !== void 0 ? { order } : {}
6784
+ });
6737
6785
  }
6738
6786
  }
6739
6787
  return [...found.values()].sort((a, b) => a.route.localeCompare(b.route));
@@ -7087,8 +7135,8 @@ async function specRule(spec, index, resolved, out) {
7087
7135
  }
7088
7136
  let document;
7089
7137
  try {
7090
- const text = await readFile4(spec, "utf8");
7091
- document = spec.toLowerCase().endsWith(".json") ? JSON.parse(text) : parseYaml(text);
7138
+ const text2 = await readFile4(spec, "utf8");
7139
+ document = spec.toLowerCase().endsWith(".json") ? JSON.parse(text2) : parseYaml(text2);
7092
7140
  } catch (error) {
7093
7141
  out.error({
7094
7142
  code: "GATEWAY_SPEC_INVALID",
@@ -7126,8 +7174,8 @@ function authRule(resolved, out) {
7126
7174
  issuers: auth.issuers?.length ?? 0,
7127
7175
  audiences: auth.audiences?.length ?? 0
7128
7176
  };
7129
- const present = Object.entries(parts).filter(([, count]) => count > 0).map(([name]) => name);
7130
- const missing = Object.entries(parts).filter(([, count]) => count === 0).map(([name]) => name);
7177
+ const present = Object.entries(parts).filter(([, count2]) => count2 > 0).map(([name]) => name);
7178
+ const missing = Object.entries(parts).filter(([, count2]) => count2 === 0).map(([name]) => name);
7131
7179
  if (missing.length === 0) return;
7132
7180
  const blocked = "Token validation would be skipped, so deploy is blocked.";
7133
7181
  if (present.length === 0) {
@@ -7407,7 +7455,7 @@ var ServiceManifestSchema = z3.looseObject({
7407
7455
  }).optional()
7408
7456
  }).default({})
7409
7457
  });
7410
- var issue = (code, path97, message, hint) => ({ code, path: path97, message, hint });
7458
+ var issue = (code, path98, message, hint) => ({ code, path: path98, message, hint });
7411
7459
  function parseManifest(raw, file = "services.json") {
7412
7460
  const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
7413
7461
  if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
@@ -7438,15 +7486,15 @@ function parseManifest(raw, file = "services.json") {
7438
7486
  }
7439
7487
  async function readManifest(state) {
7440
7488
  const file = manifestPathFor(state);
7441
- let text;
7489
+ let text2;
7442
7490
  try {
7443
- text = await readFile5(file, "utf8");
7491
+ text2 = await readFile5(file, "utf8");
7444
7492
  } catch (error) {
7445
7493
  if (error.code === "ENOENT") return void 0;
7446
7494
  throw error;
7447
7495
  }
7448
7496
  try {
7449
- return parseManifest(JSON.parse(text), file);
7497
+ return parseManifest(JSON.parse(text2), file);
7450
7498
  } catch (error) {
7451
7499
  if (error instanceof SyntaxError) {
7452
7500
  throw new ConfigError(file, [
@@ -7670,8 +7718,8 @@ var METHODS = [
7670
7718
  "trace"
7671
7719
  ];
7672
7720
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7673
- function actionKey(method, path97) {
7674
- const trimmed = path97.trim();
7721
+ function actionKey(method, path98) {
7722
+ const trimmed = path98.trim();
7675
7723
  const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
7676
7724
  const normalized = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") || "/" : withSlash;
7677
7725
  return `${method.trim().toLowerCase()}:${normalized}`;
@@ -7708,8 +7756,8 @@ var ReferenceSpecError = class extends Error {
7708
7756
  };
7709
7757
  function operationsOf(paths) {
7710
7758
  return Object.entries(paths).flatMap(
7711
- ([path97, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7712
- (method) => [path97, item, method]
7759
+ ([path98, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
7760
+ (method) => [path98, item, method]
7713
7761
  ) : []
7714
7762
  );
7715
7763
  }
@@ -7724,14 +7772,14 @@ function buildReferenceSpec(specText, options) {
7724
7772
  throw new ReferenceSpecError("The OpenAPI file could not be read.");
7725
7773
  const doc = structuredClone(root);
7726
7774
  const paths = isObject(doc.paths) ? doc.paths : {};
7727
- for (const [path97, item, method] of operationsOf(paths))
7728
- if (options.deniedOperations.has(actionKey(method, path97)))
7775
+ for (const [path98, item, method] of operationsOf(paths))
7776
+ if (options.deniedOperations.has(actionKey(method, path98)))
7729
7777
  delete item[method];
7730
- for (const [path97, item] of Object.entries(paths))
7778
+ for (const [path98, item] of Object.entries(paths))
7731
7779
  if (!isObject(item) || !METHODS.some((method) => method in item))
7732
- delete paths[path97];
7733
- for (const [path97, item, method] of operationsOf(paths))
7734
- if (options.publicOperations.has(actionKey(method, path97)))
7780
+ delete paths[path98];
7781
+ for (const [path98, item, method] of operationsOf(paths))
7782
+ if (options.publicOperations.has(actionKey(method, path98)))
7735
7783
  item[method].security = [];
7736
7784
  doc.servers = [{ url: options.gatewayBaseUrl.replace(/\/+$/, "") }];
7737
7785
  for (const item of Object.values(paths)) {
@@ -7771,38 +7819,38 @@ function buildReferenceSpec(specText, options) {
7771
7819
  if (Object.keys(components).length > 0) doc.components = components;
7772
7820
  else delete doc.components;
7773
7821
  doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
7774
- for (const [path97, item, method] of operationsOf(paths)) {
7822
+ for (const [path98, item, method] of operationsOf(paths)) {
7775
7823
  const operation = item[method];
7776
7824
  if (Array.isArray(operation.security) && operation.security.length > 0)
7777
7825
  delete operation.security;
7778
- if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path97)))
7826
+ if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path98)))
7779
7827
  operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
7780
7828
  else delete operation[KEY_HEADER_EXTENSION];
7781
7829
  }
7782
7830
  return JSON.parse(hideOrigin(JSON.stringify(doc), options));
7783
7831
  }
7784
- function hideOrigin(text, options) {
7832
+ function hideOrigin(text2, options) {
7785
7833
  const origin = options.originUrl?.trim().replace(/\/+$/, "");
7786
- if (!origin) return text;
7834
+ if (!origin) return text2;
7787
7835
  const gateway = options.gatewayBaseUrl.trim().replace(/\/+$/, "");
7788
7836
  const pattern = new RegExp(
7789
7837
  origin.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&"),
7790
7838
  "gi"
7791
7839
  );
7792
- return text.replace(pattern, () => gateway);
7840
+ return text2.replace(pattern, () => gateway);
7793
7841
  }
7794
7842
  async function renderReferenceSpec(gateway, gatewayBaseUrl, keyHeaderName) {
7795
7843
  const file = gateway.specs[0];
7796
7844
  if (!file)
7797
7845
  throw new ReferenceSpecError("gateway.specs lists no OpenAPI file.");
7798
- let text;
7846
+ let text2;
7799
7847
  try {
7800
- text = await readFile6(file, "utf8");
7848
+ text2 = await readFile6(file, "utf8");
7801
7849
  } catch {
7802
7850
  throw new ReferenceSpecError(`Cannot read the OpenAPI file ${file}.`);
7803
7851
  }
7804
7852
  const spec = buildReferenceSpec(
7805
- text,
7853
+ text2,
7806
7854
  referenceOptions(gateway, gatewayBaseUrl, keyHeaderName)
7807
7855
  );
7808
7856
  return `${JSON.stringify(spec, null, 2)}
@@ -7917,17 +7965,34 @@ async function docRoutes(patterns, options = {}) {
7917
7965
  );
7918
7966
  return [...new Set(routes.flat())].sort();
7919
7967
  }
7920
- function pickLandingRoute(routes) {
7921
- const depth = (route) => route.split("/").length;
7922
- return [...routes].sort(
7923
- (a, b) => depth(a) - depth(b) || a.localeCompare(b)
7924
- )[0];
7968
+ function pickLandingRoute(routes, orders) {
7969
+ return firstPage(routes, orders);
7970
+ }
7971
+ async function docPageOrders(patterns) {
7972
+ const globs = patterns.map(toGlobPath);
7973
+ const excluded = globs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
7974
+ const orders = /* @__PURE__ */ new Map();
7975
+ for (const pattern of globs.filter((p) => !p.startsWith("!"))) {
7976
+ const parent = globParent2(pattern);
7977
+ const files = await glob3(pattern, {
7978
+ absolute: true,
7979
+ posix: true,
7980
+ ignore: [...IGNORED2, ...excluded]
7981
+ });
7982
+ for (const raw of files) {
7983
+ const order = orderValue((await readFrontmatter(raw)).data.order);
7984
+ if (order === void 0) continue;
7985
+ const route = `/${path12.posix.relative(parent, raw.replace(/^\/\/\?\//, "")).replace(/\.mdx?$/, "")}`;
7986
+ if (!orders.has(route)) orders.set(route, order);
7987
+ }
7988
+ }
7989
+ return orders;
7925
7990
  }
7926
7991
  function homeRedirect(input5) {
7927
7992
  if (input5.hostsDocs) {
7928
7993
  if (input5.docRoutes.some((route) => route === "/" || route === "/index"))
7929
7994
  return void 0;
7930
- const landing = pickLandingRoute(input5.docRoutes);
7995
+ const landing = pickLandingRoute(input5.docRoutes, input5.pageOrders);
7931
7996
  if (landing) return { from: "/", to: landing };
7932
7997
  return input5.apiReferencePath ? { from: "/", to: input5.apiReferencePath } : void 0;
7933
7998
  }
@@ -8349,10 +8414,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8349
8414
  const hostedDocRoutes = [
8350
8415
  .../* @__PURE__ */ new Set([...markdownRoutes, ...tsxPages.map((page) => page.route)])
8351
8416
  ].sort();
8417
+ const pageOrders = plan2.docs ? await docPageOrders(profile.docs.content) : /* @__PURE__ */ new Map();
8418
+ for (const page of tsxPages)
8419
+ if (page.order !== void 0 && !pageOrders.has(page.route))
8420
+ pageOrders.set(page.route, page.order);
8352
8421
  if (plan2.plansPlugin && plan2.docs) {
8353
8422
  const landing = hostedDocRoutes.some(
8354
8423
  (route) => route === "/" || route === "/index"
8355
- ) ? "/" : pickLandingRoute(hostedDocRoutes);
8424
+ ) ? "/" : pickLandingRoute(hostedDocRoutes, pageOrders);
8356
8425
  if (landing)
8357
8426
  plan2.plansPlugin.links = { ...plan2.plansPlugin.links, docs: landing };
8358
8427
  }
@@ -8362,7 +8431,8 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8362
8431
  externalDocsUrl: profile.docs.url,
8363
8432
  apiReferencePath: plan2.apiReference ? `/${API_REFERENCE_PATH}` : void 0,
8364
8433
  userPanel: plan2.userPanel,
8365
- pricingPath: plan2.plansPlugin && hasCapability(effective.services.plans, "catalog") ? CONSOLE_ROUTES.pricing : void 0
8434
+ pricingPath: plan2.plansPlugin && hasCapability(effective.services.plans, "catalog") ? CONSOLE_ROUTES.pricing : void 0,
8435
+ pageOrders
8366
8436
  });
8367
8437
  const siteConfig = buildSiteConfig(
8368
8438
  resolved,
@@ -8376,9 +8446,10 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
8376
8446
  route: page.route,
8377
8447
  label: page.label,
8378
8448
  element: wrapperPages[index]?.id ?? ""
8379
- }))
8449
+ })),
8450
+ pageOrders
8380
8451
  ),
8381
- docsPath: pickLandingRoute(hostedDocRoutes),
8452
+ docsPath: pickLandingRoute(hostedDocRoutes, pageOrders),
8382
8453
  demoPlans: plan2.demo && plan2.plansPlugin ? await readDemoPlans(resolved.plans?.file) : void 0,
8383
8454
  protectedRoutes: protectedRoutesFor({
8384
8455
  requireAuth: resolved.portal?.requireAuth,
@@ -8486,16 +8557,16 @@ async function materializeWorkspace(resolved, prepared) {
8486
8557
  return root;
8487
8558
  }
8488
8559
  async function configFingerprint(configPath) {
8489
- const text = await readFile9(configPath, "utf8");
8560
+ const text2 = await readFile9(configPath, "utf8");
8490
8561
  try {
8491
- const raw = JSON.parse(text);
8562
+ const raw = JSON.parse(text2);
8492
8563
  if (raw.project) {
8493
8564
  delete raw.project.id;
8494
8565
  if (Object.keys(raw.project).length === 0) delete raw.project;
8495
8566
  }
8496
8567
  return JSON.stringify(raw);
8497
8568
  } catch {
8498
- return text;
8569
+ return text2;
8499
8570
  }
8500
8571
  }
8501
8572
  async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper = "") {
@@ -8588,7 +8659,7 @@ const items = [
8588
8659
  { id: "3", name: "Third item" },
8589
8660
  ];
8590
8661
 
8591
- // Lets the portal's API page call this server while you run it locally (\`apitogo dev\`). Once deployed, the gateway
8662
+ // Lets the portal's API page call this server while you run it locally (\`apitogo run\`). Once deployed, the gateway
8592
8663
  // answers browsers itself and keeps its own list of allowed sites.
8593
8664
  const cors = {
8594
8665
  "access-control-allow-origin": "*",
@@ -9282,7 +9353,10 @@ async function buildGateway(context) {
9282
9353
  const policies = {
9283
9354
  ...rateLimit ? { rateLimit: { calls: rateLimit[0], periodSeconds: rateLimit[1] } } : {},
9284
9355
  ...quota ? { quota: { calls: quota[0], periodDays: quota[1] } } : {},
9285
- ...o.cors?.length ? { cors: o.cors } : {}
9356
+ ...o.cors?.length ? { cors: o.cors } : {},
9357
+ // The sample server's health check needs no key: monitors and the platform call it, and it is not something a
9358
+ // plan sells, so it stays out of the plans' feature lists (API-126).
9359
+ ...sampleServer ? { operations: { public: [`GET ${FULL_SERVER_HEALTH_PATH}`] } } : {}
9286
9360
  };
9287
9361
  return {
9288
9362
  ...options.disabled ? { enabled: false } : {},
@@ -9573,9 +9647,9 @@ var emptyLedger = () => ({
9573
9647
  services: {}
9574
9648
  });
9575
9649
  async function readLedger(state) {
9576
- let text;
9650
+ let text2;
9577
9651
  try {
9578
- text = await readFile10(state.file, "utf8");
9652
+ text2 = await readFile10(state.file, "utf8");
9579
9653
  } catch (error) {
9580
9654
  if (error.code === "ENOENT")
9581
9655
  return emptyLedger();
@@ -9583,7 +9657,7 @@ async function readLedger(state) {
9583
9657
  }
9584
9658
  let raw;
9585
9659
  try {
9586
- raw = JSON.parse(text);
9660
+ raw = JSON.parse(text2);
9587
9661
  } catch {
9588
9662
  throw corrupt(state.file, "is not valid JSON");
9589
9663
  }
@@ -9824,8 +9898,8 @@ var PlatformApiClient = class {
9824
9898
  constructor(config2) {
9825
9899
  this.config = config2;
9826
9900
  }
9827
- url(path97) {
9828
- return `${this.config.apiUrl}${path97.startsWith("/") ? path97 : `/${path97}`}`;
9901
+ url(path98) {
9902
+ return `${this.config.apiUrl}${path98.startsWith("/") ? path98 : `/${path98}`}`;
9829
9903
  }
9830
9904
  headers(extra) {
9831
9905
  return {
@@ -9835,9 +9909,9 @@ var PlatformApiClient = class {
9835
9909
  ...extra
9836
9910
  };
9837
9911
  }
9838
- async request(method, path97, init3) {
9912
+ async request(method, path98, init3) {
9839
9913
  const response = await fetchWithRetry(
9840
- this.url(path97),
9914
+ this.url(path98),
9841
9915
  {
9842
9916
  method,
9843
9917
  ...init3,
@@ -10283,15 +10357,15 @@ var PlatformApiClient = class {
10283
10357
  }
10284
10358
  );
10285
10359
  }
10286
- deleteDevPortalPage(projectId, path97) {
10287
- const query = new URLSearchParams({ path: path97 });
10360
+ deleteDevPortalPage(projectId, path98) {
10361
+ const query = new URLSearchParams({ path: path98 });
10288
10362
  return this.request(
10289
10363
  "DELETE",
10290
10364
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
10291
10365
  );
10292
10366
  }
10293
- renameDevPortalPage(projectId, path97, newName) {
10294
- const query = new URLSearchParams({ path: path97 });
10367
+ renameDevPortalPage(projectId, path98, newName) {
10368
+ const query = new URLSearchParams({ path: path98 });
10295
10369
  return this.request(
10296
10370
  "POST",
10297
10371
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -10407,8 +10481,8 @@ var PlatformApiClient = class {
10407
10481
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
10408
10482
  */
10409
10483
  validateGatewayPlans(plans, options = {}) {
10410
- const path97 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10411
- return this.request("POST", path97, {
10484
+ const path98 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
10485
+ return this.request("POST", path98, {
10412
10486
  headers: { "Content-Type": "application/json" },
10413
10487
  body: JSON.stringify({ plans, planTier: options.planTier })
10414
10488
  });
@@ -10787,7 +10861,7 @@ import {
10787
10861
  // package.json
10788
10862
  var package_default = {
10789
10863
  name: "@lukoweb/apitogo",
10790
- version: "0.2.53",
10864
+ version: "0.2.54",
10791
10865
  type: "module",
10792
10866
  sideEffects: [
10793
10867
  "**/*.css",
@@ -11505,9 +11579,9 @@ var slugifyWithCounter = () => {
11505
11579
  const counts = /* @__PURE__ */ new Map();
11506
11580
  return (str) => {
11507
11581
  const base = slugify(str);
11508
- const count = counts.get(base) ?? 0;
11509
- counts.set(base, count + 1);
11510
- return count === 0 ? base : `${base}-${count + 1}`;
11582
+ const count2 = counts.get(base) ?? 0;
11583
+ counts.set(base, count2 + 1);
11584
+ return count2 === 0 ? base : `${base}-${count2 + 1}`;
11511
11585
  };
11512
11586
  };
11513
11587
 
@@ -11572,14 +11646,14 @@ var flattenAllOf = (schema2) => {
11572
11646
  };
11573
11647
 
11574
11648
  // src/lib/util/traverse.ts
11575
- var traverse = (specification, transform, path97 = []) => {
11576
- const transformed = transform(specification, path97);
11649
+ var traverse = (specification, transform, path98 = []) => {
11650
+ const transformed = transform(specification, path98);
11577
11651
  if (typeof transformed !== "object" || transformed === null) {
11578
11652
  return transformed;
11579
11653
  }
11580
11654
  const result = Array.isArray(transformed) ? [] : {};
11581
11655
  for (const [key, value] of Object.entries(transformed)) {
11582
- const currentPath = [...path97, key];
11656
+ const currentPath = [...path98, key];
11583
11657
  if (Array.isArray(value)) {
11584
11658
  result[key] = value.map(
11585
11659
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -11607,9 +11681,9 @@ var resolveLocalRef = (schema2, ref) => {
11607
11681
  if (schemaCache?.has(ref)) {
11608
11682
  return schemaCache.get(ref);
11609
11683
  }
11610
- const path97 = ref.split("/").slice(1);
11684
+ const path98 = ref.split("/").slice(1);
11611
11685
  let current = schema2;
11612
- for (const segment of path97) {
11686
+ for (const segment of path98) {
11613
11687
  if (!current || typeof current !== "object") {
11614
11688
  current = null;
11615
11689
  }
@@ -11628,7 +11702,7 @@ var dereference = async (schema2, resolvers = []) => {
11628
11702
  }
11629
11703
  const cloned = structuredClone(schema2);
11630
11704
  const visited = /* @__PURE__ */ new Set();
11631
- const resolve = async (current, path97) => {
11705
+ const resolve = async (current, path98) => {
11632
11706
  if (isIndexableObject(current)) {
11633
11707
  if (visited.has(current)) {
11634
11708
  return CIRCULAR_REF;
@@ -11636,7 +11710,7 @@ var dereference = async (schema2, resolvers = []) => {
11636
11710
  visited.add(current);
11637
11711
  if (Array.isArray(current)) {
11638
11712
  for (let index = 0; index < current.length; index++) {
11639
- current[index] = await resolve(current[index], `${path97}/${index}`);
11713
+ current[index] = await resolve(current[index], `${path98}/${index}`);
11640
11714
  }
11641
11715
  } else {
11642
11716
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -11647,13 +11721,13 @@ var dereference = async (schema2, resolvers = []) => {
11647
11721
  for (const resolver of resolvers) {
11648
11722
  const resolved = await resolver($ref);
11649
11723
  if (resolved) {
11650
- result2 = await resolve(resolved, path97);
11724
+ result2 = await resolve(resolved, path98);
11651
11725
  break;
11652
11726
  }
11653
11727
  }
11654
11728
  if (result2 === void 0) {
11655
11729
  const resolved = await resolveLocalRef(cloned, $ref);
11656
- result2 = await resolve(resolved, path97);
11730
+ result2 = await resolve(resolved, path98);
11657
11731
  }
11658
11732
  if (hasSiblings) {
11659
11733
  if (result2 === CIRCULAR_REF) {
@@ -11666,7 +11740,7 @@ var dereference = async (schema2, resolvers = []) => {
11666
11740
  return result2;
11667
11741
  }
11668
11742
  for (const key in current) {
11669
- current[key] = await resolve(current[key], `${path97}/${key}`);
11743
+ current[key] = await resolve(current[key], `${path98}/${key}`);
11670
11744
  }
11671
11745
  }
11672
11746
  visited.delete(current);
@@ -11705,9 +11779,9 @@ var upgradeSchema = (schema2) => {
11705
11779
  }
11706
11780
  return sub;
11707
11781
  });
11708
- schema2 = traverse(schema2, (sub, path97) => {
11782
+ schema2 = traverse(schema2, (sub, path98) => {
11709
11783
  if (sub.example !== void 0) {
11710
- if (isSchemaPath(path97 ?? [])) {
11784
+ if (isSchemaPath(path98 ?? [])) {
11711
11785
  sub.examples = [sub.example];
11712
11786
  } else {
11713
11787
  sub.examples = {
@@ -11720,11 +11794,11 @@ var upgradeSchema = (schema2) => {
11720
11794
  }
11721
11795
  return sub;
11722
11796
  });
11723
- schema2 = traverse(schema2, (schema3, path97) => {
11797
+ schema2 = traverse(schema2, (schema3, path98) => {
11724
11798
  if (schema3.type === "object" && schema3.properties !== void 0) {
11725
- const parentPath = path97?.slice(0, -1);
11799
+ const parentPath = path98?.slice(0, -1);
11726
11800
  const isMultipart = parentPath?.some((segment, index) => {
11727
- return segment === "content" && path97?.[index + 1] === "multipart/form-data";
11801
+ return segment === "content" && path98?.[index + 1] === "multipart/form-data";
11728
11802
  });
11729
11803
  if (isMultipart) {
11730
11804
  const entries = Object.entries(schema3.properties);
@@ -11738,8 +11812,8 @@ var upgradeSchema = (schema2) => {
11738
11812
  }
11739
11813
  return schema3;
11740
11814
  });
11741
- schema2 = traverse(schema2, (schema3, path97) => {
11742
- if (path97?.includes("content") && path97.includes("application/octet-stream")) {
11815
+ schema2 = traverse(schema2, (schema3, path98) => {
11816
+ if (path98?.includes("content") && path98.includes("application/octet-stream")) {
11743
11817
  return {};
11744
11818
  }
11745
11819
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -11765,11 +11839,11 @@ var upgradeSchema = (schema2) => {
11765
11839
  }
11766
11840
  return sub;
11767
11841
  });
11768
- schema2 = traverse(schema2, (schema3, path97) => {
11842
+ schema2 = traverse(schema2, (schema3, path98) => {
11769
11843
  if (schema3.type === "string" && schema3.format === "byte") {
11770
- const parentPath = path97?.slice(0, -1);
11844
+ const parentPath = path98?.slice(0, -1);
11771
11845
  const contentMediaType = parentPath?.find(
11772
- (_, index) => path97?.[index - 1] === "content"
11846
+ (_, index) => path98?.[index - 1] === "content"
11773
11847
  );
11774
11848
  return {
11775
11849
  type: "string",
@@ -11781,7 +11855,7 @@ var upgradeSchema = (schema2) => {
11781
11855
  });
11782
11856
  return schema2;
11783
11857
  };
11784
- function isSchemaPath(path97) {
11858
+ function isSchemaPath(path98) {
11785
11859
  const schemaLocations = [
11786
11860
  ["components", "schemas"],
11787
11861
  "properties",
@@ -11794,10 +11868,10 @@ function isSchemaPath(path97) {
11794
11868
  ];
11795
11869
  return schemaLocations.some((location) => {
11796
11870
  if (Array.isArray(location)) {
11797
- return location.every((segment, index) => path97[index] === segment);
11871
+ return location.every((segment, index) => path98[index] === segment);
11798
11872
  }
11799
- return path97.includes(location);
11800
- }) || path97.includes("schema") || path97.some((segment) => segment.endsWith("Schema"));
11873
+ return path98.includes(location);
11874
+ }) || path98.includes("schema") || path98.some((segment) => segment.endsWith("Schema"));
11801
11875
  }
11802
11876
 
11803
11877
  // src/lib/oas/parser/index.ts
@@ -11879,13 +11953,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
11879
11953
  "anyOf",
11880
11954
  "oneOf"
11881
11955
  ]);
11882
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path97 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
11956
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path98 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
11883
11957
  if (obj === null || typeof obj !== "object") return obj;
11884
11958
  const refPath = obj.__$ref;
11885
11959
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
11886
11960
  if (isCircular) {
11887
11961
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
11888
- const circularProp = path97.find((p) => !OPENAPI_PROPS.has(p)) || path97[0];
11962
+ const circularProp = path98.find((p) => !OPENAPI_PROPS.has(p)) || path98[0];
11889
11963
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
11890
11964
  }
11891
11965
  if (refs.has(obj)) return refs.get(obj);
@@ -11895,7 +11969,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
11895
11969
  value,
11896
11970
  currentPath,
11897
11971
  refs,
11898
- [...path97, key],
11972
+ [...path98, key],
11899
11973
  currentRefPaths
11900
11974
  );
11901
11975
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -11930,7 +12004,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
11930
12004
  var getAllTags = (schema2) => {
11931
12005
  const rootTags = schema2.tags ?? [];
11932
12006
  const operations = Object.values(schema2.paths ?? {}).flatMap(
11933
- (path97) => HttpMethods.map((k) => path97?.[k]).filter((op) => op != null)
12007
+ (path98) => HttpMethods.map((k) => path98?.[k]).filter((op) => op != null)
11934
12008
  );
11935
12009
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
11936
12010
  const hasUntaggedOperations = operations.some(
@@ -11985,7 +12059,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
11985
12059
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
11986
12060
  var getAllOperations = (paths) => {
11987
12061
  const operations = Object.entries(paths ?? {}).flatMap(
11988
- ([path97, value]) => HttpMethods.flatMap((method) => {
12062
+ ([path98, value]) => HttpMethods.flatMap((method) => {
11989
12063
  if (!value?.[method]) return [];
11990
12064
  const operation = value[method];
11991
12065
  const pathParameters = value.parameters ?? [];
@@ -12005,7 +12079,7 @@ var getAllOperations = (paths) => {
12005
12079
  return {
12006
12080
  ...operation,
12007
12081
  method,
12008
- path: path97,
12082
+ path: path98,
12009
12083
  parameters,
12010
12084
  servers,
12011
12085
  tags: operation.tags ?? []
@@ -12363,8 +12437,8 @@ var Schema = builder.objectRef("Schema").implement({
12363
12437
  }),
12364
12438
  paths: t.field({
12365
12439
  type: [PathItem],
12366
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path97, value]) => ({
12367
- path: path97,
12440
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path98, value]) => ({
12441
+ path: path98,
12368
12442
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
12369
12443
  methods: Object.keys(value)
12370
12444
  }))
@@ -12529,7 +12603,7 @@ init_joinUrl();
12529
12603
 
12530
12604
  // src/vite/api/schema-codegen.ts
12531
12605
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
12532
- var getSegmentsFromPath = (path97) => path97.split("/").slice(1).map(unescapeJsonPointer);
12606
+ var getSegmentsFromPath = (path98) => path98.split("/").slice(1).map(unescapeJsonPointer);
12533
12607
  var createLocalRefMap = (obj) => {
12534
12608
  const refMap = /* @__PURE__ */ new Map();
12535
12609
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -12560,16 +12634,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
12560
12634
  /"__refMap\+Siblings:(.*?)"/g,
12561
12635
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
12562
12636
  );
12563
- var lookup = (schema2, path97, filePath) => {
12564
- const parts = getSegmentsFromPath(path97);
12637
+ var lookup = (schema2, path98, filePath) => {
12638
+ const parts = getSegmentsFromPath(path98);
12565
12639
  let val = schema2;
12566
12640
  for (const part of parts) {
12567
12641
  while (val.$ref?.startsWith("#/")) {
12568
- val = val.$ref === path97 ? val : lookup(schema2, val.$ref, filePath);
12642
+ val = val.$ref === path98 ? val : lookup(schema2, val.$ref, filePath);
12569
12643
  }
12570
12644
  if (val[part] === void 0) {
12571
12645
  throw new Error(
12572
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path97}`
12646
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path98}`
12573
12647
  );
12574
12648
  }
12575
12649
  val = val[part];
@@ -12816,8 +12890,8 @@ var SchemaManager = class {
12816
12890
  }
12817
12891
  }
12818
12892
  };
12819
- getLatestSchema = (path97) => this.processedSchemas[path97]?.at(0);
12820
- getSchemasForPath = (path97) => this.processedSchemas[path97];
12893
+ getLatestSchema = (path98) => this.processedSchemas[path98]?.at(0);
12894
+ getSchemasForPath = (path98) => this.processedSchemas[path98];
12821
12895
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
12822
12896
  getUrlToFilePathMap = () => {
12823
12897
  const map = /* @__PURE__ */ new Map();
@@ -15309,14 +15383,14 @@ async function generateSitemap({
15309
15383
 
15310
15384
  // src/vite/prerender/utils.ts
15311
15385
  init_joinUrl();
15312
- var resolveRoutePath = (path97) => {
15313
- const segments = path97.split("/");
15386
+ var resolveRoutePath = (path98) => {
15387
+ const segments = path98.split("/");
15314
15388
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
15315
15389
  return void 0;
15316
15390
  }
15317
15391
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
15318
15392
  };
15319
- var isSkipped = (path97) => path97.includes("*") || /^\d+$/.test(path97);
15393
+ var isSkipped = (path98) => path98.includes("*") || /^\d+$/.test(path98);
15320
15394
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
15321
15395
  if (route.path && isSkipped(route.path)) return [];
15322
15396
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -15393,9 +15467,9 @@ var prerender = async ({
15393
15467
  let lastLogTime = start;
15394
15468
  const quietDeployJson = process.env.APITOGO_JSON_ONLY === "1";
15395
15469
  const writeProgress = throttle(
15396
- (count, total, urlPath) => {
15470
+ (count2, total, urlPath) => {
15397
15471
  if (quietDeployJson) return;
15398
- writeLine(`prerendering (${count}/${total}) ${colors11.dim(urlPath)}`);
15472
+ writeLine(`prerendering (${count2}/${total}) ${colors11.dim(urlPath)}`);
15399
15473
  }
15400
15474
  );
15401
15475
  if (!isTTY() && !quietDeployJson) {
@@ -15759,11 +15833,12 @@ async function preparePortalWorkspace(dir, mode) {
15759
15833
  return { resolved, prepared, root };
15760
15834
  }
15761
15835
  async function portalBuildHash(resolved, prepared) {
15836
+ const { generatedAt: _generatedAt, ...manifest } = prepared.publishedManifest ?? {};
15762
15837
  return portalArtifactHash(
15763
15838
  resolved,
15764
15839
  void 0,
15765
15840
  getZudokuPackageJson().version,
15766
- prepared.wrapper + JSON.stringify(prepared.siteConfig) + JSON.stringify(prepared.publishedManifest ?? null) + (prepared.referenceSpec ?? "")
15841
+ prepared.wrapper + JSON.stringify(prepared.siteConfig) + JSON.stringify(prepared.publishedManifest ? manifest : null) + (prepared.referenceSpec ?? "")
15767
15842
  );
15768
15843
  }
15769
15844
  function portalBuildStep(context, options = {}, build2 = runBuild) {
@@ -15824,15 +15899,15 @@ async function buildV2(options) {
15824
15899
  // src/cli/check/gateway-online.ts
15825
15900
  import { readFile as readFile16 } from "node:fs/promises";
15826
15901
  var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
15827
- var issue2 = (code, path97, message, hint) => ({ code, path: path97, message, hint });
15902
+ var issue2 = (code, path98, message, hint) => ({ code, path: path98, message, hint });
15828
15903
  function capabilityIssues(gateway, answer) {
15829
15904
  const can = answer.capabilities;
15830
15905
  const plan2 = `your ${answer.tier} plan`;
15831
15906
  const found = [];
15832
- const unsupported = (path97, what, hint) => found.push(
15907
+ const unsupported = (path98, what, hint) => found.push(
15833
15908
  issue2(
15834
15909
  "GATEWAY_CAPABILITY_UNSUPPORTED",
15835
- path97,
15910
+ path98,
15836
15911
  `${what} is not available on ${plan2}.`,
15837
15912
  hint
15838
15913
  )
@@ -15886,8 +15961,8 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
15886
15961
  const specFile = gateway.specs[0];
15887
15962
  if (specFile) {
15888
15963
  try {
15889
- const text = await readFile16(specFile, "utf8");
15890
- if (text.length <= SPEC_SEND_LIMIT) spec = text;
15964
+ const text2 = await readFile16(specFile, "utf8");
15965
+ if (text2.length <= SPEC_SEND_LIMIT) spec = text2;
15891
15966
  } catch {
15892
15967
  }
15893
15968
  }
@@ -15956,9 +16031,9 @@ var issuesOf = (error) => error.issues.map(
15956
16031
  (issue4) => `${issue4.path.join(".") || "(file)"}: ${issue4.message}`
15957
16032
  );
15958
16033
  async function readPlansFile(file) {
15959
- let text;
16034
+ let text2;
15960
16035
  try {
15961
- text = await readFile17(file, "utf8");
16036
+ text2 = await readFile17(file, "utf8");
15962
16037
  } catch {
15963
16038
  throw new PlansFileError(
15964
16039
  "PLANS_FILE_UNREADABLE",
@@ -15968,7 +16043,7 @@ async function readPlansFile(file) {
15968
16043
  }
15969
16044
  let raw;
15970
16045
  try {
15971
- raw = JSON.parse(text);
16046
+ raw = JSON.parse(text2);
15972
16047
  } catch (error) {
15973
16048
  throw new PlansFileError(
15974
16049
  "PLANS_FILE_INVALID",
@@ -16036,9 +16111,9 @@ function toPlatformPlans(plans, options = {}) {
16036
16111
  }
16037
16112
 
16038
16113
  // src/cli/check/services-online.ts
16039
- var issue3 = (code, path97, message, hint) => ({
16114
+ var issue3 = (code, path98, message, hint) => ({
16040
16115
  code,
16041
- path: path97,
16116
+ path: path98,
16042
16117
  message,
16043
16118
  hint
16044
16119
  });
@@ -16396,7 +16471,7 @@ function platformDependencies(args) {
16396
16471
  async function checkV2(dir, args, deps = platformDependencies(args)) {
16397
16472
  const output4 = await runCheckV2(dir, args, deps);
16398
16473
  if (args.json === false) {
16399
- for (const text of formatCheckV2(output4)) console.log(text);
16474
+ for (const text2 of formatCheckV2(output4)) console.log(text2);
16400
16475
  } else {
16401
16476
  console.log(JSON.stringify(output4, null, 2));
16402
16477
  }
@@ -16514,9 +16589,23 @@ async function initV2(argv) {
16514
16589
  }
16515
16590
 
16516
16591
  // src/cli/services/packages.ts
16517
- import { spawn } from "node:child_process";
16518
16592
  import { existsSync as existsSync6 } from "node:fs";
16519
16593
  import path52 from "node:path";
16594
+
16595
+ // src/cli/common/spawn-command.ts
16596
+ import {
16597
+ spawn
16598
+ } from "node:child_process";
16599
+ function quoteForWindowsShell(arg) {
16600
+ return /[\s"^&|<>()%!]/.test(arg) ? `"${arg.replace(/"/g, '""')}"` : arg;
16601
+ }
16602
+ function spawnCommand(command2, args, options = {}) {
16603
+ if (process.platform !== "win32") return spawn(command2, [...args], options);
16604
+ const line2 = [command2, ...args.map(quoteForWindowsShell)].join(" ");
16605
+ return spawn(line2, { ...options, shell: true });
16606
+ }
16607
+
16608
+ // src/cli/services/packages.ts
16520
16609
  var USER_PANEL_PACKAGE = "@lukoweb/apitogo-module-user-panel";
16521
16610
  var BILLING_PLUGIN_PACKAGE = "@lukoweb/apitogo-plugin-dev-portal-billing";
16522
16611
  var LANDING_PACKAGE = "@lukoweb/apitogo-module-landing";
@@ -16563,10 +16652,9 @@ function installSpecsCommand(manager, specs) {
16563
16652
  }
16564
16653
  var specName = (spec) => spec.slice(0, spec.lastIndexOf("@"));
16565
16654
  var runInstaller = (dir, { command: command2, args }) => new Promise((resolve, reject) => {
16566
- const child = spawn(command2, args, {
16655
+ const child = spawnCommand(command2, args, {
16567
16656
  cwd: dir,
16568
- stdio: "inherit",
16569
- shell: process.platform === "win32"
16657
+ stdio: "inherit"
16570
16658
  });
16571
16659
  child.on("error", reject);
16572
16660
  child.on(
@@ -18319,7 +18407,6 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
18319
18407
  }
18320
18408
 
18321
18409
  // src/cli/platform/helpers/npm-build.ts
18322
- import { spawn as spawn2 } from "node:child_process";
18323
18410
  import { access as access7, readFile as readFile22 } from "node:fs/promises";
18324
18411
  import path59 from "node:path";
18325
18412
  var BUILD_LOG_TAIL_BYTES = 8192;
@@ -18370,10 +18457,9 @@ async function pathExists2(filePath) {
18370
18457
  }
18371
18458
  function runCommand(command2, args, cwd, env) {
18372
18459
  return new Promise((resolve, reject) => {
18373
- const child = spawn2(command2, args, {
18460
+ const child = spawnCommand(command2, args, {
18374
18461
  cwd,
18375
- env: env ? { ...process.env, ...env } : process.env,
18376
- shell: process.platform === "win32"
18462
+ env: env ? { ...process.env, ...env } : process.env
18377
18463
  });
18378
18464
  let output4 = "";
18379
18465
  child.stdout.on("data", (chunk) => {
@@ -18929,7 +19015,7 @@ async function check(argv) {
18929
19015
 
18930
19016
  // src/cli/cmds/check.ts
18931
19017
  var check_default = {
18932
- desc: "Check the local project: config version 2 is validated offline; version 1 also checks publish readiness (secrets, manifest, validation)",
19018
+ desc: "Check the project before a deploy: apitogo.config.json, its files and, when you are logged in, the platform's rules",
18933
19019
  command: "check",
18934
19020
  builder: (yargs2) => yargs2.option("dir", {
18935
19021
  type: "string",
@@ -18942,12 +19028,12 @@ var check_default = {
18942
19028
  default: false
18943
19029
  }).option("offline", {
18944
19030
  type: "boolean",
18945
- describe: "Config version 2: do not ask the platform anything (secret names and the gateway's online rules are then not checked)",
19031
+ describe: "Do not ask the platform anything (secret names and the gateway's online rules are then not checked)",
18946
19032
  default: false
18947
19033
  }).option("json", {
18948
19034
  type: "boolean",
18949
- describe: "Print raw JSON only (use --no-json for readable text with a version 2 config)",
18950
- default: true
19035
+ describe: "Print JSON instead of text",
19036
+ default: false
18951
19037
  }),
18952
19038
  handler: async (argv) => {
18953
19039
  await captureEvent({ argv, event: "apitogo check" });
@@ -19119,13 +19205,17 @@ function normalizeDocsSlug(value) {
19119
19205
  (part) => part.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "")
19120
19206
  ).filter(Boolean).join("/");
19121
19207
  }
19122
- function buildDocsPage(title, content) {
19123
- const body = content?.trim() || "Write this page in Markdown. Every `.md` or `.mdx` file under `portal/pages/` becomes a page of your portal.";
19208
+ function withoutLeadingH1(content) {
19209
+ return content.replace(/^\s*#\s+[^\n]*(\r?\n)*/, "").trim();
19210
+ }
19211
+ function buildDocsPage(title, content, order) {
19212
+ const given = content?.trim() ? withoutLeadingH1(content) : "";
19213
+ const body = given || "{/* Write this page in Markdown. Every .md or .mdx file in the pages folder becomes a page of your portal. */}\n\nComing soon.";
19214
+ const orderLine = order === void 0 ? "" : `order: ${order}
19215
+ `;
19124
19216
  return `---
19125
19217
  title: ${JSON.stringify(title)}
19126
- ---
19127
-
19128
- # ${title}
19218
+ ${orderLine}---
19129
19219
 
19130
19220
  ${body}
19131
19221
  `;
@@ -19134,20 +19224,26 @@ function pageComponentName(slug) {
19134
19224
  const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
19135
19225
  return `${/^[0-9]/.test(name) ? "Page" : ""}${name || "Docs"}Page`;
19136
19226
  }
19137
- function buildTsxDocsPage(slug, title, file, content) {
19138
- const text = content?.trim() ? `{${JSON.stringify(content.trim())}}` : "This page is a React component. Change it like any other component: add state, fetch data, or use the portal's components.";
19227
+ function buildTsxDocsPage(slug, title, file, content, order) {
19228
+ const given = content?.trim() ? withoutLeadingH1(content) : "";
19229
+ const orderExport = order === void 0 ? "" : `
19230
+ /** Where the page sits in the sidebar: lower numbers come first. */
19231
+ export const order = ${order};
19232
+ `;
19233
+ const text2 = given ? `{${JSON.stringify(given)}}` : "Coming soon.";
19139
19234
  return `import { Callout, Heading, Typography } from "@lukoweb/apitogo/components";
19140
19235
 
19141
19236
  /** The page's label in the sidebar. */
19142
19237
  export const title = ${JSON.stringify(title)};
19143
-
19238
+ ${orderExport}
19239
+ // This page is a React component: change it like any other one (add state, fetch data, use the portal's components).
19144
19240
  export default function ${pageComponentName(slug)}() {
19145
19241
  return (
19146
19242
  <section className="mx-auto max-w-screen-lg px-4 py-12 pt-(--padding-content-top)">
19147
19243
  <Typography>
19148
19244
  <Heading level={1}>{title}</Heading>
19149
- <p>${text}</p>
19150
- {/* A note for you while you run the portal locally (\`apitogo dev\`); the deployed portal does not show it. */}
19245
+ <p>${text2}</p>
19246
+ {/* A note for you while you run the portal locally (\`apitogo run\`); the deployed portal does not show it. */}
19151
19247
  {import.meta.env.DEV && (
19152
19248
  <Callout type="info" title="Where this page lives">
19153
19249
  Edit <code>${file}</code>. Files whose name starts with <code>_</code> are not pages, so you can keep
@@ -19177,6 +19273,12 @@ async function writeDocsPage(project, input5) {
19177
19273
  }
19178
19274
  const slug = normalizeDocsSlug(input5.slug);
19179
19275
  if (!slug) throw new ConfigEditError("The page path must not be empty.");
19276
+ if (input5.order !== void 0 && !Number.isInteger(input5.order)) {
19277
+ throw new ConfigEditError(
19278
+ `--page-order must be a whole number, not ${input5.order}.`,
19279
+ "For example --page-order 1 puts the page first in the sidebar."
19280
+ );
19281
+ }
19180
19282
  const base = path62.join(portal.dir, "pages", ...slug.split("/"));
19181
19283
  const existing = (await Promise.all(
19182
19284
  [`${base}.tsx`, `${base}.mdx`, `${base}.md`].map(
@@ -19184,9 +19286,9 @@ async function writeDocsPage(project, input5) {
19184
19286
  )
19185
19287
  )).find((file2) => file2 !== null);
19186
19288
  const relative = (file2) => toPosix2(path62.relative(project.dir, file2));
19187
- const write = (file2, title) => writeFile18(
19289
+ const write = (file2, title, order = input5.order) => writeFile18(
19188
19290
  file2,
19189
- file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content) : buildDocsPage(title, input5.content),
19291
+ file2.endsWith(".tsx") ? buildTsxDocsPage(slug, title, relative(file2), input5.content, order) : buildDocsPage(title, input5.content, order),
19190
19292
  "utf8"
19191
19293
  );
19192
19294
  let file;
@@ -19202,7 +19304,8 @@ async function writeDocsPage(project, input5) {
19202
19304
  const title = input5.title?.trim() || (existing.endsWith(".tsx") ? tsxPageLabel(source, existing) : titleOf(source));
19203
19305
  file = existing;
19204
19306
  action = "updated";
19205
- await write(file, title ?? slug);
19307
+ const keptOrder = input5.order ?? (existing.endsWith(".tsx") ? tsxPageOrder(source) : orderValue(/^order:\s*(.+?)\s*$/m.exec(source)?.[1]));
19308
+ await write(file, title ?? slug, keptOrder);
19206
19309
  } else {
19207
19310
  if (existing) {
19208
19311
  throw new ConfigEditError(
@@ -19640,7 +19743,8 @@ async function makeConfig(argv) {
19640
19743
  title: argv.pageTitle,
19641
19744
  content: argv.pageContent,
19642
19745
  update: argv.pageUpdate === true,
19643
- format: argv.pageFormat
19746
+ format: argv.pageFormat,
19747
+ order: argv.pageOrder
19644
19748
  });
19645
19749
  changes.push(page.action === "added" ? "page_add" : "page_update");
19646
19750
  }
@@ -19652,7 +19756,10 @@ async function makeConfig(argv) {
19652
19756
  }
19653
19757
  }
19654
19758
  }
19655
- const message = changes.length === 0 ? "Nothing to change." : `Updated the project (${changes.join(", ")}).`;
19759
+ const message = changes.length === 0 ? "Nothing to change." : (
19760
+ // The change codes (page_add, plan_update, ...) are for scripts: they stay in the JSON output (API-126).
19761
+ "Done."
19762
+ );
19656
19763
  if (json) {
19657
19764
  console.log(
19658
19765
  JSON.stringify(
@@ -19738,6 +19845,9 @@ function addConfigOptions(yargs2) {
19738
19845
  type: "string",
19739
19846
  choices: ["tsx", "mdx"],
19740
19847
  describe: "Format of a new page: tsx (a React component, the default) or mdx (Markdown)"
19848
+ }).option("page-order", {
19849
+ type: "number",
19850
+ describe: "Place of --page-slug in the sidebar: lower numbers come first (pages without one follow, by name)"
19741
19851
  }).option("page-update", {
19742
19852
  type: "boolean",
19743
19853
  default: false,
@@ -19747,11 +19857,11 @@ function addConfigOptions(yargs2) {
19747
19857
  describe: "JSON plan added to the plans file (plans.file, default apitogo/plans.json), or replacing the one with the same sku"
19748
19858
  }).option("patch", {
19749
19859
  type: "string",
19750
- describe: "JSON object deep-merged into apitogo.config.json; the result must be a valid version 2 config"
19860
+ describe: "JSON object deep-merged into apitogo.config.json; the result must still be a valid config"
19751
19861
  });
19752
19862
  }
19753
19863
  var config_default = {
19754
- desc: "Change a config version 2 project: portal name, API code, OpenAPI files, docs pages and plans (interactive on TTY)",
19864
+ desc: "Change the project: portal name, API code, OpenAPI files, docs pages and plans (asks in a terminal)",
19755
19865
  command: "config",
19756
19866
  builder: (yargs2) => addConfigOptions(yargs2),
19757
19867
  handler: async (argv) => {
@@ -19764,14 +19874,62 @@ var config_default = {
19764
19874
  init_apitogo_config_core();
19765
19875
  import { execFile } from "node:child_process";
19766
19876
  import { readFile as readFile24 } from "node:fs/promises";
19767
- import path64 from "node:path";
19877
+ import path65 from "node:path";
19768
19878
  import { promisify } from "node:util";
19879
+
19880
+ // src/cli/create/dry-run.ts
19881
+ import { mkdir as mkdir16, mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
19882
+ import { tmpdir } from "node:os";
19883
+ import path64 from "node:path";
19884
+ async function listFiles(root, dir = root) {
19885
+ const entries = await readdir4(dir, { withFileTypes: true });
19886
+ const nested = await Promise.all(
19887
+ entries.map(async (entry) => {
19888
+ const full = path64.join(dir, entry.name);
19889
+ if (entry.isDirectory()) return listFiles(root, full);
19890
+ return [path64.relative(root, full).split(path64.sep).join("/")];
19891
+ })
19892
+ );
19893
+ return nested.flat();
19894
+ }
19895
+ async function previewCreate(input5) {
19896
+ const scratch = await mkdtemp(path64.join(tmpdir(), "apitogo-create-"));
19897
+ const dir = path64.join(scratch, "project");
19898
+ try {
19899
+ await mkdir16(dir);
19900
+ await applyInitPlan(await planInit({ dir, name: input5.name }));
19901
+ const packages = /* @__PURE__ */ new Set();
19902
+ for (const service of input5.services) {
19903
+ const edit = await planAddService(service, {
19904
+ ...addOptionsFromArguments(service, {
19905
+ service,
19906
+ dir,
19907
+ template: input5.template,
19908
+ yes: true
19909
+ })
19910
+ });
19911
+ for (const pkg of Object.values(
19912
+ packagesToInstall(dir, edit.config)
19913
+ ).flat())
19914
+ packages.add(pkg);
19915
+ await applyServiceEdit(edit);
19916
+ }
19917
+ return {
19918
+ files: (await listFiles(dir)).sort(),
19919
+ packages: [...packages].sort()
19920
+ };
19921
+ } finally {
19922
+ await rm9(scratch, { recursive: true, force: true });
19923
+ }
19924
+ }
19925
+
19926
+ // src/cli/create/handler.ts
19769
19927
  var execFileAsync = promisify(execFile);
19770
19928
  function nextStepHint(dir) {
19771
19929
  const linked = loadConfigV2(dir).resolved.project.id;
19772
- const cd = path64.resolve(dir) === path64.resolve(process.cwd());
19930
+ const cd = path65.resolve(dir) === path65.resolve(process.cwd());
19773
19931
  const steps = linked ? "apitogo deploy" : "apitogo project create, then apitogo deploy";
19774
- return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path64.relative(process.cwd(), dir))}, then ${steps}`;
19932
+ return cd ? `Next: ${steps}` : `Next: cd ${JSON.stringify(path65.relative(process.cwd(), dir))}, then ${steps}`;
19775
19933
  }
19776
19934
  function projectFolderName(name) {
19777
19935
  return name.trim().replace(/[<>:"/\\|?*\s]+/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "apitogo-project";
@@ -19797,13 +19955,13 @@ async function gitHasUncommittedChanges(dir) {
19797
19955
  async function defaultName(dir) {
19798
19956
  try {
19799
19957
  const parsed = JSON.parse(
19800
- await readFile24(path64.join(dir, "package.json"), "utf8")
19958
+ await readFile24(path65.join(dir, "package.json"), "utf8")
19801
19959
  );
19802
19960
  if (typeof parsed.name === "string" && parsed.name.trim())
19803
19961
  return parsed.name.replace(/^@[^/]+\//, "");
19804
19962
  } catch {
19805
19963
  }
19806
- return path64.basename(dir);
19964
+ return path65.basename(dir);
19807
19965
  }
19808
19966
  function parseServicesFlag(value) {
19809
19967
  const names = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
@@ -19829,7 +19987,7 @@ async function createProject(argv, deps = {}) {
19829
19987
  if (!json) printDiagnosticsToConsole(line2);
19830
19988
  };
19831
19989
  try {
19832
- const base = path64.resolve(process.cwd(), argv.dir ?? ".");
19990
+ const base = path65.resolve(process.cwd(), argv.dir ?? ".");
19833
19991
  let dir = base;
19834
19992
  let requested = parseServicesFlag(argv.services);
19835
19993
  let template = argv.template;
@@ -19852,8 +20010,8 @@ async function createProject(argv, deps = {}) {
19852
20010
  default: await defaultName(base)
19853
20011
  }) : await defaultName(base));
19854
20012
  const folder = projectFolderName(name);
19855
- if (argv.dir === void 0 && folder !== path64.basename(base))
19856
- dir = path64.join(base, folder);
20013
+ if (argv.dir === void 0 && folder !== path65.basename(base))
20014
+ dir = path65.join(base, folder);
19857
20015
  }
19858
20016
  if (await (deps.hasUncommittedChanges ?? gitHasUncommittedChanges)(dir))
19859
20017
  say(
@@ -19861,26 +20019,45 @@ async function createProject(argv, deps = {}) {
19861
20019
  );
19862
20020
  if (name !== void 0 && !await isConfigV2(dir)) {
19863
20021
  const plan2 = await planInit({ dir, name });
19864
- for (const line2 of formatInitPlan(plan2)) say(line2);
20022
+ if (!argv.dryRun) for (const line2 of formatInitPlan(plan2)) say(line2);
19865
20023
  commands.push(`apitogo init --name ${JSON.stringify(name)}`);
19866
20024
  if (argv.dryRun) {
20025
+ const services = requested.length > 0 ? requested : templateServices ? STARTER_SERVICES : [];
20026
+ const preview2 = services.length > 0 ? await previewCreate({ name, template, services }) : void 0;
20027
+ const folder = path65.relative(process.cwd(), dir) || ".";
19867
20028
  if (json)
19868
20029
  console.log(
19869
20030
  JSON.stringify({
19870
20031
  status: "dry-run",
19871
- services: requested,
20032
+ dir: folder,
20033
+ services,
20034
+ files: preview2?.files ?? [],
20035
+ packages: preview2?.packages ?? [],
19872
20036
  commands: [
19873
20037
  ...commands,
19874
- ...requested.map((service) => `apitogo add ${service}`)
20038
+ ...services.map((service) => `apitogo add ${service}`)
19875
20039
  ]
19876
20040
  })
19877
20041
  );
19878
- else printResultToConsole("Dry run: nothing was written.");
20042
+ else {
20043
+ say(`Folder: ${folder}${dir === base ? "" : " (new)"}`);
20044
+ if (preview2) {
20045
+ say(`Files (${preview2.files.length}):`);
20046
+ for (const file of preview2.files) say(` ${file}`);
20047
+ if (preview2.packages.length > 0 && argv.install !== false)
20048
+ say(`Packages to install: ${preview2.packages.join(", ")}`);
20049
+ } else {
20050
+ say(
20051
+ "The services are chosen one by one in a real run; pass --template full or --services to see their files."
20052
+ );
20053
+ }
20054
+ printResultToConsole("Dry run: nothing was written.");
20055
+ }
19879
20056
  return;
19880
20057
  }
19881
20058
  await applyInitPlan(plan2);
19882
20059
  say(
19883
- dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path64.relative(base, dir)}${path64.sep}${APITOGO_CONFIG_FILENAME}.`
20060
+ dir === base ? `Created ${APITOGO_CONFIG_FILENAME}.` : `Created ${path65.relative(base, dir)}${path65.sep}${APITOGO_CONFIG_FILENAME}.`
19884
20061
  );
19885
20062
  } else {
19886
20063
  say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
@@ -19981,9 +20158,8 @@ var create_default = {
19981
20158
  command: "create",
19982
20159
  builder: (yargs2) => yargs2.option("dir", {
19983
20160
  type: "string",
19984
- describe: "Project directory (default: a new folder named after the project)",
19985
- normalize: true,
19986
- hidden: true
20161
+ describe: "Project folder (default: a new folder named after the project, inside the current one)",
20162
+ normalize: true
19987
20163
  }).option("name", {
19988
20164
  type: "string",
19989
20165
  describe: "Project name (default: folder name or package.json name)"
@@ -20005,7 +20181,7 @@ var create_default = {
20005
20181
  }).option("dry-run", {
20006
20182
  type: "boolean",
20007
20183
  default: false,
20008
- describe: "Print the will-create list and exit"
20184
+ describe: "Show the folder, every file and the packages a real run makes, and write nothing"
20009
20185
  }).option("yes", {
20010
20186
  alias: "y",
20011
20187
  type: "boolean",
@@ -20023,10 +20199,10 @@ var create_default = {
20023
20199
  };
20024
20200
 
20025
20201
  // src/cli/cmds/deploy.ts
20026
- import path82 from "node:path";
20202
+ import path83 from "node:path";
20027
20203
 
20028
20204
  // src/cli/apply/handler.ts
20029
- import path76 from "node:path";
20205
+ import path77 from "node:path";
20030
20206
 
20031
20207
  // src/cli/platform/helpers/gateway-plan-validation.ts
20032
20208
  init_gateway_limits();
@@ -20147,14 +20323,22 @@ var MODULE_DISPLAY_NAMES = {
20147
20323
  "dev-portal": "Developer website",
20148
20324
  apihub: "API hosting"
20149
20325
  };
20150
- function friendlyModuleNames(text) {
20151
- return text.replace(
20326
+ function friendlyModuleNames(text2) {
20327
+ return text2.replace(
20152
20328
  /(?:Module|module) (dev-portal|apihub)(?![\w-])/g,
20153
20329
  (_match, id) => MODULE_DISPLAY_NAMES[id] ?? id
20154
20330
  );
20155
20331
  }
20156
- function createConsoleProgressReporter(stream = process.stderr) {
20157
- const shared = { spinner: void 0 };
20332
+ var INFRASTRUCTURE_MESSAGE = /front ?door|\bapim\b|api management|fabric|container app|origin group|\borigin\b|binding|\bdns\b|static web app|\bswa\b|\bacr\b|resource group|revision|warm pool|capacity pool|pooled/i;
20333
+ function isInfrastructureMessage(message) {
20334
+ return INFRASTRUCTURE_MESSAGE.test(message);
20335
+ }
20336
+ function createConsoleProgressReporter(stream = process.stderr, options = {}) {
20337
+ const shared = {
20338
+ spinner: void 0,
20339
+ verbose: options.verbose === true,
20340
+ printed: /* @__PURE__ */ new Set()
20341
+ };
20158
20342
  return {
20159
20343
  ...createScopedConsoleProgressReporter(stream, void 0, shared),
20160
20344
  stop() {
@@ -20192,11 +20376,13 @@ function createScopedConsoleProgressReporter(stream, stepLabel, shared) {
20192
20376
  },
20193
20377
  async onNewMessages(messages) {
20194
20378
  for (const entry of messages) {
20195
- writeLine2(
20196
- friendlyModuleNames(
20197
- stepLabel ? `[${stepLabel}] ${entry.message}` : entry.message
20198
- )
20379
+ if (!shared.verbose && isInfrastructureMessage(entry.message)) continue;
20380
+ const line2 = friendlyModuleNames(
20381
+ stepLabel ? `[${stepLabel}] ${entry.message}` : entry.message
20199
20382
  );
20383
+ if (shared.printed.has(line2)) continue;
20384
+ shared.printed.add(line2);
20385
+ writeLine2(line2);
20200
20386
  }
20201
20387
  },
20202
20388
  isCancelled: () => false,
@@ -20235,11 +20421,11 @@ init_gateway_limits();
20235
20421
  // src/cli/platform/helpers/deploy-artifact-hash.ts
20236
20422
  import { createHash as createHash3 } from "node:crypto";
20237
20423
  import { createReadStream, promises as fs6 } from "node:fs";
20238
- import path66 from "node:path";
20424
+ import path67 from "node:path";
20239
20425
 
20240
20426
  // src/cli/platform/helpers/package-files.ts
20241
20427
  import { promises as fs5 } from "node:fs";
20242
- import path65 from "node:path";
20428
+ import path66 from "node:path";
20243
20429
  import ignore from "ignore";
20244
20430
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
20245
20431
  "node_modules",
@@ -20253,7 +20439,7 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20253
20439
  const ig = ignore();
20254
20440
  ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
20255
20441
  if (extraExcludes.length > 0) ig.add([...extraExcludes]);
20256
- const gitignorePath = path65.join(rootDir, ".gitignore");
20442
+ const gitignorePath = path66.join(rootDir, ".gitignore");
20257
20443
  try {
20258
20444
  const content = await fs5.readFile(gitignorePath, "utf8");
20259
20445
  ig.add(content);
@@ -20262,11 +20448,11 @@ async function loadGitignore(rootDir, extraExcludes = []) {
20262
20448
  return ig;
20263
20449
  }
20264
20450
  async function collectFiles(rootDir, relativeDir, ig, files) {
20265
- const absDir = path65.join(rootDir, relativeDir);
20451
+ const absDir = path66.join(rootDir, relativeDir);
20266
20452
  const entries = await fs5.readdir(absDir, { withFileTypes: true });
20267
20453
  for (const entry of entries) {
20268
- const rel2 = relativeDir ? path65.join(relativeDir, entry.name) : entry.name;
20269
- const relPosix = rel2.split(path65.sep).join("/");
20454
+ const rel2 = relativeDir ? path66.join(relativeDir, entry.name) : entry.name;
20455
+ const relPosix = rel2.split(path66.sep).join("/");
20270
20456
  if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
20271
20457
  continue;
20272
20458
  }
@@ -20281,7 +20467,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
20281
20467
  }
20282
20468
  }
20283
20469
  async function collectPackageableFiles(sourceDirectory, extraExcludes = []) {
20284
- const resolved = path65.resolve(sourceDirectory);
20470
+ const resolved = path66.resolve(sourceDirectory);
20285
20471
  const stat7 = await fs5.stat(resolved).catch(() => null);
20286
20472
  if (!stat7?.isDirectory()) {
20287
20473
  throw new Error(`sourceDirectory is not a directory: ${resolved}`);
@@ -20307,7 +20493,7 @@ async function sha256File(filePath) {
20307
20493
  return hash2.digest("hex");
20308
20494
  }
20309
20495
  async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20310
- const resolved = path66.resolve(rootDirectory);
20496
+ const resolved = path67.resolve(rootDirectory);
20311
20497
  const files = (await collectPackageableFiles(resolved, extraExcludes)).sort(
20312
20498
  (a, b) => a.localeCompare(b)
20313
20499
  );
@@ -20315,13 +20501,13 @@ async function hashDirectoryContents(rootDirectory, extraExcludes = []) {
20315
20501
  for (const rel2 of files) {
20316
20502
  hash2.update(rel2);
20317
20503
  hash2.update("\0");
20318
- hash2.update(await fs6.readFile(path66.join(resolved, rel2)));
20504
+ hash2.update(await fs6.readFile(path67.join(resolved, rel2)));
20319
20505
  hash2.update("\0");
20320
20506
  }
20321
20507
  return hash2.digest("hex");
20322
20508
  }
20323
20509
  async function hashDevPortalSiteSources(siteDirectory, options) {
20324
- const resolved = path66.resolve(siteDirectory);
20510
+ const resolved = path67.resolve(siteDirectory);
20325
20511
  const hash2 = createHash3("sha256");
20326
20512
  const candidates = [
20327
20513
  "apitogo.config.json",
@@ -20331,7 +20517,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20331
20517
  "zudoku.config.tsx"
20332
20518
  ];
20333
20519
  for (const name of candidates) {
20334
- const full = path66.join(resolved, name);
20520
+ const full = path67.join(resolved, name);
20335
20521
  try {
20336
20522
  hash2.update(name);
20337
20523
  hash2.update("\0");
@@ -20341,7 +20527,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
20341
20527
  }
20342
20528
  }
20343
20529
  for (const dir of ["pages", "landing", "src"]) {
20344
- const full = path66.join(resolved, dir);
20530
+ const full = path67.join(resolved, dir);
20345
20531
  try {
20346
20532
  const st = await fs6.stat(full);
20347
20533
  if (!st.isDirectory()) continue;
@@ -20570,8 +20756,8 @@ function normalizeActionKey(actionKey2) {
20570
20756
  const trimmed = actionKey2.trim();
20571
20757
  if (trimmed.includes(":")) {
20572
20758
  const [method2, ...pathParts] = trimmed.split(":");
20573
- const path98 = pathParts.join(":").trim();
20574
- return `${method2?.toLowerCase()}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
20759
+ const path99 = pathParts.join(":").trim();
20760
+ return `${method2?.toLowerCase()}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
20575
20761
  }
20576
20762
  const spaceIndex = trimmed.indexOf(" ");
20577
20763
  if (spaceIndex <= 0) {
@@ -20580,8 +20766,8 @@ function normalizeActionKey(actionKey2) {
20580
20766
  );
20581
20767
  }
20582
20768
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
20583
- const path97 = trimmed.slice(spaceIndex + 1).trim();
20584
- return `${method}:${path97.startsWith("/") ? path97 : `/${path97}`}`;
20769
+ const path98 = trimmed.slice(spaceIndex + 1).trim();
20770
+ return `${method}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
20585
20771
  }
20586
20772
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
20587
20773
  const applied = [];
@@ -21059,8 +21245,8 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
21059
21245
  }
21060
21246
 
21061
21247
  // src/cli/platform/helpers/openapi-gateway-servers.ts
21062
- import { mkdir as mkdir16, readFile as readFile25, writeFile as writeFile19 } from "node:fs/promises";
21063
- import path67 from "node:path";
21248
+ import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile19 } from "node:fs/promises";
21249
+ import path68 from "node:path";
21064
21250
  import YAML from "yaml";
21065
21251
  var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
21066
21252
  var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
@@ -21092,7 +21278,7 @@ async function resolveProjectGatewayUrl(client, projectId) {
21092
21278
  }
21093
21279
  async function readOpenApiDocument(filePath) {
21094
21280
  const content = await readFile25(filePath, "utf8");
21095
- const ext = path67.extname(filePath).toLowerCase();
21281
+ const ext = path68.extname(filePath).toLowerCase();
21096
21282
  const parsed = ext === ".json" ? JSON.parse(content) : YAML.parse(content);
21097
21283
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21098
21284
  throw new Error(`OpenAPI document must be an object: ${filePath}`);
@@ -21100,13 +21286,13 @@ async function readOpenApiDocument(filePath) {
21100
21286
  return parsed;
21101
21287
  }
21102
21288
  async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
21103
- const site = path67.resolve(siteDirectory);
21104
- const source = path67.resolve(sourceOpenApiPath);
21289
+ const site = path68.resolve(siteDirectory);
21290
+ const source = path68.resolve(sourceOpenApiPath);
21105
21291
  const relativeInput = PUBLISH_OPENAPI_RELATIVE;
21106
- const outputPath = path67.join(site, ...relativeInput.split("/"));
21292
+ const outputPath = path68.join(site, ...relativeInput.split("/"));
21107
21293
  const doc = await readOpenApiDocument(source);
21108
21294
  const rewritten = withGatewayServers(doc, gatewayUrl);
21109
- await mkdir16(path67.dirname(outputPath), { recursive: true });
21295
+ await mkdir17(path68.dirname(outputPath), { recursive: true });
21110
21296
  await writeFile19(
21111
21297
  outputPath,
21112
21298
  `${JSON.stringify(rewritten, null, 2)}
@@ -21116,8 +21302,8 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
21116
21302
  return { outputPath, relativeInput };
21117
21303
  }
21118
21304
  async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21119
- const configPath = path67.join(
21120
- path67.resolve(siteDirectory),
21305
+ const configPath = path68.join(
21306
+ path68.resolve(siteDirectory),
21121
21307
  APITOGO_CONFIG_FILENAME2
21122
21308
  );
21123
21309
  const raw = await readFile25(configPath, "utf8");
@@ -21159,7 +21345,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
21159
21345
  };
21160
21346
  }
21161
21347
  async function prepareOpenApiServersForGatewayBuild(options) {
21162
- const siteDirectory = path67.resolve(options.siteDirectory);
21348
+ const siteDirectory = path68.resolve(options.siteDirectory);
21163
21349
  let openApiPath = resolveBackendOpenApiPath(
21164
21350
  options.manifest?.backend?.openApiPath,
21165
21351
  {
@@ -21173,7 +21359,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
21173
21359
  if (!openApiPath) {
21174
21360
  try {
21175
21361
  const raw = await readFile25(
21176
- path67.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21362
+ path68.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
21177
21363
  "utf8"
21178
21364
  );
21179
21365
  const config2 = JSON.parse(raw);
@@ -21202,8 +21388,8 @@ function normalizeActionKeyFormat(actionKey2) {
21202
21388
  const trimmed = actionKey2.trim();
21203
21389
  if (trimmed.includes(":")) {
21204
21390
  const [method2, ...pathParts] = trimmed.split(":");
21205
- const path98 = pathParts.join(":").trim();
21206
- return `${method2.toLowerCase()}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
21391
+ const path99 = pathParts.join(":").trim();
21392
+ return `${method2.toLowerCase()}:${path99.startsWith("/") ? path99 : `/${path99}`}`;
21207
21393
  }
21208
21394
  const spaceIndex = trimmed.indexOf(" ");
21209
21395
  if (spaceIndex <= 0) {
@@ -21212,8 +21398,8 @@ function normalizeActionKeyFormat(actionKey2) {
21212
21398
  );
21213
21399
  }
21214
21400
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
21215
- const path97 = trimmed.slice(spaceIndex + 1).trim();
21216
- return `${method}:${path97.startsWith("/") ? path97 : `/${path97}`}`;
21401
+ const path98 = trimmed.slice(spaceIndex + 1).trim();
21402
+ return `${method}:${path98.startsWith("/") ? path98 : `/${path98}`}`;
21217
21403
  }
21218
21404
  function normalizePublicRoute(route) {
21219
21405
  const trimmed = route.trim();
@@ -21246,11 +21432,11 @@ function collectManifestPublicActionKeys(gateway) {
21246
21432
  }
21247
21433
 
21248
21434
  // src/cli/platform/helpers/publish-ledger.ts
21249
- import { mkdir as mkdir17, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
21250
- import path68 from "node:path";
21435
+ import { mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
21436
+ import path69 from "node:path";
21251
21437
  var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
21252
21438
  function publishLedgerPath(siteDirectory) {
21253
- return path68.join(path68.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21439
+ return path69.join(path69.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
21254
21440
  }
21255
21441
  async function readPublishLedger(siteDirectory) {
21256
21442
  const filePath = publishLedgerPath(siteDirectory);
@@ -21267,7 +21453,7 @@ async function readPublishLedger(siteDirectory) {
21267
21453
  }
21268
21454
  async function writePublishLedger(siteDirectory, ledger) {
21269
21455
  const filePath = publishLedgerPath(siteDirectory);
21270
- await mkdir17(path68.dirname(filePath), { recursive: true });
21456
+ await mkdir18(path69.dirname(filePath), { recursive: true });
21271
21457
  const next = {
21272
21458
  ...ledger,
21273
21459
  version: 1,
@@ -21786,20 +21972,19 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
21786
21972
  }
21787
21973
 
21788
21974
  // src/cli/platform/helpers/backend-packaging.ts
21789
- import { spawn as spawn3 } from "node:child_process";
21790
- import { mkdtemp, readdir as readdir4, rm as rm9 } from "node:fs/promises";
21791
- import { tmpdir } from "node:os";
21792
- import path69 from "node:path";
21975
+ import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10 } from "node:fs/promises";
21976
+ import { tmpdir as tmpdir2 } from "node:os";
21977
+ import path70 from "node:path";
21793
21978
  async function directoryHasRuntimeConfig(dir) {
21794
21979
  if (!await pathExists(dir)) {
21795
21980
  return false;
21796
21981
  }
21797
- const entries = await readdir4(dir);
21982
+ const entries = await readdir5(dir);
21798
21983
  return entries.some((name) => name.endsWith(".runtimeconfig.json"));
21799
21984
  }
21800
21985
  function runDotnetPublish(projectDir, outputDir) {
21801
21986
  return new Promise((resolve, reject) => {
21802
- const child = spawn3(
21987
+ const child = spawnCommand(
21803
21988
  "dotnet",
21804
21989
  [
21805
21990
  "publish",
@@ -21814,8 +21999,7 @@ function runDotnetPublish(projectDir, outputDir) {
21814
21999
  ],
21815
22000
  {
21816
22001
  cwd: projectDir,
21817
- env: process.env,
21818
- shell: process.platform === "win32"
22002
+ env: process.env
21819
22003
  }
21820
22004
  );
21821
22005
  let output4 = "";
@@ -21830,22 +22014,22 @@ function runDotnetPublish(projectDir, outputDir) {
21830
22014
  });
21831
22015
  }
21832
22016
  async function findCsproj(projectDir) {
21833
- const entries = await readdir4(projectDir);
22017
+ const entries = await readdir5(projectDir);
21834
22018
  const csproj = entries.find((name) => name.endsWith(".csproj"));
21835
- return csproj ? path69.join(projectDir, csproj) : null;
22019
+ return csproj ? path70.join(projectDir, csproj) : null;
21836
22020
  }
21837
22021
  async function prepareBackendPackagingDirectory(args) {
21838
- const sourceDirectory = path69.resolve(args.sourceDirectory);
22022
+ const sourceDirectory = path70.resolve(args.sourceDirectory);
21839
22023
  if (args.publishDirectory?.trim()) {
21840
22024
  return {
21841
- directory: path69.resolve(args.publishDirectory),
22025
+ directory: path70.resolve(args.publishDirectory),
21842
22026
  packagingMode: "published"
21843
22027
  };
21844
22028
  }
21845
22029
  if (await directoryHasRuntimeConfig(sourceDirectory)) {
21846
22030
  return { directory: sourceDirectory, packagingMode: "published" };
21847
22031
  }
21848
- const publishSubdir = path69.join(sourceDirectory, "publish");
22032
+ const publishSubdir = path70.join(sourceDirectory, "publish");
21849
22033
  if (await directoryHasRuntimeConfig(publishSubdir)) {
21850
22034
  return { directory: publishSubdir, packagingMode: "published" };
21851
22035
  }
@@ -21856,19 +22040,19 @@ async function prepareBackendPackagingDirectory(args) {
21856
22040
  if (!csproj) {
21857
22041
  return { directory: sourceDirectory, packagingMode: "source" };
21858
22042
  }
21859
- const tempRoot = await mkdtemp(
21860
- path69.join(tmpdir(), "apitogo-dotnet-publish-")
22043
+ const tempRoot = await mkdtemp2(
22044
+ path70.join(tmpdir2(), "apitogo-dotnet-publish-")
21861
22045
  );
21862
- const outputDir = path69.join(tempRoot, "publish");
22046
+ const outputDir = path70.join(tempRoot, "publish");
21863
22047
  const result = await runDotnetPublish(sourceDirectory, outputDir);
21864
22048
  if (result.exitCode !== 0) {
21865
- await rm9(tempRoot, { recursive: true, force: true });
22049
+ await rm10(tempRoot, { recursive: true, force: true });
21866
22050
  throw new Error(
21867
22051
  `dotnet publish failed (exit ${result.exitCode ?? "unknown"}): ${result.output.slice(-2e3)}`
21868
22052
  );
21869
22053
  }
21870
22054
  if (!await directoryHasRuntimeConfig(outputDir)) {
21871
- await rm9(tempRoot, { recursive: true, force: true });
22055
+ await rm10(tempRoot, { recursive: true, force: true });
21872
22056
  throw new Error(
21873
22057
  "dotnet publish completed but no *.runtimeconfig.json was found in the output directory."
21874
22058
  );
@@ -21877,7 +22061,7 @@ async function prepareBackendPackagingDirectory(args) {
21877
22061
  directory: outputDir,
21878
22062
  packagingMode: "published",
21879
22063
  cleanup: async () => {
21880
- await rm9(tempRoot, { recursive: true, force: true });
22064
+ await rm10(tempRoot, { recursive: true, force: true });
21881
22065
  }
21882
22066
  };
21883
22067
  }
@@ -21904,13 +22088,13 @@ function formatJobStatusResponse(options) {
21904
22088
 
21905
22089
  // src/cli/platform/helpers/openapi.ts
21906
22090
  import { promises as fs7 } from "node:fs";
21907
- import path70 from "node:path";
22091
+ import path71 from "node:path";
21908
22092
  import SwaggerParser from "@apidevtools/swagger-parser";
21909
22093
  import YAML2 from "yaml";
21910
22094
  async function validateOpenApiFile(filePath) {
21911
- const resolved = path70.resolve(filePath);
22095
+ const resolved = path71.resolve(filePath);
21912
22096
  const content = await fs7.readFile(resolved, "utf8");
21913
- const ext = path70.extname(resolved).toLowerCase();
22097
+ const ext = path71.extname(resolved).toLowerCase();
21914
22098
  let parsed;
21915
22099
  try {
21916
22100
  parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
@@ -21953,14 +22137,14 @@ async function validateOpenApiFile(filePath) {
21953
22137
 
21954
22138
  // src/cli/platform/helpers/zip.ts
21955
22139
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs8 } from "node:fs";
21956
- import { tmpdir as tmpdir2 } from "node:os";
21957
- import path71 from "node:path";
22140
+ import { tmpdir as tmpdir3 } from "node:os";
22141
+ import path72 from "node:path";
21958
22142
  import { ZipArchive } from "archiver";
21959
22143
  async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
21960
- const resolved = path71.resolve(sourceDirectory);
22144
+ const resolved = path72.resolve(sourceDirectory);
21961
22145
  const files = await collectPackageableFiles(resolved, extraExcludes);
21962
- const zipPath = path71.join(
21963
- tmpdir2(),
22146
+ const zipPath = path72.join(
22147
+ tmpdir3(),
21964
22148
  `apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
21965
22149
  );
21966
22150
  await new Promise((resolve, reject) => {
@@ -21971,8 +22155,8 @@ async function packageBackendDirectory(sourceDirectory, extraExcludes = []) {
21971
22155
  output4.on("error", reject);
21972
22156
  archive.pipe(output4);
21973
22157
  for (const rel2 of files) {
21974
- archive.append(createReadStream2(path71.join(resolved, rel2)), {
21975
- name: rel2.split(path71.sep).join("/")
22158
+ archive.append(createReadStream2(path72.join(resolved, rel2)), {
22159
+ name: rel2.split(path72.sep).join("/")
21976
22160
  });
21977
22161
  }
21978
22162
  void archive.finalize();
@@ -23523,11 +23707,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
23523
23707
  }
23524
23708
 
23525
23709
  // src/cli/platform/tools/dev-portal-site-build.ts
23526
- import path75 from "node:path";
23710
+ import path76 from "node:path";
23527
23711
 
23528
23712
  // src/cli/platform/helpers/dev-portal-site-api-url.ts
23529
- import { readdir as readdir5, readFile as readFile27, writeFile as writeFile21 } from "node:fs/promises";
23530
- import path72 from "node:path";
23713
+ import { readdir as readdir6, readFile as readFile27, writeFile as writeFile21 } from "node:fs/promises";
23714
+ import path73 from "node:path";
23531
23715
  var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
23532
23716
  var PLACEHOLDER_HOST_MARKERS = [
23533
23717
  "dp-example.azurecontainerapps.io",
@@ -23548,10 +23732,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
23548
23732
  return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
23549
23733
  }
23550
23734
  async function readSiteApiBaseUrl(sourceDirectory) {
23551
- const resolved = path72.resolve(sourceDirectory);
23735
+ const resolved = path73.resolve(sourceDirectory);
23552
23736
  for (const envFile of [".env", ".env.production"]) {
23553
23737
  try {
23554
- const content = await readFile27(path72.join(resolved, envFile), "utf8");
23738
+ const content = await readFile27(path73.join(resolved, envFile), "utf8");
23555
23739
  const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
23556
23740
  if (lines.length === 0) {
23557
23741
  continue;
@@ -23576,11 +23760,11 @@ async function readSiteApiBaseUrl(sourceDirectory) {
23576
23760
  return null;
23577
23761
  }
23578
23762
  async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
23579
- const resolved = path72.resolve(sourceDirectory);
23763
+ const resolved = path73.resolve(sourceDirectory);
23580
23764
  const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
23581
23765
  let patchedExisting = false;
23582
23766
  for (const envFile of [".env.production", ".env"]) {
23583
- const envPath = path72.join(resolved, envFile);
23767
+ const envPath = path73.join(resolved, envFile);
23584
23768
  try {
23585
23769
  const content = await readFile27(envPath, "utf8");
23586
23770
  const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
@@ -23594,7 +23778,7 @@ ${line2}
23594
23778
  }
23595
23779
  }
23596
23780
  if (!patchedExisting) {
23597
- await writeFile21(path72.join(resolved, ".env"), `${line2}
23781
+ await writeFile21(path73.join(resolved, ".env"), `${line2}
23598
23782
  `);
23599
23783
  }
23600
23784
  }
@@ -23602,7 +23786,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
23602
23786
  return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
23603
23787
  }
23604
23788
  async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
23605
- const sourceDirectory = path72.resolve(args.sourceDirectory);
23789
+ const sourceDirectory = path73.resolve(args.sourceDirectory);
23606
23790
  const explicit = args.apiBaseUrl?.trim();
23607
23791
  if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
23608
23792
  await patchSiteApiBaseUrl(sourceDirectory, explicit);
@@ -23633,12 +23817,12 @@ async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveF
23633
23817
  async function* walkFiles(root) {
23634
23818
  let entries;
23635
23819
  try {
23636
- entries = await readdir5(root, { withFileTypes: true });
23820
+ entries = await readdir6(root, { withFileTypes: true });
23637
23821
  } catch {
23638
23822
  return;
23639
23823
  }
23640
23824
  for (const entry of entries) {
23641
- const full = path72.join(root, entry.name);
23825
+ const full = path73.join(root, entry.name);
23642
23826
  if (entry.isDirectory()) {
23643
23827
  if (entry.name === "node_modules" || entry.name === ".git") {
23644
23828
  continue;
@@ -23650,8 +23834,8 @@ async function* walkFiles(root) {
23650
23834
  }
23651
23835
  }
23652
23836
  async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
23653
- const dist = path72.resolve(distDirectory);
23654
- const siteRoot = path72.dirname(dist);
23837
+ const dist = path73.resolve(distDirectory);
23838
+ const siteRoot = path73.dirname(dist);
23655
23839
  const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
23656
23840
  if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
23657
23841
  throw new Error(
@@ -23687,7 +23871,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
23687
23871
 
23688
23872
  // src/cli/platform/tools/dev-portal.ts
23689
23873
  import { promises as fs10 } from "node:fs";
23690
- import path74 from "node:path";
23874
+ import path75 from "node:path";
23691
23875
 
23692
23876
  // src/cli/platform/helpers/dev-portal-backend-job.ts
23693
23877
  var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
@@ -23796,8 +23980,8 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
23796
23980
 
23797
23981
  // src/cli/platform/helpers/tarball.ts
23798
23982
  import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs9 } from "node:fs";
23799
- import { tmpdir as tmpdir3 } from "node:os";
23800
- import path73 from "node:path";
23983
+ import { tmpdir as tmpdir4 } from "node:os";
23984
+ import path74 from "node:path";
23801
23985
  import { TarArchive } from "archiver";
23802
23986
  var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
23803
23987
  function normalizeArchiveRoot(archiveRoot) {
@@ -23808,11 +23992,11 @@ function normalizeArchiveRoot(archiveRoot) {
23808
23992
  return `${trimmed}/`;
23809
23993
  }
23810
23994
  async function packageDirectoryAsTarGz(sourceDirectory, options) {
23811
- const resolved = path73.resolve(sourceDirectory);
23995
+ const resolved = path74.resolve(sourceDirectory);
23812
23996
  const files = await collectPackageableFiles(resolved);
23813
23997
  const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
23814
- const archivePath = path73.join(
23815
- tmpdir3(),
23998
+ const archivePath = path74.join(
23999
+ tmpdir4(),
23816
24000
  `apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
23817
24001
  );
23818
24002
  await new Promise((resolve, reject) => {
@@ -23823,8 +24007,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
23823
24007
  output4.on("error", reject);
23824
24008
  archive.pipe(output4);
23825
24009
  for (const rel2 of files) {
23826
- const entryName = entryPrefix + rel2.split(path73.sep).join("/");
23827
- archive.append(createReadStream3(path73.join(resolved, rel2)), {
24010
+ const entryName = entryPrefix + rel2.split(path74.sep).join("/");
24011
+ archive.append(createReadStream3(path74.join(resolved, rel2)), {
23828
24012
  name: entryName
23829
24013
  });
23830
24014
  }
@@ -24066,7 +24250,7 @@ function validateDeployArchiveInput(args) {
24066
24250
  async function resolveDevPortalArchive(args) {
24067
24251
  validateDeployArchiveInput(args);
24068
24252
  if (args.archivePath?.trim()) {
24069
- const archivePath = path74.resolve(args.archivePath.trim());
24253
+ const archivePath = path75.resolve(args.archivePath.trim());
24070
24254
  const stat7 = await fs10.stat(archivePath).catch(() => null);
24071
24255
  if (!stat7?.isFile()) {
24072
24256
  throw new Error(`archivePath is not a file: ${archivePath}`);
@@ -24131,7 +24315,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
24131
24315
  // src/cli/platform/tools/dev-portal-site-build.ts
24132
24316
  async function handleBuildDevPortalSite(client, config2, args, progress) {
24133
24317
  const buildProgress = progress?.child("Build dev portal site");
24134
- const sourceDirectory = path75.resolve(args.sourceDirectory);
24318
+ const sourceDirectory = path76.resolve(args.sourceDirectory);
24135
24319
  await buildProgress?.onStep("Ensuring live portal API URL");
24136
24320
  const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
24137
24321
  client,
@@ -25637,7 +25821,7 @@ function resultLooksFailed(payload) {
25637
25821
  return parsed.status === "failed" || parsed.status === "blocked";
25638
25822
  }
25639
25823
  async function apply(argv) {
25640
- const sourceDirectory = path76.resolve(process.cwd(), argv.dir);
25824
+ const sourceDirectory = path77.resolve(process.cwd(), argv.dir);
25641
25825
  let config2;
25642
25826
  try {
25643
25827
  config2 = loadConfig();
@@ -25707,7 +25891,7 @@ async function apply(argv) {
25707
25891
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
25708
25892
  const resolution = await resolveProjectNotFoundInteractively(
25709
25893
  parsed,
25710
- path76.basename(sourceDirectory)
25894
+ path77.basename(sourceDirectory)
25711
25895
  );
25712
25896
  if (resolution) {
25713
25897
  effectiveProjectId = void 0;
@@ -25727,7 +25911,7 @@ async function apply(argv) {
25727
25911
  while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
25728
25912
  const resolution = await resolveProjectNameExistsInteractively(
25729
25913
  parsed,
25730
- effectiveProjectName ?? path76.basename(sourceDirectory)
25914
+ effectiveProjectName ?? path77.basename(sourceDirectory)
25731
25915
  );
25732
25916
  if (!resolution) {
25733
25917
  break;
@@ -25798,11 +25982,11 @@ async function apply(argv) {
25798
25982
  }
25799
25983
 
25800
25984
  // src/cli/deploy/command.ts
25801
- import path81 from "node:path";
25985
+ import path82 from "node:path";
25802
25986
  init_invariant();
25803
25987
 
25804
25988
  // src/cli/project/create-handler.ts
25805
- import path77 from "node:path";
25989
+ import path78 from "node:path";
25806
25990
  import open2 from "open";
25807
25991
  init_invariant();
25808
25992
 
@@ -25825,13 +26009,13 @@ function modulesForConfig(config2) {
25825
26009
  }
25826
26010
  function assertConfigChecksOut(output4) {
25827
26011
  if (output4.status === "ok") return;
25828
- const count = output4.errors.length;
26012
+ const count2 = output4.errors.length;
25829
26013
  throw new ProjectCreateError(
25830
26014
  "CONFIG_INVALID",
25831
- `The config has ${count} error${count === 1 ? "" : "s"}, so no project is created.`,
26015
+ `The config has ${count2} error${count2 === 1 ? "" : "s"}, so no project is created.`,
25832
26016
  [
25833
26017
  ...output4.errors.slice(0, 5).map((issue4) => `- ${formatConfigIssue(issue4)}`),
25834
- ...count > 5 ? [`- and ${count - 5} more`] : [],
26018
+ ...count2 > 5 ? [`- and ${count2 - 5} more`] : [],
25835
26019
  "Fix them (`apitogo check` lists everything), then run `apitogo project create` again."
25836
26020
  ].join("\n")
25837
26021
  );
@@ -26028,7 +26212,13 @@ async function createLinkedProject(argv, dir, options = {}) {
26028
26212
  }
26029
26213
  const config2 = loadConfig();
26030
26214
  const client = new PlatformApiClient(config2);
26031
- const progress = createConsoleProgressReporter();
26215
+ const progress = createConsoleProgressReporter(process.stderr, {
26216
+ verbose: argv.verbose === true
26217
+ });
26218
+ if (!json)
26219
+ printDiagnosticsToConsole(
26220
+ `Creating your project: reserving hosting, setting up its addresses, then getting the website ready. This usually takes 1 to 3 minutes.${plan2 && plan2 !== "free" ? " Paying for the plan on the dashboard works too: this command goes on once the payment is in." : ""} Add --verbose to see every step.`
26221
+ );
26032
26222
  const result = await createProjectV2(
26033
26223
  {
26034
26224
  dir,
@@ -26106,7 +26296,7 @@ async function createLinkedProject(argv, dir, options = {}) {
26106
26296
  }
26107
26297
  }
26108
26298
  async function createProject2(argv) {
26109
- const dir = path77.resolve(process.cwd(), argv.dir ?? ".");
26299
+ const dir = path78.resolve(process.cwd(), argv.dir ?? ".");
26110
26300
  if (await isConfigV2(dir)) {
26111
26301
  await createLinkedProject(argv, dir);
26112
26302
  return;
@@ -26217,7 +26407,7 @@ async function confirm(question) {
26217
26407
  }
26218
26408
 
26219
26409
  // src/cli/deploy/stripe-key.ts
26220
- import path78 from "node:path";
26410
+ import path79 from "node:path";
26221
26411
  init_invariant();
26222
26412
  var StripeKeyError = class extends ApitogoError {
26223
26413
  constructor(message, developerHint) {
@@ -26235,7 +26425,7 @@ async function ensureStripeKey(argv, deps) {
26235
26425
  if (!wantsPlans(argv.only)) return { status: "not-needed" };
26236
26426
  let resolved;
26237
26427
  try {
26238
- resolved = loadConfigV2(path78.resolve(process.cwd(), argv.dir)).resolved;
26428
+ resolved = loadConfigV2(path79.resolve(process.cwd(), argv.dir)).resolved;
26239
26429
  } catch {
26240
26430
  return { status: "not-needed" };
26241
26431
  }
@@ -26278,12 +26468,12 @@ async function ensureStripeKey(argv, deps) {
26278
26468
  }
26279
26469
 
26280
26470
  // src/cli/deploy/v2.ts
26281
- import { rm as rm10 } from "node:fs/promises";
26471
+ import { rm as rm11 } from "node:fs/promises";
26282
26472
  init_invariant();
26283
26473
 
26284
26474
  // src/cli/portal/report.ts
26285
26475
  import { access as access9 } from "node:fs/promises";
26286
- import path79 from "node:path";
26476
+ import path80 from "node:path";
26287
26477
 
26288
26478
  // src/cli/deploy/gateway.ts
26289
26479
  import { createHash as createHash4 } from "node:crypto";
@@ -26302,7 +26492,7 @@ function isHostingNotActivated(error) {
26302
26492
  `${error.message} ${error.problem?.detail ?? ""}`
26303
26493
  );
26304
26494
  }
26305
- var sha256 = (text) => createHash4("sha256").update(text).digest("hex");
26495
+ var sha256 = (text2) => createHash4("sha256").update(text2).digest("hex");
26306
26496
  function gatewayNotDeployableReason(gateway) {
26307
26497
  if (gateway.specs.length > 1)
26308
26498
  return "the platform takes one OpenAPI file per gateway, but the config lists several";
@@ -27751,7 +27941,7 @@ async function planPortalService(resolved, ledger) {
27751
27941
  const hash2 = await portalBuildHash(resolved, prepared);
27752
27942
  const previous = stepOf(ledger, "portal", "build");
27753
27943
  const built = await exists5(
27754
- path79.join(resolved.state.buildDir, "index.html")
27944
+ path80.join(resolved.state.buildDir, "index.html")
27755
27945
  );
27756
27946
  if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
27757
27947
  return {
@@ -27772,6 +27962,18 @@ async function planPortalService(resolved, ledger) {
27772
27962
  };
27773
27963
  } catch (error) {
27774
27964
  if (!(error instanceof ConfigError)) throw error;
27965
+ const waitingFor = error.issues.every(
27966
+ (item) => item.code === "PORTAL_MANIFEST_MISSING"
27967
+ ) ? error.issues.map((item) => item.path.replace(/^services\./, "")) : [];
27968
+ if (waitingFor.length > 0) {
27969
+ return {
27970
+ ...base,
27971
+ action: "build",
27972
+ reason: `will build after ${joinNames(waitingFor)} ${waitingFor.length === 1 ? "is" : "are"} deployed (deploy does this in order)`,
27973
+ details: [],
27974
+ issues: []
27975
+ };
27976
+ }
27775
27977
  return {
27776
27978
  ...base,
27777
27979
  action: "blocked",
@@ -27781,15 +27983,30 @@ async function planPortalService(resolved, ledger) {
27781
27983
  };
27782
27984
  }
27783
27985
  }
27986
+ function joinNames(names) {
27987
+ if (names.length <= 1) return names[0] ?? "";
27988
+ return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
27989
+ }
27990
+ function callersNeed(gateway) {
27991
+ const access11 = gateway.policies.access;
27992
+ const token = access11?.jwt ?? gateway.auth !== void 0;
27993
+ const key = access11?.apiKey ?? true;
27994
+ if (token && key) return "a sign-in token and an API key";
27995
+ if (token) return "a sign-in token";
27996
+ if (key) return "an API key";
27997
+ return "nothing (the API is public)";
27998
+ }
27999
+ var count = (value) => value.toLocaleString("en-US");
28000
+ function perPeriod(calls, seconds) {
28001
+ const period = seconds === 60 ? "minute" : seconds === 3600 ? "hour" : seconds === 86400 ? "day" : `${count(seconds)} seconds`;
28002
+ return `${count(calls)} calls per ${period}`;
28003
+ }
27784
28004
  function describePolicies(gateway) {
27785
28005
  const policies = gateway.policies;
27786
28006
  return [
27787
- policies.access ? `access: ${[
27788
- policies.access.jwt === void 0 ? void 0 : `jwt ${policies.access.jwt ? "on" : "off"}`,
27789
- policies.access.apiKey === void 0 ? void 0 : `api key ${policies.access.apiKey ? "on" : "off"}`
27790
- ].filter(Boolean).join(", ")}` : void 0,
27791
- policies.rateLimit ? `rate limit: ${policies.rateLimit.calls} per ${policies.rateLimit.periodSeconds} s` : void 0,
27792
- policies.quota ? `quota: ${policies.quota.calls} per ${policies.quota.periodDays} day(s)` : void 0,
28007
+ `callers need: ${callersNeed(gateway)}`,
28008
+ policies.rateLimit ? `rate limit: ${perPeriod(policies.rateLimit.calls, policies.rateLimit.periodSeconds)}` : void 0,
28009
+ policies.quota ? `quota: ${count(policies.quota.calls)} calls per ${policies.quota.periodDays === 1 ? "day" : `${policies.quota.periodDays} days`}` : void 0,
27793
28010
  policies.cors ? `cors: ${policies.cors.length} origin(s)` : void 0,
27794
28011
  policies.operations?.public ? `public operations: ${policies.operations.public.length}` : void 0
27795
28012
  ].filter((line2) => line2 !== void 0);
@@ -28110,7 +28327,7 @@ async function statusV2(dir, only, options = {}) {
28110
28327
  if (service === "portal") {
28111
28328
  const build2 = stepOf(ledger, "portal", "build");
28112
28329
  const built = await exists5(
28113
- path79.join(resolved.state.buildDir, "index.html")
28330
+ path80.join(resolved.state.buildDir, "index.html")
28114
28331
  );
28115
28332
  const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
28116
28333
  services.push({
@@ -28121,7 +28338,7 @@ async function statusV2(dir, only, options = {}) {
28121
28338
  ...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
28122
28339
  ...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
28123
28340
  ...state === "built" ? [
28124
- `output: ${path79.relative(resolved.configDir, resolved.state.buildDir)}`
28341
+ `output: ${path80.relative(resolved.configDir, resolved.state.buildDir)}`
28125
28342
  ] : [],
28126
28343
  ...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"],
28127
28344
  ...await hostingLine(resolved, manifest, options.hostingRemote)
@@ -28167,10 +28384,10 @@ async function statusV2(dir, only, options = {}) {
28167
28384
  services
28168
28385
  };
28169
28386
  }
28170
- function formatPlan(output4) {
28387
+ function formatPlan(output4, options = {}) {
28171
28388
  return output4.services.flatMap((svc) => [
28172
28389
  `${svc.service}: ${svc.reason}`,
28173
- ...svc.details.map((line2) => ` ${line2}`),
28390
+ ...svc.details.filter((line2) => options.verbose === true || !line2.startsWith("step ")).map((line2) => ` ${line2}`),
28174
28391
  ...svc.issues.map(
28175
28392
  (issue4) => ` problem: ${issue4.message}${issue4.hint ? ` ${issue4.hint}` : ""} [${issue4.code}]`
28176
28393
  )
@@ -28188,7 +28405,7 @@ function formatStatus(output4) {
28188
28405
 
28189
28406
  // src/cli/deploy/static-web-config.ts
28190
28407
  import { access as access10, writeFile as writeFile22 } from "node:fs/promises";
28191
- import path80 from "node:path";
28408
+ import path81 from "node:path";
28192
28409
  var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
28193
28410
  var STATIC_WEB_CONFIG_VERSION = 3;
28194
28411
  var CLIENT_ONLY_ROUTES = [
@@ -28206,7 +28423,7 @@ var DEFAULT_STATIC_WEB_CONFIG = {
28206
28423
  }
28207
28424
  };
28208
28425
  async function ensureStaticWebConfig(buildDir) {
28209
- const file = path80.join(buildDir, STATIC_WEB_CONFIG_FILE);
28426
+ const file = path81.join(buildDir, STATIC_WEB_CONFIG_FILE);
28210
28427
  try {
28211
28428
  await access10(file);
28212
28429
  return false;
@@ -28276,7 +28493,7 @@ function publishStep(deps, options) {
28276
28493
  );
28277
28494
  throw error;
28278
28495
  } finally {
28279
- await rm10(archive.archivePath, { force: true });
28496
+ await rm11(archive.archivePath, { force: true });
28280
28497
  }
28281
28498
  }
28282
28499
  };
@@ -28455,11 +28672,14 @@ async function deployPlansService(options, deps, projectId, named) {
28455
28672
  { ...deps.plans, client },
28456
28673
  { force: options.force }
28457
28674
  );
28675
+ const planCount = resolved.plans?.file ? (await readPlansFile(resolved.plans.file).catch(() => [])).length : 0;
28676
+ const billing = resolved.plans?.billing.provider;
28458
28677
  return {
28459
28678
  service: "plans",
28460
28679
  status: result.status,
28461
28680
  steps: result.steps,
28462
28681
  url: result.apiBaseUrl,
28682
+ summary: `${planCount} plan${planCount === 1 ? "" : "s"}${billing && billing !== "none" ? `, billing: ${billing}` : ""}`,
28463
28683
  ...result.notes ? { notes: result.notes } : {}
28464
28684
  };
28465
28685
  }
@@ -28541,7 +28761,7 @@ async function deployPortal(options, deps, projectId) {
28541
28761
  }
28542
28762
  function formatDeploy(output4) {
28543
28763
  return output4.services.flatMap((result) => {
28544
- const url2 = result.url ? ` (${result.url})` : "";
28764
+ const url2 = result.summary ? `, ${result.summary}` : result.url ? ` (${result.url})` : "";
28545
28765
  const ops = result.operationCount !== void 0 ? `, ${result.operationCount} operation(s)` : "";
28546
28766
  const head = result.status === "deployed" ? `${result.service}: deployed${result.environment ? ` to ${result.environment}` : ""}${url2}${ops}` : result.status === "unchanged" ? `${result.service}: unchanged, nothing to publish${url2}` : `${result.service}: skipped, ${result.reason}`;
28547
28767
  return [
@@ -28557,7 +28777,7 @@ function formatDeploy(output4) {
28557
28777
  // src/cli/deploy/command.ts
28558
28778
  async function ensureLinkedProject(argv, deps) {
28559
28779
  if (argv.projectId?.trim() || deps.envProjectId?.trim()) return true;
28560
- const dir = path81.resolve(process.cwd(), argv.dir);
28780
+ const dir = path82.resolve(process.cwd(), argv.dir);
28561
28781
  let linked;
28562
28782
  try {
28563
28783
  linked = Boolean(loadConfigV2(dir).resolved.project.id);
@@ -28609,7 +28829,7 @@ async function deployProject(argv) {
28609
28829
  const config2 = loadConfig();
28610
28830
  const output4 = await deployV2(
28611
28831
  {
28612
- dir: path81.resolve(process.cwd(), argv.dir),
28832
+ dir: path82.resolve(process.cwd(), argv.dir),
28613
28833
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
28614
28834
  projectId: argv.projectId,
28615
28835
  env: argv.env,
@@ -28653,7 +28873,7 @@ async function reportFailure(error, json) {
28653
28873
 
28654
28874
  // src/cli/cmds/deploy.ts
28655
28875
  var deploy_default = {
28656
- desc: "Deploy to the API ToGo platform: config version 2 publishes the portal; version 1 deploys apihub, portal backend and portal frontend",
28876
+ desc: "Deploy the project to API ToGo: the services in apitogo.config.json (identity, gateway, plans, portal), each only when it changed",
28657
28877
  command: "deploy",
28658
28878
  builder: (yargs2) => yargs2.option("dir", {
28659
28879
  type: "string",
@@ -28665,17 +28885,19 @@ var deploy_default = {
28665
28885
  describe: "Override project GUID"
28666
28886
  }).option("project-name", {
28667
28887
  type: "string",
28668
- describe: "Version 1: display name when creating a project. Version 2 ignores it: the project is made by `apitogo project create` and named by project.name"
28888
+ describe: "Older projects only. The project's name is project.name in apitogo.config.json",
28889
+ hidden: true
28669
28890
  }).option("platform-plan", {
28670
28891
  type: "string",
28671
- describe: "Version 1: platform hosting tier. Version 2 ignores it: a project made by `apitogo project create` is on the Free plan",
28892
+ describe: "Older projects only. Choose the project's plan with `apitogo project create --plan`, or change it on the dashboard",
28893
+ hidden: true,
28672
28894
  choices: ["free", "starter", "professional"]
28673
28895
  }).option("only", {
28674
28896
  type: "string",
28675
- describe: "Limit to resources. Version 1: apihub, portal-backend, portal-frontend. Version 2: portal, gateway, identity, plans (comma-separated)"
28897
+ describe: "Deploy only these services, comma separated: portal, gateway, identity, plans"
28676
28898
  }).option("env", {
28677
28899
  type: "string",
28678
- describe: "Config version 2: Static Web App environment (default: production)"
28900
+ describe: "Portal environment to publish to (default: production)"
28679
28901
  }).option("force", {
28680
28902
  type: "boolean",
28681
28903
  describe: "Force redeploy of selected resources even when healthy",
@@ -28683,7 +28905,7 @@ var deploy_default = {
28683
28905
  }).option("yes", {
28684
28906
  alias: "y",
28685
28907
  type: "boolean",
28686
- describe: "Config version 2: confirm a change that cannot be undone, such as switching the identity provider",
28908
+ describe: "Confirm a change that cannot be undone, such as switching the identity provider",
28687
28909
  default: false
28688
28910
  }).option("resume-from", {
28689
28911
  type: "string",
@@ -28694,7 +28916,7 @@ var deploy_default = {
28694
28916
  default: true
28695
28917
  }).option("verbose", {
28696
28918
  type: "boolean",
28697
- describe: "Config version 2: show the full build log (by default only the result of each service is printed)",
28919
+ describe: "Show the full build log (by default only the result of each service is printed)",
28698
28920
  default: false
28699
28921
  }).option("json", {
28700
28922
  type: "boolean",
@@ -28703,7 +28925,7 @@ var deploy_default = {
28703
28925
  }),
28704
28926
  handler: async (argv) => {
28705
28927
  await captureEvent({ argv, event: "apitogo deploy" });
28706
- if (await isConfigV2(path82.resolve(process.cwd(), argv.dir))) {
28928
+ if (await isConfigV2(path83.resolve(process.cwd(), argv.dir))) {
28707
28929
  await deployProject(argv);
28708
28930
  return;
28709
28931
  }
@@ -28712,8 +28934,8 @@ var deploy_default = {
28712
28934
  };
28713
28935
 
28714
28936
  // src/cli/export/handler.ts
28715
- import { mkdir as mkdir18, writeFile as writeFile23 } from "node:fs/promises";
28716
- import path83 from "node:path";
28937
+ import { mkdir as mkdir19, writeFile as writeFile23 } from "node:fs/promises";
28938
+ import path84 from "node:path";
28717
28939
  init_invariant();
28718
28940
  var ExportError = class extends ApitogoError {
28719
28941
  constructor(message, developerHint) {
@@ -28727,7 +28949,7 @@ async function exportOpenApi(args, deps) {
28727
28949
  `Cannot export "${args.target}".`,
28728
28950
  "Only `apitogo export openapi` exists."
28729
28951
  );
28730
- const dir = path83.resolve(process.cwd(), args.dir ?? ".");
28952
+ const dir = path84.resolve(process.cwd(), args.dir ?? ".");
28731
28953
  if (!await isConfigV2(dir))
28732
28954
  throw new ExportError(
28733
28955
  "This command needs a config version 2 project.",
@@ -28740,9 +28962,9 @@ async function exportOpenApi(args, deps) {
28740
28962
  "This folder is not linked to a project.",
28741
28963
  "Run `apitogo project create`, or pass --project-id."
28742
28964
  );
28743
- let text;
28965
+ let text2;
28744
28966
  try {
28745
- text = await deps.client.getGatewayOpenApi(projectId);
28967
+ text2 = await deps.client.getGatewayOpenApi(projectId);
28746
28968
  } catch (error) {
28747
28969
  if (error instanceof PlatformApiError && error.status === 404)
28748
28970
  throw new ExportError(
@@ -28751,11 +28973,11 @@ async function exportOpenApi(args, deps) {
28751
28973
  );
28752
28974
  throw error;
28753
28975
  }
28754
- if (!args.out) return { text };
28755
- const file = path83.resolve(process.cwd(), args.out);
28756
- await mkdir18(path83.dirname(file), { recursive: true });
28757
- await writeFile23(file, text);
28758
- return { text, file };
28976
+ if (!args.out) return { text: text2 };
28977
+ const file = path84.resolve(process.cwd(), args.out);
28978
+ await mkdir19(path84.dirname(file), { recursive: true });
28979
+ await writeFile23(file, text2);
28980
+ return { text: text2, file };
28759
28981
  }
28760
28982
  async function exportCommand(args) {
28761
28983
  try {
@@ -28777,7 +28999,7 @@ async function exportCommand(args) {
28777
28999
 
28778
29000
  // src/cli/cmds/export.ts
28779
29001
  var export_default = {
28780
- desc: "Print or save the OpenAPI file the platform publishes for your gateway (config version 2)",
29002
+ desc: "Print or save the OpenAPI file the platform publishes for your gateway",
28781
29003
  command: "export <target>",
28782
29004
  builder: (yargs2) => yargs2.positional("target", {
28783
29005
  type: "string",
@@ -28846,8 +29068,8 @@ import open3 from "open";
28846
29068
  import colors14 from "picocolors";
28847
29069
 
28848
29070
  // src/cli/platform/device-auth.ts
28849
- function url(apiUrl, path97) {
28850
- return `${apiUrl}${path97.startsWith("/") ? path97 : `/${path97}`}`;
29071
+ function url(apiUrl, path98) {
29072
+ return `${apiUrl}${path98.startsWith("/") ? path98 : `/${path98}`}`;
28851
29073
  }
28852
29074
  async function requestDeviceCode(apiUrl) {
28853
29075
  const response = await fetchWithRetry(
@@ -29029,7 +29251,7 @@ var logout_default = {
29029
29251
  };
29030
29252
 
29031
29253
  // src/cli/portal/commands.ts
29032
- import path84 from "node:path";
29254
+ import path85 from "node:path";
29033
29255
  init_invariant();
29034
29256
  function parseOnly(value) {
29035
29257
  if (value === void 0) return void 0;
@@ -29046,7 +29268,7 @@ function parseOnly(value) {
29046
29268
  async function run2(command2, argv, compute, format) {
29047
29269
  const json = argv.json === true;
29048
29270
  try {
29049
- const dir = path84.resolve(process.cwd(), argv.dir ?? ".");
29271
+ const dir = path85.resolve(process.cwd(), argv.dir ?? ".");
29050
29272
  if (!await isConfigV2(dir)) {
29051
29273
  throw new ServiceEditError(
29052
29274
  `${command2} works with a config version 2 project, and there is none in ${dir}.`,
@@ -29082,7 +29304,7 @@ var plan = (argv) => run2(
29082
29304
  gatewayDryRun: gatewayDryRun(),
29083
29305
  plansRemote: plansRemote()
29084
29306
  }),
29085
- formatPlan
29307
+ (output4) => formatPlan(output4, { verbose: argv.verbose === true })
29086
29308
  );
29087
29309
  function gatewayRemote() {
29088
29310
  try {
@@ -29152,7 +29374,7 @@ var statusOfProject = (argv) => run2(
29152
29374
 
29153
29375
  // src/cli/cmds/plan.ts
29154
29376
  var plan_default = {
29155
- desc: "Show what deploy would do for each service of a config version 2 project (changes nothing)",
29377
+ desc: "Show what deploy would do for each service (changes nothing)",
29156
29378
  command: "plan",
29157
29379
  builder: (yargs2) => yargs2.option("dir", {
29158
29380
  type: "string",
@@ -29163,6 +29385,10 @@ var plan_default = {
29163
29385
  }).option("only", {
29164
29386
  type: "string",
29165
29387
  describe: "Only these services, comma separated (portal, gateway, identity, plans)"
29388
+ }).option("verbose", {
29389
+ type: "boolean",
29390
+ default: false,
29391
+ describe: "Also show each service's steps and their state"
29166
29392
  }).option("json", {
29167
29393
  type: "boolean",
29168
29394
  default: false,
@@ -29176,11 +29402,11 @@ var plan_default = {
29176
29402
 
29177
29403
  // src/cli/cmds/project-create.ts
29178
29404
  var project_create_default = {
29179
- desc: "Create an API ToGo platform project. In a config version 2 folder: free plan, hosting for the enabled services, and project.id written into the config",
29405
+ desc: "Create the API ToGo project for this folder: hosting for its services on the plan you choose, and project.id written into apitogo.config.json",
29180
29406
  command: "project create",
29181
29407
  builder: (yargs2) => yargs2.option("name", {
29182
29408
  type: "string",
29183
- describe: "Project display name (required for version 1; version 2 defaults to project.name)"
29409
+ describe: "Project display name (default: project.name in apitogo.config.json)"
29184
29410
  }).option("dir", {
29185
29411
  type: "string",
29186
29412
  describe: "Project directory",
@@ -29192,12 +29418,13 @@ var project_create_default = {
29192
29418
  describe: "Optional project description"
29193
29419
  }).option("modules", {
29194
29420
  type: "string",
29195
- describe: "Version 1 only: comma-separated modules to activate (default: apihub). Version 2 takes them from the config (the portal needs dev-portal) and ignores this option",
29421
+ describe: "Older projects only: comma-separated modules to activate. The services in apitogo.config.json decide them",
29422
+ hidden: true,
29196
29423
  default: "apihub"
29197
29424
  }).option("plan", {
29198
29425
  type: "string",
29199
29426
  choices: ["free", "starter", "professional"],
29200
- describe: "Version 2: the platform plan of the new project (default: free). Starter and Professional are paid through the platform's own checkout, and a project needs one of them to sell paid plans"
29427
+ describe: "The plan of the new project: free (default), starter or professional. Starter and Professional are paid through the platform's checkout, and a project needs one of them to sell paid plans"
29201
29428
  }).option("open", {
29202
29429
  type: "boolean",
29203
29430
  describe: "Open the checkout page in the browser for a paid plan (use --no-open to only print the link)",
@@ -29209,8 +29436,12 @@ var project_create_default = {
29209
29436
  hidden: true
29210
29437
  }).option("wait", {
29211
29438
  type: "boolean",
29212
- describe: "Wait until the hosting from the warm pool is ready before returning, once a platform plan is selected (always so in version 2). Use --no-wait to return sooner",
29439
+ describe: "Wait until the hosting is ready before returning. Use --no-wait to return sooner",
29213
29440
  default: true
29441
+ }).option("verbose", {
29442
+ type: "boolean",
29443
+ default: false,
29444
+ describe: "Show every platform step, including hosting details"
29214
29445
  }).option("json", {
29215
29446
  type: "boolean",
29216
29447
  describe: "Print raw JSON only",
@@ -29224,7 +29455,7 @@ var project_create_default = {
29224
29455
 
29225
29456
  // src/cli/cmds/remove.ts
29226
29457
  var remove_default = {
29227
- desc: "Remove a service section from a config version 2 project (its files are kept unless --delete-files)",
29458
+ desc: "Remove a service from the project (its files are kept unless --delete-files)",
29228
29459
  command: "remove <service>",
29229
29460
  builder: (yargs2) => yargs2.positional("service", {
29230
29461
  type: "string",
@@ -29262,14 +29493,14 @@ var remove_default = {
29262
29493
 
29263
29494
  // src/cli/dev/handler.ts
29264
29495
  init_joinUrl();
29265
- import path87 from "node:path";
29496
+ import path88 from "node:path";
29266
29497
 
29267
29498
  // src/vite/dev-server.ts
29268
29499
  init_logger();
29269
29500
  import fs11 from "node:fs/promises";
29270
29501
  import http from "node:http";
29271
29502
  import https from "node:https";
29272
- import path86 from "node:path";
29503
+ import path87 from "node:path";
29273
29504
  import { createHttpTerminator } from "http-terminator";
29274
29505
  import {
29275
29506
  createServer as createViteServer,
@@ -29302,7 +29533,7 @@ init_loader();
29302
29533
  // src/vite/pagefind-dev-index.ts
29303
29534
  init_invariant();
29304
29535
  init_joinUrl();
29305
- import path85 from "node:path";
29536
+ import path86 from "node:path";
29306
29537
  import { createIndex as createIndex2 } from "pagefind";
29307
29538
  import { isRunnableDevEnvironment } from "vite";
29308
29539
  async function* buildPagefindDevIndex(vite, config2) {
@@ -29355,7 +29586,7 @@ async function* buildPagefindDevIndex(vite, config2) {
29355
29586
  path: urlPath
29356
29587
  };
29357
29588
  }
29358
- const outputPath = path85.join(vite.config.publicDir, "pagefind");
29589
+ const outputPath = path86.join(vite.config.publicDir, "pagefind");
29359
29590
  await pagefindIndex.writeFiles({ outputPath });
29360
29591
  yield { type: "complete", success: true, indexed };
29361
29592
  }
@@ -29374,9 +29605,9 @@ var DevServer = class {
29374
29605
  this.protocol = "https";
29375
29606
  const { dir } = this.options;
29376
29607
  const [key, cert, ca] = await Promise.all([
29377
- fs11.readFile(path86.resolve(dir, config2.https.key)),
29378
- fs11.readFile(path86.resolve(dir, config2.https.cert)),
29379
- config2.https.ca ? fs11.readFile(path86.resolve(dir, config2.https.ca)) : void 0
29608
+ fs11.readFile(path87.resolve(dir, config2.https.key)),
29609
+ fs11.readFile(path87.resolve(dir, config2.https.cert)),
29610
+ config2.https.ca ? fs11.readFile(path87.resolve(dir, config2.https.ca)) : void 0
29380
29611
  ]);
29381
29612
  return https.createServer({ key, cert, ca });
29382
29613
  }
@@ -29393,7 +29624,7 @@ var DevServer = class {
29393
29624
  );
29394
29625
  const server = await this.createNodeServer(config2);
29395
29626
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
29396
- const publicRoot = path86.resolve(
29627
+ const publicRoot = path87.resolve(
29397
29628
  this.options.dir,
29398
29629
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
29399
29630
  );
@@ -29410,7 +29641,7 @@ var DevServer = class {
29410
29641
  // built-in transform middleware which would treat the path as a static asset.
29411
29642
  name: "apitogo:entry-client",
29412
29643
  configureServer(server2) {
29413
- const entryPath = path86.posix.join(
29644
+ const entryPath = path87.posix.join(
29414
29645
  server2.config.base,
29415
29646
  "/__z/entry.client.tsx"
29416
29647
  );
@@ -29575,7 +29806,7 @@ init_package_json();
29575
29806
  async function dev(argv) {
29576
29807
  const packageJson3 = getZudokuPackageJson();
29577
29808
  process.env.NODE_ENV = "development";
29578
- let dir = path87.resolve(process.cwd(), argv.dir);
29809
+ let dir = path88.resolve(process.cwd(), argv.dir);
29579
29810
  if (await isConfigV2(dir)) {
29580
29811
  const { root, prepared } = await preparePortalWorkspace(dir, "development");
29581
29812
  dir = root;
@@ -29674,7 +29905,7 @@ var run_default = {
29674
29905
  };
29675
29906
 
29676
29907
  // src/cli/secrets/handler.ts
29677
- import path88 from "node:path";
29908
+ import path89 from "node:path";
29678
29909
  init_invariant();
29679
29910
  var SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
29680
29911
  var SecretsError = class extends ApitogoError {
@@ -29686,7 +29917,7 @@ var SecretsError = class extends ApitogoError {
29686
29917
  async function resolveProjectId2(argv, deps) {
29687
29918
  const explicit = argv.projectId?.trim();
29688
29919
  if (explicit) return explicit;
29689
- const dir = path88.resolve(process.cwd(), argv.dir ?? ".");
29920
+ const dir = path89.resolve(process.cwd(), argv.dir ?? ".");
29690
29921
  if (await isConfigV2(dir)) {
29691
29922
  const id = loadConfigV2(dir).resolved.project.id;
29692
29923
  if (id) return id;
@@ -29846,12 +30077,12 @@ var secrets_default = {
29846
30077
  };
29847
30078
 
29848
30079
  // src/cli/cmds/status.ts
29849
- import path90 from "node:path";
30080
+ import path91 from "node:path";
29850
30081
 
29851
30082
  // src/cli/status/handler.ts
29852
- import path89 from "node:path";
30083
+ import path90 from "node:path";
29853
30084
  async function status(argv) {
29854
- const sourceDirectory = path89.resolve(process.cwd(), argv.dir);
30085
+ const sourceDirectory = path90.resolve(process.cwd(), argv.dir);
29855
30086
  let config2;
29856
30087
  try {
29857
30088
  config2 = loadConfig();
@@ -29920,12 +30151,40 @@ async function status(argv) {
29920
30151
  liveError,
29921
30152
  sourceDirectory
29922
30153
  };
29923
- console.log(JSON.stringify(payload, null, argv.json ? void 0 : 2));
30154
+ if (argv.json) {
30155
+ console.log(JSON.stringify(payload));
30156
+ return;
30157
+ }
30158
+ for (const line2 of formatAccountStatus(payload)) console.log(line2);
30159
+ }
30160
+ var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
30161
+ function formatAccountStatus(payload) {
30162
+ const tenant = payload.account.tenant ?? {};
30163
+ const who = text(tenant.email) ?? text(tenant.displayName) ?? "unknown";
30164
+ const lines = [`Signed in as ${who}.`];
30165
+ if (!payload.projectId) {
30166
+ lines.push(
30167
+ "No project here. Run `apitogo create` to start one, or run this in a project folder."
30168
+ );
30169
+ return lines;
30170
+ }
30171
+ const project = payload.project ?? {};
30172
+ const name = text(project.name) ?? payload.projectId;
30173
+ const state = text(project.status);
30174
+ lines.push(`Project: ${name}${state ? ` (${state})` : ""}`);
30175
+ if (payload.liveError) lines.push(`Live check: ${payload.liveError}`);
30176
+ else if (payload.live) {
30177
+ const ok = payload.live.ok ?? payload.live.healthy ?? payload.live.status;
30178
+ lines.push(
30179
+ `Live check: ${typeof ok === "boolean" ? ok ? "online" : "not reachable" : String(ok ?? "done")}`
30180
+ );
30181
+ }
30182
+ return lines;
29924
30183
  }
29925
30184
 
29926
30185
  // src/cli/cmds/status.ts
29927
30186
  var status_default = {
29928
- desc: "Show the state of each service (config version 2), or account, project and live readiness (version 1)",
30187
+ desc: "Show the state of each service of the project, or of your account outside a project",
29929
30188
  command: "status",
29930
30189
  builder: (yargs2) => yargs2.option("dir", {
29931
30190
  type: "string",
@@ -29941,15 +30200,15 @@ var status_default = {
29941
30200
  default: false
29942
30201
  }).option("only", {
29943
30202
  type: "string",
29944
- describe: "Config version 2: only these services, comma separated (portal, gateway, identity, plans)"
30203
+ describe: "Only these services, comma separated (portal, gateway, identity, plans)"
29945
30204
  }).option("json", {
29946
30205
  type: "boolean",
29947
- describe: "Print raw JSON only",
30206
+ describe: "Print JSON instead of text",
29948
30207
  default: false
29949
30208
  }),
29950
30209
  handler: async (argv) => {
29951
30210
  await captureEvent({ argv, event: "apitogo status" });
29952
- if (await isConfigV2(path90.resolve(process.cwd(), argv.dir))) {
30211
+ if (await isConfigV2(path91.resolve(process.cwd(), argv.dir))) {
29953
30212
  await statusOfProject(argv);
29954
30213
  return;
29955
30214
  }
@@ -29958,11 +30217,11 @@ var status_default = {
29958
30217
  };
29959
30218
 
29960
30219
  // src/cli/cmds/teardown.ts
29961
- import path93 from "node:path";
30220
+ import path94 from "node:path";
29962
30221
 
29963
30222
  // src/cli/teardown/command.ts
29964
30223
  init_invariant();
29965
- import path91 from "node:path";
30224
+ import path92 from "node:path";
29966
30225
  import { createInterface as createInterface7 } from "node:readline/promises";
29967
30226
 
29968
30227
  // src/cli/teardown/v2.ts
@@ -30323,7 +30582,7 @@ async function teardownProject(argv) {
30323
30582
  const progress = createConsoleProgressReporter();
30324
30583
  const result = await teardownV2(
30325
30584
  {
30326
- dir: path91.resolve(process.cwd(), argv.dir),
30585
+ dir: path92.resolve(process.cwd(), argv.dir),
30327
30586
  projectId: argv.projectId,
30328
30587
  only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
30329
30588
  yes: argv.yes === true,
@@ -30382,7 +30641,7 @@ async function teardownProject(argv) {
30382
30641
  }
30383
30642
 
30384
30643
  // src/cli/teardown/handler.ts
30385
- import path92 from "node:path";
30644
+ import path93 from "node:path";
30386
30645
  function resultLooksFailed3(payload) {
30387
30646
  try {
30388
30647
  const parsed = JSON.parse(payload);
@@ -30398,7 +30657,7 @@ async function teardown(argv) {
30398
30657
  );
30399
30658
  return;
30400
30659
  }
30401
- const sourceDirectory = path92.resolve(process.cwd(), argv.dir);
30660
+ const sourceDirectory = path93.resolve(process.cwd(), argv.dir);
30402
30661
  let config2;
30403
30662
  try {
30404
30663
  config2 = loadConfig();
@@ -30447,7 +30706,7 @@ async function teardown(argv) {
30447
30706
 
30448
30707
  // src/cli/cmds/teardown.ts
30449
30708
  var teardown_default = {
30450
- desc: "Delete the linked API ToGo project. Config version 2 asks you to type the project name; local files are kept",
30709
+ desc: "Delete the linked API ToGo project. You type the project name to confirm; local files are kept",
30451
30710
  command: "teardown",
30452
30711
  builder: (yargs2) => yargs2.option("dir", {
30453
30712
  type: "string",
@@ -30459,11 +30718,11 @@ var teardown_default = {
30459
30718
  describe: "Override project GUID"
30460
30719
  }).option("only", {
30461
30720
  type: "string",
30462
- describe: "Config version 2: `gateway`, `identity` or `plans` removes just that service and keeps the project (`identity` deletes every user in it); other hosting is released only by deleting the whole project"
30721
+ describe: "`gateway`, `identity` or `plans` removes just that service and keeps the project (`identity` deletes every user in it); other hosting is released only by deleting the whole project"
30463
30722
  }).option("yes", {
30464
30723
  alias: "y",
30465
30724
  type: "boolean",
30466
- describe: "Confirm without asking (version 1: required)",
30725
+ describe: "Confirm without asking",
30467
30726
  default: false
30468
30727
  }).option("wait", {
30469
30728
  type: "boolean",
@@ -30476,7 +30735,7 @@ var teardown_default = {
30476
30735
  }),
30477
30736
  handler: async (argv) => {
30478
30737
  await captureEvent({ argv, event: "apitogo teardown" });
30479
- if (await isConfigV2(path93.resolve(process.cwd(), argv.dir))) {
30738
+ if (await isConfigV2(path94.resolve(process.cwd(), argv.dir))) {
30480
30739
  await teardownProject(argv);
30481
30740
  return;
30482
30741
  }
@@ -30486,7 +30745,7 @@ var teardown_default = {
30486
30745
 
30487
30746
  // src/cli/teardown/reset-command.ts
30488
30747
  init_invariant();
30489
- import path94 from "node:path";
30748
+ import path95 from "node:path";
30490
30749
 
30491
30750
  // src/cli/teardown/reset.ts
30492
30751
  init_invariant();
@@ -30609,7 +30868,7 @@ async function resetProject(argv) {
30609
30868
  const config2 = loadConfig();
30610
30869
  const result = await resetProjectV2(
30611
30870
  {
30612
- dir: path94.resolve(process.cwd(), argv.dir),
30871
+ dir: path95.resolve(process.cwd(), argv.dir),
30613
30872
  projectId: argv.projectId,
30614
30873
  yes: argv.yes === true
30615
30874
  },
@@ -30673,7 +30932,7 @@ var test_reset_default = {
30673
30932
  };
30674
30933
 
30675
30934
  // src/cli/test-sign-in/handler.ts
30676
- import path95 from "node:path";
30935
+ import path96 from "node:path";
30677
30936
  init_invariant();
30678
30937
  var TestSignInError = class extends ApitogoError {
30679
30938
  constructor(message, developerHint) {
@@ -30684,7 +30943,7 @@ var TestSignInError = class extends ApitogoError {
30684
30943
  async function resolveProjectId3(argv) {
30685
30944
  const explicit = argv.projectId?.trim();
30686
30945
  if (explicit) return explicit;
30687
- const dir = path95.resolve(process.cwd(), argv.dir ?? ".");
30946
+ const dir = path96.resolve(process.cwd(), argv.dir ?? ".");
30688
30947
  if (await isConfigV2(dir)) {
30689
30948
  const id = loadConfigV2(dir).resolved.project.id;
30690
30949
  if (id) return id;
@@ -30822,38 +31081,38 @@ function createApitogoCli(argv) {
30822
31081
  }
30823
31082
 
30824
31083
  // src/cli/local-handoff.ts
30825
- import { spawn as spawn4 } from "node:child_process";
31084
+ import { spawn as spawn2 } from "node:child_process";
30826
31085
  import { existsSync as existsSync8, realpathSync as realpathSync2 } from "node:fs";
30827
- import path96 from "node:path";
31086
+ import path97 from "node:path";
30828
31087
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
30829
31088
  var PACKAGE_NAME = "@lukoweb/apitogo";
30830
31089
  var resolveProjectDir = (argv, cwd) => {
30831
31090
  for (let i = 0; i < argv.length; i++) {
30832
31091
  const arg = argv[i];
30833
31092
  const next = argv[i + 1];
30834
- if (arg === "--dir" && next) return path96.resolve(cwd, next);
31093
+ if (arg === "--dir" && next) return path97.resolve(cwd, next);
30835
31094
  if (arg?.startsWith("--dir=")) {
30836
- return path96.resolve(cwd, arg.slice("--dir=".length));
31095
+ return path97.resolve(cwd, arg.slice("--dir=".length));
30837
31096
  }
30838
31097
  }
30839
31098
  return cwd;
30840
31099
  };
30841
31100
  var findLocalCli = (projectDir, runningRoot) => {
30842
- let dir = path96.resolve(projectDir);
31101
+ let dir = path97.resolve(projectDir);
30843
31102
  let localPackageRoot;
30844
31103
  while (!localPackageRoot) {
30845
- const candidate = path96.join(dir, "node_modules", PACKAGE_NAME);
30846
- if (existsSync8(path96.join(candidate, "package.json"))) {
31104
+ const candidate = path97.join(dir, "node_modules", PACKAGE_NAME);
31105
+ if (existsSync8(path97.join(candidate, "package.json"))) {
30847
31106
  localPackageRoot = candidate;
30848
- } else if (dir === path96.dirname(dir)) {
31107
+ } else if (dir === path97.dirname(dir)) {
30849
31108
  return void 0;
30850
31109
  } else {
30851
- dir = path96.dirname(dir);
31110
+ dir = path97.dirname(dir);
30852
31111
  }
30853
31112
  }
30854
31113
  const localRoot = realpathSync2(localPackageRoot);
30855
31114
  if (localRoot === realpathSync2(runningRoot)) return void 0;
30856
- return path96.join(localRoot, "cli.js");
31115
+ return path97.join(localRoot, "cli.js");
30857
31116
  };
30858
31117
  var handoffToLocalCli = async (argv, runningRoot) => {
30859
31118
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -30865,9 +31124,9 @@ var handoffToLocalCli = async (argv, runningRoot) => {
30865
31124
  );
30866
31125
  if (!localCli) return void 0;
30867
31126
  printDiagnosticsToConsole(
30868
- `Using the project's local ${PACKAGE_NAME} (${path96.dirname(localCli)})`
31127
+ `Using the project's local ${PACKAGE_NAME} (${path97.dirname(localCli)})`
30869
31128
  );
30870
- const child = spawn4(process.execPath, [localCli, ...argv], {
31129
+ const child = spawn2(process.execPath, [localCli, ...argv], {
30871
31130
  stdio: "inherit",
30872
31131
  env: { ...process.env, [LOCAL_HANDOFF_ENV]: "1" }
30873
31132
  });