@absolutejs/absolute 0.19.0-beta.703 → 0.19.0-beta.705

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/angular/components/constants.js +21 -0
  2. package/dist/angular/index.js +53 -73
  3. package/dist/angular/index.js.map +9 -9
  4. package/dist/angular/server.js +52 -72
  5. package/dist/angular/server.js.map +9 -9
  6. package/dist/build.js +8 -7
  7. package/dist/build.js.map +7 -7
  8. package/dist/cli/index.js +320 -284
  9. package/dist/client/index.js +4 -3
  10. package/dist/client/index.js.map +3 -3
  11. package/dist/index.js +96 -63
  12. package/dist/index.js.map +15 -15
  13. package/dist/islands/index.js +5 -4
  14. package/dist/islands/index.js.map +5 -5
  15. package/dist/react/index.js +49 -69
  16. package/dist/react/index.js.map +9 -9
  17. package/dist/react/jsxDevRuntimeCompat.js +1 -6
  18. package/dist/react/jsxDevRuntimeCompat.js.map +3 -3
  19. package/dist/react/server.js +43 -64
  20. package/dist/react/server.js.map +7 -7
  21. package/dist/src/angular/components/constants.d.ts +21 -0
  22. package/dist/src/angular/pageHandler.d.ts +6 -1
  23. package/dist/src/angular/ssrRender.d.ts +1 -1
  24. package/dist/src/constants.d.ts +21 -0
  25. package/dist/src/core/ssrCache.d.ts +1 -1
  26. package/dist/src/core/wrapPageHandlerWithStreamingSlots.d.ts +1 -1
  27. package/dist/src/react/jsxDevRuntimeCompat.d.ts +3 -6
  28. package/dist/src/react/pageHandler.d.ts +2 -1
  29. package/dist/src/svelte/pageHandler.d.ts +2 -2
  30. package/dist/src/utils/defineConfig.d.ts +2 -2
  31. package/dist/src/utils/loadConfig.d.ts +38 -2
  32. package/dist/src/vue/components/Image.d.ts +2 -2
  33. package/dist/src/vue/components/index.d.ts +1 -1
  34. package/dist/src/vue/index.d.ts +1 -1
  35. package/dist/src/vue/pageHandler.d.ts +2 -1
  36. package/dist/svelte/index.js +34 -49
  37. package/dist/svelte/index.js.map +9 -9
  38. package/dist/svelte/server.js +31 -47
  39. package/dist/svelte/server.js.map +8 -8
  40. package/dist/vue/components/Image.js +18 -18
  41. package/dist/vue/components/Image.js.map +3 -3
  42. package/dist/vue/components/index.js +77 -62
  43. package/dist/vue/components/index.js.map +5 -5
  44. package/dist/vue/index.js +119 -133
  45. package/dist/vue/index.js.map +12 -12
  46. package/dist/vue/server.js +40 -70
  47. package/dist/vue/server.js.map +7 -7
  48. package/package.json +48 -48
package/dist/cli/index.js CHANGED
@@ -18,7 +18,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = import.meta.require;
19
19
 
20
20
  // src/constants.ts
21
- var ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, DEFAULT_PORT = 3000, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1;
21
+ var ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, DEFAULT_PORT = 3000, HTTP_STATUS_OK = 200, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WORKSPACE_COMMAND_ARGS_OFFSET = 3, WORKSPACE_FAILURE_LOG_PRINT_LIMIT = 30, WORKSPACE_FAILURE_RECENT_LOG_LIMIT = 60, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS = 5000, WORKSPACE_READY_PROBE_INTERVAL_MS = 250, WORKSPACE_READY_TIMEOUT_MS = 30000, WORKSPACE_SHUTDOWN_TIMEOUT_MS = 1e4, WORKSPACE_TUI_DEFAULT_HEIGHT = 28, WORKSPACE_TUI_DEFAULT_WIDTH = 100, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, WORKSPACE_TUI_FOOTER_LINE_COUNT = 3, WORKSPACE_TUI_MIN_LOG_HEIGHT = 3, WORKSPACE_TUI_MIN_SERVICE_NAME_WIDTH = 7, WORKSPACE_TUI_MIN_TARGET_WIDTH = 8, WORKSPACE_TUI_MIN_WRAP_WIDTH = 12, WORKSPACE_TUI_PROMPT_CURSOR_OFFSET = 3, WORKSPACE_TUI_RECENT_LOG_LIMIT = 40, WORKSPACE_TUI_RENDER_DEBOUNCE_MS = 16, WORKSPACE_TUI_STATUS_WIDTH = 10, WORKSPACE_TUI_TARGET_PADDING_WIDTH = 6, WORKSPACE_TUI_VISIBILITY_WIDTH = 8;
22
22
  var init_constants = __esm(() => {
23
23
  MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
24
24
  MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
@@ -222,7 +222,7 @@ var RESERVED_TOP_LEVEL_KEYS, isObject = (value) => typeof value === "object" &&
222
222
  return false;
223
223
  }
224
224
  return entries.every(([, value]) => isServiceCandidate(value));
225
- }, getWorkspaceServices = (config) => {
225
+ }, isConfigInput = (value) => isObject(value), getWorkspaceServices = (config) => {
226
226
  if (!isWorkspaceConfig(config)) {
227
227
  throw new Error("absolute.config.ts is not a multi-service config. Define top-level named services with `entry` or `command` before using `absolute workspace dev`.");
228
228
  }
@@ -249,6 +249,16 @@ var RESERVED_TOP_LEVEL_KEYS, isObject = (value) => typeof value === "object" &&
249
249
  ...serviceConfig
250
250
  } = service;
251
251
  return serviceConfig;
