@open-mercato/cli 0.6.5-develop.4490.1.d8e873f3cf → 0.6.5-develop.4516.1.88e6ab71a9

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.
@@ -7,6 +7,7 @@ import path from "node:path";
7
7
  import { createInterface } from "node:readline/promises";
8
8
  import { stdin as input, stdout as output } from "node:process";
9
9
  import spawn from "cross-spawn";
10
+ import { fetchWithTimeout } from "@open-mercato/shared/lib/http/fetchWithTimeout";
10
11
  import { resolveEnvironment } from "../resolver.js";
11
12
  import { resolveSpawnCommand } from "../spawn.js";
12
13
  import { discoverIntegrationSpecFiles as discoverIntegrationSpecFilesShared } from "./integration-discovery.js";
@@ -468,6 +469,10 @@ function delay(milliseconds) {
468
469
  setTimeout(resolve, milliseconds);
469
470
  });
470
471
  }
472
+ const READINESS_PROBE_FETCH_TIMEOUT_MS = 15e3;
473
+ function probeFetch(input2, init = {}) {
474
+ return fetchWithTimeout(input2, { timeoutMs: READINESS_PROBE_FETCH_TIMEOUT_MS, ...init });
475
+ }
471
476
  function resolveBuildCacheTtlSeconds(logPrefix) {
472
477
  const rawValue = process.env[BUILD_CACHE_TTL_ENV_VAR];
473
478
  if (!rawValue) {
@@ -885,7 +890,7 @@ function isSuccessfulBrowserNavigationStatus(status) {
885
890
  }
886
891
  async function probeLoginPage(baseUrl) {
887
892
  try {
888
- const response = await fetch(`${baseUrl}/login`, {
893
+ const response = await probeFetch(`${baseUrl}/login`, {
889
894
  method: "GET",
890
895
  redirect: "manual"
891
896
  });
@@ -930,7 +935,7 @@ async function probeBackendLoginEndpoint(baseUrl) {
930
935
  const form = new URLSearchParams();
931
936
  form.set("email", "integration-healthcheck@example.invalid");
932
937
  form.set("password", "invalid-password");
933
- const response = await fetch(`${baseUrl}/api/auth/login`, {
938
+ const response = await probeFetch(`${baseUrl}/api/auth/login`, {
934
939
  method: "POST",
935
940
  redirect: "manual",
936
941
  headers: {
@@ -958,7 +963,7 @@ async function probeAuthenticatedApi(baseUrl) {
958
963
  const form = new URLSearchParams();
959
964
  form.set("email", "admin@acme.com");
960
965
  form.set("password", "secret");
961
- const loginResponse = await fetch(`${baseUrl}/api/auth/login`, {
966
+ const loginResponse = await probeFetch(`${baseUrl}/api/auth/login`, {
962
967
  method: "POST",
963
968
  redirect: "manual",
964
969
  headers: {
@@ -984,7 +989,7 @@ async function probeAuthenticatedApi(baseUrl) {
984
989
  detail: loginResponse.ok ? "POST /api/auth/login did not return an auth token" : `POST /api/auth/login returned ${loginResponse.status}`
985
990
  };
986
991
  }
987
- const apiResponse = await fetch(`${baseUrl}/api/customers/people?pageSize=1`, {
992
+ const apiResponse = await probeFetch(`${baseUrl}/api/customers/people?pageSize=1`, {
988
993
  headers: {
989
994
  Authorization: `Bearer ${token}`
990
995
  }
@@ -1288,22 +1293,33 @@ async function tryReuseExistingEnvironment(options) {
1288
1293
  }
1289
1294
  async function waitForApplicationReadiness(baseUrl, appProcess, options) {
1290
1295
  const startTimestamp = Date.now();
1291
- const exitPromise = getProcessExitPromise(appProcess);
1292
- const readinessStabilizationMs = 600;
1296
+ const intervalMs = options.intervalMs ?? APP_READY_INTERVAL_MS;
1297
+ const readinessStabilizationMs = options.stabilizationMs ?? 600;
1293
1298
  let lastProbe = null;
1299
+ let exitCode = null;
1300
+ const exitSignal = getProcessExitPromise(appProcess).then(
1301
+ (code) => {
1302
+ exitCode = code ?? null;
1303
+ return { exited: true };
1304
+ },
1305
+ () => {
1306
+ exitCode = null;
1307
+ return { exited: true };
1308
+ }
1309
+ );
1310
+ const exitError = () => new Error(`Application process exited before readiness check (exit ${exitCode ?? "unknown"})`);
1294
1311
  while (Date.now() - startTimestamp < options.timeoutMs) {
1295
- const result = await Promise.race([
1296
- probeApplicationReadiness(baseUrl).then((probe) => ({ kind: "probe", probe })),
1297
- exitPromise.then((code) => ({ kind: "exit", code })),
1298
- delay(APP_READY_INTERVAL_MS).then(() => ({ kind: "timeout" }))
1312
+ const cycle = await Promise.race([
1313
+ probeApplicationReadiness(baseUrl).then((probe) => ({ probe })),
1314
+ exitSignal
1299
1315
  ]);
1300
- if (result.kind === "probe") {
1301
- lastProbe = result.probe;
1302
- if (!result.probe.ready) {
1303
- continue;
1304
- }
1316
+ if ("exited" in cycle) {
1317
+ throw exitError();
1318
+ }
1319
+ lastProbe = cycle.probe;
1320
+ if (cycle.probe.ready) {
1305
1321
  const processExited = await Promise.race([
1306
- exitPromise.then(() => true),
1322
+ exitSignal.then(() => true),
1307
1323
  delay(readinessStabilizationMs).then(() => false)
1308
1324
  ]);
1309
1325
  if (processExited) {
@@ -1311,8 +1327,14 @@ async function waitForApplicationReadiness(baseUrl, appProcess, options) {
1311
1327
  }
1312
1328
  return;
1313
1329
  }
1314
- if (result.kind === "exit") {
1315
- throw new Error(`Application process exited before readiness check (exit ${result.code ?? "unknown"})`);
1330
+ const remainingMs = options.timeoutMs - (Date.now() - startTimestamp);
1331
+ if (remainingMs <= 0) break;
1332
+ const waited = await Promise.race([
1333
+ exitSignal,
1334
+ delay(Math.min(intervalMs, remainingMs)).then(() => null)
1335
+ ]);
1336
+ if (waited && "exited" in waited) {
1337
+ throw exitError();
1316
1338
  }
1317
1339
  }
1318
1340
  const lastFrontendDetail = lastProbe?.frontend.detail ?? "GET /login was never observed";
@@ -2738,6 +2760,7 @@ export {
2738
2760
  shouldUseIsolatedPortForFreshEnvironment,
2739
2761
  startEphemeralEnvironment,
2740
2762
  tryReuseExistingEnvironment,
2763
+ waitForApplicationReadiness,
2741
2764
  writeEphemeralEnvironmentState
2742
2765
  };
2743
2766
  //# sourceMappingURL=integration.js.map