@absolutejs/absolute 0.19.0-beta.704 → 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 +312 -285
  9. package/dist/client/index.js +4 -3
  10. package/dist/client/index.js.map +3 -3
  11. package/dist/index.js +88 -64
  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 +42 -42
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([
@@ -734,16 +734,21 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
734
734
  }, waitForServerReady = async (port) => {
735
735
  const deadline = performance.now() + getStartupTimeoutMs();
736
736
  while (performance.now() < deadline) {
737
- const res = await fetch(`http://localhost:${port}/`).catch(() => null);
738
- if (res) {
739
- await res.body?.cancel().catch(() => {
740
- return;
741
- });
737
+ if (await probePrerenderServer(port)) {
742
738
  return true;
743
739
  }
744
740
  await Bun.sleep(STARTUP_POLL_INTERVAL_MS);
745
741
  }
746
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;
747
752
  }, captureStreamOutput = (stream, output) => {
748
753
  if (!stream)
749
754
  return;
@@ -822,7 +827,7 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
822
827
  result.push(fullPath);
823
828
  }
824
829
  return result;
825
- }, readPackageVersion3 = (candidate) => {
830
+ }, readPackageVersion4 = (candidate) => {
826
831
  try {
827
832
  const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
828
833
  if (pkg.name !== "@absolutejs/absolute")
@@ -834,7 +839,7 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
834
839
  }
835
840
  }, resolvePackageVersion3 = (candidates) => {
836
841
  for (const candidate of candidates) {
837
- const version2 = readPackageVersion3(candidate);
842
+ const version2 = readPackageVersion4(candidate);
838
843
  if (version2)
839
844
  return version2;
840
845
  }
@@ -1348,15 +1353,11 @@ Found ${errorCount} error${suffix}.`;
1348
1353
  await mkdir2(cacheDir, { recursive: true });
1349
1354
  const checks = [];
1350
1355
  checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
1351
- if (hasSvelte) {
1352
- for (const svelteDir of svelteDirs) {
1353
- checks.push(buildSvelteCheck(cacheDir, svelteDir));
1354
- }
1356
+ for (const svelteDir of hasSvelte ? svelteDirs : []) {
1357
+ checks.push(buildSvelteCheck(cacheDir, svelteDir));
1355
1358
  }
1356
- if (hasAngular) {
1357
- for (const angularDir of angularDirs) {
1358
- checks.push(buildAngularCheck(cacheDir, angularDir));
1359
- }
1359
+ for (const angularDir of hasAngular ? angularDirs : []) {
1360
+ checks.push(buildAngularCheck(cacheDir, angularDir));
1360
1361
  }
1361
1362
  const results = await Promise.all(checks);
1362
1363
  const failed = results.filter((res) => res.exitCode !== 0);
@@ -1999,18 +2000,19 @@ var getAbsoluteVersion = () => {
1999
2000
  resolve4(import.meta.dir, "..", "..", "package.json"),
2000
2001
  resolve4(import.meta.dir, "..", "..", "..", "package.json")
2001
2002
  ];
2002
- for (const pkgPath of candidates) {
2003
- if (!existsSync6(pkgPath))
2004
- continue;
2005
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2006
- const ver = pkg.version;
2007
- return ver;
2008
- }
2003
+ const pkgPath = candidates.find((candidate) => existsSync6(candidate));
2004
+ if (pkgPath)
2005
+ return readPackageVersion(pkgPath);
2009
2006
  } catch {
2010
2007
  return getPackageVersion("@absolutejs/absolute");
2011
2008
  }
2012
2009
  return getPackageVersion("@absolutejs/absolute");
2013
2010
  };
2011
+ var readPackageVersion = (pkgPath) => {
2012
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2013
+ const ver = pkg.version;
2014
+ return ver;
2015
+ };
2014
2016
  var detectCI = () => {
2015
2017
  const { env: env2 } = process;
2016
2018
  if (env2.GITHUB_ACTIONS)
@@ -2258,14 +2260,14 @@ import { basename, resolve as resolve5 } from "path";
2258
2260
  var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
2259
2261
  var resolvePackageVersion = (candidates) => {
2260
2262
  for (const candidate of candidates) {
2261
- const version2 = readPackageVersion(candidate);
2263
+ const version2 = readPackageVersion2(candidate);
2262
2264
  if (version2) {
2263
2265
  return version2;
2264
2266
  }
2265
2267
  }
2266
2268
  return "";
2267
2269
  };
2268
- var readPackageVersion = (candidate) => {
2270
+ var readPackageVersion2 = (candidate) => {
2269
2271
  try {
2270
2272
  const pkg = JSON.parse(readFileSync7(candidate, "utf-8"));
2271
2273
  if (pkg.name !== "@absolutejs/absolute")
@@ -2659,54 +2661,49 @@ var appendRightEdge = (value, width, marker) => {
2659
2661
  }
2660
2662
  return `${padLine(value, Math.max(0, width - 1))}${marker}`;
2661
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
+ };
2662
2701
  var wrapText = (value, width) => {
2663
2702
  if (width <= 0) {
2664
2703
  return [""];
2665
2704
  }
2666
- const lines = [];
2667
- for (const rawLine of value.split(`
2668
- `)) {
2669
- const line = rawLine.trimEnd();
2670
- if (line.length === 0) {
2671
- lines.push("");
2672
- continue;
2673
- }
2674
- if (line.length <= width) {
2675
- lines.push(line);
2676
- continue;
2677
- }
2678
- const words = line.split(/\s+/);
2679
- let current = "";
2680
- for (const word of words) {
2681
- if (current.length === 0) {
2682
- if (word.length <= width) {
2683
- current = word;
2684
- continue;
2685
- }
2686
- for (let index = 0;index < word.length; index += width) {
2687
- lines.push(word.slice(index, index + width));
2688
- }
2689
- continue;
2690
- }
2691
- const next = `${current} ${word}`;
2692
- if (next.length <= width) {
2693
- current = next;
2694
- continue;
2695
- }
2696
- lines.push(current);
2697
- if (word.length <= width) {
2698
- current = word;
2699
- continue;
2700
- }
2701
- for (let index = 0;index < word.length; index += width) {
2702
- lines.push(word.slice(index, index + width));
2703
- }
2704
- current = "";
2705
- }
2706
- if (current.length > 0) {
2707
- lines.push(current);
2708
- }
2709
- }
2705
+ const lines = value.split(`
2706
+ `).flatMap((rawLine) => wrapLine(rawLine.trimEnd(), width));
2710
2707
  return lines.length > 0 ? lines : [""];
2711
2708
  };
2712
2709
  var formatTimestamp2 = () => new Date().toLocaleTimeString([], {
@@ -2811,7 +2808,7 @@ var createWorkspaceTui = ({
2811
2808
  renderTimer = setTimeout(() => {
2812
2809
  renderTimer = null;
2813
2810
  render();
2814
- }, 16);
2811
+ }, WORKSPACE_TUI_RENDER_DEBOUNCE_MS);
2815
2812
  };
2816
2813
  const clearPendingEscape = () => {
2817
2814
  if (!escapeTimer) {
@@ -2826,7 +2823,7 @@ var createWorkspaceTui = ({
2826
2823
  escapeTimer = null;
2827
2824
  escapeBuffer = "";
2828
2825
  exitEscapeMode();
2829
- }, 30);
2826
+ }, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS);
2830
2827
  };
2831
2828
  const resetPrompt = () => {
2832
2829
  promptBuffer = "";
@@ -2848,16 +2845,16 @@ var createWorkspaceTui = ({
2848
2845
  if (disposed) {
2849
2846
  return;
2850
2847
  }
2851
- const width = process.stdout.columns ?? 100;
2852
- 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;
2853
2850
  const servicesSnapshot = [...serviceStates.values()];
2854
2851
  const workspaceStatus = getWorkspaceStatus(servicesSnapshot);
2855
2852
  const statusLabel = workspaceStatus === "ready" && readyDurationMs !== null ? `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(readyDurationMs)}${colors.reset}` : `${colors.dim}${workspaceStatus}${colors.reset}`;
2856
2853
  const title = `${colors.cyan}${colors.bold}ABSOLUTEJS WORKSPACE${colors.reset} ${colors.dim}v${version2}${colors.reset} ${statusLabel}`;
2857
2854
  const divider = `${colors.dim}${"\u2500".repeat(Math.max(width, 1))}${colors.reset}`;
2858
- const serviceNameWidth = Math.max(7, ...servicesSnapshot.map((service) => service.name.length));
2859
- const visibilityWidth = 8;
2860
- 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;
2861
2858
  const rows = [];
2862
2859
  rows.push(padLine(title, width));
2863
2860
  rows.push(divider);
@@ -2865,21 +2862,21 @@ var createWorkspaceTui = ({
2865
2862
  for (const service of servicesSnapshot) {
2866
2863
  const stateColor = getStatusColor(service.status);
2867
2864
  const detail = service.detail ? ` \xB7 ${service.detail}` : "";
2868
- 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);
2869
2866
  const target = truncateText(`${getTargetLabel(service)}${detail}`, targetWidth);
2870
2867
  const targetColor = service.visibility === "public" && service.status !== "ready" ? colors.dim : colors.reset;
2871
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}`;
2872
2869
  rows.push(padLine(row, width));
2873
2870
  }