252
+ }, loadConfig = async (configPath2) => {
253
+ const config = await loadRawConfig(configPath2);
254
+ const serviceName = process.env.ABSOLUTE_WORKSPACE_SERVICE_NAME;
255
+ if (typeof serviceName === "string" && serviceName.length > 0) {
256
+ return projectServiceConfig(config, serviceName);
257
+ }
258
+ if (isWorkspaceConfig(config)) {
259
+ throw new Error("absolute.config.ts defines multiple services. Use `absolute workspace dev` or set ABSOLUTE_WORKSPACE_SERVICE_NAME before loading a specific service config.");
260
+ }
261
+ return config;
252
262
  }, loadRawConfig = async (configPath2) => {
253
263
  const resolved = resolve(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
254
264
  const mod = await import(resolved);
@@ -257,20 +267,10 @@ var RESERVED_TOP_LEVEL_KEYS, isObject = (value) => typeof value === "object" &&
257
267
  throw new Error(`Config file "${resolved}" does not export a valid configuration.
258
268
  Expected: export default defineConfig({ ... })`);
259
269
  }
260
- if (!isObject(config)) {
270
+ if (!isConfigInput(config)) {
261
271
  throw new Error(`Config file "${resolved}" must export an object configuration.`);
262
272
  }
263
273
  return config;
264
- }, loadConfig = async (configPath2) => {
265
- const config = await loadRawConfig(configPath2);
266
- const serviceName = process.env.ABSOLUTE_WORKSPACE_SERVICE_NAME;
267
- if (typeof serviceName === "string" && serviceName.length > 0) {
268
- return projectServiceConfig(config, serviceName);
269
- }
270
- if (isWorkspaceConfig(config)) {
271
- throw new Error("absolute.config.ts defines multiple services. Use `absolute workspace dev` or set ABSOLUTE_WORKSPACE_SERVICE_NAME before loading a specific service config.");
272
- }
273
- return config;
274
274
  };
275
275
  var init_loadConfig = __esm(() => {
276
276
  RESERVED_TOP_LEVEL_KEYS = new Set([
@@ -629,7 +629,7 @@ __export(exports_prerender, {
629
629
  });
630
630
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
631
631
  import { join as join5 } from "path";
632
- var SERVER_OUTPUT_LIMIT = 4000, MAX_STARTUP_ATTEMPTS = 50, STARTUP_POLL_INTERVAL_MS = 100, PRERENDER_BYPASS_HEADER = "X-Absolute-Prerender-Bypass", routeToFilename = (route) => route === "/" ? "index.html" : `${route.slice(1).replace(/\//g, "-")}.html`, writeTimestamp = async (htmlPath) => {
632
+ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_TIMEOUT_MS = 30000, PRERENDER_BYPASS_HEADER = "X-Absolute-Prerender-Bypass", routeToFilename = (route) => route === "/" ? "index.html" : `${route.slice(1).replace(/\//g, "-")}.html`, writeTimestamp = async (htmlPath) => {
633
633
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
634
634
  await Bun.write(metaPath, String(Date.now()));
635
635
  }, readTimestamp = (htmlPath) => {
@@ -727,14 +727,28 @@ var SERVER_OUTPUT_LIMIT = 4000, MAX_STARTUP_ATTEMPTS = 50, STARTUP_POLL_INTERVAL
727
727
  await prerenderRoute(baseUrl, route, prerenderDir, result, log);
728
728
  }
729
729
  return result;
730
+ }, getStartupTimeoutMs = () => {
731
+ const rawTimeout = Bun.env.ABSOLUTE_PRERENDER_STARTUP_TIMEOUT_MS;
732
+ const parsedTimeout = rawTimeout ? Number(rawTimeout) : NaN;
733
+ return Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : DEFAULT_STARTUP_TIMEOUT_MS;
730
734
  }, waitForServerReady = async (port) => {
731
- for (let attempt = 0;attempt < MAX_STARTUP_ATTEMPTS; attempt++) {
732
- const res = await fetch(`http://localhost:${port}/`).catch(() => null);
733
- if (res?.ok)
735
+ const deadline = performance.now() + getStartupTimeoutMs();
736
+ while (performance.now() < deadline) {
737
+ if (await probePrerenderServer(port)) {
734
738
  return true;
739
+ }
735
740
  await Bun.sleep(STARTUP_POLL_INTERVAL_MS);
736
741
  }
737
742
  return false;
743
+ }, probePrerenderServer = async (port) => {
744
+ const res = await fetch(`http://localhost:${port}/`).catch(() => null);
745
+ if (!res) {
746
+ return false;
747
+ }
748
+ await res.body?.cancel().catch(() => {
749
+ return;
750
+ });
751
+ return true;
738
752
  }, captureStreamOutput = (stream, output) => {
739
753
  if (!stream)
740
754
  return;
@@ -813,7 +827,7 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
813
827
  result.push(fullPath);
814
828
  }
815
829
  return result;
816
- }, readPackageVersion3 = (candidate) => {
830
+ }, readPackageVersion4 = (candidate) => {
817
831
  try {
818
832
  const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
819
833
  if (pkg.name !== "@absolutejs/absolute")
@@ -825,7 +839,7 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
825
839
  }
826
840
  }, resolvePackageVersion3 = (candidates) => {
827
841
  for (const candidate of candidates) {
828
- const version2 = readPackageVersion3(candidate);
842
+ const version2 = readPackageVersion4(candidate);
829
843
  if (version2)
830
844
  return version2;
831
845
  }
@@ -1339,15 +1353,11 @@ Found ${errorCount} error${suffix}.`;
1339
1353
  await mkdir2(cacheDir, { recursive: true });
1340
1354
  const checks = [];
1341
1355
  checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
1342
- if (hasSvelte) {
1343
- for (const svelteDir of svelteDirs) {
1344
- checks.push(buildSvelteCheck(cacheDir, svelteDir));
1345
- }
1356
+ for (const svelteDir of hasSvelte ? svelteDirs : []) {
1357
+ checks.push(buildSvelteCheck(cacheDir, svelteDir));
1346
1358
  }
1347
- if (hasAngular) {
1348
- for (const angularDir of angularDirs) {
1349
- checks.push(buildAngularCheck(cacheDir, angularDir));
1350
- }
1359
+ for (const angularDir of hasAngular ? angularDirs : []) {
1360
+ checks.push(buildAngularCheck(cacheDir, angularDir));
1351
1361
  }
1352
1362
  const results = await Promise.all(checks);
1353
1363
  const failed = results.filter((res) => res.exitCode !== 0);
@@ -1990,18 +2000,19 @@ var getAbsoluteVersion = () => {
1990
2000
  resolve4(import.meta.dir, "..", "..", "package.json"),
1991
2001
  resolve4(import.meta.dir, "..", "..", "..", "package.json")
1992
2002
  ];
1993
- for (const pkgPath of candidates) {
1994
- if (!existsSync6(pkgPath))
1995
- continue;
1996
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1997
- const ver = pkg.version;
1998
- return ver;
1999
- }
2003
+ const pkgPath = candidates.find((candidate) => existsSync6(candidate));
2004
+ if (pkgPath)
2005
+ return readPackageVersion(pkgPath);
2000
2006
  } catch {
2001
2007
  return getPackageVersion("@absolutejs/absolute");
2002
2008
  }
2003
2009
  return getPackageVersion("@absolutejs/absolute");
2004
2010
  };
2011
+ var readPackageVersion = (pkgPath) => {
2012
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2013
+ const ver = pkg.version;
2014
+ return ver;
2015
+ };
2005
2016
  var detectCI = () => {
2006
2017
  const { env: env2 } = process;
2007
2018
  if (env2.GITHUB_ACTIONS)
@@ -2249,14 +2260,14 @@ import { basename, resolve as resolve5 } from "path";
2249
2260
  var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
2250
2261
  var resolvePackageVersion = (candidates) => {
2251
2262
  for (const candidate of candidates) {
2252
- const version2 = readPackageVersion(candidate);
2263
+ const version2 = readPackageVersion2(candidate);
2253
2264
  if (version2) {
2254
2265
  return version2;
2255
2266
  }
2256
2267
  }
2257
2268
  return "";
2258
2269
  };
2259
- var readPackageVersion = (candidate) => {
2270
+ var readPackageVersion2 = (candidate) => {
2260
2271
  try {
2261
2272
  const pkg = JSON.parse(readFileSync7(candidate, "utf-8"));
2262
2273
  if (pkg.name !== "@absolutejs/absolute")
@@ -2650,54 +2661,49 @@ var appendRightEdge = (value, width, marker) => {
2650
2661
  }
2651
2662
  return `${padLine(value, Math.max(0, width - 1))}${marker}`;
2652
2663
  };
2664
+ var splitLongWord = (word, width) => {
2665
+ const parts = [];
2666
+ for (let index = 0;index < word.length; index += width) {
2667
+ parts.push(word.slice(index, index + width));
2668
+ }
2669
+ return parts;
2670
+ };
2671
+ var appendWrappedWord = (lines, current, word, width) => {
2672
+ if (current.length === 0) {
2673
+ if (word.length <= width)
2674
+ return word;
2675
+ lines.push(...splitLongWord(word, width));
2676
+ return "";
2677
+ }
2678
+ const next = `${current} ${word}`;
2679
+ if (next.length <= width)
2680
+ return next;
2681
+ lines.push(current);
2682
+ if (word.length <= width)
2683
+ return word;
2684
+ lines.push(...splitLongWord(word, width));
2685
+ return "";
2686
+ };
2687
+ var wrapLine = (line, width) => {
2688
+ if (line.length === 0)
2689
+ return [""];
2690
+ if (line.length <= width)
2691
+ return [line];
2692
+ const lines = [];
2693
+ let current = "";
2694
+ for (const word of line.split(/\s+/)) {
2695
+ current = appendWrappedWord(lines, current, word, width);
2696
+ }
2697
+ if (current.length > 0)
2698
+ lines.push(current);
2699
+ return lines;
2700
+ };
2653
2701
  var wrapText = (value, width) => {
2654
2702
  if (width <= 0) {
2655
2703
  return [""];
2656
2704
  }
2657
- const lines = [];
2658
- for (const rawLine of value.split(`
2659
- `)) {
2660
- const line = rawLine.trimEnd();
2661
- if (line.length === 0) {
2662
- lines.push("");
2663
- continue;
2664
- }
2665
- if (line.length <= width) {
2666
- lines.push(line);
2667
- continue;
2668
- }
2669
- const words = line.split(/\s+/);
2670
- let current = "";
2671
- for (const word of words) {
2672
- if (current.length === 0) {
2673
- if (word.length <= width) {
2674
- current = word;
2675
- continue;
2676
- }
2677
- for (let index = 0;index < word.length; index += width) {
2678
- lines.push(word.slice(index, index + width));
2679
- }
2680
- continue;
2681
- }
2682
- const next = `${current} ${word}`;
2683
- if (next.length <= width) {
2684
- current = next;
2685
- continue;
2686
- }
2687
- lines.push(current);
2688
- if (word.length <= width) {
2689
- current = word;
2690
- continue;
2691
- }
2692
- for (let index = 0;index < word.length; index += width) {
2693
- lines.push(word.slice(index, index + width));
2694
- }
2695
- current = "";
2696
- }
2697
- if (current.length > 0) {
2698
- lines.push(current);
2699
- }
2700
- }
2705
+ const lines = value.split(`
2706
+ `).flatMap((rawLine) => wrapLine(rawLine.trimEnd(), width));
2701
2707
  return lines.length > 0 ? lines : [""];
2702
2708
  };
2703
2709
  var formatTimestamp2 = () => new Date().toLocaleTimeString([], {
@@ -2802,7 +2808,7 @@ var createWorkspaceTui = ({
2802
2808
  renderTimer = setTimeout(() => {
2803
2809
  renderTimer = null;
2804
2810
  render();
2805
- }, 16);
2811
+ }, WORKSPACE_TUI_RENDER_DEBOUNCE_MS);
2806
2812
  };
2807
2813
  const clearPendingEscape = () => {
2808
2814
  if (!escapeTimer) {
@@ -2817,7 +2823,7 @@ var createWorkspaceTui = ({
2817
2823
  escapeTimer = null;
2818
2824
  escapeBuffer = "";
2819
2825
  exitEscapeMode();
2820
- }, 30);
2826
+ }, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS);
2821
2827
  };
2822
2828
  const resetPrompt = () => {
2823
2829
  promptBuffer = "";
@@ -2839,16 +2845,16 @@ var createWorkspaceTui = ({
2839
2845
  if (disposed) {
2840
2846
  return;
2841
2847
  }
2842
- const width = process.stdout.columns ?? 100;
2843
- const height = process.stdout.rows ?? 28;
2848
+ const width = process.stdout.columns ?? WORKSPACE_TUI_DEFAULT_WIDTH;
2849
+ const height = process.stdout.rows ?? WORKSPACE_TUI_DEFAULT_HEIGHT;
2844
2850
  const servicesSnapshot = [...serviceStates.values()];
2845
2851
  const workspaceStatus = getWorkspaceStatus(servicesSnapshot);
2846
2852
  const statusLabel = workspaceStatus === "ready" && readyDurationMs !== null ? `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(readyDurationMs)}${colors.reset}` : `${colors.dim}${workspaceStatus}${colors.reset}`;
2847
2853
  const title = `${colors.cyan}${colors.bold}ABSOLUTEJS WORKSPACE${colors.reset} ${colors.dim}v${version2}${colors.reset} ${statusLabel}`;
2848
2854
  const divider = `${colors.dim}${"\u2500".repeat(Math.max(width, 1))}${colors.reset}`;
2849
- const serviceNameWidth = Math.max(7, ...servicesSnapshot.map((service) => service.name.length));
2850
- const visibilityWidth = 8;
2851
- const statusWidth = 10;
2855
+ const serviceNameWidth = Math.max(WORKSPACE_TUI_MIN_SERVICE_NAME_WIDTH, ...servicesSnapshot.map((service) => service.name.length));
2856
+ const visibilityWidth = WORKSPACE_TUI_VISIBILITY_WIDTH;
2857
+ const statusWidth = WORKSPACE_TUI_STATUS_WIDTH;
2852
2858
  const rows = [];
2853
2859
  rows.push(padLine(title, width));
2854
2860
  rows.push(divider);
@@ -2856,21 +2862,21 @@ var createWorkspaceTui = ({
2856
2862
  for (const service of servicesSnapshot) {
2857
2863
  const stateColor = getStatusColor(service.status);
2858
2864
  const detail = service.detail ? ` \xB7 ${service.detail}` : "";
2859
- const targetWidth = Math.max(width - serviceNameWidth - visibilityWidth - statusWidth - 6, 8);
2865
+ const targetWidth = Math.max(width - serviceNameWidth - visibilityWidth - statusWidth - WORKSPACE_TUI_TARGET_PADDING_WIDTH, WORKSPACE_TUI_MIN_TARGET_WIDTH);
2860
2866
  const target = truncateText(`${getTargetLabel(service)}${detail}`, targetWidth);
2861
2867
  const targetColor = service.visibility === "public" && service.status !== "ready" ? colors.dim : colors.reset;
2862
2868
  const row = `${colors.bold}${service.name.padEnd(serviceNameWidth)}${colors.reset} ${colors.dim}${service.visibility.padEnd(visibilityWidth)}${colors.reset} ${stateColor}${service.status.padEnd(statusWidth)}${colors.reset} ${targetColor}${target}${colors.reset}`;
2863
2869
  rows.push(padLine(row, width));
2864
2870
  }
2865
2871
  rows.push(divider);
2866
- const footerLines = 3;
2872
+ const footerLines = WORKSPACE_TUI_FOOTER_LINE_COUNT;
2867
2873
  const fixedHeight = rows.length + footerLines;
2868
- const logHeight = Math.max(height - fixedHeight, 3);
2874
+ const logHeight = Math.max(height - fixedHeight, WORKSPACE_TUI_MIN_LOG_HEIGHT);
2869
2875
  const logWidth = Math.max(width - 1, 1);
2870
2876
  const contentLines = helpVisible ? helpLines : logEntries.flatMap((entry) => {
2871
2877
  const prefixPlain = `${entry.timestamp} [${entry.source}] `;
2872
2878
  const prefixColor = `${colors.dim}${entry.timestamp}${colors.reset} ${getSourceColor(entry.source)}[${entry.source}]${colors.reset} `;
2873
- const wrapped = wrapText(entry.message, Math.max(logWidth - prefixPlain.length, 12));
2879
+ const wrapped = wrapText(entry.message, Math.max(logWidth - prefixPlain.length, WORKSPACE_TUI_MIN_WRAP_WIDTH));
2874
2880
  return wrapped.map((line, index) => {
2875
2881
  if (index === 0) {
2876
2882
  return `${prefixColor}${getLogColor(entry.level)}${line}${colors.reset}`;
@@ -2918,7 +2924,7 @@ var createWorkspaceTui = ({
2918
2924
  `);
2919
2925
  process.stdout.write(`\x1B[H${screen}`);
2920
2926
  if (shellMode) {
2921
- const promptColumn = Math.min(promptBuffer.length + 3, width);
2927
+ const promptColumn = Math.min(promptBuffer.length + WORKSPACE_TUI_PROMPT_CURSOR_OFFSET, width);
2922
2928
  const promptRow = Math.min(rows.length, height);
2923
2929
  process.stdout.write(`\x1B[${promptRow};${promptColumn}H\x1B[?25h`);
2924
2930
  return;
@@ -2962,7 +2968,7 @@ var createWorkspaceTui = ({
2962
2968
  logScrollOffset = 0;
2963
2969
  scheduleRender();
2964
2970
  };
2965
- const getRecentLogs = (limit = 40) => logEntries.slice(Math.max(0, logEntries.length - limit));
2971
+ const getRecentLogs = (limit = WORKSPACE_TUI_RECENT_LOG_LIMIT) => logEntries.slice(Math.max(0, logEntries.length - limit));
2966
2972
  const getServiceSnapshot = () => [...serviceStates.values()].map((service) => ({
2967
2973
  detail: service.detail,
2968
2974
  name: service.name,
@@ -2974,21 +2980,35 @@ var createWorkspaceTui = ({
2974
2980
  if (!shellMode || shellHistory.length === 0) {
2975
2981
  return;
2976
2982
  }
2977
- if (direction === "up") {
2978
- if (shellHistoryIndex < shellHistory.length - 1) {
2979
- shellHistoryIndex++;
2980
- }
2981
- } else if (shellHistoryIndex <= 0) {
2983
+ if (direction === "up" && shellHistoryIndex < shellHistory.length - 1) {
2984
+ shellHistoryIndex++;
2985
+ }
2986
+ if (direction === "down" && shellHistoryIndex <= 0) {
2982
2987
  shellHistoryIndex = UNFOUND_INDEX;
2983
2988
  promptBuffer = "";
2984
2989
  scheduleRender();
2985
2990
  return;
2986
- } else {
2991
+ }
2992
+ if (direction === "down") {
2987
2993
  shellHistoryIndex--;
2988
2994
  }
2989
2995
  promptBuffer = shellHistoryIndex === UNFOUND_INDEX ? "" : shellHistory[shellHistory.length - 1 - shellHistoryIndex] ?? "";
2990
2996
  scheduleRender();
2991
2997
  };
2998
+ const handleArrowEscape = (direction) => {
2999
+ clearPendingEscape();
3000
+ escapeBuffer = "";
3001
+ if (shellMode) {
3002
+ navigateShellHistory(direction);
3003
+ return;
3004
+ }
3005
+ scrollLogs(direction);
3006
+ };
3007
+ const handleScrollEscape = (mode) => {
3008
+ clearPendingEscape();
3009
+ escapeBuffer = "";
3010
+ scrollLogs(mode);
3011
+ };
2992
3012
  const scrollLogs = (direction) => {
2993
3013
  if (helpVisible) {
2994
3014
  return;
@@ -3043,47 +3063,27 @@ var createWorkspaceTui = ({
3043
3063
  return;
3044
3064
  }
3045
3065
  if (escapeBuffer === `${ESCAPE}[A`) {
3046
- clearPendingEscape();
3047
- escapeBuffer = "";
3048
- if (shellMode) {
3049
- navigateShellHistory("up");
3050
- } else {
3051
- scrollLogs("up");
3052
- }
3066
+ handleArrowEscape("up");
3053
3067
  return;
3054
3068
  }
3055
3069
  if (escapeBuffer === `${ESCAPE}[B`) {
3056
- clearPendingEscape();
3057
- escapeBuffer = "";
3058
- if (shellMode) {
3059
- navigateShellHistory("down");
3060
- } else {
3061
- scrollLogs("down");
3062
- }
3070
+ handleArrowEscape("down");
3063
3071
  return;
3064
3072
  }
3065
3073
  if (escapeBuffer === `${ESCAPE}[5~`) {
3066
- clearPendingEscape();
3067
- escapeBuffer = "";
3068
- scrollLogs("pageUp");
3074
+ handleScrollEscape("pageUp");
3069
3075
  return;
3070
3076
  }
3071
3077
  if (escapeBuffer === `${ESCAPE}[6~`) {
3072
- clearPendingEscape();
3073
- escapeBuffer = "";
3074
- scrollLogs("pageDown");
3078
+ handleScrollEscape("pageDown");
3075
3079
  return;
3076
3080
  }
3077
3081
  if (escapeBuffer === `${ESCAPE}[H` || escapeBuffer === `${ESCAPE}[1~`) {
3078
- clearPendingEscape();
3079
- escapeBuffer = "";
3080
- scrollLogs("home");
3082
+ handleScrollEscape("home");
3081
3083
  return;
3082
3084
  }
3083
3085
  if (escapeBuffer === `${ESCAPE}[F` || escapeBuffer === `${ESCAPE}[4~`) {
3084
- clearPendingEscape();
3085
- escapeBuffer = "";
3086
- scrollLogs("end");
3086
+ handleScrollEscape("end");
3087
3087
  return;
3088
3088
  }
3089
3089
  if (/^\x1b\[[0-9]*$/.test(escapeBuffer)) {
@@ -3092,6 +3092,40 @@ var createWorkspaceTui = ({
3092
3092
  }
3093
3093
  exitEscapeMode();
3094
3094
  };
3095
+ const handleBackspace = () => {
3096
+ if (!shellMode) {
3097
+ return;
3098
+ }
3099
+ if (promptBuffer.length > 0) {
3100
+ promptBuffer = promptBuffer.slice(0, UNFOUND_INDEX);
3101
+ scheduleRender();
3102
+ return;
3103
+ }
3104
+ resetPrompt();
3105
+ };
3106
+ const handleEnter = async () => {
3107
+ if (!shellMode) {
3108
+ return;
3109
+ }
3110
+ await submitShellCommand();
3111
+ };
3112
+ const handlePrintableChar = async (char) => {
3113
+ if (shellMode) {
3114
+ promptBuffer += char;
3115
+ scheduleRender();
3116
+ return;
3117
+ }
3118
+ if (char === "$") {
3119
+ shellMode = true;
3120
+ promptBuffer = "";
3121
+ scheduleRender();
3122
+ return;
3123
+ }
3124
+ const shortcut = SHORTCUTS2.get(char.toLowerCase());
3125
+ if (shortcut) {
3126
+ await runShortcut(shortcut);
3127
+ }
3128
+ };
3095
3129
  const handleChar = async (char) => {
3096
3130
  if (char === "\x03") {
3097
3131
  await actions.quit();
@@ -3107,42 +3141,18 @@ var createWorkspaceTui = ({
3107
3141
  return;
3108
3142
  }
3109
3143
  if (char === "\x7F" || char === "\b") {
3110
- if (!shellMode) {
3111
- return;
3112
- }
3113
- if (promptBuffer.length > 0) {
3114
- promptBuffer = promptBuffer.slice(0, UNFOUND_INDEX);
3115
- scheduleRender();
3116
- return;
3117
- }
3118
- resetPrompt();
3144
+ handleBackspace();
3119
3145
  return;
3120
3146
  }
3121
3147
  if (char === "\r" || char === `
3122
3148
  `) {
3123
- if (shellMode) {
3124
- await submitShellCommand();
3125
- }
3149
+ await handleEnter();
3126
3150
  return;
3127
3151
  }
3128
3152
  if (char.charCodeAt(0) < ASCII_SPACE) {
3129
3153
  return;
3130
3154
  }
3131
- if (!shellMode) {
3132
- if (char === "$") {
3133
- shellMode = true;
3134
- promptBuffer = "";
3135
- scheduleRender();
3136
- return;
3137
- }
3138
- const shortcut = SHORTCUTS2.get(char.toLowerCase());
3139
- if (shortcut) {
3140
- await runShortcut(shortcut);
3141
- }
3142
- return;
3143
- }
3144
- promptBuffer += char;
3145
- scheduleRender();
3155
+ await handlePrintableChar(char);
3146
3156
  };
3147
3157
  const onResize = () => {
3148
3158
  scheduleRender();
@@ -3167,6 +3177,16 @@ var createWorkspaceTui = ({
3167
3177
  process.stdout.on("resize", onResize);
3168
3178
  render();
3169
3179
  };
3180
+ const disposeInput = () => {
3181
+ if (!input) {
3182
+ return;
3183
+ }
3184
+ input.off("data", onData);
3185
+ setRawMode(false);
3186
+ if (input !== process.stdin) {
3187
+ input.destroy();
3188
+ }
3189
+ };
3170
3190
  const dispose = () => {
3171
3191
  if (disposed) {
3172
3192
  return;
@@ -3178,13 +3198,7 @@ var createWorkspaceTui = ({
3178
3198
  renderTimer = null;
3179
3199
  }
3180
3200
  process.stdout.off("resize", onResize);
3181
- if (input) {
3182
- input.off("data", onData);
3183
- setRawMode(false);
3184
- if (input !== process.stdin) {
3185
- input.destroy();
3186
- }
3187
- }
3201
+ disposeInput();
3188
3202
  process.stdout.write("\x1B[?25h\x1B[?1049l");
3189
3203
  };
3190
3204
  return {
@@ -3208,11 +3222,7 @@ var sanitizeLogFileName = (value) => value.replace(/[^a-zA-Z0-9._-]/g, "_") || "
3208
3222
  var createWorkspaceLogSink = (appendLog) => {
3209
3223
  const logDirectory = resolve6(".absolutejs", "workspace", "logs");
3210
3224
  mkdirSync4(logDirectory, { recursive: true });
3211
- for (const file of readdirSync(logDirectory)) {
3212
- if (file.endsWith(".log")) {
3213
- unlinkSync(resolve6(logDirectory, file));
3214
- }
3215
- }
3225
+ readdirSync(logDirectory).filter((file) => file.endsWith(".log")).forEach((file) => unlinkSync(resolve6(logDirectory, file)));
3216
3226
  writeFileSync2(resolve6(logDirectory, "all.log"), "");
3217
3227
  writeFileSync2(resolve6(logDirectory, "workspace.log"), "");
3218
3228
  const initializedSources = new Set(["workspace"]);
@@ -3240,13 +3250,13 @@ var createWorkspaceLogSink = (appendLog) => {
3240
3250
  }
3241
3251
  };
3242
3252
  };
3243
- var readPackageVersion2 = (candidate) => {
3253
+ var readPackageVersion3 = (candidate) => {
3244
3254
  try {
3245
3255
  const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
3246
3256
  if (pkg.name !== "@absolutejs/absolute") {
3247
3257
  return null;
3248
3258
  }
3249
- const version2 = pkg.version;
3259
+ const { version: version2 } = pkg;
3250
3260
  return version2;
3251
3261
  } catch {
3252
3262
  return null;
@@ -3259,7 +3269,7 @@ var resolvePackageVersion2 = () => {
3259
3269
  resolve6(import.meta.dir, "..", "..", "..", "..", "package.json")
3260
3270
  ];
3261
3271
  for (const candidate of candidates) {
3262
- const version2 = readPackageVersion2(candidate);
3272
+ const version2 = readPackageVersion3(candidate);
3263
3273
  if (version2) {
3264
3274
  return version2;
3265
3275
  }
@@ -3281,7 +3291,7 @@ var getDefaultReadyConfig = (service) => {
3281
3291
  }
3282
3292
  return;
3283
3293
  };
3284
- var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? 200];
3294
+ var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? HTTP_STATUS_OK];
3285
3295
  var resolveServiceHttpUrl = (service, path) => {
3286
3296
  if (!path.startsWith("/")) {
3287
3297
  throw new Error(`ready path must start with "/" for service probes. Received "${path}".`);
@@ -3298,20 +3308,20 @@ var resolveHttpReadyProbe = (service, ready) => {
3298
3308
  type: "http",
3299
3309
  url: resolveServiceHttpUrl(service, ready),
3300
3310
  method: "GET",
3301
- expectStatus: [200],
3311
+ expectStatus: [HTTP_STATUS_OK],
3302
3312
  headers: {},
3303
- intervalMs: 250,
3304
- timeoutMs: 30000
3313
+ intervalMs: WORKSPACE_READY_PROBE_INTERVAL_MS,
3314
+ timeoutMs: WORKSPACE_READY_TIMEOUT_MS
3305
3315
  };
3306
3316
  }
3307
3317
  return {
3308
- type: "http",
3309
- url: ready,
3310
- method: "GET",
3311
- expectStatus: [200],
3318
+ expectStatus: [HTTP_STATUS_OK],
3312
3319
  headers: {},
3313
- intervalMs: 250,
3314
- timeoutMs: 30000
3320
+ intervalMs: WORKSPACE_READY_PROBE_INTERVAL_MS,
3321
+ method: "GET",
3322
+ timeoutMs: WORKSPACE_READY_TIMEOUT_MS,
3323
+ type: "http",
3324
+ url: ready
3315
3325
  };
3316
3326
  }
3317
3327
  if (ready.path && ready.url) {
@@ -3327,8 +3337,8 @@ var resolveHttpReadyProbe = (service, ready) => {
3327
3337
  method: ready.method ?? "GET",
3328
3338
  expectStatus: normalizeExpectedStatuses(ready.expectStatus),
3329
3339
  headers: ready.headers ?? {},
3330
- intervalMs: ready.intervalMs ?? 250,
3331
- timeoutMs: ready.timeoutMs ?? 30000
3340
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3341
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3332
3342
  };
3333
3343
  };
3334
3344
  var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig(service)) => {
@@ -3343,22 +3353,22 @@ var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig
3343
3353
  type: "tcp",
3344
3354
  host: ready.host ?? getServicePublicHost(service),
3345
3355
  port: ready.port,
3346
- intervalMs: ready.intervalMs ?? 250,
3347
- timeoutMs: ready.timeoutMs ?? 30000
3356
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3357
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3348
3358
  };
3349
3359
  }
3350
3360
  if (ready.type === "command") {
3351
3361
  return {
3352
- type: "command",
3353
3362
  command: ready.command,
3354
- intervalMs: ready.intervalMs ?? 250,
3355
- timeoutMs: ready.timeoutMs ?? 30000
3363
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3364
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS,
3365
+ type: "command"
3356
3366
  };
3357
3367
  }
3358
3368
  if (ready.type === "delay") {
3359
3369
  return {
3360
- type: "delay",
3361
- ms: ready.ms
3370
+ ms: ready.ms,
3371
+ type: "delay"
3362
3372
  };
3363
3373
  }
3364
3374
  return resolveHttpReadyProbe(service, ready);
@@ -3367,7 +3377,7 @@ var probeHttpReady = async (ready) => {
3367
3377
  const response = await fetch(ready.url, {
3368
3378
  method: ready.method,
3369
3379
  headers: ready.headers,
3370
- signal: AbortSignal.timeout(Math.min(ready.timeoutMs, 5000))
3380
+ signal: AbortSignal.timeout(Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS))
3371
3381
  });
3372
3382
  return ready.expectStatus.includes(response.status);
3373
3383
  };
@@ -3379,7 +3389,7 @@ var probeTcpReady = async (ready) => new Promise((resolveProbe) => {
3379
3389
  const timeout = setTimeout(() => {
3380
3390
  socket.destroy();
3381
3391
  resolveProbe(false);
3382
- }, Math.min(ready.timeoutMs, 5000));
3392
+ }, Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS));
3383
3393
  socket.once("connect", () => {
3384
3394
  clearTimeout(timeout);
3385
3395
  socket.end();
@@ -3403,7 +3413,7 @@ var probeCommandReady = async (ready, service) => {
3403
3413
  try {
3404
3414
  processHandle.kill();
3405
3415
  } catch {}
3406
- }, Math.min(ready.timeoutMs, 5000));
3416
+ }, Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS));
3407
3417
  try {
3408
3418
  const exitCode = await processHandle.exited;
3409
3419
  return exitCode === 0;
@@ -3422,16 +3432,24 @@ var waitForReady = async (service) => {
3422
3432
  }
3423
3433
  const startedAt = Date.now();
3424
3434
  while (Date.now() - startedAt < resolved.timeoutMs) {
3425
- try {
3426
- const isReady = resolved.type === "http" ? await probeHttpReady(resolved) : resolved.type === "tcp" ? await probeTcpReady(resolved) : await probeCommandReady(resolved, service);
3427
- if (isReady) {
3428
- return;
3429
- }
3430
- } catch {}
3435
+ if (await probeReady(resolved, service)) {
3436
+ return;
3437
+ }
3431
3438
  await sleep(resolved.intervalMs);
3432
3439
  }
3433
3440
  throw new Error(resolved.type === "http" ? `service did not become ready within ${resolved.timeoutMs}ms (${resolved.url})` : resolved.type === "tcp" ? `service did not become ready within ${resolved.timeoutMs}ms (tcp://${resolved.host}:${resolved.port})` : `service did not become ready within ${resolved.timeoutMs}ms (${resolved.command.join(" ")})`);
3434
3441
  };
3442
+ var probeReady = async (resolved, service) => {
3443
+ try {
3444
+ if (resolved.type === "http")
3445
+ return probeHttpReady(resolved);
3446
+ if (resolved.type === "tcp")
3447
+ return probeTcpReady(resolved);
3448
+ return probeCommandReady(resolved, service);
3449
+ } catch {
3450
+ return false;
3451
+ }
3452
+ };
3435
3453
  var resolveShutdownHook = (shutdown) => {
3436
3454
  if (!shutdown) {
3437
3455
  return null;
@@ -3439,12 +3457,12 @@ var resolveShutdownHook = (shutdown) => {
3439
3457
  if (Array.isArray(shutdown)) {
3440
3458
  return {
3441
3459
  command: shutdown,
3442
- timeoutMs: 1e4
3460
+ timeoutMs: WORKSPACE_SHUTDOWN_TIMEOUT_MS
3443
3461
  };
3444
3462
  }
3445
3463
  return {
3446
3464
  command: shutdown.command,
3447
- timeoutMs: shutdown.timeoutMs ?? 1e4
3465
+ timeoutMs: shutdown.timeoutMs ?? WORKSPACE_SHUTDOWN_TIMEOUT_MS
3448
3466
  };
3449
3467
  };
3450
3468
  var runShutdownHook = async (service, onLog) => {
@@ -3468,11 +3486,7 @@ var runShutdownHook = async (service, onLog) => {
3468
3486
  }, hook.timeoutMs);
3469
3487
  try {
3470
3488
  const exitCode = await processHandle.exited;
3471
- if (exitCode === 0) {
3472
- onLog("workspace", `${service.name} shutdown hook finished.`, "success");
3473
- return;
3474
- }
3475
- onLog("workspace", `${service.name} shutdown hook exited with code ${exitCode || 1}.`, "warn");
3489
+ onLog("workspace", exitCode === 0 ? `${service.name} shutdown hook finished.` : `${service.name} shutdown hook exited with code ${exitCode || 1}.`, exitCode === 0 ? "success" : "warn");
3476
3490
  } finally {
3477
3491
  clearTimeout(timeout);
3478
3492
  }
@@ -3512,36 +3526,38 @@ var pipeProcessLogs = (name, processHandle, appendLog) => {
3512
3526
  const forward = async (stream, level) => {
3513
3527
  let buffer = "";
3514
3528
  const reader = stream.getReader();
3515
- try {
3516
- while (true) {
3517
- const { done, value } = await reader.read();
3518
- if (done) {
3519
- break;
3520
- }
3521
- if (!value) {
3522
- continue;
3523
- }
3524
- buffer += Buffer.from(value).toString();
3525
- const lines = buffer.split(`
3526
- `);
3527
- buffer = lines.pop() ?? "";
3528
- for (const line of lines) {
3529
- if (line.trim().length === 0) {
3530
- continue;
3531
- }
3532
- appendLog(name, line, level);
3533
- }
3534
- }
3535
- } finally {
3536
- if (buffer.trim().length > 0) {
3537
- appendLog(name, buffer, level);
3538
- }
3539
- reader.releaseLock();
3529
+ let chunk = await readLogChunk(reader);
3530
+ while (chunk !== null) {
3531
+ buffer = appendLogChunk(buffer, chunk, name, level, appendLog);
3532
+ chunk = await readLogChunk(reader);
3540
3533
  }
3534
+ appendRemainingLogBuffer(buffer, name, level, appendLog);
3535
+ reader.releaseLock();
3541
3536
  };
3542
3537
  forward(processHandle.stdout, "info");
3543
3538
  forward(processHandle.stderr, "error");
3544
3539
  };
3540
+ var readLogChunk = async (reader) => {
3541
+ const { done, value } = await reader.read();
3542
+ if (done)
3543
+ return null;
3544
+ if (!value)
3545
+ return "";
3546
+ return Buffer.from(value).toString();
3547
+ };
3548
+ var appendLogChunk = (buffer, chunk, name, level, appendLog) => {
3549
+ const lines = `${buffer}${chunk}`.split(`
3550
+ `);
3551
+ const nextBuffer = lines.pop() ?? "";
3552
+ lines.filter((line) => line.trim().length > 0).forEach((line) => appendLog(name, line, level));
3553
+ return nextBuffer;
3554
+ };
3555
+ var appendRemainingLogBuffer = (buffer, name, level, appendLog) => {
3556
+ if (buffer.trim().length === 0) {
3557
+ return;
3558
+ }
3559
+ appendLog(name, buffer, level);
3560
+ };
3545
3561
  var getServicePublicHost = (service) => {
3546
3562
  const host = service.env?.HOST ?? process.env.HOST ?? "localhost";
3547
3563
  if (host === "0.0.0.0" || host === "::") {
@@ -3552,35 +3568,37 @@ var getServicePublicHost = (service) => {
3552
3568
  var getServiceProtocol = (service) => service.env?.ABSOLUTE_HTTPS === "true" || process.env.ABSOLUTE_HTTPS === "true" ? "https" : "http";
3553
3569
  var createWorkspaceServiceEnv = (services) => {
3554
3570
  const workspaceEnv = {};
3555
- for (const [name, service] of Object.entries(services)) {
3556
- if (!service.port) {
3557
- continue;
3558
- }
3571
+ for (const [name, service] of Object.entries(services).filter(([, service2]) => Boolean(service2.port))) {
3559
3572
  const envKey = `ABSOLUTE_SERVICE_${name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_URL`;
3560
3573
  workspaceEnv[envKey] = `${getServiceProtocol(service)}://${getServicePublicHost(service)}:${service.port}`;
3561
3574
  }
3562
3575
  return workspaceEnv;
3563
3576
  };
3577
+ var getDefinedProcessEnv = () => Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string"));
3578
+ var resolveAbsoluteServiceConfigPath = (service, cwd, options) => {
3579
+ if (service.config)
3580
+ return resolve6(cwd, service.config);
3581
+ if (options.configPath)
3582
+ return resolve6(options.configPath);
3583
+ if (process.env.ABSOLUTE_CONFIG)
3584
+ return resolve6(process.env.ABSOLUTE_CONFIG);
3585
+ return;
3586
+ };
3564
3587
  var resolveService = (name, service, workspaceEnv, options) => {
3565
3588
  const cwd = resolve6(service.cwd ?? ".");
3566
- const envVars = {
3567
- ...process.env,
3568
- ...workspaceEnv,
3569
- ...service.env,
3589
+ const envVars = Object.assign(getDefinedProcessEnv(), workspaceEnv, service.env, {
3570
3590
  ABSOLUTE_WORKSPACE_MANAGED: "1",
3571
3591
  ABSOLUTE_WORKSPACE_SERVICE_NAME: name,
3572
3592
  ABSOLUTE_WORKSPACE_SERVICE_VISIBILITY: getVisibility(service),
3573
3593
  FORCE_COLOR: "1",
3574
3594
  NODE_ENV: "development"
3575
- };
3595
+ });
3576
3596
  if (service.port && !envVars.PORT) {
3577
3597
  envVars.PORT = String(service.port);
3578
3598
  }
3579
3599
  if (isAbsoluteService(service)) {
3580
- const configPath2 = service.config ? resolve6(cwd, service.config) : options.configPath ? resolve6(options.configPath) : process.env.ABSOLUTE_CONFIG ? resolve6(process.env.ABSOLUTE_CONFIG) : undefined;
3581
- if (configPath2) {
3582
- envVars.ABSOLUTE_CONFIG = configPath2;
3583
- }
3600
+ const configPath2 = resolveAbsoluteServiceConfigPath(service, cwd, options);
3601
+ Object.assign(envVars, configPath2 ? { ABSOLUTE_CONFIG: configPath2 } : {});
3584
3602
  const command = [
3585
3603
  process.execPath,
3586
3604
  "--hot",
@@ -3647,30 +3665,43 @@ var workspace = async (subcommand, options) => {
3647
3665
  });
3648
3666
  const workspaceLogs = createWorkspaceLogSink(tui.addLog);
3649
3667
  const addLog = workspaceLogs.appendLog;
3668
+ const killProcess = (service) => {
3669
+ try {
3670
+ service.process.kill();
3671
+ } catch {}
3672
+ };
3673
+ const runShutdownHookSafely = async (service) => {
3674
+ try {
3675
+ await runShutdownHook(service.resolved, addLog);
3676
+ } catch (error) {
3677
+ const message = error instanceof Error ? error.message : String(error);
3678
+ addLog("workspace", `${service.name} shutdown hook failed: ${message}`, "warn");
3679
+ }
3680
+ };
3650
3681
  const killProcesses = async () => {
3651
3682
  const snapshot = [...running];
3652
3683
  running.length = 0;
3653
- for (const service of snapshot) {
3654
- try {
3655
- service.process.kill();
3656
- } catch {}
3657
- }
3684
+ snapshot.forEach((service) => killProcess(service));
3658
3685
  await Promise.all(snapshot.map((service) => service.process.exited));
3659
3686
  for (const service of snapshot.reverse()) {
3660
- try {
3661
- await runShutdownHook(service.resolved, addLog);
3662
- } catch (error) {
3663
- const message = error instanceof Error ? error.message : String(error);
3664
- addLog("workspace", `${service.name} shutdown hook failed: ${message}`, "warn");
3665
- }
3687
+ await runShutdownHookSafely(service);
3666
3688
  }
3667
3689
  };
3690
+ const appendRecentLogs = (lines, logsToPrint) => {
3691
+ if (logsToPrint.length === 0) {
3692
+ return;
3693
+ }
3694
+ lines.push("", "Recent logs:");
3695
+ logsToPrint.forEach((entry) => {
3696
+ lines.push(` ${entry.timestamp} [${entry.source}] ${entry.message}`);
3697
+ });
3698
+ };
3668
3699
  const printFailureSummary = (exitCode) => {
3669
3700
  const servicesSnapshot = tui.getServiceSnapshot();
3670
- const recentLogs = tui.getRecentLogs(60);
3701
+ const recentLogs = tui.getRecentLogs(WORKSPACE_FAILURE_RECENT_LOG_LIMIT);
3671
3702
  const failedServices = servicesSnapshot.filter((service) => service.status === "error");
3672
3703
  const relevantLogs = recentLogs.filter((entry) => entry.level === "error" || entry.level === "warn" || entry.source === "workspace" || failedServices.some((service) => service.name === entry.source));
3673
- const logsToPrint = (relevantLogs.length > 0 ? relevantLogs : recentLogs).slice(-30);
3704
+ const logsToPrint = (relevantLogs.length > 0 ? relevantLogs : recentLogs).slice(-WORKSPACE_FAILURE_LOG_PRINT_LIMIT);
3674
3705
  const lines = [
3675
3706
  "",
3676
3707
  `\x1B[31mABSOLUTEJS WORKSPACE exited with code ${exitCode}\x1B[0m`,
@@ -3681,12 +3712,7 @@ var workspace = async (subcommand, options) => {
3681
3712
  return ` - ${service.name}: ${service.status} \xB7 ${service.target}${detail}`;
3682
3713
  })
3683
3714
  ];
3684
- if (logsToPrint.length > 0) {
3685
- lines.push("", "Recent logs:");
3686
- for (const entry of logsToPrint) {
3687
- lines.push(` ${entry.timestamp} [${entry.source}] ${entry.message}`);
3688
- }
3689
- }
3715
+ appendRecentLogs(lines, logsToPrint);
3690
3716
  lines.push("");
3691
3717
  process.stderr.write(`${lines.join(`
3692
3718
  `)}
@@ -3701,6 +3727,34 @@ var workspace = async (subcommand, options) => {
3701
3727
  process.kill(processHandle.pid, signal);
3702
3728
  } catch {}
3703
3729
  };
3730
+ const resumeRunningServices = () => {
3731
+ running.forEach((service) => {
3732
+ sendSignalToService(service.process, "SIGCONT");
3733
+ });
3734
+ paused = false;
3735
+ };
3736
+ const markRunningServicesReady = () => {
3737
+ running.forEach((service) => {
3738
+ readyServiceNames.add(service.name);
3739
+ tui.setServiceStatus(service.name, "ready");
3740
+ });
3741
+ };
3742
+ const pauseRunningServices = () => {
3743
+ running.forEach((service) => {
3744
+ sendSignalToService(service.process, "SIGSTOP");
3745
+ readyServiceNames.delete(service.name);
3746
+ tui.setServiceStatus(service.name, "paused");
3747
+ });
3748
+ paused = true;
3749
+ };
3750
+ const killStaleServicePort = (port) => {
3751
+ if (port <= 0) {
3752
+ return;
3753
+ }
3754
+ killStaleProcesses(port, (message) => {
3755
+ addLog("workspace", message, "warn");
3756
+ });
3757
+ };
3704
3758
  const shutdown = async (exitCode = 0) => {
3705
3759
  if (shuttingDown) {
3706
3760
  return;
@@ -3712,10 +3766,7 @@ var workspace = async (subcommand, options) => {
3712
3766
  printFailureSummary(exitCode);
3713
3767
  }
3714
3768
  if (paused) {
3715
- for (const service of running) {
3716
- sendSignalToService(service.process, "SIGCONT");
3717
- }
3718
- paused = false;
3769
+ resumeRunningServices();
3719
3770
  }
3720
3771
  await killProcesses();
3721
3772
  process.exit(exitCode);
@@ -3729,11 +3780,7 @@ var workspace = async (subcommand, options) => {
3729
3780
  }
3730
3781
  const resolved = resolveService(name, service, workspaceEnv, options);
3731
3782
  const port = (resolved.service.port ?? Number(resolved.env.PORT ?? "")) || DEFAULT_PORT;
3732
- if (port > 0) {
3733
- killStaleProcesses(port, (message) => {
3734
- addLog("workspace", message, "warn");
3735
- });
3736
- }
3783
+ killStaleServicePort(port);
3737
3784
  if (isAbsoluteService(resolved.service) && resolved.configPath && !existsSync8(resolved.configPath)) {
3738
3785
  throw new Error(`${name} references missing config "${resolved.configPath}"`);
3739
3786
  }
@@ -3779,10 +3826,7 @@ var workspace = async (subcommand, options) => {
3779
3826
  }
3780
3827
  restarting = true;
3781
3828
  if (paused) {
3782
- for (const service of running) {
3783
- sendSignalToService(service.process, "SIGCONT");
3784
- }
3785
- paused = false;
3829
+ resumeRunningServices();
3786
3830
  }
3787
3831
  addLog("workspace", "Restarting workspace...", "info");
3788
3832
  readyServiceNames.clear();
@@ -3797,20 +3841,12 @@ var workspace = async (subcommand, options) => {
3797
3841
  };
3798
3842
  const togglePause = () => {
3799
3843
  if (paused) {
3800
- for (const service of running) {
3801
- sendSignalToService(service.process, "SIGCONT");
3802
- readyServiceNames.add(service.name);
3803
- tui.setServiceStatus(service.name, "ready");
3804
- }
3844
+ resumeRunningServices();
3845
+ markRunningServicesReady();
3805
3846
  paused = false;
3806
3847
  addLog("workspace", "Workspace resumed.", "success");
3807
3848
  } else {
3808
- for (const service of running) {
3809
- sendSignalToService(service.process, "SIGSTOP");
3810
- readyServiceNames.delete(service.name);
3811
- tui.setServiceStatus(service.name, "paused");
3812
- }
3813
- paused = true;
3849
+ pauseRunningServices();
3814
3850
  addLog("workspace", "Workspace paused.", "warn");
3815
3851
  }
3816
3852
  };
@@ -3876,7 +3912,7 @@ init_telemetryEvent();
3876
3912
  init_constants();
3877
3913
  init_utils();
3878
3914
  var [command] = process.argv.slice(2);
3879
- var [workspaceCommand] = process.argv.slice(3);
3915
+ var [workspaceCommand] = process.argv.slice(WORKSPACE_COMMAND_ARGS_OFFSET);
3880
3916
  var args = process.argv.slice(CLI_ARGS_OFFSET);
3881
3917
  var parseNamedArg = (flag) => {
3882
3918
  const idx = args.indexOf(flag);