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

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 (41) hide show
  1. package/dist/angular/browser.js +6 -4
  2. package/dist/angular/browser.js.map +3 -3
  3. package/dist/angular/components/constants.js +1 -0
  4. package/dist/angular/index.js +17 -10
  5. package/dist/angular/index.js.map +5 -5
  6. package/dist/angular/server.js +17 -10
  7. package/dist/angular/server.js.map +5 -5
  8. package/dist/build.js +126 -66
  9. package/dist/build.js.map +12 -12
  10. package/dist/cli/index.js +186 -126
  11. package/dist/dev/client/cssUtils.ts +47 -47
  12. package/dist/dev/client/handlers/angular.ts +40 -19
  13. package/dist/dev/client/handlers/angularRuntime.ts +28 -8
  14. package/dist/dev/client/handlers/html.ts +6 -5
  15. package/dist/dev/client/handlers/htmx.ts +8 -2
  16. package/dist/dev/client/handlers/svelte.ts +16 -14
  17. package/dist/dev/client/hmrClient.ts +25 -3
  18. package/dist/dev/client/reactRefreshSetup.ts +2 -3
  19. package/dist/index.js +151 -79
  20. package/dist/index.js.map +14 -14
  21. package/dist/islands/index.js +4 -4
  22. package/dist/islands/index.js.map +4 -4
  23. package/dist/react/index.js +17 -10
  24. package/dist/react/index.js.map +5 -5
  25. package/dist/react/server.js +15 -8
  26. package/dist/react/server.js.map +4 -4
  27. package/dist/src/angular/components/constants.d.ts +1 -0
  28. package/dist/src/build/buildAngularVendor.d.ts +3 -4
  29. package/dist/src/constants.d.ts +1 -0
  30. package/dist/src/utils/imageProcessing.d.ts +1 -1
  31. package/dist/src/vue/components/Image.d.ts +1 -1
  32. package/dist/svelte/index.js +17 -10
  33. package/dist/svelte/index.js.map +5 -5
  34. package/dist/svelte/server.js +15 -8
  35. package/dist/svelte/server.js.map +4 -4
  36. package/dist/types/globals.d.ts +1 -0
  37. package/dist/vue/index.js +17 -10
  38. package/dist/vue/index.js.map +5 -5
  39. package/dist/vue/server.js +15 -8
  40. package/dist/vue/server.js.map +4 -4
  41. package/package.json +1 -1
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, 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;
21
+ var ANSI_ESCAPE_CODE = 27, 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;
@@ -853,12 +853,15 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
853
853
  return null;
854
854
  }