2874
2871
  rows.push(divider);
2875
- const footerLines = 3;
2872
+ const footerLines = WORKSPACE_TUI_FOOTER_LINE_COUNT;
2876
2873
  const fixedHeight = rows.length + footerLines;
2877
- const logHeight = Math.max(height - fixedHeight, 3);
2874
+ const logHeight = Math.max(height - fixedHeight, WORKSPACE_TUI_MIN_LOG_HEIGHT);
2878
2875
  const logWidth = Math.max(width - 1, 1);
2879
2876
  const contentLines = helpVisible ? helpLines : logEntries.flatMap((entry) => {
2880
2877
  const prefixPlain = `${entry.timestamp} [${entry.source}] `;
2881
2878
  const prefixColor = `${colors.dim}${entry.timestamp}${colors.reset} ${getSourceColor(entry.source)}[${entry.source}]${colors.reset} `;
2882
- 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));
2883
2880
  return wrapped.map((line, index) => {
2884
2881
  if (index === 0) {
2885
2882
  return `${prefixColor}${getLogColor(entry.level)}${line}${colors.reset}`;
@@ -2927,7 +2924,7 @@ var createWorkspaceTui = ({
2927
2924
  `);
2928
2925
  process.stdout.write(`\x1B[H${screen}`);
2929
2926
  if (shellMode) {
2930
- const promptColumn = Math.min(promptBuffer.length + 3, width);
2927
+ const promptColumn = Math.min(promptBuffer.length + WORKSPACE_TUI_PROMPT_CURSOR_OFFSET, width);
2931
2928
  const promptRow = Math.min(rows.length, height);
2932
2929
  process.stdout.write(`\x1B[${promptRow};${promptColumn}H\x1B[?25h`);
2933
2930
  return;
@@ -2971,7 +2968,7 @@ var createWorkspaceTui = ({
2971
2968
  logScrollOffset = 0;
2972
2969
  scheduleRender();
2973
2970
  };
2974
- 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));
2975
2972
  const getServiceSnapshot = () => [...serviceStates.values()].map((service) => ({
2976
2973
  detail: service.detail,
2977
2974
  name: service.name,
@@ -2983,21 +2980,35 @@ var createWorkspaceTui = ({
2983
2980
  if (!shellMode || shellHistory.length === 0) {
2984
2981
  return;
2985
2982
  }
2986
- if (direction === "up") {
2987
- if (shellHistoryIndex < shellHistory.length - 1) {
2988
- shellHistoryIndex++;
2989
- }
2990
- } else if (shellHistoryIndex <= 0) {
2983
+ if (direction === "up" && shellHistoryIndex < shellHistory.length - 1) {
2984
+ shellHistoryIndex++;
2985
+ }
2986
+ if (direction === "down" && shellHistoryIndex <= 0) {
2991
2987
  shellHistoryIndex = UNFOUND_INDEX;
2992
2988
  promptBuffer = "";
2993
2989
  scheduleRender();
2994
2990
  return;
2995
- } else {
2991
+ }
2992
+ if (direction === "down") {
2996
2993
  shellHistoryIndex--;
2997
2994
  }
2998
2995
  promptBuffer = shellHistoryIndex === UNFOUND_INDEX ? "" : shellHistory[shellHistory.length - 1 - shellHistoryIndex] ?? "";
2999
2996
  scheduleRender();
3000
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
+ };
3001
3012
  const scrollLogs = (direction) => {
3002
3013
  if (helpVisible) {
3003
3014
  return;
@@ -3052,47 +3063,27 @@ var createWorkspaceTui = ({
3052
3063
  return;
3053
3064
  }
3054
3065
  if (escapeBuffer === `${ESCAPE}[A`) {
3055
- clearPendingEscape();
3056
- escapeBuffer = "";
3057
- if (shellMode) {
3058
- navigateShellHistory("up");
3059
- } else {
3060
- scrollLogs("up");
3061
- }
3066
+ handleArrowEscape("up");
3062
3067
  return;
3063
3068
  }
3064
3069
  if (escapeBuffer === `${ESCAPE}[B`) {
3065
- clearPendingEscape();
3066
- escapeBuffer = "";
3067
- if (shellMode) {
3068
- navigateShellHistory("down");
3069
- } else {
3070
- scrollLogs("down");
3071
- }
3070
+ handleArrowEscape("down");
3072
3071
  return;
3073
3072
  }
3074
3073
  if (escapeBuffer === `${ESCAPE}[5~`) {
3075
- clearPendingEscape();
3076
- escapeBuffer = "";
3077
- scrollLogs("pageUp");
3074
+ handleScrollEscape("pageUp");
3078
3075
  return;
3079
3076
  }
3080
3077
  if (escapeBuffer === `${ESCAPE}[6~`) {
3081
- clearPendingEscape();
3082
- escapeBuffer = "";
3083
- scrollLogs("pageDown");
3078
+ handleScrollEscape("pageDown");
3084
3079
  return;
3085
3080
  }
3086
3081
  if (escapeBuffer === `${ESCAPE}[H` || escapeBuffer === `${ESCAPE}[1~`) {
3087
- clearPendingEscape();
3088
- escapeBuffer = "";
3089
- scrollLogs("home");
3082
+ handleScrollEscape("home");
3090
3083
  return;
3091
3084
  }
3092
3085
  if (escapeBuffer === `${ESCAPE}[F` || escapeBuffer === `${ESCAPE}[4~`) {
3093
- clearPendingEscape();
3094
- escapeBuffer = "";
3095
- scrollLogs("end");
3086
+ handleScrollEscape("end");
3096
3087
  return;
3097
3088
  }
3098
3089
  if (/^\x1b\[[0-9]*$/.test(escapeBuffer)) {
@@ -3101,6 +3092,40 @@ var createWorkspaceTui = ({
3101
3092
  }
3102
3093
  exitEscapeMode();
3103
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
+ };
3104
3129
  const handleChar = async (char) => {
3105
3130
  if (char === "\x03") {
3106
3131
  await actions.quit();
@@ -3116,42 +3141,18 @@ var createWorkspaceTui = ({
3116
3141
  return;
3117
3142
  }
3118
3143
  if (char === "\x7F" || char === "\b") {
3119
- if (!shellMode) {
3120
- return;
3121
- }
3122
- if (promptBuffer.length > 0) {
3123
- promptBuffer = promptBuffer.slice(0, UNFOUND_INDEX);
3124
- scheduleRender();
3125
- return;
3126
- }
3127
- resetPrompt();
3144
+ handleBackspace();
3128
3145
  return;
3129
3146
  }
3130
3147
  if (char === "\r" || char === `
3131
3148
  `) {
3132
- if (shellMode) {
3133
- await submitShellCommand();
3134
- }
3149
+ await handleEnter();
3135
3150
  return;
3136
3151
  }
3137
3152
  if (char.charCodeAt(0) < ASCII_SPACE) {
3138
3153
  return;
3139
3154
  }
3140
- if (!shellMode) {
3141
- if (char === "$") {
3142
- shellMode = true;
3143
- promptBuffer = "";
3144
- scheduleRender();
3145
- return;
3146
- }
3147
- const shortcut = SHORTCUTS2.get(char.toLowerCase());
3148
- if (shortcut) {
3149
- await runShortcut(shortcut);
3150
- }
3151
- return;
3152
- }
3153
- promptBuffer += char;
3154
- scheduleRender();
3155
+ await handlePrintableChar(char);
3155
3156
  };
3156
3157
  const onResize = () => {
3157
3158
  scheduleRender();
@@ -3176,6 +3177,16 @@ var createWorkspaceTui = ({
3176
3177
  process.stdout.on("resize", onResize);
3177
3178
  render();
3178
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
+ };
3179
3190
  const dispose = () => {
3180
3191
  if (disposed) {
3181
3192
  return;
@@ -3187,13 +3198,7 @@ var createWorkspaceTui = ({
3187
3198
  renderTimer = null;
3188
3199
  }
3189
3200
  process.stdout.off("resize", onResize);
3190
- if (input) {
3191
- input.off("data", onData);
3192
- setRawMode(false);
3193
- if (input !== process.stdin) {
3194
- input.destroy();
3195
- }
3196
- }
3201
+ disposeInput();
3197
3202
  process.stdout.write("\x1B[?25h\x1B[?1049l");
3198
3203
  };
3199
3204
  return {
@@ -3217,11 +3222,7 @@ var sanitizeLogFileName = (value) => value.replace(/[^a-zA-Z0-9._-]/g, "_") || "
3217
3222
  var createWorkspaceLogSink = (appendLog) => {
3218
3223
  const logDirectory = resolve6(".absolutejs", "workspace", "logs");
3219
3224
  mkdirSync4(logDirectory, { recursive: true });
3220
- for (const file of readdirSync(logDirectory)) {
3221
- if (file.endsWith(".log")) {
3222
- unlinkSync(resolve6(logDirectory, file));
3223
- }
3224
- }
3225
+ readdirSync(logDirectory).filter((file) => file.endsWith(".log")).forEach((file) => unlinkSync(resolve6(logDirectory, file)));
3225
3226
  writeFileSync2(resolve6(logDirectory, "all.log"), "");
3226
3227
  writeFileSync2(resolve6(logDirectory, "workspace.log"), "");
3227
3228
  const initializedSources = new Set(["workspace"]);
@@ -3249,13 +3250,13 @@ var createWorkspaceLogSink = (appendLog) => {
3249
3250
  }
3250
3251
  };
3251
3252
  };
3252
- var readPackageVersion2 = (candidate) => {
3253
+ var readPackageVersion3 = (candidate) => {
3253
3254
  try {
3254
3255
  const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
3255
3256
  if (pkg.name !== "@absolutejs/absolute") {
3256
3257
  return null;
3257
3258
  }
3258
- const version2 = pkg.version;
3259
+ const { version: version2 } = pkg;
3259
3260
  return version2;
3260
3261
  } catch {
3261
3262
  return null;
@@ -3268,7 +3269,7 @@ var resolvePackageVersion2 = () => {
3268
3269
  resolve6(import.meta.dir, "..", "..", "..", "..", "package.json")
3269
3270
  ];
3270
3271
  for (const candidate of candidates) {
3271
- const version2 = readPackageVersion2(candidate);
3272
+ const version2 = readPackageVersion3(candidate);
3272
3273
  if (version2) {
3273
3274
  return version2;
3274
3275
  }
@@ -3290,7 +3291,7 @@ var getDefaultReadyConfig = (service) => {
3290
3291
  }
3291
3292
  return;
3292
3293
  };
3293
- var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? 200];
3294
+ var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? HTTP_STATUS_OK];
3294
3295
  var resolveServiceHttpUrl = (service, path) => {
3295
3296
  if (!path.startsWith("/")) {
3296
3297
  throw new Error(`ready path must start with "/" for service probes. Received "${path}".`);
@@ -3307,20 +3308,20 @@ var resolveHttpReadyProbe = (service, ready) => {
3307
3308
  type: "http",
3308
3309
  url: resolveServiceHttpUrl(service, ready),
3309
3310
  method: "GET",
3310
- expectStatus: [200],
3311
+ expectStatus: [HTTP_STATUS_OK],
3311
3312
  headers: {},
3312
- intervalMs: 250,
3313
- timeoutMs: 30000
3313
+ intervalMs: WORKSPACE_READY_PROBE_INTERVAL_MS,
3314
+ timeoutMs: WORKSPACE_READY_TIMEOUT_MS
3314
3315
  };
3315
3316
  }
3316
3317
  return {
3317
- type: "http",
3318
- url: ready,
3319
- method: "GET",
3320
- expectStatus: [200],
3318
+ expectStatus: [HTTP_STATUS_OK],
3321
3319
  headers: {},
3322
- intervalMs: 250,
3323
- timeoutMs: 30000
3320
+ intervalMs: WORKSPACE_READY_PROBE_INTERVAL_MS,
3321
+ method: "GET",
3322
+ timeoutMs: WORKSPACE_READY_TIMEOUT_MS,
3323
+ type: "http",
3324
+ url: ready
3324
3325
  };
3325
3326
  }
3326
3327
  if (ready.path && ready.url) {
@@ -3336,8 +3337,8 @@ var resolveHttpReadyProbe = (service, ready) => {
3336
3337
  method: ready.method ?? "GET",
3337
3338
  expectStatus: normalizeExpectedStatuses(ready.expectStatus),
3338
3339
  headers: ready.headers ?? {},
3339
- intervalMs: ready.intervalMs ?? 250,
3340
- timeoutMs: ready.timeoutMs ?? 30000
3340
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3341
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3341
3342
  };
3342
3343
  };
3343
3344
  var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig(service)) => {
@@ -3352,22 +3353,22 @@ var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig
3352
3353
  type: "tcp",
3353
3354
  host: ready.host ?? getServicePublicHost(service),
3354
3355
  port: ready.port,
3355
- intervalMs: ready.intervalMs ?? 250,
3356
- timeoutMs: ready.timeoutMs ?? 30000
3356
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3357
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3357
3358
  };
3358
3359
  }
3359
3360
  if (ready.type === "command") {
3360
3361
  return {
3361
- type: "command",
3362
3362
  command: ready.command,
3363
- intervalMs: ready.intervalMs ?? 250,
3364
- timeoutMs: ready.timeoutMs ?? 30000
3363
+ intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3364
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS,
3365
+ type: "command"
3365
3366
  };
3366
3367
  }
3367
3368
  if (ready.type === "delay") {
3368
3369
  return {
3369
- type: "delay",
3370
- ms: ready.ms
3370
+ ms: ready.ms,
3371
+ type: "delay"
3371
3372
  };
3372
3373
  }
3373
3374
  return resolveHttpReadyProbe(service, ready);
@@ -3376,7 +3377,7 @@ var probeHttpReady = async (ready) => {
3376
3377
  const response = await fetch(ready.url, {
3377
3378
  method: ready.method,
3378
3379
  headers: ready.headers,
3379
- signal: AbortSignal.timeout(Math.min(ready.timeoutMs, 5000))
3380
+ signal: AbortSignal.timeout(Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS))
3380
3381
  });
3381
3382
  return ready.expectStatus.includes(response.status);
3382
3383
  };
@@ -3388,7 +3389,7 @@ var probeTcpReady = async (ready) => new Promise((resolveProbe) => {
3388
3389
  const timeout = setTimeout(() => {
3389
3390
  socket.destroy();
3390
3391
  resolveProbe(false);
3391
- }, Math.min(ready.timeoutMs, 5000));
3392
+ }, Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS));
3392
3393
  socket.once("connect", () => {
3393
3394
  clearTimeout(timeout);
3394
3395
  socket.end();
@@ -3412,7 +3413,7 @@ var probeCommandReady = async (ready, service) => {
3412
3413
  try {
3413
3414
  processHandle.kill();
3414
3415
  } catch {}
3415
- }, Math.min(ready.timeoutMs, 5000));
3416
+ }, Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS));
3416
3417
  try {
3417
3418
  const exitCode = await processHandle.exited;
3418
3419
  return exitCode === 0;
@@ -3431,16 +3432,24 @@ var waitForReady = async (service) => {
3431
3432
  }
3432
3433
  const startedAt = Date.now();
3433
3434
  while (Date.now() - startedAt < resolved.timeoutMs) {
3434
- try {
3435
- const isReady = resolved.type === "http" ? await probeHttpReady(resolved) : resolved.type === "tcp" ? await probeTcpReady(resolved) : await probeCommandReady(resolved, service);
3436
- if (isReady) {
3437
- return;
3438
- }
3439
- } catch {}
3435
+ if (await probeReady(resolved, service)) {
3436
+ return;
3437
+ }
3440
3438
  await sleep(resolved.intervalMs);
3441
3439
  }
3442
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(" ")})`);
3443
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
+ };
3444
3453
  var resolveShutdownHook = (shutdown) => {
3445
3454
  if (!shutdown) {
3446
3455
  return null;
@@ -3448,12 +3457,12 @@ var resolveShutdownHook = (shutdown) => {
3448
3457
  if (Array.isArray(shutdown)) {
3449
3458
  return {
3450
3459
  command: shutdown,
3451
- timeoutMs: 1e4
3460
+ timeoutMs: WORKSPACE_SHUTDOWN_TIMEOUT_MS
3452
3461
  };
3453
3462
  }
3454
3463
  return {
3455
3464
  command: shutdown.command,
3456
- timeoutMs: shutdown.timeoutMs ?? 1e4
3465
+ timeoutMs: shutdown.timeoutMs ?? WORKSPACE_SHUTDOWN_TIMEOUT_MS
3457
3466
  };
3458
3467
  };
3459
3468
  var runShutdownHook = async (service, onLog) => {
@@ -3477,11 +3486,7 @@ var runShutdownHook = async (service, onLog) => {
3477
3486
  }, hook.timeoutMs);
3478
3487
  try {
3479
3488
  const exitCode = await processHandle.exited;
3480
- if (exitCode === 0) {
3481
- onLog("workspace", `${service.name} shutdown hook finished.`, "success");
3482
- return;
3483
- }
3484
- 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");
3485
3490
  } finally {
3486
3491
  clearTimeout(timeout);
3487
3492
  }
@@ -3521,36 +3526,38 @@ var pipeProcessLogs = (name, processHandle, appendLog) => {
3521
3526
  const forward = async (stream, level) => {
3522
3527
  let buffer = "";
3523
3528
  const reader = stream.getReader();
3524
- try {
3525
- while (true) {
3526
- const { done, value } = await reader.read();
3527
- if (done) {
3528
- break;
3529
- }
3530
- if (!value) {
3531
- continue;
3532
- }
3533
- buffer += Buffer.from(value).toString();
3534
- const lines = buffer.split(`
3535
- `);
3536
- buffer = lines.pop() ?? "";
3537
- for (const line of lines) {
3538
- if (line.trim().length === 0) {
3539
- continue;
3540
- }
3541
- appendLog(name, line, level);
3542
- }
3543
- }
3544
- } finally {
3545
- if (buffer.trim().length > 0) {
3546
- appendLog(name, buffer, level);
3547
- }
3548
- 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);
3549
3533
  }
3534
+ appendRemainingLogBuffer(buffer, name, level, appendLog);
3535
+ reader.releaseLock();
3550
3536
  };
3551
3537
  forward(processHandle.stdout, "info");
3552
3538
  forward(processHandle.stderr, "error");
3553
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
+ };
3554
3561
  var getServicePublicHost = (service) => {
3555
3562
  const host = service.env?.HOST ?? process.env.HOST ?? "localhost";
3556
3563
  if (host === "0.0.0.0" || host === "::") {
@@ -3561,35 +3568,37 @@ var getServicePublicHost = (service) => {
3561
3568
  var getServiceProtocol = (service) => service.env?.ABSOLUTE_HTTPS === "true" || process.env.ABSOLUTE_HTTPS === "true" ? "https" : "http";
3562
3569
  var createWorkspaceServiceEnv = (services) => {
3563
3570
  const workspaceEnv = {};
3564
- for (const [name, service] of Object.entries(services)) {
3565
- if (!service.port) {
3566
- continue;
3567
- }
3571
+ for (const [name, service] of Object.entries(services).filter(([, service2]) => Boolean(service2.port))) {
3568
3572
  const envKey = `ABSOLUTE_SERVICE_${name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_URL`;
3569
3573
  workspaceEnv[envKey] = `${getServiceProtocol(service)}://${getServicePublicHost(service)}:${service.port}`;
3570
3574
  }
3571
3575
  return workspaceEnv;
3572
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
+ };
3573
3587
  var resolveService = (name, service, workspaceEnv, options) => {
3574
3588
  const cwd = resolve6(service.cwd ?? ".");
3575
- const envVars = {
3576
- ...process.env,
3577
- ...workspaceEnv,
3578
- ...service.env,
3589
+ const envVars = Object.assign(getDefinedProcessEnv(), workspaceEnv, service.env, {
3579
3590
  ABSOLUTE_WORKSPACE_MANAGED: "1",
3580
3591
  ABSOLUTE_WORKSPACE_SERVICE_NAME: name,
3581
3592
  ABSOLUTE_WORKSPACE_SERVICE_VISIBILITY: getVisibility(service),
3582
3593
  FORCE_COLOR: "1",
3583
3594
  NODE_ENV: "development"
3584
- };
3595
+ });
3585
3596
  if (service.port && !envVars.PORT) {
3586
3597
  envVars.PORT = String(service.port);
3587
3598
  }
3588
3599
  if (isAbsoluteService(service)) {
3589
- const configPath2 = service.config ? resolve6(cwd, service.config) : options.configPath ? resolve6(options.configPath) : process.env.ABSOLUTE_CONFIG ? resolve6(process.env.ABSOLUTE_CONFIG) : undefined;
3590
- if (configPath2) {
3591
- envVars.ABSOLUTE_CONFIG = configPath2;
3592
- }
3600
+ const configPath2 = resolveAbsoluteServiceConfigPath(service, cwd, options);
3601
+ Object.assign(envVars, configPath2 ? { ABSOLUTE_CONFIG: configPath2 } : {});
3593
3602
  const command = [
3594
3603
  process.execPath,
3595
3604
  "--hot",
@@ -3656,30 +3665,43 @@ var workspace = async (subcommand, options) => {
3656
3665
  });
3657
3666
  const workspaceLogs = createWorkspaceLogSink(tui.addLog);
3658
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
+ };
3659
3681
  const killProcesses = async () => {
3660
3682
  const snapshot = [...running];
3661
3683
  running.length = 0;
3662
- for (const service of snapshot) {
3663
- try {
3664
- service.process.kill();
3665
- } catch {}
3666
- }
3684
+ snapshot.forEach((service) => killProcess(service));
3667
3685
  await Promise.all(snapshot.map((service) => service.process.exited));
3668
3686
  for (const service of snapshot.reverse()) {
3669
- try {
3670
- await runShutdownHook(service.resolved, addLog);
3671
- } catch (error) {
3672
- const message = error instanceof Error ? error.message : String(error);
3673
- addLog("workspace", `${service.name} shutdown hook failed: ${message}`, "warn");
3674
- }
3687
+ await runShutdownHookSafely(service);
3675
3688
  }
3676
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
+ };
3677
3699
  const printFailureSummary = (exitCode) => {
3678
3700
  const servicesSnapshot = tui.getServiceSnapshot();
3679
- const recentLogs = tui.getRecentLogs(60);
3701
+ const recentLogs = tui.getRecentLogs(WORKSPACE_FAILURE_RECENT_LOG_LIMIT);
3680
3702
  const failedServices = servicesSnapshot.filter((service) => service.status === "error");
3681
3703
  const relevantLogs = recentLogs.filter((entry) => entry.level === "error" || entry.level === "warn" || entry.source === "workspace" || failedServices.some((service) => service.name === entry.source));
3682
- const logsToPrint = (relevantLogs.length > 0 ? relevantLogs : recentLogs).slice(-30);
3704
+ const logsToPrint = (relevantLogs.length > 0 ? relevantLogs : recentLogs).slice(-WORKSPACE_FAILURE_LOG_PRINT_LIMIT);
3683
3705
  const lines = [
3684
3706
  "",
3685
3707
  `\x1B[31mABSOLUTEJS WORKSPACE exited with code ${exitCode}\x1B[0m`,
@@ -3690,12 +3712,7 @@ var workspace = async (subcommand, options) => {
3690
3712
  return ` - ${service.name}: ${service.status} \xB7 ${service.target}${detail}`;
3691
3713
  })
3692
3714
  ];
3693
- if (logsToPrint.length > 0) {
3694
- lines.push("", "Recent logs:");
3695
- for (const entry of logsToPrint) {
3696
- lines.push(` ${entry.timestamp} [${entry.source}] ${entry.message}`);
3697
- }
3698
- }
3715
+ appendRecentLogs(lines, logsToPrint);
3699
3716
  lines.push("");
3700
3717
  process.stderr.write(`${lines.join(`
3701
3718
  `)}
@@ -3710,6 +3727,34 @@ var workspace = async (subcommand, options) => {
3710
3727
  process.kill(processHandle.pid, signal);
3711
3728
  } catch {}
3712
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
+ };
3713
3758
  const shutdown = async (exitCode = 0) => {
3714
3759
  if (shuttingDown) {
3715
3760
  return;
@@ -3721,10 +3766,7 @@ var workspace = async (subcommand, options) => {
3721
3766
  printFailureSummary(exitCode);
3722
3767
  }
3723
3768
  if (paused) {
3724
- for (const service of running) {
3725
- sendSignalToService(service.process, "SIGCONT");
3726
- }
3727
- paused = false;
3769
+ resumeRunningServices();
3728
3770
  }
3729
3771
  await killProcesses();
3730
3772
  process.exit(exitCode);
@@ -3738,11 +3780,7 @@ var workspace = async (subcommand, options) => {
3738
3780
  }
3739
3781
  const resolved = resolveService(name, service, workspaceEnv, options);
3740
3782
  const port = (resolved.service.port ?? Number(resolved.env.PORT ?? "")) || DEFAULT_PORT;
3741
- if (port > 0) {
3742
- killStaleProcesses(port, (message) => {
3743
- addLog("workspace", message, "warn");
3744
- });
3745
- }
3783
+ killStaleServicePort(port);
3746
3784
  if (isAbsoluteService(resolved.service) && resolved.configPath && !existsSync8(resolved.configPath)) {
3747
3785
  throw new Error(`${name} references missing config "${resolved.configPath}"`);
3748
3786
  }
@@ -3788,10 +3826,7 @@ var workspace = async (subcommand, options) => {
3788
3826
  }
3789
3827
  restarting = true;
3790
3828
  if (paused) {
3791
- for (const service of running) {
3792
- sendSignalToService(service.process, "SIGCONT");
3793
- }
3794
- paused = false;
3829
+ resumeRunningServices();
3795
3830
  }
3796
3831
  addLog("workspace", "Restarting workspace...", "info");
3797
3832
  readyServiceNames.clear();
@@ -3806,20 +3841,12 @@ var workspace = async (subcommand, options) => {
3806
3841
  };
3807
3842
  const togglePause = () => {
3808
3843
  if (paused) {
3809
- for (const service of running) {
3810
- sendSignalToService(service.process, "SIGCONT");
3811
- readyServiceNames.add(service.name);
3812
- tui.setServiceStatus(service.name, "ready");
3813
- }
3844
+ resumeRunningServices();
3845
+ markRunningServicesReady();
3814
3846
  paused = false;
3815
3847
  addLog("workspace", "Workspace resumed.", "success");
3816
3848
  } else {
3817
- for (const service of running) {
3818
- sendSignalToService(service.process, "SIGSTOP");
3819
- readyServiceNames.delete(service.name);
3820
- tui.setServiceStatus(service.name, "paused");
3821
- }
3822
- paused = true;
3849
+ pauseRunningServices();
3823
3850
  addLog("workspace", "Workspace paused.", "warn");
3824
3851
  }
3825
3852
  };
@@ -3885,7 +3912,7 @@ init_telemetryEvent();
3885
3912
  init_constants();
3886
3913
  init_utils();
3887
3914
  var [command] = process.argv.slice(2);
3888
- var [workspaceCommand] = process.argv.slice(3);
3915
+ var [workspaceCommand] = process.argv.slice(WORKSPACE_COMMAND_ARGS_OFFSET);
3889
3916
  var args = process.argv.slice(CLI_ARGS_OFFSET);
3890
3917
  var parseNamedArg = (flag) => {
3891
3918
  const idx = args.indexOf(flag);