@lukoweb/apitogo 0.1.144 → 0.1.147

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
@@ -836,7 +836,7 @@ import { stat } from "node:fs/promises";
836
836
  var fileExists;
837
837
  var init_file_exists = __esm({
838
838
  "src/config/file-exists.ts"() {
839
- fileExists = (path73) => stat(path73).then(() => true).catch(() => false);
839
+ fileExists = (path74) => stat(path74).then(() => true).catch(() => false);
840
840
  }
841
841
  });
842
842
 
@@ -4450,7 +4450,7 @@ __export(ZudokuConfig_exports, {
4450
4450
  ZudokuConfig: () => ZudokuConfig,
4451
4451
  validateConfig: () => validateConfig
4452
4452
  });
4453
- import colors4 from "picocolors";
4453
+ import colors5 from "picocolors";
4454
4454
  import { isValidElement as isValidElement2 } from "react";
4455
4455
  import { z as z9 } from "zod";
4456
4456
  function validateConfig(config2, configPath) {
@@ -4462,8 +4462,8 @@ function validateConfig(config2, configPath) {
4462
4462
  ${z9.prettifyError(validationResult.error)}`
4463
4463
  );
4464
4464
  }
4465
- console.log(colors4.yellow("Validation errors:"));
4466
- console.log(colors4.yellow(z9.prettifyError(validationResult.error)));
4465
+ console.log(colors5.yellow("Validation errors:"));
4466
+ console.log(colors5.yellow(z9.prettifyError(validationResult.error)));
4467
4467
  }
4468
4468
  }
4469
4469
  var ThemeSchema, ApiCatalogCategorySchema, LanguageOption, AiAssistantCustomSchema, AiAssistantPresets, AiAssistantsSchema, ApiOptionsSchema, ApiConfigSchema, VersionConfigSchema, ApiSchema, ApiKeysSchema, LogoSchema, FooterSocialIcons, FooterSocialSchema, FooterSchema, SiteMapSchema, DEFAULT_DOCS_FILES, LlmsConfigSchema, DocsConfigSchema, Redirect, SearchSchema, AuthenticationSchema, MetadataSchema, FontConfigSchema, FontsConfigSchema, CssObject, ThemeConfigSchema, SiteSchema, PlacementPosition, HeaderConfigSchema, ApiCatalogSchema, CdnUrlSchema, BaseConfigSchema, ZudokuConfig;
@@ -5002,7 +5002,7 @@ var init_ZudokuConfig = __esm({
5002
5002
  // src/config/loader.ts
5003
5003
  import { stat as stat2 } from "node:fs/promises";
5004
5004
  import path6 from "node:path";
5005
- import colors5 from "picocolors";
5005
+ import colors6 from "picocolors";
5006
5006
  import {
5007
5007
  runnerImport,
5008
5008
  loadEnv as viteLoadEnv
@@ -5188,7 +5188,7 @@ async function loadZudokuConfig(configEnv, rootDir) {
5188
5188
  };
5189
5189
  if (!process.env.APITOGO_JSON_ONLY) {
5190
5190
  logger.info(
5191
- colors5.cyan(`loaded config file `) + colors5.dim(config.__meta.configPath),
5191
+ colors6.cyan(`loaded config file `) + colors6.dim(config.__meta.configPath),
5192
5192
  { timestamp: true }
5193
5193
  );
5194
5194
  }
@@ -5258,7 +5258,7 @@ __export(llms_exports, {
5258
5258
  });
5259
5259
  import { writeFile as writeFile5 } from "node:fs/promises";
5260
5260
  import path29 from "node:path";
5261
- import colors9 from "picocolors";
5261
+ import colors10 from "picocolors";
5262
5262
  async function generateLlmsTxtFiles({
5263
5263
  markdownFileInfos,
5264
5264
  outputUrls,
@@ -5294,7 +5294,7 @@ async function generateLlmsTxtFiles({
5294
5294
  const llmsTxt2 = llmsTxtParts.join("\n");
5295
5295
  await writeFile5(path29.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
5296
5296
  if (process.env.APITOGO_JSON_ONLY !== "1") {
5297
- console.log(colors9.blue("\u2713 generated llms.txt"));
5297
+ console.log(colors10.blue("\u2713 generated llms.txt"));
5298
5298
  }
5299
5299
  }
5300
5300
  if (llmsTxtFull) {
@@ -5325,7 +5325,7 @@ ${info.content}
5325
5325
  "utf-8"
5326
5326
  );
5327
5327
  if (process.env.APITOGO_JSON_ONLY !== "1") {
5328
- console.log(colors9.blue("\u2713 generated llms-full.txt"));
5328
+ console.log(colors10.blue("\u2713 generated llms-full.txt"));
5329
5329
  }
5330
5330
  }
5331
5331
  }
@@ -5448,7 +5448,7 @@ async function shutdownAnalytics() {
5448
5448
  import { existsSync, mkdirSync } from "node:fs";
5449
5449
  import { readFile, writeFile } from "node:fs/promises";
5450
5450
  import { join } from "node:path";
5451
- import colors3 from "picocolors";
5451
+ import colors4 from "picocolors";
5452
5452
  import { gt } from "semver";
5453
5453
 
5454
5454
  // src/cli/common/json-only-deploy.ts
@@ -5470,7 +5470,106 @@ function isDeployJsonOnlyArgv() {
5470
5470
 
5471
5471
  // src/cli/common/output.ts
5472
5472
  import * as Sentry from "@sentry/node";
5473
+ import colors2 from "picocolors";
5474
+
5475
+ // src/cli/common/spinner.ts
5473
5476
  import colors from "picocolors";
5477
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
5478
+ var INTERVAL_MS = 80;
5479
+ var SHOW_ELAPSED_AFTER_MS = 3e3;
5480
+ var isAnimated = (stream) => Boolean(stream.isTTY) && !process.env.CI && process.env.TERM !== "dumb";
5481
+ var active = /* @__PURE__ */ new Set();
5482
+ function stopAllSpinners() {
5483
+ for (const spinner of [...active]) spinner.stop();
5484
+ }
5485
+ var cursorHidden;
5486
+ var restoreCursor = () => {
5487
+ cursorHidden?.write("\x1B[?25h");
5488
+ cursorHidden = void 0;
5489
+ };
5490
+ var formatElapsed = (ms) => {
5491
+ const seconds = Math.floor(ms / 1e3);
5492
+ return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
5493
+ };
5494
+ function createSpinner(initialText, { stream = process.stderr, silent = false } = {}) {
5495
+ if (silent) {
5496
+ return { update() {
5497
+ }, log() {
5498
+ }, stop() {
5499
+ } };
5500
+ }
5501
+ if (!isAnimated(stream)) {
5502
+ let last = initialText;
5503
+ stream.write(`${initialText}
5504
+ `);
5505
+ return {
5506
+ update(text2) {
5507
+ if (text2 === last) return;
5508
+ last = text2;
5509
+ stream.write(`${text2}
5510
+ `);
5511
+ },
5512
+ log: (line) => void stream.write(`${line}
5513
+ `),
5514
+ stop() {
5515
+ }
5516
+ };
5517
+ }
5518
+ const startedAt = Date.now();
5519
+ let text = initialText;
5520
+ let frame = 0;
5521
+ let timer;
5522
+ const clear = () => stream.write("\r\x1B[K");
5523
+ const render = () => {
5524
+ const elapsed = Date.now() - startedAt;
5525
+ const suffix = elapsed >= SHOW_ELAPSED_AFTER_MS ? ` ${colors.dim(`(${formatElapsed(elapsed)})`)}` : "";
5526
+ stream.write(
5527
+ `\r\x1B[K${colors.cyan(FRAMES[frame++ % FRAMES.length])} ${text}${suffix}`
5528
+ );
5529
+ };
5530
+ stream.write("\x1B[?25l");
5531
+ cursorHidden = stream;
5532
+ process.once("exit", restoreCursor);
5533
+ render();
5534
+ timer = setInterval(render, INTERVAL_MS);
5535
+ timer.unref();
5536
+ const spinner = {
5537
+ update(next) {
5538
+ text = next;
5539
+ },
5540
+ log(line) {
5541
+ if (!timer) {
5542
+ stream.write(`${line}
5543
+ `);
5544
+ return;
5545
+ }
5546
+ clear();
5547
+ stream.write(`${line}
5548
+ `);
5549
+ render();
5550
+ },
5551
+ stop() {
5552
+ if (!timer) return;
5553
+ clearInterval(timer);
5554
+ timer = void 0;
5555
+ clear();
5556
+ restoreCursor();
5557
+ active.delete(spinner);
5558
+ }
5559
+ };
5560
+ active.add(spinner);
5561
+ return spinner;
5562
+ }
5563
+ async function withSpinner(text, fn, options) {
5564
+ const spinner = createSpinner(text, options);
5565
+ try {
5566
+ return await fn(spinner);
5567
+ } finally {
5568
+ spinner.stop();
5569
+ }
5570
+ }
5571
+
5572
+ // src/cli/common/output.ts
5474
5573
  var symbols = {
5475
5574
  success: "\u2714",
5476
5575
  error: "\u2716",
@@ -5478,37 +5577,38 @@ var symbols = {
5478
5577
  };
5479
5578
  var withSymbol = (symbol, isTTY2) => isTTY2 ? `${symbol} ` : "";
5480
5579
  function printDiagnosticsToConsole(message) {
5481
- console.error(colors.cyan(message));
5580
+ console.error(colors2.cyan(message));
5482
5581
  }
5483
5582
  function printSuccessToConsole(message) {
5484
5583
  console.error(
5485
- colors.green(
5584
+ colors2.green(
5486
5585
  `${withSymbol(symbols.success, process.stderr.isTTY)}${message}`
5487
5586
  )
5488
5587
  );
5489
5588
  }
5490
5589
  function printHintToConsole(message) {
5491
- console.error(colors.dim(message));
5590
+ console.error(colors2.dim(message));
5492
5591
  }
5493
5592
  function printWarningToConsole(message) {
5494
5593
  console.error(
5495
- colors.yellow(
5594
+ colors2.yellow(
5496
5595
  `${withSymbol(symbols.warning, process.stderr.isTTY)}${message}`
5497
5596
  )
5498
5597
  );
5499
5598
  }
5500
5599
  function printErrorToConsole(message) {
5501
5600
  console.error(
5502
- colors.red(`${withSymbol(symbols.error, process.stderr.isTTY)}${message}`)
5601
+ colors2.red(`${withSymbol(symbols.error, process.stderr.isTTY)}${message}`)
5503
5602
  );
5504
5603
  }
5505
5604
  function printBoxToConsole(message) {
5506
5605
  console.error(message);
5507
5606
  }
5508
5607
  async function printCriticalFailureToConsoleAndExit(message) {
5608
+ stopAllSpinners();
5509
5609
  const text = typeof message === "string" && message.trim() === "" ? "Command failed with no further details." : message;
5510
5610
  console.error(
5511
- `${colors.bold(colors.red(`${withSymbol(symbols.error, process.stderr.isTTY)}Error:`))} ${colors.red(text)}`
5611
+ `${colors2.bold(colors2.red(`${withSymbol(symbols.error, process.stderr.isTTY)}Error:`))} ${colors2.red(text)}`
5512
5612
  );
5513
5613
  await Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
5514
5614
  process.exit(1);
@@ -5520,8 +5620,8 @@ function printJsonOnlyErrorAndExit(message) {
5520
5620
  }
5521
5621
  function printResultToConsole(message) {
5522
5622
  console.log(
5523
- colors.bold(
5524
- colors.green(
5623
+ colors2.bold(
5624
+ colors2.green(
5525
5625
  `${withSymbol(symbols.success, process.stdout.isTTY)}${message}`
5526
5626
  )
5527
5627
  )
@@ -5529,7 +5629,7 @@ function printResultToConsole(message) {
5529
5629
  }
5530
5630
 
5531
5631
  // src/cli/common/utils/box.ts
5532
- import colors2 from "picocolors";
5632
+ import colors3 from "picocolors";
5533
5633
  import stripAnsi from "strip-ansi";
5534
5634
  var border = ["\u2500", "\u256D", "\u256E", "\u2502", "\u2502", "\u2570", "\u256F"];
5535
5635
  var nothing = ["\u2500", "", "", "", "", "", ""];
@@ -5541,7 +5641,7 @@ function box(message, {
5541
5641
  } = {}) {
5542
5642
  const lines = message.split(/\r?\n/).map((line) => [line, stripAnsi(line).length]);
5543
5643
  const maxLine = lines.reduce((p, [, len]) => Math.max(p, len), 0);
5544
- const borderColorFn = borderColor && colors2[borderColor] || colors2.yellow;
5644
+ const borderColorFn = borderColor && colors3[borderColor] || colors3.yellow;
5545
5645
  const clampedSidePadding = Math.max(1, padding * 3);
5546
5646
  const narrowMode = maxLine + 2 + clampedSidePadding * 2 > cols;
5547
5647
  const sidePadding = narrowMode ? 0 : clampedSidePadding;
@@ -5622,12 +5722,12 @@ async function warnIfOutdatedVersion(currentVersion) {
5622
5722
  if (shouldWarn) {
5623
5723
  printBoxToConsole(
5624
5724
  box(
5625
- `Update available! ${colors3.gray(`v${currentVersion}`)} \u226B ${colors3.green(
5725
+ `Update available! ${colors4.gray(`v${currentVersion}`)} \u226B ${colors4.green(
5626
5726
  `v${versionCheckInfo.latestVersion}`
5627
5727
  )}
5628
- Run ${colors3.cyan("npm install apitogo@latest")} to update.
5728
+ Run ${colors4.cyan("npm install apitogo@latest")} to update.
5629
5729
 
5630
- ${colors3.gray("Upgrade to receive the latest features and fixes.")}`
5730
+ ${colors4.gray("Upgrade to receive the latest features and fixes.")}`
5631
5731
  )
5632
5732
  );
5633
5733
  }
@@ -5836,7 +5936,7 @@ init_joinUrl();
5836
5936
  // src/vite/config.ts
5837
5937
  import path26 from "node:path";
5838
5938
  import dotenv from "dotenv";
5839
- import colors7 from "picocolors";
5939
+ import colors8 from "picocolors";
5840
5940
  import {
5841
5941
  loadConfigFromFile,
5842
5942
  mergeConfig as mergeConfig2
@@ -5845,7 +5945,7 @@ import {
5845
5945
  // package.json
5846
5946
  var package_default = {
5847
5947
  name: "@lukoweb/apitogo",
5848
- version: "0.1.144",
5948
+ version: "0.1.147",
5849
5949
  type: "module",
5850
5950
  sideEffects: [
5851
5951
  "**/*.css",
@@ -6624,14 +6724,14 @@ var flattenAllOf = (schema2) => {
6624
6724
  };
6625
6725
 
6626
6726
  // src/lib/util/traverse.ts
6627
- var traverse = (specification, transform, path73 = []) => {
6628
- const transformed = transform(specification, path73);
6727
+ var traverse = (specification, transform, path74 = []) => {
6728
+ const transformed = transform(specification, path74);
6629
6729
  if (typeof transformed !== "object" || transformed === null) {
6630
6730
  return transformed;
6631
6731
  }
6632
6732
  const result = Array.isArray(transformed) ? [] : {};
6633
6733
  for (const [key, value] of Object.entries(transformed)) {
6634
- const currentPath = [...path73, key];
6734
+ const currentPath = [...path74, key];
6635
6735
  if (Array.isArray(value)) {
6636
6736
  result[key] = value.map(
6637
6737
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -6659,9 +6759,9 @@ var resolveLocalRef = (schema2, ref) => {
6659
6759
  if (schemaCache?.has(ref)) {
6660
6760
  return schemaCache.get(ref);
6661
6761
  }
6662
- const path73 = ref.split("/").slice(1);
6762
+ const path74 = ref.split("/").slice(1);
6663
6763
  let current = schema2;
6664
- for (const segment of path73) {
6764
+ for (const segment of path74) {
6665
6765
  if (!current || typeof current !== "object") {
6666
6766
  current = null;
6667
6767
  }
@@ -6680,7 +6780,7 @@ var dereference = async (schema2, resolvers = []) => {
6680
6780
  }
6681
6781
  const cloned = structuredClone(schema2);
6682
6782
  const visited = /* @__PURE__ */ new Set();
6683
- const resolve = async (current, path73) => {
6783
+ const resolve = async (current, path74) => {
6684
6784
  if (isIndexableObject(current)) {
6685
6785
  if (visited.has(current)) {
6686
6786
  return CIRCULAR_REF;
@@ -6688,7 +6788,7 @@ var dereference = async (schema2, resolvers = []) => {
6688
6788
  visited.add(current);
6689
6789
  if (Array.isArray(current)) {
6690
6790
  for (let index = 0; index < current.length; index++) {
6691
- current[index] = await resolve(current[index], `${path73}/${index}`);
6791
+ current[index] = await resolve(current[index], `${path74}/${index}`);
6692
6792
  }
6693
6793
  } else {
6694
6794
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -6699,13 +6799,13 @@ var dereference = async (schema2, resolvers = []) => {
6699
6799
  for (const resolver of resolvers) {
6700
6800
  const resolved = await resolver($ref);
6701
6801
  if (resolved) {
6702
- result2 = await resolve(resolved, path73);
6802
+ result2 = await resolve(resolved, path74);
6703
6803
  break;
6704
6804
  }
6705
6805
  }
6706
6806
  if (result2 === void 0) {
6707
6807
  const resolved = await resolveLocalRef(cloned, $ref);
6708
- result2 = await resolve(resolved, path73);
6808
+ result2 = await resolve(resolved, path74);
6709
6809
  }
6710
6810
  if (hasSiblings) {
6711
6811
  if (result2 === CIRCULAR_REF) {
@@ -6718,7 +6818,7 @@ var dereference = async (schema2, resolvers = []) => {
6718
6818
  return result2;
6719
6819
  }
6720
6820
  for (const key in current) {
6721
- current[key] = await resolve(current[key], `${path73}/${key}`);
6821
+ current[key] = await resolve(current[key], `${path74}/${key}`);
6722
6822
  }
6723
6823
  }
6724
6824
  visited.delete(current);
@@ -6757,9 +6857,9 @@ var upgradeSchema = (schema2) => {
6757
6857
  }
6758
6858
  return sub;
6759
6859
  });
6760
- schema2 = traverse(schema2, (sub, path73) => {
6860
+ schema2 = traverse(schema2, (sub, path74) => {
6761
6861
  if (sub.example !== void 0) {
6762
- if (isSchemaPath(path73 ?? [])) {
6862
+ if (isSchemaPath(path74 ?? [])) {
6763
6863
  sub.examples = [sub.example];
6764
6864
  } else {
6765
6865
  sub.examples = {
@@ -6772,11 +6872,11 @@ var upgradeSchema = (schema2) => {
6772
6872
  }
6773
6873
  return sub;
6774
6874
  });
6775
- schema2 = traverse(schema2, (schema3, path73) => {
6875
+ schema2 = traverse(schema2, (schema3, path74) => {
6776
6876
  if (schema3.type === "object" && schema3.properties !== void 0) {
6777
- const parentPath = path73?.slice(0, -1);
6877
+ const parentPath = path74?.slice(0, -1);
6778
6878
  const isMultipart = parentPath?.some((segment, index) => {
6779
- return segment === "content" && path73?.[index + 1] === "multipart/form-data";
6879
+ return segment === "content" && path74?.[index + 1] === "multipart/form-data";
6780
6880
  });
6781
6881
  if (isMultipart) {
6782
6882
  const entries = Object.entries(schema3.properties);
@@ -6790,8 +6890,8 @@ var upgradeSchema = (schema2) => {
6790
6890
  }
6791
6891
  return schema3;
6792
6892
  });
6793
- schema2 = traverse(schema2, (schema3, path73) => {
6794
- if (path73?.includes("content") && path73.includes("application/octet-stream")) {
6893
+ schema2 = traverse(schema2, (schema3, path74) => {
6894
+ if (path74?.includes("content") && path74.includes("application/octet-stream")) {
6795
6895
  return {};
6796
6896
  }
6797
6897
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -6817,11 +6917,11 @@ var upgradeSchema = (schema2) => {
6817
6917
  }
6818
6918
  return sub;
6819
6919
  });
6820
- schema2 = traverse(schema2, (schema3, path73) => {
6920
+ schema2 = traverse(schema2, (schema3, path74) => {
6821
6921
  if (schema3.type === "string" && schema3.format === "byte") {
6822
- const parentPath = path73?.slice(0, -1);
6922
+ const parentPath = path74?.slice(0, -1);
6823
6923
  const contentMediaType = parentPath?.find(
6824
- (_, index) => path73?.[index - 1] === "content"
6924
+ (_, index) => path74?.[index - 1] === "content"
6825
6925
  );
6826
6926
  return {
6827
6927
  type: "string",
@@ -6833,7 +6933,7 @@ var upgradeSchema = (schema2) => {
6833
6933
  });
6834
6934
  return schema2;
6835
6935
  };
6836
- function isSchemaPath(path73) {
6936
+ function isSchemaPath(path74) {
6837
6937
  const schemaLocations = [
6838
6938
  ["components", "schemas"],
6839
6939
  "properties",
@@ -6846,10 +6946,10 @@ function isSchemaPath(path73) {
6846
6946
  ];
6847
6947
  return schemaLocations.some((location) => {
6848
6948
  if (Array.isArray(location)) {
6849
- return location.every((segment, index) => path73[index] === segment);
6949
+ return location.every((segment, index) => path74[index] === segment);
6850
6950
  }
6851
- return path73.includes(location);
6852
- }) || path73.includes("schema") || path73.some((segment) => segment.endsWith("Schema"));
6951
+ return path74.includes(location);
6952
+ }) || path74.includes("schema") || path74.some((segment) => segment.endsWith("Schema"));
6853
6953
  }
6854
6954
 
6855
6955
  // src/lib/oas/parser/index.ts
@@ -6931,13 +7031,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
6931
7031
  "anyOf",
6932
7032
  "oneOf"
6933
7033
  ]);
6934
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path73 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
7034
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path74 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
6935
7035
  if (obj === null || typeof obj !== "object") return obj;
6936
7036
  const refPath = obj.__$ref;
6937
7037
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
6938
7038
  if (isCircular) {
6939
7039
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
6940
- const circularProp = path73.find((p) => !OPENAPI_PROPS.has(p)) || path73[0];
7040
+ const circularProp = path74.find((p) => !OPENAPI_PROPS.has(p)) || path74[0];
6941
7041
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
6942
7042
  }
6943
7043
  if (refs.has(obj)) return refs.get(obj);
@@ -6947,7 +7047,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
6947
7047
  value,
6948
7048
  currentPath,
6949
7049
  refs,
6950
- [...path73, key],
7050
+ [...path74, key],
6951
7051
  currentRefPaths
6952
7052
  );
6953
7053
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -6982,7 +7082,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
6982
7082
  var getAllTags = (schema2) => {
6983
7083
  const rootTags = schema2.tags ?? [];
6984
7084
  const operations = Object.values(schema2.paths ?? {}).flatMap(
6985
- (path73) => HttpMethods.map((k) => path73?.[k]).filter((op) => op != null)
7085
+ (path74) => HttpMethods.map((k) => path74?.[k]).filter((op) => op != null)
6986
7086
  );
6987
7087
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
6988
7088
  const hasUntaggedOperations = operations.some(
@@ -7037,7 +7137,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
7037
7137
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
7038
7138
  var getAllOperations = (paths) => {
7039
7139
  const operations = Object.entries(paths ?? {}).flatMap(
7040
- ([path73, value]) => HttpMethods.flatMap((method) => {
7140
+ ([path74, value]) => HttpMethods.flatMap((method) => {
7041
7141
  if (!value?.[method]) return [];
7042
7142
  const operation = value[method];
7043
7143
  const pathParameters = value.parameters ?? [];
@@ -7057,7 +7157,7 @@ var getAllOperations = (paths) => {
7057
7157
  return {
7058
7158
  ...operation,
7059
7159
  method,
7060
- path: path73,
7160
+ path: path74,
7061
7161
  parameters,
7062
7162
  servers,
7063
7163
  tags: operation.tags ?? []
@@ -7415,8 +7515,8 @@ var Schema = builder.objectRef("Schema").implement({
7415
7515
  }),
7416
7516
  paths: t.field({
7417
7517
  type: [PathItem],
7418
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path73, value]) => ({
7419
- path: path73,
7518
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path74, value]) => ({
7519
+ path: path74,
7420
7520
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
7421
7521
  methods: Object.keys(value)
7422
7522
  }))
@@ -7581,7 +7681,7 @@ init_joinUrl();
7581
7681
 
7582
7682
  // src/vite/api/schema-codegen.ts
7583
7683
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
7584
- var getSegmentsFromPath = (path73) => path73.split("/").slice(1).map(unescapeJsonPointer);
7684
+ var getSegmentsFromPath = (path74) => path74.split("/").slice(1).map(unescapeJsonPointer);
7585
7685
  var createLocalRefMap = (obj) => {
7586
7686
  const refMap = /* @__PURE__ */ new Map();
7587
7687
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -7612,16 +7712,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
7612
7712
  /"__refMap\+Siblings:(.*?)"/g,
7613
7713
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
7614
7714
  );
7615
- var lookup = (schema2, path73, filePath) => {
7616
- const parts = getSegmentsFromPath(path73);
7715
+ var lookup = (schema2, path74, filePath) => {
7716
+ const parts = getSegmentsFromPath(path74);
7617
7717
  let val = schema2;
7618
7718
  for (const part of parts) {
7619
7719
  while (val.$ref?.startsWith("#/")) {
7620
- val = val.$ref === path73 ? val : lookup(schema2, val.$ref, filePath);
7720
+ val = val.$ref === path74 ? val : lookup(schema2, val.$ref, filePath);
7621
7721
  }
7622
7722
  if (val[part] === void 0) {
7623
7723
  throw new Error(
7624
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path73}`
7724
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path74}`
7625
7725
  );
7626
7726
  }
7627
7727
  val = val[part];
@@ -7866,8 +7966,8 @@ var SchemaManager = class {
7866
7966
  }
7867
7967
  }
7868
7968
  };
7869
- getLatestSchema = (path73) => this.processedSchemas[path73]?.at(0);
7870
- getSchemasForPath = (path73) => this.processedSchemas[path73];
7969
+ getLatestSchema = (path74) => this.processedSchemas[path74]?.at(0);
7970
+ getSchemasForPath = (path74) => this.processedSchemas[path74];
7871
7971
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
7872
7972
  getUrlToFilePathMap = () => {
7873
7973
  const map = /* @__PURE__ */ new Map();
@@ -7913,7 +8013,7 @@ var SchemaManager = class {
7913
8013
  init_logger();
7914
8014
  init_loader();
7915
8015
  import path14 from "node:path";
7916
- import colors6 from "picocolors";
8016
+ import colors7 from "picocolors";
7917
8017
 
7918
8018
  // src/vite/plugin-navigation.ts
7919
8019
  init_loader();
@@ -8220,7 +8320,7 @@ var viteConfigReloadPlugin = () => ({
8220
8320
  invalidate(server);
8221
8321
  reload(server);
8222
8322
  logger.info(
8223
- colors6.blue(
8323
+ colors7.blue(
8224
8324
  `Config ${path14.basename(currentConfig.__meta.configPath)} changed. Reloading...`
8225
8325
  ),
8226
8326
  { timestamp: true }
@@ -9930,9 +10030,9 @@ async function getViteConfig(dir, configEnv) {
9930
10030
  const cdnUrl = CdnUrlSchema.parse(config2.cdnUrl);
9931
10031
  const base = cdnUrl?.base ? joinUrl(cdnUrl.base, config2.basePath) : config2.basePath;
9932
10032
  if (cdnUrl && !hasLoggedCdnInfo) {
9933
- logger.info(colors7.blue(`Using CDN URL:`));
9934
- logger.info(colors7.blue(` base: `) + colors7.dim(cdnUrl.base));
9935
- logger.info(colors7.blue(` media: `) + colors7.dim(cdnUrl.media));
10033
+ logger.info(colors8.blue(`Using CDN URL:`));
10034
+ logger.info(colors8.blue(` base: `) + colors8.dim(cdnUrl.base));
10035
+ logger.info(colors8.blue(` media: `) + colors8.dim(cdnUrl.media));
9936
10036
  hasLoggedCdnInfo = true;
9937
10037
  }
9938
10038
  const publicVarsProcessEnvDefine = Object.fromEntries(
@@ -10082,7 +10182,7 @@ async function getViteConfig(dir, configEnv) {
10082
10182
  if (!loaded) continue;
10083
10183
  mergedViteConfig = mergeConfig2(mergedViteConfig, loaded.config, true);
10084
10184
  if (root === dir) {
10085
- logger.info(colors7.blue(`merged with custom user Vite config`), {
10185
+ logger.info(colors8.blue(`merged with custom user Vite config`), {
10086
10186
  timestamp: true
10087
10187
  });
10088
10188
  }
@@ -10218,7 +10318,7 @@ import os from "node:os";
10218
10318
  import path30 from "node:path";
10219
10319
  import { pathToFileURL } from "node:url";
10220
10320
  import { createIndex } from "pagefind";
10221
- import colors10 from "picocolors";
10321
+ import colors11 from "picocolors";
10222
10322
  import PiscinaImport from "piscina";
10223
10323
  init_transform_config();
10224
10324
  init_invariant();
@@ -10258,7 +10358,7 @@ init_joinUrl();
10258
10358
  import { createWriteStream, existsSync as existsSync3 } from "node:fs";
10259
10359
  import { mkdir as mkdir4 } from "node:fs/promises";
10260
10360
  import path28 from "node:path";
10261
- import colors8 from "picocolors";
10361
+ import colors9 from "picocolors";
10262
10362
  import { SitemapStream } from "sitemap";
10263
10363
  async function generateSitemap({
10264
10364
  outputUrls,
@@ -10299,20 +10399,20 @@ async function generateSitemap({
10299
10399
  writeStream.on("error", reject);
10300
10400
  });
10301
10401
  console.debug(
10302
- colors8.blue(`\u2713 wrote sitemap to ${colors8.bold(sitemapOutputPath)}`)
10402
+ colors9.blue(`\u2713 wrote sitemap to ${colors9.bold(sitemapOutputPath)}`)
10303
10403
  );
10304
10404
  }
10305
10405
 
10306
10406
  // src/vite/prerender/utils.ts
10307
10407
  init_joinUrl();
10308
- var resolveRoutePath = (path73) => {
10309
- const segments = path73.split("/");
10408
+ var resolveRoutePath = (path74) => {
10409
+ const segments = path74.split("/");
10310
10410
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
10311
10411
  return void 0;
10312
10412
  }
10313
10413
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
10314
10414
  };
10315
- var isSkipped = (path73) => path73.includes("*") || /^\d+$/.test(path73);
10415
+ var isSkipped = (path74) => path74.includes("*") || /^\d+$/.test(path74);
10316
10416
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
10317
10417
  if (route.path && isSkipped(route.path)) return [];
10318
10418
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -10391,12 +10491,12 @@ var prerender = async ({
10391
10491
  const writeProgress = throttle(
10392
10492
  (count, total, urlPath) => {
10393
10493
  if (quietDeployJson) return;
10394
- writeLine(`prerendering (${count}/${total}) ${colors10.dim(urlPath)}`);
10494
+ writeLine(`prerendering (${count}/${total}) ${colors11.dim(urlPath)}`);
10395
10495
  }
10396
10496
  );
10397
10497
  if (!isTTY() && !quietDeployJson) {
10398
10498
  logger.info(
10399
- colors10.dim(
10499
+ colors11.dim(
10400
10500
  `prerendering ${paths.length} routes using ${maxThreads} workers...`
10401
10501
  )
10402
10502
  );
@@ -10440,7 +10540,7 @@ var prerender = async ({
10440
10540
  const now = performance.now();
10441
10541
  if (now - lastLogTime >= LOG_INTERVAL_MS) {
10442
10542
  logger.info(
10443
- colors10.blue(
10543
+ colors11.blue(
10444
10544
  `prerendered ${completedCount}/${paths.length} routes using ${maxThreads} workers`
10445
10545
  )
10446
10546
  );
@@ -10457,14 +10557,14 @@ var prerender = async ({
10457
10557
  const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
10458
10558
  if (!quietDeployJson) {
10459
10559
  if (isTTY()) {
10460
- writeLine(colors10.blue(`${message}
10560
+ writeLine(colors11.blue(`${message}
10461
10561
  `));
10462
10562
  } else {
10463
- logger.info(colors10.blue(message));
10563
+ logger.info(colors11.blue(message));
10464
10564
  }
10465
10565
  if (pagefindWriteResult?.outputPath) {
10466
10566
  logger.info(
10467
- colors10.blue(
10567
+ colors11.blue(
10468
10568
  `\u2713 pagefind index built: ${pagefindWriteResult.outputPath}`
10469
10569
  )
10470
10570
  );
@@ -10944,8 +11044,8 @@ var PlatformApiClient = class {
10944
11044
  constructor(config2) {
10945
11045
  this.config = config2;
10946
11046
  }
10947
- url(path73) {
10948
- return `${this.config.apiUrl}${path73.startsWith("/") ? path73 : `/${path73}`}`;
11047
+ url(path74) {
11048
+ return `${this.config.apiUrl}${path74.startsWith("/") ? path74 : `/${path74}`}`;
10949
11049
  }
10950
11050
  headers(extra) {
10951
11051
  return {
@@ -10955,9 +11055,9 @@ var PlatformApiClient = class {
10955
11055
  ...extra
10956
11056
  };
10957
11057
  }
10958
- async request(method, path73, init4) {
11058
+ async request(method, path74, init4) {
10959
11059
  const response = await fetchWithRetry(
10960
- this.url(path73),
11060
+ this.url(path74),
10961
11061
  {
10962
11062
  method,
10963
11063
  ...init4,
@@ -11169,15 +11269,15 @@ var PlatformApiClient = class {
11169
11269
  }
11170
11270
  );
11171
11271
  }
11172
- deleteDevPortalPage(projectId, path73) {
11173
- const query = new URLSearchParams({ path: path73 });
11272
+ deleteDevPortalPage(projectId, path74) {
11273
+ const query = new URLSearchParams({ path: path74 });
11174
11274
  return this.request(
11175
11275
  "DELETE",
11176
11276
  `/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
11177
11277
  );
11178
11278
  }
11179
- renameDevPortalPage(projectId, path73, newName) {
11180
- const query = new URLSearchParams({ path: path73 });
11279
+ renameDevPortalPage(projectId, path74, newName) {
11280
+ const query = new URLSearchParams({ path: path74 });
11181
11281
  return this.request(
11182
11282
  "POST",
11183
11283
  `/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
@@ -11293,8 +11393,8 @@ var PlatformApiClient = class {
11293
11393
  * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
11294
11394
  */
11295
11395
  validateGatewayPlans(plans, options = {}) {
11296
- const path73 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
11297
- return this.request("POST", path73, {
11396
+ const path74 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
11397
+ return this.request("POST", path74, {
11298
11398
  headers: { "Content-Type": "application/json" },
11299
11399
  body: JSON.stringify({ plans, planTier: options.planTier })
11300
11400
  });
@@ -11395,8 +11495,8 @@ var PlatformApiClient = class {
11395
11495
  }
11396
11496
  };
11397
11497
  async function readFileBytes(filePath) {
11398
- const { readFile: readFile20 } = await import("node:fs/promises");
11399
- const buffer = await readFile20(filePath);
11498
+ const { readFile: readFile21 } = await import("node:fs/promises");
11499
+ const buffer = await readFile21(filePath);
11400
11500
  const arrayBuffer = new ArrayBuffer(buffer.byteLength);
11401
11501
  new Uint8Array(arrayBuffer).set(buffer);
11402
11502
  return arrayBuffer;
@@ -12818,6 +12918,13 @@ function mergePlansBySku2(existing, patch) {
12818
12918
  }
12819
12919
  return Array.from(bySku.values());
12820
12920
  }
12921
+ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
12922
+ const trimmed = sourceDirectory?.trim();
12923
+ if (!trimmed) {
12924
+ return void 0;
12925
+ }
12926
+ return path37.resolve(siteDirectory, trimmed);
12927
+ }
12821
12928
  function resolveBackendOpenApiPath(openApiPath, options) {
12822
12929
  const trimmed = openApiPath?.trim();
12823
12930
  if (!trimmed) {
@@ -14084,10 +14191,14 @@ async function check(argv) {
14084
14191
  const client = new PlatformApiClient(config2);
14085
14192
  let result;
14086
14193
  try {
14087
- result = await checkPublishReadiness(client, config2, {
14088
- sourceDirectory,
14089
- validateBuild: argv.validateBuild === true
14090
- });
14194
+ result = await withSpinner(
14195
+ "Checking publish readiness...",
14196
+ () => checkPublishReadiness(client, config2, {
14197
+ sourceDirectory,
14198
+ validateBuild: argv.validateBuild === true
14199
+ }),
14200
+ { silent: argv.json }
14201
+ );
14091
14202
  } catch (error) {
14092
14203
  await printCriticalFailureToConsoleAndExit(
14093
14204
  error instanceof Error ? error.message : String(error)
@@ -15370,8 +15481,8 @@ async function pathExists3(filePath) {
15370
15481
  }
15371
15482
  async function directoryHasEntries(dir) {
15372
15483
  try {
15373
- const { readdir: readdir4 } = await import("node:fs/promises");
15374
- const entries = await readdir4(dir);
15484
+ const { readdir: readdir5 } = await import("node:fs/promises");
15485
+ const entries = await readdir5(dir);
15375
15486
  return entries.length > 0;
15376
15487
  } catch {
15377
15488
  return false;
@@ -15948,7 +16059,7 @@ import { stdin as input, stdout as output } from "node:process";
15948
16059
  import { createInterface } from "node:readline/promises";
15949
16060
 
15950
16061
  // src/cli/make-config/wrap-up.ts
15951
- import colors11 from "picocolors";
16062
+ import colors12 from "picocolors";
15952
16063
  var DOCS = "https://docs.apitogo.com";
15953
16064
  function printPageAddedHint(relativePath, routePath) {
15954
16065
  printSuccessToConsole(`Page created: ${relativePath} (route ${routePath})`);
@@ -15972,7 +16083,7 @@ function printPaidPlanPlatformWarning() {
15972
16083
  function printInteractiveWrapUp(hasPaidPlans) {
15973
16084
  printDiagnosticsToConsole("");
15974
16085
  printSuccessToConsole("Configuration is done. Next steps:");
15975
- printDiagnosticsToConsole(`- Preview locally: ${colors11.bold("npm run dev")}`);
16086
+ printDiagnosticsToConsole(`- Preview locally: ${colors12.bold("npm run dev")}`);
15976
16087
  if (hasPaidPlans) {
15977
16088
  printPaidPlanPlatformWarning();
15978
16089
  } else {
@@ -15981,7 +16092,7 @@ function printInteractiveWrapUp(hasPaidPlans) {
15981
16092
  );
15982
16093
  }
15983
16094
  printDiagnosticsToConsole("");
15984
- printDiagnosticsToConsole(colors11.bold("Configure more:"));
16095
+ printDiagnosticsToConsole(colors12.bold("Configure more:"));
15985
16096
  printHintToConsole(`- Overview: ${DOCS}/docs/configuration/overview`);
15986
16097
  printDiagnosticsToConsole(
15987
16098
  `- Gateway plans: ${DOCS}/docs/configuration/plans`
@@ -16965,30 +17076,50 @@ function buildProgressSummary(options) {
16965
17076
  };
16966
17077
  }
16967
17078
  function createConsoleProgressReporter(stream = process.stderr) {
16968
- return createScopedConsoleProgressReporter(stream, void 0);
17079
+ const shared = { spinner: void 0 };
17080
+ return {
17081
+ ...createScopedConsoleProgressReporter(stream, void 0, shared),
17082
+ stop() {
17083
+ shared.spinner?.stop();
17084
+ shared.spinner = void 0;
17085
+ }
17086
+ };
16969
17087
  }
16970
- function createScopedConsoleProgressReporter(stream, stepLabel) {
17088
+ function createScopedConsoleProgressReporter(stream, stepLabel, shared) {
16971
17089
  let lastMessageCount = 0;
16972
17090
  let lastLine;
17091
+ const animated = "isTTY" in stream && Boolean(stream.isTTY);
17092
+ const writeLine2 = (line) => shared.spinner ? shared.spinner.log(line) : stream.write(`${line}
17093
+ `);
16973
17094
  const reporter = {
16974
17095
  async onStep(step, detail) {
16975
17096
  const message = detail?.message ?? (detail?.platformStatus ? `${step}: ${detail.platformStatus}` : step);
16976
17097
  const line = stepLabel ? `[${stepLabel}] ${message}` : message;
16977
17098
  if (line === lastLine) return;
16978
17099
  lastLine = line;
16979
- stream.write(`${line}
17100
+ if (!animated) {
17101
+ stream.write(`${line}
16980
17102
  `);
17103
+ return;
17104
+ }
17105
+ if (shared.spinner) {
17106
+ shared.spinner.update(line);
17107
+ } else {
17108
+ shared.spinner = createSpinner(line, {
17109
+ stream
17110
+ });
17111
+ }
16981
17112
  },
16982
17113
  async onNewMessages(messages) {
16983
17114
  for (const entry of messages) {
16984
- const line = stepLabel ? `[${stepLabel}] ${entry.message}` : entry.message;
16985
- stream.write(`${line}
16986
- `);
17115
+ writeLine2(
17116
+ stepLabel ? `[${stepLabel}] ${entry.message}` : entry.message
17117
+ );
16987
17118
  }
16988
17119
  },
16989
17120
  isCancelled: () => false,
16990
17121
  child(label) {
16991
- return createScopedConsoleProgressReporter(stream, label);
17122
+ return createScopedConsoleProgressReporter(stream, label, shared);
16992
17123
  }
16993
17124
  };
16994
17125
  const baseOnNewMessages = reporter.onNewMessages.bind(reporter);
@@ -17356,8 +17487,8 @@ function normalizeActionKey(actionKey) {
17356
17487
  const trimmed = actionKey.trim();
17357
17488
  if (trimmed.includes(":")) {
17358
17489
  const [method2, ...pathParts] = trimmed.split(":");
17359
- const path74 = pathParts.join(":").trim();
17360
- return `${method2?.toLowerCase()}:${path74.startsWith("/") ? path74 : `/${path74}`}`;
17490
+ const path75 = pathParts.join(":").trim();
17491
+ return `${method2?.toLowerCase()}:${path75.startsWith("/") ? path75 : `/${path75}`}`;
17361
17492
  }
17362
17493
  const spaceIndex = trimmed.indexOf(" ");
17363
17494
  if (spaceIndex <= 0) {
@@ -17366,8 +17497,8 @@ function normalizeActionKey(actionKey) {
17366
17497
  );
17367
17498
  }
17368
17499
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
17369
- const path73 = trimmed.slice(spaceIndex + 1).trim();
17370
- return `${method}:${path73.startsWith("/") ? path73 : `/${path73}`}`;
17500
+ const path74 = trimmed.slice(spaceIndex + 1).trim();
17501
+ return `${method}:${path74.startsWith("/") ? path74 : `/${path74}`}`;
17371
17502
  }
17372
17503
  async function applyGatewayPublicRoutes(client, projectId, routes, options) {
17373
17504
  const applied = [];
@@ -17950,7 +18081,10 @@ async function prepareOpenApiServersForGatewayBuild(options) {
17950
18081
  options.manifest?.backend?.openApiPath,
17951
18082
  {
17952
18083
  siteDirectory,
17953
- backendSourceDirectory: options.manifest?.backend?.sourceDirectory
18084
+ backendSourceDirectory: resolveBackendSourceDirectory(
18085
+ siteDirectory,
18086
+ options.manifest?.backend?.sourceDirectory
18087
+ )
17954
18088
  }
17955
18089
  );
17956
18090
  if (!openApiPath) {
@@ -17985,8 +18119,8 @@ function normalizeActionKeyFormat(actionKey) {
17985
18119
  const trimmed = actionKey.trim();
17986
18120
  if (trimmed.includes(":")) {
17987
18121
  const [method2, ...pathParts] = trimmed.split(":");
17988
- const path74 = pathParts.join(":").trim();
17989
- return `${method2.toLowerCase()}:${path74.startsWith("/") ? path74 : `/${path74}`}`;
18122
+ const path75 = pathParts.join(":").trim();
18123
+ return `${method2.toLowerCase()}:${path75.startsWith("/") ? path75 : `/${path75}`}`;
17990
18124
  }
17991
18125
  const spaceIndex = trimmed.indexOf(" ");
17992
18126
  if (spaceIndex <= 0) {
@@ -17995,8 +18129,8 @@ function normalizeActionKeyFormat(actionKey) {
17995
18129
  );
17996
18130
  }
17997
18131
  const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
17998
- const path73 = trimmed.slice(spaceIndex + 1).trim();
17999
- return `${method}:${path73.startsWith("/") ? path73 : `/${path73}`}`;
18132
+ const path74 = trimmed.slice(spaceIndex + 1).trim();
18133
+ return `${method}:${path74.startsWith("/") ? path74 : `/${path74}`}`;
18000
18134
  }
18001
18135
  function normalizePublicRoute(route) {
18002
18136
  const trimmed = route.trim();
@@ -20404,9 +20538,9 @@ function findBackendUpdateJob(jobs, options) {
20404
20538
  if (options?.jobId) {
20405
20539
  return sorted.find((job) => job.id === options.jobId) ?? null;
20406
20540
  }
20407
- const active = sorted.find(isActiveBackendUpdateJob);
20408
- if (active) {
20409
- return active;
20541
+ const active2 = sorted.find(isActiveBackendUpdateJob);
20542
+ if (active2) {
20543
+ return active2;
20410
20544
  }
20411
20545
  const baseline = options?.baselineJobIds;
20412
20546
  if (baseline) {
@@ -21448,7 +21582,10 @@ async function runApplyFromSiteDirectory(client, config2, args, progress) {
21448
21582
  );
21449
21583
  const skipBackend = skipBackendDecision.skip;
21450
21584
  const backendConfig = manifest.backend;
21451
- const backendSourceDirectory = backendConfig?.sourceDirectory?.trim();
21585
+ const backendSourceDirectory = resolveBackendSourceDirectory(
21586
+ args.sourceDirectory,
21587
+ backendConfig?.sourceDirectory
21588
+ );
21452
21589
  const hasBackendSource = Boolean(backendSourceDirectory);
21453
21590
  let backendDeploySucceeded = false;
21454
21591
  let backendDeployFailed = false;
@@ -22102,7 +22239,7 @@ function formatResultForHuman(parsed) {
22102
22239
  import { stdin as input3, stdout as output2 } from "node:process";
22103
22240
  import { createInterface as createInterface2 } from "node:readline/promises";
22104
22241
  import open from "open";
22105
- import colors12 from "picocolors";
22242
+ import colors13 from "picocolors";
22106
22243
  var PLAN_CHOICES = [
22107
22244
  "free",
22108
22245
  "starter",
@@ -22121,7 +22258,7 @@ function printPlanCatalog(payload) {
22121
22258
  const plan = entry;
22122
22259
  printDiagnosticsToConsole("");
22123
22260
  printDiagnosticsToConsole(
22124
- colors12.bold(`${plan.displayName} \u2014 ${plan.priceLabel}`)
22261
+ colors13.bold(`${plan.displayName} \u2014 ${plan.priceLabel}`)
22125
22262
  );
22126
22263
  if (plan.summary) {
22127
22264
  printHintToConsole(` ${plan.summary}`);
@@ -22341,6 +22478,13 @@ async function apply(argv) {
22341
22478
  await progress.onStep(
22342
22479
  `Starting deploy from ${sourceDirectory}... this may take a few minutes.`
22343
22480
  );
22481
+ const runOnceAndStop = async (plan) => {
22482
+ try {
22483
+ return await runOnce(plan);
22484
+ } finally {
22485
+ progress.stop();
22486
+ }
22487
+ };
22344
22488
  let effectiveProjectId = argv.projectId?.trim();
22345
22489
  let effectiveProjectName = argv.projectName?.trim();
22346
22490
  let createNewProject = false;
@@ -22364,7 +22508,7 @@ async function apply(argv) {
22364
22508
  );
22365
22509
  let result;
22366
22510
  try {
22367
- result = await runOnce(platformPlan);
22511
+ result = await runOnceAndStop(platformPlan);
22368
22512
  } catch (error) {
22369
22513
  await printCriticalFailureToConsoleAndExit(
22370
22514
  error instanceof Error ? error.message : String(error)
@@ -22383,7 +22527,7 @@ async function apply(argv) {
22383
22527
  effectiveProjectName = resolution.projectName;
22384
22528
  createNewProject = true;
22385
22529
  try {
22386
- result = await runOnce(platformPlan);
22530
+ result = await runOnceAndStop(platformPlan);
22387
22531
  } catch (error) {
22388
22532
  await printCriticalFailureToConsoleAndExit(
22389
22533
  error instanceof Error ? error.message : String(error)
@@ -22408,7 +22552,7 @@ async function apply(argv) {
22408
22552
  createNewProject = true;
22409
22553
  }
22410
22554
  try {
22411
- result = await runOnce(platformPlan);
22555
+ result = await runOnceAndStop(platformPlan);
22412
22556
  } catch (error) {
22413
22557
  await printCriticalFailureToConsoleAndExit(
22414
22558
  error instanceof Error ? error.message : String(error)
@@ -22439,7 +22583,7 @@ async function apply(argv) {
22439
22583
  createNewProject = false;
22440
22584
  }
22441
22585
  try {
22442
- result = await runOnce(void 0);
22586
+ result = await runOnceAndStop(void 0);
22443
22587
  } catch (error) {
22444
22588
  await printCriticalFailureToConsoleAndExit(
22445
22589
  error instanceof Error ? error.message : String(error)
@@ -22511,24 +22655,123 @@ var deploy_default = {
22511
22655
  };
22512
22656
 
22513
22657
  // src/cli/init/handler.ts
22514
- import path66 from "node:path";
22658
+ import path67 from "node:path";
22515
22659
 
22516
22660
  // src/cli/scaffold/run-scaffold-discovery.ts
22517
- import { access as access10, readFile as readFile19, writeFile as writeFile18 } from "node:fs/promises";
22518
- import path65 from "node:path";
22661
+ import { access as access10, readFile as readFile20, writeFile as writeFile18 } from "node:fs/promises";
22662
+ import path66 from "node:path";
22663
+
22664
+ // src/cli/scaffold/detect-backend.ts
22665
+ import { readdir as readdir3, readFile as readFile18 } from "node:fs/promises";
22666
+ import path64 from "node:path";
22667
+ var DEFAULT_RUNTIME_VERSION = {
22668
+ dotnet: "DOTNET_10",
22669
+ nodejs: "NODE_22_LTS",
22670
+ python: "PYTHON_312"
22671
+ };
22672
+ var PYTHON_MARKERS = ["pyproject.toml", "requirements.txt", "Pipfile"];
22673
+ async function listFiles(directory) {
22674
+ try {
22675
+ const entries = await readdir3(directory, { withFileTypes: true });
22676
+ return entries.filter((e) => e.isFile()).map((e) => e.name);
22677
+ } catch {
22678
+ return [];
22679
+ }
22680
+ }
22681
+ async function readText(filePath) {
22682
+ try {
22683
+ return await readFile18(filePath, "utf8");
22684
+ } catch {
22685
+ return void 0;
22686
+ }
22687
+ }
22688
+ function dotnetVersionFromCsproj(content) {
22689
+ const match = content?.match(/<TargetFrameworks?>\s*net(\d+)\.\d/i);
22690
+ const major = match?.[1];
22691
+ if (major === "8" || major === "9" || major === "10") {
22692
+ return `DOTNET_${major}`;
22693
+ }
22694
+ return DEFAULT_RUNTIME_VERSION.dotnet;
22695
+ }
22696
+ function nodeVersionFromEngines(engines) {
22697
+ const range = engines && typeof engines === "object" && !Array.isArray(engines) ? engines.node : void 0;
22698
+ if (typeof range === "string" && /(^|[^\d])24/.test(range)) {
22699
+ return "NODE_24_LTS";
22700
+ }
22701
+ return DEFAULT_RUNTIME_VERSION.nodejs;
22702
+ }
22703
+ function pythonVersionFromText(content) {
22704
+ const match = content?.match(/3\.(1[0-4])/);
22705
+ return match ? `PYTHON_3${match[1]}` : DEFAULT_RUNTIME_VERSION.python;
22706
+ }
22707
+ function isRunnableNodePackage(pkg) {
22708
+ const scripts = pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {};
22709
+ return typeof scripts.start === "string" || typeof pkg.main === "string";
22710
+ }
22711
+ async function detectParentBackend(siteDirectory) {
22712
+ const site = path64.resolve(siteDirectory);
22713
+ const directory = path64.dirname(site);
22714
+ if (directory === site) {
22715
+ return void 0;
22716
+ }
22717
+ const files = await listFiles(directory);
22718
+ const toResult = (runtime, runtimeVersion, marker) => ({
22719
+ directory,
22720
+ sourceDirectory: path64.relative(site, directory).split(path64.sep).join("/") || ".",
22721
+ runtime,
22722
+ runtimeVersion,
22723
+ marker
22724
+ });
22725
+ const csproj = files.find((f) => f.endsWith(".csproj"));
22726
+ const solution = files.find((f) => /\.slnx?$/.test(f));
22727
+ if (csproj || solution) {
22728
+ const content = csproj ? await readText(path64.join(directory, csproj)) : void 0;
22729
+ return toResult(
22730
+ "dotnet",
22731
+ dotnetVersionFromCsproj(content),
22732
+ csproj ?? solution
22733
+ );
22734
+ }
22735
+ if (files.includes("package.json")) {
22736
+ const raw = await readText(path64.join(directory, "package.json"));
22737
+ try {
22738
+ const pkg = JSON.parse(raw ?? "");
22739
+ if (isRunnableNodePackage(pkg)) {
22740
+ return toResult(
22741
+ "nodejs",
22742
+ nodeVersionFromEngines(pkg.engines),
22743
+ "package.json"
22744
+ );
22745
+ }
22746
+ } catch {
22747
+ }
22748
+ }
22749
+ const pythonMarker = PYTHON_MARKERS.find((m) => files.includes(m));
22750
+ if (pythonMarker) {
22751
+ const versionSource = await readText(path64.join(directory, ".python-version")) ?? (pythonMarker === "pyproject.toml" ? (await readText(path64.join(directory, pythonMarker)))?.match(
22752
+ /requires-python\s*=\s*"([^"]+)"/
22753
+ )?.[1] : void 0);
22754
+ return toResult(
22755
+ "python",
22756
+ pythonVersionFromText(versionSource),
22757
+ pythonMarker
22758
+ );
22759
+ }
22760
+ return void 0;
22761
+ }
22519
22762
 
22520
22763
  // src/cli/scaffold/wire-docs.ts
22521
22764
  import {
22522
22765
  access as access9,
22523
22766
  lstat,
22524
22767
  mkdir as mkdir13,
22525
- readdir as readdir3,
22526
- readFile as readFile18,
22768
+ readdir as readdir4,
22769
+ readFile as readFile19,
22527
22770
  readlink,
22528
22771
  symlink,
22529
22772
  writeFile as writeFile17
22530
22773
  } from "node:fs/promises";
22531
- import path64 from "node:path";
22774
+ import path65 from "node:path";
22532
22775
  var DOCS_DIR_WHITELIST = [
22533
22776
  "docs",
22534
22777
  "documents",
@@ -22565,20 +22808,20 @@ async function isDirectory(filePath) {
22565
22808
  }
22566
22809
  }
22567
22810
  function toPosix2(value) {
22568
- return value.split(path64.sep).join(path64.posix.sep);
22811
+ return value.split(path65.sep).join(path65.posix.sep);
22569
22812
  }
22570
22813
  function isWhitelistedDirName(name) {
22571
22814
  return DOCS_DIR_WHITELIST.includes(name.toLowerCase());
22572
22815
  }
22573
22816
  function isInside2(parent, child) {
22574
- const relative = path64.relative(parent, child);
22575
- return relative === "" || !relative.startsWith("..") && !path64.isAbsolute(relative);
22817
+ const relative = path65.relative(parent, child);
22818
+ return relative === "" || !relative.startsWith("..") && !path65.isAbsolute(relative);
22576
22819
  }
22577
22820
  async function collectMarkdownFiles(dir, excludeDir) {
22578
22821
  const files = [];
22579
22822
  let entries;
22580
22823
  try {
22581
- entries = await readdir3(dir, { withFileTypes: true });
22824
+ entries = await readdir4(dir, { withFileTypes: true });
22582
22825
  } catch {
22583
22826
  return files;
22584
22827
  }
@@ -22586,9 +22829,9 @@ async function collectMarkdownFiles(dir, excludeDir) {
22586
22829
  if (entry.name.startsWith(".")) {
22587
22830
  continue;
22588
22831
  }
22589
- const full = path64.join(dir, entry.name);
22832
+ const full = path65.join(dir, entry.name);
22590
22833
  if (entry.isDirectory()) {
22591
- if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir && path64.resolve(full) === excludeDir) {
22834
+ if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir && path65.resolve(full) === excludeDir) {
22592
22835
  continue;
22593
22836
  }
22594
22837
  files.push(...await collectMarkdownFiles(full, excludeDir));
@@ -22601,11 +22844,11 @@ async function collectMarkdownFiles(dir, excludeDir) {
22601
22844
  return files;
22602
22845
  }
22603
22846
  async function collectSearchRoots(siteDirectory) {
22604
- const site = path64.resolve(siteDirectory);
22605
- const parent = path64.dirname(site);
22847
+ const site = path65.resolve(siteDirectory);
22848
+ const parent = path65.dirname(site);
22606
22849
  const roots = [];
22607
22850
  const add = (dir) => {
22608
- const resolved = path64.resolve(dir);
22851
+ const resolved = path65.resolve(dir);
22609
22852
  if (!roots.includes(resolved)) {
22610
22853
  roots.push(resolved);
22611
22854
  }
@@ -22613,12 +22856,12 @@ async function collectSearchRoots(siteDirectory) {
22613
22856
  if (parent && parent !== site) {
22614
22857
  add(parent);
22615
22858
  try {
22616
- const entries = await readdir3(parent, { withFileTypes: true });
22859
+ const entries = await readdir4(parent, { withFileTypes: true });
22617
22860
  for (const entry of entries) {
22618
22861
  if (!entry.isDirectory() || SKIP_SEARCH_DIRS.has(entry.name)) {
22619
22862
  continue;
22620
22863
  }
22621
- add(path64.join(parent, entry.name));
22864
+ add(path65.join(parent, entry.name));
22622
22865
  }
22623
22866
  } catch {
22624
22867
  }
@@ -22627,13 +22870,13 @@ async function collectSearchRoots(siteDirectory) {
22627
22870
  return roots;
22628
22871
  }
22629
22872
  async function discoverNearbyDocsDirectories(siteDirectory) {
22630
- const site = path64.resolve(siteDirectory);
22631
- const sitePages = path64.join(site, PAGES_DIR);
22873
+ const site = path65.resolve(siteDirectory);
22874
+ const sitePages = path65.join(site, PAGES_DIR);
22632
22875
  const searchedDirectories = await collectSearchRoots(site);
22633
22876
  const directories = [];
22634
22877
  const seen = /* @__PURE__ */ new Set();
22635
22878
  const consider = async (dir) => {
22636
- const resolved = path64.resolve(dir);
22879
+ const resolved = path65.resolve(dir);
22637
22880
  if (resolved === site || seen.has(resolved) || isInside2(sitePages, resolved)) {
22638
22881
  return;
22639
22882
  }
@@ -22650,11 +22893,11 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
22650
22893
  directories.push({ sourceDirectory: resolved, files });
22651
22894
  };
22652
22895
  for (const root of searchedDirectories) {
22653
- if (isWhitelistedDirName(path64.basename(root))) {
22896
+ if (isWhitelistedDirName(path65.basename(root))) {
22654
22897
  await consider(root);
22655
22898
  }
22656
22899
  try {
22657
- const entries = await readdir3(root, { withFileTypes: true });
22900
+ const entries = await readdir4(root, { withFileTypes: true });
22658
22901
  for (const entry of entries) {
22659
22902
  if (!entry.isDirectory() || SKIP_SEARCH_DIRS.has(entry.name)) {
22660
22903
  continue;
@@ -22662,7 +22905,7 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
22662
22905
  if (!isWhitelistedDirName(entry.name)) {
22663
22906
  continue;
22664
22907
  }
22665
- await consider(path64.join(root, entry.name));
22908
+ await consider(path65.join(root, entry.name));
22666
22909
  }
22667
22910
  } catch {
22668
22911
  }
@@ -22681,8 +22924,8 @@ function slugifyDirName(name) {
22681
22924
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "docs";
22682
22925
  }
22683
22926
  async function allocateLinkName(pagesDir, sourceDirectory, used) {
22684
- const base = slugifyDirName(path64.basename(sourceDirectory));
22685
- const parent = slugifyDirName(path64.basename(path64.dirname(sourceDirectory)));
22927
+ const base = slugifyDirName(path65.basename(sourceDirectory));
22928
+ const parent = slugifyDirName(path65.basename(path65.dirname(sourceDirectory)));
22686
22929
  const candidates = [base, `${parent}-${base}`];
22687
22930
  let index = 2;
22688
22931
  while (true) {
@@ -22690,7 +22933,7 @@ async function allocateLinkName(pagesDir, sourceDirectory, used) {
22690
22933
  if (used.has(name)) {
22691
22934
  continue;
22692
22935
  }
22693
- const target = path64.join(pagesDir, name);
22936
+ const target = path65.join(pagesDir, name);
22694
22937
  if (await pathExists5(target)) {
22695
22938
  const stat4 = await lstat(target);
22696
22939
  if (!stat4.isDirectory() || !await isManagedLinkDir(target)) {
@@ -22703,7 +22946,7 @@ async function allocateLinkName(pagesDir, sourceDirectory, used) {
22703
22946
  }
22704
22947
  async function isManagedLinkDir(dir) {
22705
22948
  try {
22706
- const entries = await readdir3(dir, { withFileTypes: true });
22949
+ const entries = await readdir4(dir, { withFileTypes: true });
22707
22950
  if (entries.length === 0) {
22708
22951
  return true;
22709
22952
  }
@@ -22721,8 +22964,8 @@ async function alreadyLinked(linkPath, sourcePath) {
22721
22964
  return false;
22722
22965
  }
22723
22966
  const current = await readlink(linkPath);
22724
- const resolved = path64.resolve(path64.dirname(linkPath), current);
22725
- return resolved === path64.resolve(sourcePath);
22967
+ const resolved = path65.resolve(path65.dirname(linkPath), current);
22968
+ return resolved === path65.resolve(sourcePath);
22726
22969
  } catch {
22727
22970
  return false;
22728
22971
  }
@@ -22734,8 +22977,8 @@ async function linkMarkdownFile(sourcePath, linkPath) {
22734
22977
  if (await pathExists5(linkPath)) {
22735
22978
  return "skipped";
22736
22979
  }
22737
- await mkdir13(path64.dirname(linkPath), { recursive: true });
22738
- const relativeTarget = path64.relative(path64.dirname(linkPath), sourcePath);
22980
+ await mkdir13(path65.dirname(linkPath), { recursive: true });
22981
+ const relativeTarget = path65.relative(path65.dirname(linkPath), sourcePath);
22739
22982
  await symlink(
22740
22983
  relativeTarget,
22741
22984
  linkPath,
@@ -22750,11 +22993,11 @@ async function applyDocsNavigation(siteDirectory, routes) {
22750
22993
  if (routes.length === 0) {
22751
22994
  return;
22752
22995
  }
22753
- const configPath = path64.join(siteDirectory, CONFIG_FILENAME);
22996
+ const configPath = path65.join(siteDirectory, CONFIG_FILENAME);
22754
22997
  if (!await pathExists5(configPath)) {
22755
22998
  return;
22756
22999
  }
22757
- const raw = await readFile18(configPath, "utf8");
23000
+ const raw = await readFile19(configPath, "utf8");
22758
23001
  const parsed = JSON.parse(raw);
22759
23002
  if (!isPlainObject5(parsed)) {
22760
23003
  return;
@@ -22767,8 +23010,8 @@ async function applyDocsNavigation(siteDirectory, routes) {
22767
23010
  `, "utf8");
22768
23011
  }
22769
23012
  async function discoverAndWireDocs(siteDirectory) {
22770
- const site = path64.resolve(siteDirectory);
22771
- const pagesDir = path64.join(site, PAGES_DIR);
23013
+ const site = path65.resolve(siteDirectory);
23014
+ const pagesDir = path65.join(site, PAGES_DIR);
22772
23015
  const { directories, searchedDirectories } = await discoverNearbyDocsDirectories(site);
22773
23016
  if (directories.length === 0) {
22774
23017
  return {
@@ -22790,15 +23033,15 @@ async function discoverAndWireDocs(siteDirectory) {
22790
23033
  directory.sourceDirectory,
22791
23034
  usedNames
22792
23035
  );
22793
- const linkDirectory = path64.join(pagesDir, linkName);
23036
+ const linkDirectory = path65.join(pagesDir, linkName);
22794
23037
  const linked = [];
22795
23038
  const skipped = [];
22796
23039
  for (const sourcePath of directory.files) {
22797
23040
  const relativeSource = toPosix2(
22798
- path64.relative(directory.sourceDirectory, sourcePath)
23041
+ path65.relative(directory.sourceDirectory, sourcePath)
22799
23042
  );
22800
- const linkPath = path64.join(linkDirectory, relativeSource);
22801
- const relativeLink = toPosix2(path64.relative(site, linkPath));
23043
+ const linkPath = path65.join(linkDirectory, relativeSource);
23044
+ const relativeLink = toPosix2(path65.relative(site, linkPath));
22802
23045
  const route = toRoutePath(relativeLink, PAGES_DIR);
22803
23046
  if (routes.includes(route)) {
22804
23047
  skipped.push({
@@ -22846,11 +23089,11 @@ async function discoverAndWireDocs(siteDirectory) {
22846
23089
  };
22847
23090
  }
22848
23091
  function siteRelative(siteDirectory, filePath) {
22849
- const relative = toPosix2(path64.relative(siteDirectory, filePath));
23092
+ const relative = toPosix2(path65.relative(siteDirectory, filePath));
22850
23093
  if (!relative) {
22851
23094
  return ".";
22852
23095
  }
22853
- if (path64.isAbsolute(relative)) {
23096
+ if (path65.isAbsolute(relative)) {
22854
23097
  return toPosix2(filePath);
22855
23098
  }
22856
23099
  if (relative.startsWith("..")) {
@@ -22899,10 +23142,31 @@ function formatDocsDiscoveryLines(siteDirectory, docsDiscovery) {
22899
23142
  }
22900
23143
  return lines;
22901
23144
  }
22902
- function formatInitDiscoveryReport(siteDirectory, openApiDiscovery, docsDiscovery) {
23145
+ function formatBackendDiscoveryLines(backendDiscovery) {
23146
+ const status2 = String(backendDiscovery?.status ?? "unknown");
23147
+ if (status2 === "wired" || status2 === "already_configured") {
23148
+ const runtime = backendDiscovery?.runtime ?? "unknown runtime";
23149
+ const version = backendDiscovery?.runtimeVersion ?? "";
23150
+ const source = backendDiscovery?.sourceDirectory ?? "";
23151
+ const verb = status2 === "wired" ? "detected" : "kept";
23152
+ return [`Backend: ${verb} ${runtime} ${version} at ${source}`.trimEnd()];
23153
+ }
23154
+ if (status2 === "wire_failed") {
23155
+ const error = typeof backendDiscovery?.error === "string" ? `: ${backendDiscovery.error}` : "";
23156
+ return [`Backend: found a project but did not wire it${error}`];
23157
+ }
23158
+ if (status2 === "skipped") {
23159
+ return ["Backend: search skipped"];
23160
+ }
23161
+ return [
23162
+ "Backend: none detected in the parent folder (set one with apitogo config)"
23163
+ ];
23164
+ }
23165
+ function formatInitDiscoveryReport(siteDirectory, openApiDiscovery, docsDiscovery, backendDiscovery) {
22903
23166
  return [
22904
23167
  ...formatOpenApiDiscoveryLines(siteDirectory, openApiDiscovery),
22905
- ...formatDocsDiscoveryLines(siteDirectory, docsDiscovery)
23168
+ ...formatDocsDiscoveryLines(siteDirectory, docsDiscovery),
23169
+ ...backendDiscovery ? formatBackendDiscoveryLines(backendDiscovery) : []
22906
23170
  ];
22907
23171
  }
22908
23172
 
@@ -22917,8 +23181,8 @@ async function pathExists6(filePath) {
22917
23181
  }
22918
23182
  async function wireOpenApiIntoJsonConfig(siteDirectory, openApiPath) {
22919
23183
  const input5 = toSiteRelativeOpenApiInput(siteDirectory, openApiPath);
22920
- const jsonConfigPath = path65.join(siteDirectory, "apitogo.config.json");
22921
- const raw = await readFile19(jsonConfigPath, "utf8");
23184
+ const jsonConfigPath = path66.join(siteDirectory, "apitogo.config.json");
23185
+ const raw = await readFile20(jsonConfigPath, "utf8");
22922
23186
  const siteConfig = JSON.parse(raw);
22923
23187
  const updated = applyOpenApiToSiteConfig(siteConfig, {
22924
23188
  input: input5,
@@ -22945,7 +23209,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
22945
23209
  const invalidCandidates = [];
22946
23210
  let selected;
22947
23211
  for (const candidate of search.candidates) {
22948
- const content = await readFile19(candidate, "utf8");
23212
+ const content = await readFile20(candidate, "utf8");
22949
23213
  const parsed = liteParseOpenApi(content, candidate);
22950
23214
  if (parsed.valid) {
22951
23215
  selected = {
@@ -22969,7 +23233,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
22969
23233
  wired: false
22970
23234
  };
22971
23235
  }
22972
- const jsonConfigPath = path65.join(siteDirectory, "apitogo.config.json");
23236
+ const jsonConfigPath = path66.join(siteDirectory, "apitogo.config.json");
22973
23237
  if (!await pathExists6(jsonConfigPath)) {
22974
23238
  return {
22975
23239
  openApiDiscovery: {
@@ -23015,6 +23279,58 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
23015
23279
  };
23016
23280
  }
23017
23281
  }
23282
+ async function discoverAndWireBackend(siteDirectory, mode) {
23283
+ if (mode !== "nearby") {
23284
+ return { status: "skipped", mode };
23285
+ }
23286
+ const jsonConfigPath = path66.join(siteDirectory, "apitogo.config.json");
23287
+ if (!await pathExists6(jsonConfigPath)) {
23288
+ return { status: "not_found" };
23289
+ }
23290
+ try {
23291
+ const siteConfig = JSON.parse(
23292
+ await readFile20(jsonConfigPath, "utf8")
23293
+ );
23294
+ const existing = siteConfig.backend && typeof siteConfig.backend === "object" && !Array.isArray(siteConfig.backend) ? siteConfig.backend : {};
23295
+ if (typeof existing.sourceDirectory === "string" && existing.sourceDirectory.trim()) {
23296
+ return {
23297
+ status: "already_configured",
23298
+ sourceDirectory: existing.sourceDirectory,
23299
+ runtime: existing.runtime ?? null,
23300
+ runtimeVersion: existing.runtimeVersion ?? null
23301
+ };
23302
+ }
23303
+ const detected = await detectParentBackend(siteDirectory);
23304
+ if (!detected) {
23305
+ return { status: "not_found" };
23306
+ }
23307
+ siteConfig.backend = {
23308
+ ...existing,
23309
+ sourceDirectory: detected.sourceDirectory,
23310
+ runtime: detected.runtime,
23311
+ runtimeVersion: detected.runtimeVersion
23312
+ };
23313
+ await writeFile18(
23314
+ jsonConfigPath,
23315
+ `${JSON.stringify(siteConfig, null, 2)}
23316
+ `,
23317
+ "utf8"
23318
+ );
23319
+ return {
23320
+ status: "wired",
23321
+ directory: detected.directory,
23322
+ sourceDirectory: detected.sourceDirectory,
23323
+ runtime: detected.runtime,
23324
+ runtimeVersion: detected.runtimeVersion,
23325
+ marker: detected.marker
23326
+ };
23327
+ } catch (error) {
23328
+ return {
23329
+ status: "wire_failed",
23330
+ error: error instanceof Error ? error.message : String(error)
23331
+ };
23332
+ }
23333
+ }
23018
23334
  async function runScaffoldDiscovery(siteDirectory, options) {
23019
23335
  const openapiSearch = options?.openapiSearch ?? "nearby";
23020
23336
  const { openApiDiscovery } = await discoverAndWireOpenApiLite(
@@ -23022,7 +23338,11 @@ async function runScaffoldDiscovery(siteDirectory, options) {
23022
23338
  openapiSearch
23023
23339
  );
23024
23340
  const docsDiscovery = await discoverAndWireDocs(siteDirectory);
23025
- return { openApiDiscovery, docsDiscovery };
23341
+ const backendDiscovery = await discoverAndWireBackend(
23342
+ siteDirectory,
23343
+ openapiSearch
23344
+ );
23345
+ return { openApiDiscovery, docsDiscovery, backendDiscovery };
23026
23346
  }
23027
23347
 
23028
23348
  // src/cli/init/handler.ts
@@ -23030,8 +23350,8 @@ function slugifyName2(name) {
23030
23350
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "dev-portal-site";
23031
23351
  }
23032
23352
  function resolveInitOutputDirectory(dirArg, name) {
23033
- const resolved = path66.resolve(process.cwd(), dirArg);
23034
- const base = path66.basename(resolved);
23353
+ const resolved = path67.resolve(process.cwd(), dirArg);
23354
+ const base = path67.basename(resolved);
23035
23355
  const isDot = dirArg === "." || dirArg === "./" || base === "." || resolved === process.cwd();
23036
23356
  if (isDot) {
23037
23357
  const fromName = name?.trim();
@@ -23041,7 +23361,7 @@ function resolveInitOutputDirectory(dirArg, name) {
23041
23361
  );
23042
23362
  }
23043
23363
  return {
23044
- outputDirectory: path66.resolve(process.cwd(), slugifyName2(fromName)),
23364
+ outputDirectory: path67.resolve(process.cwd(), slugifyName2(fromName)),
23045
23365
  siteName: fromName
23046
23366
  };
23047
23367
  }
@@ -23081,22 +23401,21 @@ async function init2(argv) {
23081
23401
  );
23082
23402
  let npmInstall = null;
23083
23403
  if (install) {
23084
- if (!json) {
23085
- printDiagnosticsToConsole("Installing npm dependencies\u2026");
23086
- }
23087
- npmInstall = await runNpmInstall(result.outputDirectory);
23404
+ npmInstall = await withSpinner(
23405
+ "Installing npm dependencies\u2026",
23406
+ () => runNpmInstall(result.outputDirectory),
23407
+ { silent: json }
23408
+ );
23088
23409
  }
23089
- const { openApiDiscovery, docsDiscovery } = await runScaffoldDiscovery(
23090
- result.outputDirectory,
23091
- { openapiSearch }
23092
- );
23410
+ const { openApiDiscovery, docsDiscovery, backendDiscovery } = await runScaffoldDiscovery(result.outputDirectory, { openapiSearch });
23093
23411
  const siteValidation = await validateDevPortalSiteConfig(
23094
23412
  result.outputDirectory
23095
23413
  );
23096
23414
  const discoveryReport = formatInitDiscoveryReport(
23097
23415
  result.outputDirectory,
23098
23416
  openApiDiscovery,
23099
- docsDiscovery
23417
+ docsDiscovery,
23418
+ backendDiscovery
23100
23419
  );
23101
23420
  const devCommand = "npm run dev";
23102
23421
  const response = {
@@ -23116,6 +23435,7 @@ async function init2(argv) {
23116
23435
  npmInstallDurationMs: npmInstall?.durationMs ?? null,
23117
23436
  openApiDiscovery,
23118
23437
  docsDiscovery,
23438
+ backendDiscovery,
23119
23439
  openapiSearch,
23120
23440
  previewStarted: false,
23121
23441
  devCommand,
@@ -23191,11 +23511,11 @@ var init_default = {
23191
23511
  // src/cli/login/handler.ts
23192
23512
  import readline from "node:readline/promises";
23193
23513
  import open2 from "open";
23194
- import colors13 from "picocolors";
23514
+ import colors14 from "picocolors";
23195
23515
 
23196
23516
  // src/cli/platform/device-auth.ts
23197
- function url(apiUrl, path73) {
23198
- return `${apiUrl}${path73.startsWith("/") ? path73 : `/${path73}`}`;
23517
+ function url(apiUrl, path74) {
23518
+ return `${apiUrl}${path74.startsWith("/") ? path74 : `/${path74}`}`;
23199
23519
  }
23200
23520
  async function requestDeviceCode(apiUrl) {
23201
23521
  const response = await fetchWithRetry(
@@ -23259,7 +23579,7 @@ async function login() {
23259
23579
  try {
23260
23580
  const device = await requestDeviceCode(apiUrl);
23261
23581
  printDiagnosticsToConsole(
23262
- `Your login code: ${colors13.bold(colors13.white(device.userCode))}`
23582
+ `Your login code: ${colors14.bold(colors14.white(device.userCode))}`
23263
23583
  );
23264
23584
  printHintToConsole(
23265
23585
  "We'll open your browser to confirm this code and sign in. Come back to this terminal once you're done."
@@ -23279,12 +23599,19 @@ ${device.verificationUriComplete}`
23279
23599
  }
23280
23600
  const deadline = Date.now() + device.expiresIn * 1e3;
23281
23601
  const intervalMs = Math.max(1, device.interval) * 1e3;
23602
+ const spinner = createSpinner("Waiting for browser confirmation...");
23282
23603
  while (Date.now() < deadline) {
23283
23604
  await sleep4(intervalMs);
23284
- const result = await pollDeviceToken(apiUrl, device.deviceCode);
23605
+ const result = await pollDeviceToken(apiUrl, device.deviceCode).catch(
23606
+ (error) => {
23607
+ spinner.stop();
23608
+ throw error;
23609
+ }
23610
+ );
23285
23611
  if (result.status === "pending") {
23286
23612
  continue;
23287
23613
  }
23614
+ spinner.stop();
23288
23615
  if (result.status === "denied") {
23289
23616
  await printCriticalFailureToConsoleAndExit("Login request was denied.");
23290
23617
  return;
@@ -23311,6 +23638,7 @@ ${device.verificationUriComplete}`
23311
23638
  );
23312
23639
  return;
23313
23640
  }
23641
+ spinner.stop();
23314
23642
  await printCriticalFailureToConsoleAndExit(
23315
23643
  "Login code expired. Run `apitogo login` again."
23316
23644
  );
@@ -23399,6 +23727,8 @@ async function createProject(argv) {
23399
23727
  return;
23400
23728
  }
23401
23729
  const client = new PlatformApiClient(config2);
23730
+ const progress = createConsoleProgressReporter();
23731
+ progress.onStep("Creating project...");
23402
23732
  let result;
23403
23733
  try {
23404
23734
  result = await handleBootstrapProject(
@@ -23412,7 +23742,7 @@ async function createProject(argv) {
23412
23742
  wait: argv.wait !== false,
23413
23743
  configureGatewayBaseline: false
23414
23744
  },
23415
- createConsoleProgressReporter()
23745
+ progress
23416
23746
  );
23417
23747
  } catch (error) {
23418
23748
  await printCriticalFailureToConsoleAndExit(
@@ -23420,6 +23750,7 @@ async function createProject(argv) {
23420
23750
  );
23421
23751
  return;
23422
23752
  }
23753
+ progress.stop();
23423
23754
  console.log(result);
23424
23755
  if (resultLooksFailed2(result)) {
23425
23756
  process.exit(1);
@@ -23458,14 +23789,14 @@ var project_create_default = {
23458
23789
 
23459
23790
  // src/cli/dev/handler.ts
23460
23791
  init_joinUrl();
23461
- import path69 from "node:path";
23792
+ import path70 from "node:path";
23462
23793
 
23463
23794
  // src/vite/dev-server.ts
23464
23795
  init_logger();
23465
23796
  import fs12 from "node:fs/promises";
23466
23797
  import http from "node:http";
23467
23798
  import https from "node:https";
23468
- import path68 from "node:path";
23799
+ import path69 from "node:path";
23469
23800
  import { createHttpTerminator } from "http-terminator";
23470
23801
  import {
23471
23802
  createServer as createViteServer,
@@ -23498,7 +23829,7 @@ init_loader();
23498
23829
  // src/vite/pagefind-dev-index.ts
23499
23830
  init_invariant();
23500
23831
  init_joinUrl();
23501
- import path67 from "node:path";
23832
+ import path68 from "node:path";
23502
23833
  import { createIndex as createIndex2 } from "pagefind";
23503
23834
  import { isRunnableDevEnvironment } from "vite";
23504
23835
  async function* buildPagefindDevIndex(vite, config2) {
@@ -23551,7 +23882,7 @@ async function* buildPagefindDevIndex(vite, config2) {
23551
23882
  path: urlPath
23552
23883
  };
23553
23884
  }
23554
- const outputPath = path67.join(vite.config.publicDir, "pagefind");
23885
+ const outputPath = path68.join(vite.config.publicDir, "pagefind");
23555
23886
  await pagefindIndex.writeFiles({ outputPath });
23556
23887
  yield { type: "complete", success: true, indexed };
23557
23888
  }
@@ -23570,9 +23901,9 @@ var DevServer = class {
23570
23901
  this.protocol = "https";
23571
23902
  const { dir } = this.options;
23572
23903
  const [key, cert, ca] = await Promise.all([
23573
- fs12.readFile(path68.resolve(dir, config2.https.key)),
23574
- fs12.readFile(path68.resolve(dir, config2.https.cert)),
23575
- config2.https.ca ? fs12.readFile(path68.resolve(dir, config2.https.ca)) : void 0
23904
+ fs12.readFile(path69.resolve(dir, config2.https.key)),
23905
+ fs12.readFile(path69.resolve(dir, config2.https.cert)),
23906
+ config2.https.ca ? fs12.readFile(path69.resolve(dir, config2.https.ca)) : void 0
23576
23907
  ]);
23577
23908
  return https.createServer({ key, cert, ca });
23578
23909
  }
@@ -23589,7 +23920,7 @@ var DevServer = class {
23589
23920
  );
23590
23921
  const server = await this.createNodeServer(config2);
23591
23922
  if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
23592
- const publicRoot = path68.resolve(
23923
+ const publicRoot = path69.resolve(
23593
23924
  this.options.dir,
23594
23925
  typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
23595
23926
  );
@@ -23606,7 +23937,7 @@ var DevServer = class {
23606
23937
  // built-in transform middleware which would treat the path as a static asset.
23607
23938
  name: "apitogo:entry-client",
23608
23939
  configureServer(server2) {
23609
- const entryPath = path68.posix.join(
23940
+ const entryPath = path69.posix.join(
23610
23941
  server2.config.base,
23611
23942
  "/__z/entry.client.tsx"
23612
23943
  );
@@ -23771,7 +24102,7 @@ init_package_json();
23771
24102
  async function dev(argv) {
23772
24103
  const packageJson3 = getZudokuPackageJson();
23773
24104
  process.env.NODE_ENV = "development";
23774
- const dir = path69.resolve(process.cwd(), argv.dir);
24105
+ const dir = path70.resolve(process.cwd(), argv.dir);
23775
24106
  const server = new DevServer({
23776
24107
  dir,
23777
24108
  argPort: argv.port,
@@ -23860,9 +24191,9 @@ var run_default = {
23860
24191
  };
23861
24192
 
23862
24193
  // src/cli/status/handler.ts
23863
- import path70 from "node:path";
24194
+ import path71 from "node:path";
23864
24195
  async function status(argv) {
23865
- const sourceDirectory = path70.resolve(process.cwd(), argv.dir);
24196
+ const sourceDirectory = path71.resolve(process.cwd(), argv.dir);
23866
24197
  let config2;
23867
24198
  try {
23868
24199
  config2 = loadConfig();
@@ -23875,7 +24206,11 @@ async function status(argv) {
23875
24206
  const client = new PlatformApiClient(config2);
23876
24207
  let whoamiJson;
23877
24208
  try {
23878
- whoamiJson = await handleWhoami(client);
24209
+ whoamiJson = await withSpinner(
24210
+ "Checking account...",
24211
+ () => handleWhoami(client),
24212
+ { silent: argv.json }
24213
+ );
23879
24214
  } catch (error) {
23880
24215
  await printCriticalFailureToConsoleAndExit(
23881
24216
  error instanceof Error ? error.message : String(error)
@@ -23893,17 +24228,25 @@ async function status(argv) {
23893
24228
  let live = null;
23894
24229
  let liveError = null;
23895
24230
  if (projectId) {
23896
- const projectJson = await handleGetProject(client, config2, {
23897
- projectId,
23898
- includeInfrastructureStatus: true
23899
- });
24231
+ const projectJson = await withSpinner(
24232
+ "Fetching project status...",
24233
+ () => handleGetProject(client, config2, {
24234
+ projectId,
24235
+ includeInfrastructureStatus: true
24236
+ }),
24237
+ { silent: argv.json }
24238
+ );
23900
24239
  project = JSON.parse(projectJson);
23901
24240
  if (!argv.skipLive) {
23902
24241
  try {
23903
- const liveJson = await handleVerifyDevPortalLive(client, config2, {
23904
- projectId,
23905
- sourceDirectory
23906
- });
24242
+ const liveJson = await withSpinner(
24243
+ "Verifying live site...",
24244
+ () => handleVerifyDevPortalLive(client, config2, {
24245
+ projectId,
24246
+ sourceDirectory
24247
+ }),
24248
+ { silent: argv.json }
24249
+ );
23907
24250
  live = JSON.parse(liveJson);
23908
24251
  } catch (error) {
23909
24252
  liveError = error instanceof Error ? error.message : String(error);
@@ -23950,7 +24293,7 @@ var status_default = {
23950
24293
  };
23951
24294
 
23952
24295
  // src/cli/teardown/handler.ts
23953
- import path71 from "node:path";
24296
+ import path72 from "node:path";
23954
24297
  function resultLooksFailed3(payload) {
23955
24298
  try {
23956
24299
  const parsed = JSON.parse(payload);
@@ -23966,7 +24309,7 @@ async function teardown(argv) {
23966
24309
  );
23967
24310
  return;
23968
24311
  }
23969
- const sourceDirectory = path71.resolve(process.cwd(), argv.dir);
24312
+ const sourceDirectory = path72.resolve(process.cwd(), argv.dir);
23970
24313
  let config2;
23971
24314
  try {
23972
24315
  config2 = loadConfig();
@@ -23988,6 +24331,7 @@ async function teardown(argv) {
23988
24331
  );
23989
24332
  return;
23990
24333
  }
24334
+ const progress = createConsoleProgressReporter();
23991
24335
  let result;
23992
24336
  try {
23993
24337
  result = await handleTeardownProject(
@@ -23997,7 +24341,7 @@ async function teardown(argv) {
23997
24341
  projectId,
23998
24342
  wait: argv.wait !== false
23999
24343
  },
24000
- createConsoleProgressReporter()
24344
+ progress
24001
24345
  );
24002
24346
  } catch (error) {
24003
24347
  await printCriticalFailureToConsoleAndExit(
@@ -24005,6 +24349,7 @@ async function teardown(argv) {
24005
24349
  );
24006
24350
  return;
24007
24351
  }
24352
+ progress.stop();
24008
24353
  console.log(result);
24009
24354
  if (resultLooksFailed3(result)) {
24010
24355
  process.exit(1);
@@ -24047,7 +24392,7 @@ var teardown_default = {
24047
24392
  init_package_json();
24048
24393
 
24049
24394
  // src/cli/common/version-check.ts
24050
- import colors14 from "picocolors";
24395
+ import colors15 from "picocolors";
24051
24396
  import { minVersion, satisfies } from "semver";
24052
24397
  init_package_json();
24053
24398
  var getPkgManager = () => {
@@ -24084,16 +24429,16 @@ var warnPackageVersionMismatch = () => {
24084
24429
  return;
24085
24430
  }
24086
24431
  const pkgManager = getPkgManager();
24087
- const message = `${colors14.yellow("React version mismatch detected!")}
24432
+ const message = `${colors15.yellow("React version mismatch detected!")}
24088
24433
 
24089
24434
  API ToGo requires specific versions of React
24090
24435
  and React DOM to function correctly.
24091
24436
 
24092
- Installed: ${colors14.bold(`react@${installed.react}, react-dom@${installed["react-dom"]}`)}
24093
- Required: ${colors14.green(`react@${required.react}, react-dom@${required["react-dom"]}`)}
24437
+ Installed: ${colors15.bold(`react@${installed.react}, react-dom@${installed["react-dom"]}`)}
24438
+ Required: ${colors15.green(`react@${required.react}, react-dom@${required["react-dom"]}`)}
24094
24439
 
24095
24440
  To fix, run:
24096
- ${colors14.cyan(`${pkgManager} install react@${minVersion(required.react)} react-dom@${minVersion(required["react-dom"])}`)}`;
24441
+ ${colors15.cyan(`${pkgManager} install react@${minVersion(required.react)} react-dom@${minVersion(required["react-dom"])}`)}`;
24097
24442
  if (isDeployJsonOnlyArgv()) {
24098
24443
  printJsonOnlyErrorAndExit(
24099
24444
  `React version mismatch. Installed react@${installed.react}, react-dom@${installed["react-dom"]}; required react@${required.react}, react-dom@${required["react-dom"]}. Run: ${pkgManager} install react@${minVersion(required.react)} react-dom@${minVersion(required["react-dom"])}`
@@ -24123,36 +24468,36 @@ function createApitogoCli(argv) {
24123
24468
  // src/cli/local-handoff.ts
24124
24469
  import { spawn as spawn4 } from "node:child_process";
24125
24470
  import { existsSync as existsSync6, realpathSync } from "node:fs";
24126
- import path72 from "node:path";
24471
+ import path73 from "node:path";
24127
24472
  var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
24128
24473
  var PACKAGE_NAME = "@lukoweb/apitogo";
24129
24474
  var resolveProjectDir = (argv, cwd) => {
24130
24475
  for (let i = 0; i < argv.length; i++) {
24131
24476
  const arg = argv[i];
24132
24477
  const next = argv[i + 1];
24133
- if (arg === "--dir" && next) return path72.resolve(cwd, next);
24478
+ if (arg === "--dir" && next) return path73.resolve(cwd, next);
24134
24479
  if (arg?.startsWith("--dir=")) {
24135
- return path72.resolve(cwd, arg.slice("--dir=".length));
24480
+ return path73.resolve(cwd, arg.slice("--dir=".length));
24136
24481
  }
24137
24482
  }
24138
24483
  return cwd;
24139
24484
  };
24140
24485
  var findLocalCli = (projectDir, runningRoot) => {
24141
- let dir = path72.resolve(projectDir);
24486
+ let dir = path73.resolve(projectDir);
24142
24487
  let localPackageRoot;
24143
24488
  while (!localPackageRoot) {
24144
- const candidate = path72.join(dir, "node_modules", PACKAGE_NAME);
24145
- if (existsSync6(path72.join(candidate, "package.json"))) {
24489
+ const candidate = path73.join(dir, "node_modules", PACKAGE_NAME);
24490
+ if (existsSync6(path73.join(candidate, "package.json"))) {
24146
24491
  localPackageRoot = candidate;
24147
- } else if (dir === path72.dirname(dir)) {
24492
+ } else if (dir === path73.dirname(dir)) {
24148
24493
  return void 0;
24149
24494
  } else {
24150
- dir = path72.dirname(dir);
24495
+ dir = path73.dirname(dir);
24151
24496
  }
24152
24497
  }
24153
24498
  const localRoot = realpathSync(localPackageRoot);
24154
24499
  if (localRoot === realpathSync(runningRoot)) return void 0;
24155
- return path72.join(localRoot, "cli.js");
24500
+ return path73.join(localRoot, "cli.js");
24156
24501
  };
24157
24502
  var handoffToLocalCli = async (argv, runningRoot) => {
24158
24503
  if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
@@ -24164,7 +24509,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
24164
24509
  );
24165
24510
  if (!localCli) return void 0;
24166
24511
  printDiagnosticsToConsole(
24167
- `Using the project's local ${PACKAGE_NAME} (${path72.dirname(localCli)})`
24512
+ `Using the project's local ${PACKAGE_NAME} (${path73.dirname(localCli)})`
24168
24513
  );
24169
24514
  const child = spawn4(process.execPath, [localCli, ...argv], {
24170
24515
  stdio: "inherit",