855
855
  }, resolveBuildModule2 = async (candidates) => {
856
- for (const candidate of candidates) {
857
- const mod = await tryImportBuild2(candidate);
858
- if (mod)
859
- return mod;
856
+ const [candidate, ...remaining] = candidates;
857
+ if (!candidate) {
858
+ return;
860
859
  }
861
- return;
860
+ const mod = await tryImportBuild2(candidate);
861
+ if (mod) {
862
+ return mod;
863
+ }
864
+ return resolveBuildModule2(remaining);
862
865
  }, resolveJsxDevRuntimeCompatPath2 = () => {
863
866
  const candidates = [
864
867
  resolve7(import.meta.dir, "..", "..", "dist", "react", "jsxDevRuntimeCompat.js"),
@@ -1203,7 +1206,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
1203
1206
  }, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
1204
1207
  const local = resolve8("node_modules", ".bin", name);
1205
1208
  return existsSync10(local) ? local : null;
1206
- }, stripAnsi3 = (str) => str.replace(/\x1b\[[0-9;]*m/g, ""), formatSvelteOutput = (output) => {
1209
+ }, ANSI_COLOR_REGEX, ANSI_PURPLE_REGEX, ANSI_CYAN_REGEX, ANSI_TOKEN_END_REGEX, stripAnsi3 = (str) => str.replace(ANSI_COLOR_REGEX, ""), formatSvelteOutput = (output) => {
1207
1210
  const cwd = `${process.cwd()}/`;
1208
1211
  const summaryMatch = stripAnsi3(output).match(/svelte-check found (\d+) error/);
1209
1212
  const errorCount = summaryMatch ? parseInt(summaryMatch[1] ?? "0", 10) : 0;
@@ -1220,10 +1223,10 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
1220
1223
  `\x1B[96m${pathMatch[1]}\x1B[0m:\x1B[93m${pathMatch[2]}\x1B[0m`
1221
1224
  ];
1222
1225
  }
1223
- if (result.includes("\x1B[35m")) {
1226
+ if (result.includes(ANSI_PURPLE_REGEX)) {
1224
1227
  const plainLine = stripAnsi3(result);
1225
- const before = stripAnsi3(result.split("\x1B[35m")[0] ?? "");
1226
- const token = stripAnsi3((result.split("\x1B[35m")[1] ?? "").split(/\x1b\[3[69]m/)[0] ?? "");
1228
+ const before = stripAnsi3(result.split(ANSI_PURPLE_REGEX)[0] ?? "");
1229
+ const token = stripAnsi3((result.split(ANSI_PURPLE_REGEX)[1] ?? "").split(ANSI_TOKEN_END_REGEX)[0] ?? "");
1227
1230
  if (!token)
1228
1231
  return [result];
1229
1232
  const expanded = before.replace(/\t/g, " ");
@@ -1234,7 +1237,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
1234
1237
  `${" ".repeat(expanded.length)}\x1B[91m${underline}\x1B[0m`
1235
1238
  ];
1236
1239
  }
1237
- if (/^\x1b\[36m|\t/.test(result) && !result.includes("Error") && !result.includes("\x1B[35m")) {
1240
+ if (ANSI_CYAN_REGEX.test(result) && !result.includes("Error") && !result.includes(ANSI_PURPLE_REGEX)) {
1238
1241
  return [];
1239
1242
  }
1240
1243
  return [result];
@@ -1377,6 +1380,11 @@ Found ${errorCount} error${suffix}.`;
1377
1380
  };
1378
1381
  var init_typecheck = __esm(() => {
1379
1382
  init_loadConfig();
1383
+ init_constants();
1384
+ ANSI_COLOR_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-9;]*m`, "g");
1385
+ ANSI_PURPLE_REGEX = `${String.fromCharCode(ANSI_ESCAPE_CODE)}[35m`;
1386
+ ANSI_CYAN_REGEX = new RegExp(`^${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[36m|\\t`);
1387
+ ANSI_TOKEN_END_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[3[69]m`);
1380
1388
  TYPECHECK_EXCLUDE = [
1381
1389
  "../node_modules/**/*",
1382
1390
  "../**/.absolutejs/**/*",
@@ -1431,7 +1439,10 @@ var trySetRawMode = () => {
1431
1439
  } catch {
1432
1440
  return null;
1433
1441
  }
1434
- return process.stdin;
1442
+ return {
1443
+ destroyOnDispose: false,
1444
+ stream: process.stdin
1445
+ };
1435
1446
  };
1436
1447
  var openTtyStream = () => {
1437
1448
  const fromStdin = trySetRawMode();
@@ -1441,7 +1452,10 @@ var openTtyStream = () => {
1441
1452
  try {
1442
1453
  const ttyStream = new ReadStream(openSync("/dev/tty", "r"));
1443
1454
  ttyStream.setRawMode(true);
1444
- return ttyStream;
1455
+ return {
1456
+ destroyOnDispose: true,
1457
+ stream: ttyStream
1458
+ };
1445
1459
  } catch {
1446
1460
  return null;
1447
1461
  }
@@ -1633,19 +1647,19 @@ var createInteractiveHandler = (actions) => {
1633
1647
  processChar(str.charAt(idx));
1634
1648
  }
1635
1649
  };
1636
- const ttyStream = openTtyStream();
1637
- const input = ttyStream ?? process.stdin;
1650
+ const ttyInput = openTtyStream();
1651
+ const input = ttyInput?.stream ?? process.stdin;
1638
1652
  input.resume();
1639
1653
  input.on("data", onData);
1640
1654
  const disposeTtyStream = () => {
1641
- if (!ttyStream) {
1655
+ if (!ttyInput) {
1642
1656
  return;
1643
1657
  }
1644
1658
  try {
1645
- ttyStream.setRawMode(false);
1659
+ ttyInput.stream.setRawMode?.(false);
1646
1660
  } catch {}
1647
- if (ttyStream !== process.stdin) {
1648
- ttyStream.destroy();
1661
+ if (ttyInput.destroyOnDispose) {
1662
+ ttyInput.stream.destroy?.();
1649
1663
  }
1650
1664
  };
1651
1665
  const dispose = () => {
@@ -2196,9 +2210,7 @@ var runTool = async (adapter, args) => {
2196
2210
  batches.push(changed.slice(idx, idx + MAX_FILES_PER_BATCH));
2197
2211
  }
2198
2212
  const failedFiles = new Set;
2199
- for (const batch of batches) {
2200
- await runBatch(adapter, batch, args, failedFiles);
2201
- }
2213
+ await batches.reduce((chain, batch) => chain.then(() => runBatch(adapter, batch, args, failedFiles)), Promise.resolve());
2202
2214
  for (const file of failedFiles) {
2203
2215
  delete cache.files[file];
2204
2216
  }
@@ -2278,13 +2290,15 @@ var readPackageVersion2 = (candidate) => {
2278
2290
  return null;
2279
2291
  };
2280
2292
  var resolveBuildModule = async (candidates) => {
2281
- for (const candidate of candidates) {
2282
- const mod = await tryImportBuild(candidate);
2283
- if (mod) {
2284
- return mod;
2285
- }
2293
+ const [candidate, ...remaining] = candidates;
2294
+ if (!candidate) {
2295
+ return;
2286
2296
  }
2287
- return;
2297
+ const mod = await tryImportBuild(candidate);
2298
+ if (mod) {
2299
+ return mod;
2300
+ }
2301
+ return resolveBuildModule(remaining);
2288
2302
  };
2289
2303
  var tryImportBuild = async (candidate) => {
2290
2304
  try {
@@ -2574,7 +2588,8 @@ import { openSync as openSync2 } from "fs";
2574
2588
  import { ReadStream as ReadStream2 } from "tty";
2575
2589
  var MAX_LOG_ENTRIES = 400;
2576
2590
  var ESCAPE = "\x1B";
2577
- var ANSI_REGEX = /\x1B\[[0-?]*[ -/]*[@-~]/g;
2591
+ var ANSI_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
2592
+ var ANSI_ESCAPE_PREFIX = `${ESCAPE}[`;
2578
2593
  var SHORTCUTS2 = new Map([
2579
2594
  ["c", "clear"],
2580
2595
  ["h", "help"],
@@ -2766,6 +2781,17 @@ var getWorkspaceStatus = (services) => {
2766
2781
  }
2767
2782
  return "ready";
2768
2783
  };
2784
+ var getVisibleLogContent = (contentLines, logHeight, logScrollOffset) => {
2785
+ const end = Math.max(0, contentLines.length - logScrollOffset);
2786
+ const start2 = Math.max(0, end - logHeight);
2787
+ return contentLines.slice(start2, end);
2788
+ };
2789
+ var isPartialEscapeSequence = (value) => {
2790
+ if (!value.startsWith(ANSI_ESCAPE_PREFIX)) {
2791
+ return false;
2792
+ }
2793
+ return Array.from(value.slice(ANSI_ESCAPE_PREFIX.length)).every((char) => char >= "0" && char <= "9");
2794
+ };
2769
2795
  var createWorkspaceTui = ({
2770
2796
  actions,
2771
2797
  services,
@@ -2889,11 +2915,7 @@ var createWorkspaceTui = ({
2889
2915
  lastLogViewportHeight = logHeight;
2890
2916
  logScrollOffset = Math.min(logScrollOffset, Math.max(0, contentLines.length - logHeight));
2891
2917
  }
2892
- const visibleContent = helpVisible ? contentLines.slice(0, logHeight) : (() => {
2893
- const end = Math.max(0, contentLines.length - logScrollOffset);
2894
- const start3 = Math.max(0, end - logHeight);
2895
- return contentLines.slice(start3, end);
2896
- })();
2918
+ const visibleContent = helpVisible ? contentLines.slice(0, logHeight) : getVisibleLogContent(contentLines, logHeight, logScrollOffset);
2897
2919
  const maxLogScrollOffset = !helpVisible ? Math.max(0, contentLines.length - logHeight) : 0;
2898
2920
  const shouldShowScrollbar = !helpVisible && maxLogScrollOffset > 0;
2899
2921
  const scrollbarThumbHeight = shouldShowScrollbar ? Math.max(1, Math.min(logHeight, Math.round(logHeight / contentLines.length * logHeight))) : 0;
@@ -3086,7 +3108,7 @@ var createWorkspaceTui = ({
3086
3108
  handleScrollEscape("end");
3087
3109
  return;
3088
3110
  }
3089
- if (/^\x1b\[[0-9]*$/.test(escapeBuffer)) {
3111
+ if (isPartialEscapeSequence(escapeBuffer)) {
3090
3112
  armEscapeTimer();
3091
3113
  return;
3092
3114
  }
@@ -3154,16 +3176,15 @@ var createWorkspaceTui = ({
3154
3176
  }
3155
3177
  await handlePrintableChar(char);
3156
3178
  };
3179
+ const processInputChars = async (chars) => {
3180
+ await Array.from(chars).reduce((chain, char) => chain.then(() => handleChar(char)), Promise.resolve());
3181
+ };
3157
3182
  const onResize = () => {
3158
3183
  scheduleRender();
3159
3184
  };
3160
3185
  const onData = (chunk) => {
3161
3186
  const chars = chunk.toString();
3162
- (async () => {
3163
- for (const char of chars) {
3164
- await handleChar(char);
3165
- }
3166
- })();
3187
+ processInputChars(chars);
3167
3188
  };
3168
3189
  const start2 = () => {
3169
3190
  process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
@@ -3215,8 +3236,8 @@ var createWorkspaceTui = ({
3215
3236
 
3216
3237
  // src/cli/scripts/workspace.ts
3217
3238
  init_utils();
3218
- var ANSI_REGEX2 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
3219
- var sleep = (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
3239
+ var ANSI_REGEX2 = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
3240
+ var sleep = (durationMs) => Bun.sleep(durationMs);
3220
3241
  var stripAnsi2 = (value) => value.replace(ANSI_REGEX2, "");
3221
3242
  var sanitizeLogFileName = (value) => value.replace(/[^a-zA-Z0-9._-]/g, "_") || "unknown";
3222
3243
  var createWorkspaceLogSink = (appendLog) => {
@@ -3243,11 +3264,11 @@ var createWorkspaceLogSink = (appendLog) => {
3243
3264
  appendFileSync(resolve6(logDirectory, "all.log"), line);
3244
3265
  };
3245
3266
  return {
3246
- logDirectory,
3247
3267
  appendLog: (source, message, level = "info") => {
3248
3268
  writeLog(source, message, level);
3249
3269
  appendLog(source, message, level);
3250
- }
3270
+ },
3271
+ logDirectory
3251
3272
  };
3252
3273
  };
3253
3274
  var readPackageVersion3 = (candidate) => {
@@ -3292,6 +3313,7 @@ var getDefaultReadyConfig = (service) => {
3292
3313
  return;
3293
3314
  };
3294
3315
  var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? HTTP_STATUS_OK];
3316
+ var runSequentially = (items, action) => items.reduce((chain, item) => chain.then(() => action(item)), Promise.resolve());
3295
3317
  var resolveServiceHttpUrl = (service, path) => {
3296
3318
  if (!path.startsWith("/")) {
3297
3319
  throw new Error(`ready path must start with "/" for service probes. Received "${path}".`);
@@ -3303,17 +3325,18 @@ var resolveServiceHttpUrl = (service, path) => {
3303
3325
  };
3304
3326
  var resolveHttpReadyProbe = (service, ready) => {
3305
3327
  if (typeof ready === "string") {
3306
- if (isAbsoluteService(service)) {
3328
+ if (!isAbsoluteService(service)) {
3307
3329
  return {
3308
- type: "http",
3309
- url: resolveServiceHttpUrl(service, ready),
3310
- method: "GET",
3311
3330
  expectStatus: [HTTP_STATUS_OK],
3312
3331
  headers: {},
3313
3332
  intervalMs: WORKSPACE_READY_PROBE_INTERVAL_MS,
3314
- timeoutMs: WORKSPACE_READY_TIMEOUT_MS
3333
+ method: "GET",
3334
+ timeoutMs: WORKSPACE_READY_TIMEOUT_MS,
3335
+ type: "http",
3336
+ url: ready
3315
3337
  };
3316
3338
  }
3339
+ const url2 = resolveServiceHttpUrl(service, ready);
3317
3340
  return {
3318
3341
  expectStatus: [HTTP_STATUS_OK],
3319
3342
  headers: {},
@@ -3321,26 +3344,38 @@ var resolveHttpReadyProbe = (service, ready) => {
3321
3344
  method: "GET",
3322
3345
  timeoutMs: WORKSPACE_READY_TIMEOUT_MS,
3323
3346
  type: "http",
3324
- url: ready
3347
+ url: url2
3325
3348
  };
3326
3349
  }
3327
3350
  if (ready.path && ready.url) {
3328
3351
  throw new Error('ready HTTP probe cannot define both "path" and "url".');
3329
3352
  }
3330
- const url = ready.path ? resolveServiceHttpUrl(service, ready.path) : ready.url ? ready.url : isAbsoluteService(service) ? resolveServiceHttpUrl(service, "/hmr-status") : null;
3353
+ const url = resolveHttpReadyProbeUrl(service, ready);
3331
3354
  if (!url) {
3332
3355
  throw new Error('ready HTTP probe requires either "url" or "path".');
3333
3356
  }
3334
3357
  return {
3335
- type: "http",
3336
- url,
3337
- method: ready.method ?? "GET",
3338
3358
  expectStatus: normalizeExpectedStatuses(ready.expectStatus),
3339
3359
  headers: ready.headers ?? {},
3340
3360
  intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3341
- timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3361
+ method: ready.method ?? "GET",
3362
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS,
3363
+ type: "http",
3364
+ url
3342
3365
  };
3343
3366
  };
3367
+ var resolveHttpReadyProbeUrl = (service, ready) => {
3368
+ if (ready.path) {
3369
+ return resolveServiceHttpUrl(service, ready.path);
3370
+ }
3371
+ if (ready.url) {
3372
+ return ready.url;
3373
+ }
3374
+ if (isAbsoluteService(service)) {
3375
+ return resolveServiceHttpUrl(service, "/hmr-status");
3376
+ }
3377
+ return null;
3378
+ };
3344
3379
  var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig(service)) => {
3345
3380
  if (ready === false || !ready) {
3346
3381
  return null;
@@ -3350,11 +3385,11 @@ var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig
3350
3385
  }
3351
3386
  if (ready.type === "tcp") {
3352
3387
  return {
3353
- type: "tcp",
3354
3388
  host: ready.host ?? getServicePublicHost(service),
3355
- port: ready.port,
3356
3389
  intervalMs: ready.intervalMs ?? WORKSPACE_READY_PROBE_INTERVAL_MS,
3357
- timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS
3390
+ port: ready.port,
3391
+ timeoutMs: ready.timeoutMs ?? WORKSPACE_READY_TIMEOUT_MS,
3392
+ type: "tcp"
3358
3393
  };
3359
3394
  }
3360
3395
  if (ready.type === "command") {
@@ -3374,14 +3409,16 @@ var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig
3374
3409
  return resolveHttpReadyProbe(service, ready);
3375
3410
  };
3376
3411
  var probeHttpReady = async (ready) => {
3412
+ const signal = AbortSignal.timeout(Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS));
3377
3413
  const response = await fetch(ready.url, {
3378
- method: ready.method,
3379
3414
  headers: ready.headers,
3380
- signal: AbortSignal.timeout(Math.min(ready.timeoutMs, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS))
3415
+ method: ready.method,
3416
+ signal
3381
3417
  });
3382
3418
  return ready.expectStatus.includes(response.status);
3383
3419
  };
3384
- var probeTcpReady = async (ready) => new Promise((resolveProbe) => {
3420
+ var probeTcpReady = async (ready) => {
3421
+ const { promise, resolve: resolveProbe } = Promise.withResolvers();
3385
3422
  const socket = createConnection({
3386
3423
  host: ready.host,
3387
3424
  port: ready.port
@@ -3400,7 +3437,8 @@ var probeTcpReady = async (ready) => new Promise((resolveProbe) => {
3400
3437
  socket.destroy();
3401
3438
  resolveProbe(false);
3402
3439
  });
3403
- });
3440
+ return promise;
3441
+ };
3404
3442
  var probeCommandReady = async (ready, service) => {
3405
3443
  const processHandle = Bun.spawn(ready.command, {
3406
3444
  cwd: service.cwd,
@@ -3430,14 +3468,30 @@ var waitForReady = async (service) => {
3430
3468
  await sleep(resolved.ms);
3431
3469
  return;
3432
3470
  }
3433
- const startedAt = Date.now();
3434
- while (Date.now() - startedAt < resolved.timeoutMs) {
3435
- if (await probeReady(resolved, service)) {
3436
- return;
3437
- }
3438
- await sleep(resolved.intervalMs);
3471
+ const isReady = await pollReady(resolved, service, Date.now());
3472
+ if (isReady) {
3473
+ return;
3474
+ }
3475
+ throw new Error(formatReadyTimeoutMessage(resolved));
3476
+ };
3477
+ var pollReady = async (resolved, service, startedAt) => {
3478
+ if (Date.now() - startedAt >= resolved.timeoutMs) {
3479
+ return false;
3480
+ }
3481
+ if (await probeReady(resolved, service)) {
3482
+ return true;
3483
+ }
3484
+ await sleep(resolved.intervalMs);
3485
+ return pollReady(resolved, service, startedAt);
3486
+ };
3487
+ var formatReadyTimeoutMessage = (resolved) => {
3488
+ if (resolved.type === "http") {
3489
+ return `service did not become ready within ${resolved.timeoutMs}ms (${resolved.url})`;
3490
+ }
3491
+ if (resolved.type === "tcp") {
3492
+ return `service did not become ready within ${resolved.timeoutMs}ms (tcp://${resolved.host}:${resolved.port})`;
3439
3493
  }
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(" ")})`);
3494
+ return `service did not become ready within ${resolved.timeoutMs}ms (${resolved.command.join(" ")})`;
3441
3495
  };
3442
3496
  var probeReady = async (resolved, service) => {
3443
3497
  try {
@@ -3526,13 +3580,17 @@ var pipeProcessLogs = (name, processHandle, appendLog) => {
3526
3580
  const forward = async (stream, level) => {
3527
3581
  let buffer = "";
3528
3582
  const reader = stream.getReader();
3529
- let chunk = await readLogChunk(reader);
3530
- while (chunk !== null) {
3583
+ const forwardNextChunk = async () => {
3584
+ const chunk = await readLogChunk(reader);
3585
+ if (chunk === null) {
3586
+ appendRemainingLogBuffer(buffer, name, level, appendLog);
3587
+ reader.releaseLock();
3588
+ return;
3589
+ }
3531
3590
  buffer = appendLogChunk(buffer, chunk, name, level, appendLog);
3532
- chunk = await readLogChunk(reader);
3533
- }
3534
- appendRemainingLogBuffer(buffer, name, level, appendLog);
3535
- reader.releaseLock();
3591
+ await forwardNextChunk();
3592
+ };
3593
+ await forwardNextChunk();
3536
3594
  };
3537
3595
  forward(processHandle.stdout, "info");
3538
3596
  forward(processHandle.stderr, "error");
@@ -3568,7 +3626,7 @@ var getServicePublicHost = (service) => {
3568
3626
  var getServiceProtocol = (service) => service.env?.ABSOLUTE_HTTPS === "true" || process.env.ABSOLUTE_HTTPS === "true" ? "https" : "http";
3569
3627
  var createWorkspaceServiceEnv = (services) => {
3570
3628
  const workspaceEnv = {};
3571
- for (const [name, service] of Object.entries(services).filter(([, service2]) => Boolean(service2.port))) {
3629
+ for (const [name, service] of Object.entries(services).filter(([, filteredService]) => Boolean(filteredService.port))) {
3572
3630
  const envKey = `ABSOLUTE_SERVICE_${name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_URL`;
3573
3631
  workspaceEnv[envKey] = `${getServiceProtocol(service)}://${getServicePublicHost(service)}:${service.port}`;
3574
3632
  }
@@ -3683,9 +3741,7 @@ var workspace = async (subcommand, options) => {
3683
3741
  running.length = 0;
3684
3742
  snapshot.forEach((service) => killProcess(service));
3685
3743
  await Promise.all(snapshot.map((service) => service.process.exited));
3686
- for (const service of snapshot.reverse()) {
3687
- await runShutdownHookSafely(service);
3688
- }
3744
+ await runSequentially(snapshot.reverse(), runShutdownHookSafely);
3689
3745
  };
3690
3746
  const appendRecentLogs = (lines, logsToPrint) => {
3691
3747
  if (logsToPrint.length === 0) {
@@ -3771,54 +3827,58 @@ var workspace = async (subcommand, options) => {
3771
3827
  await killProcesses();
3772
3828
  process.exit(exitCode);
3773
3829
  };
3830
+ const handleServiceExit = (runningService, exitCode) => {
3831
+ if (shuttingDown || restarting) {
3832
+ return;
3833
+ }
3834
+ if (!running.includes(runningService)) {
3835
+ return;
3836
+ }
3837
+ const serviceName = runningService.name;
3838
+ const normalizedExitCode = exitCode || 1;
3839
+ tui.setServiceStatus(serviceName, "error", `exit code ${normalizedExitCode}`);
3840
+ readyServiceNames.delete(serviceName);
3841
+ addLog("workspace", `${serviceName} exited with code ${normalizedExitCode}. Shutting down workspace.`, "error");
3842
+ shutdown(normalizedExitCode);
3843
+ };
3844
+ const startService = async (name) => {
3845
+ const service = services[name];
3846
+ if (!service) {
3847
+ throw new Error(`services is missing "${name}"`);
3848
+ }
3849
+ const resolved = resolveService(name, service, workspaceEnv, options);
3850
+ const port = (resolved.service.port ?? Number(resolved.env.PORT ?? "")) || DEFAULT_PORT;
3851
+ killStaleServicePort(port);
3852
+ if (isAbsoluteService(resolved.service) && resolved.configPath && !existsSync8(resolved.configPath)) {
3853
+ throw new Error(`${name} references missing config "${resolved.configPath}"`);
3854
+ }
3855
+ serviceBootStartedAt.set(name, performance.now());
3856
+ readyServiceNames.delete(name);
3857
+ tui.setServiceStatus(name, restarting ? "restarting" : "starting");
3858
+ const processHandle = Bun.spawn(resolved.command, {
3859
+ cwd: resolved.cwd,
3860
+ env: resolved.env,
3861
+ stderr: "pipe",
3862
+ stdin: "ignore",
3863
+ stdout: "pipe"
3864
+ });
3865
+ pipeProcessLogs(name, processHandle, addLog);
3866
+ const runningService = {
3867
+ name,
3868
+ process: processHandle,
3869
+ resolved
3870
+ };
3871
+ running.push(runningService);
3872
+ processHandle.exited.then(handleServiceExit.bind(null, runningService));
3873
+ await waitForReady(resolved);
3874
+ const startedAt = serviceBootStartedAt.get(name);
3875
+ const readyDuration = typeof startedAt === "number" ? `ready in ${getDurationString(performance.now() - startedAt)}` : undefined;
3876
+ readyServiceNames.add(name);
3877
+ tui.setServiceStatus(name, "ready", readyDuration);
3878
+ };
3774
3879
  const startServices = async () => {
3775
3880
  tui.setReadyDuration(null);
3776
- for (const name of orderedNames) {
3777
- const service = services[name];
3778
- if (!service) {
3779
- throw new Error(`services is missing "${name}"`);
3780
- }
3781
- const resolved = resolveService(name, service, workspaceEnv, options);
3782
- const port = (resolved.service.port ?? Number(resolved.env.PORT ?? "")) || DEFAULT_PORT;
3783
- killStaleServicePort(port);
3784
- if (isAbsoluteService(resolved.service) && resolved.configPath && !existsSync8(resolved.configPath)) {
3785
- throw new Error(`${name} references missing config "${resolved.configPath}"`);
3786
- }
3787
- serviceBootStartedAt.set(name, performance.now());
3788
- readyServiceNames.delete(name);
3789
- tui.setServiceStatus(name, restarting ? "restarting" : "starting");
3790
- const processHandle = Bun.spawn(resolved.command, {
3791
- cwd: resolved.cwd,
3792
- env: resolved.env,
3793
- stderr: "pipe",
3794
- stdin: "ignore",
3795
- stdout: "pipe"
3796
- });
3797
- pipeProcessLogs(name, processHandle, addLog);
3798
- const runningService = {
3799
- name,
3800
- process: processHandle,
3801
- resolved
3802
- };
3803
- running.push(runningService);
3804
- processHandle.exited.then((exitCode) => {
3805
- if (shuttingDown || restarting) {
3806
- return;
3807
- }
3808
- if (!running.includes(runningService)) {
3809
- return;
3810
- }
3811
- tui.setServiceStatus(name, "error", `exit code ${exitCode || 1}`);
3812
- readyServiceNames.delete(name);
3813
- addLog("workspace", `${name} exited with code ${exitCode || 1}. Shutting down workspace.`, "error");
3814
- shutdown(exitCode || 1);
3815
- });
3816
- await waitForReady(resolved);
3817
- const startedAt = serviceBootStartedAt.get(name);
3818
- const readyDuration = typeof startedAt === "number" ? `ready in ${getDurationString(performance.now() - startedAt)}` : undefined;
3819
- readyServiceNames.add(name);
3820
- tui.setServiceStatus(name, "ready", readyDuration);
3821
- }
3881
+ await runSequentially(orderedNames, startService);
3822
3882
  };
3823
3883
  const restartWorkspace = async () => {
3824
3884
  if (shuttingDown || restarting) {
@@ -3903,7 +3963,7 @@ var workspace = async (subcommand, options) => {
3903
3963
  tui.start();
3904
3964
  await startServices();
3905
3965
  tui.setReadyDuration(performance.now() - workspaceBootStartedAt);
3906
- await new Promise(() => {});
3966
+ await Promise.withResolvers().promise;
3907
3967
  };
3908
3968
 
3909
3969
  // src/cli/index.ts
@@ -179,61 +179,61 @@ export const reloadCSSStylesheets = (manifest: Record<string, string>) => {
179
179
  });
180
180
  };
181
181
 
182
- const createCSSLoadPromise = (linkElement: HTMLLinkElement, newHref: string) =>
183
- // eslint-disable-next-line promise/avoid-new
184
- new Promise<void>((resolve) => {
185
- let resolved = false;
186
- const doResolve = function () {
187
- if (resolved) return;
188
- resolved = true;
189
- resolve();
190
- };
182
+ const createCSSLoadPromise = (linkElement: HTMLLinkElement, newHref: string) => {
183
+ const { promise, resolve } = Promise.withResolvers<void>();
184
+ let resolved = false;
185
+ const doResolve = function () {
186
+ if (resolved) return;
187
+ resolved = true;
188
+ resolve();
189
+ };
191
190
 
192
- const verifyCSSOM = function () {
193
- try {
194
- const sheets = Array.from(document.styleSheets);
195
-
196
- return sheets.some(
197
- (sheet) =>
198
- sheet.href &&
199
- sheet.href.includes(newHref.split('?')[0] ?? '')
200
- );
201
- } catch {
202
- return false;
203
- }
204
- };
191
+ const verifyCSSOM = function () {
192
+ try {
193
+ const sheets = Array.from(document.styleSheets);
205
194
 
206
- linkElement.onload = function () {
207
- let checkCount = 0;
208
- const checkCSSOM = function () {
209
- checkCount++;
210
- if (verifyCSSOM() || checkCount > CSS_MAX_CHECK_ATTEMPTS) {
211
- doResolve();
212
- } else {
213
- requestAnimationFrame(checkCSSOM);
214
- }
215
- };
216
- requestAnimationFrame(checkCSSOM);
217
- };
195
+ return sheets.some(
196
+ (sheet) =>
197
+ sheet.href && sheet.href.includes(newHref.split('?')[0] ?? '')
198
+ );
199
+ } catch {
200
+ return false;
201
+ }
202
+ };
218
203
 
219
- linkElement.onerror = function () {
220
- setTimeout(() => {
204
+ linkElement.onload = function () {
205
+ let checkCount = 0;
206
+ const checkCSSOM = function () {
207
+ checkCount++;
208
+ if (verifyCSSOM() || checkCount > CSS_MAX_CHECK_ATTEMPTS) {
221
209
  doResolve();
222
- }, CSS_ERROR_RESOLVE_DELAY_MS);
210
+ } else {
211
+ requestAnimationFrame(checkCSSOM);
212
+ }
223
213
  };
214
+ requestAnimationFrame(checkCSSOM);
215
+ };
224
216
 
217
+ linkElement.onerror = function () {
225
218
  setTimeout(() => {
226
- if (linkElement.sheet && !resolved) {
227
- doResolve();
228
- }
229
- }, CSS_SHEET_READY_TIMEOUT_MS);
219
+ doResolve();
220
+ }, CSS_ERROR_RESOLVE_DELAY_MS);
221
+ };
230
222
 
231
- setTimeout(() => {
232
- if (!resolved) {
233
- doResolve();
234
- }
235
- }, CSS_MAX_PARSE_TIMEOUT_MS);
236
- });
223
+ setTimeout(() => {
224
+ if (linkElement.sheet && !resolved) {
225
+ doResolve();
226
+ }
227
+ }, CSS_SHEET_READY_TIMEOUT_MS);
228
+
229
+ setTimeout(() => {
230
+ if (!resolved) {
231
+ doResolve();
232
+ }
233
+ }, CSS_MAX_PARSE_TIMEOUT_MS);
234
+
235
+ return promise;
236
+ };
237
237
 
238
238
  const removeLinks = (linksToRemove: HTMLLinkElement[]) => {
239
239
  linksToRemove.forEach((link) => {