@stacksjs/buddy 0.70.111 → 0.70.113

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.
@@ -208,25 +208,34 @@ export async function startDevelopmentServer(_options, _startTime) {
208
208
  return;
209
209
  isExiting = !0;
210
210
  closeNativeApp?.();
211
- unregisterRpxProxies(activeRpxRegistryIds);
212
- activeRpxRegistryIds.length = 0;
213
- try {
214
- process.kill(-process.pid, "SIGTERM");
215
- } catch {
211
+ const rpxTeardown = (async () => {
216
212
  try {
217
- process.kill(0, "SIGTERM");
213
+ await unregisterRpxProxies(activeRpxRegistryIds);
214
+ activeRpxRegistryIds.length = 0;
215
+ if (hasCustomDomain && domain)
216
+ await removeStalePublicDomainOverrides(domain, includeDashboard, options.verbose ?? !1);
218
217
  } catch {}
219
- }
220
- setTimeout(() => {
218
+ })(), teardownDeadline = new Promise((resolve) => setTimeout(resolve, SHUTDOWN_GRACE_MS));
219
+ Promise.race([rpxTeardown, teardownDeadline]).finally(() => {
221
220
  try {
222
- process.kill(0, "SIGKILL");
221
+ process.kill(-process.pid, "SIGTERM");
223
222
  } catch {
224
- process.exit(1);
223
+ try {
224
+ process.kill(0, "SIGTERM");
225
+ } catch {}
225
226
  }
226
- }, SHUTDOWN_GRACE_MS).unref();
227
+ setTimeout(() => {
228
+ try {
229
+ process.kill(0, "SIGKILL");
230
+ } catch {
231
+ process.exit(1);
232
+ }
233
+ }, SHUTDOWN_GRACE_MS).unref();
234
+ });
227
235
  };
228
236
  process.on("SIGINT", cleanup);
229
237
  process.on("SIGTERM", cleanup);
238
+ process.on("SIGHUP", cleanup);
230
239
  const quietOpts = { ...options, quiet: !0 }, a = await actions(), ports = [
231
240
  { name: "Frontend", port: frontendPort },
232
241
  { name: "API", port: apiPort }
@@ -922,6 +931,7 @@ async function registerRpxProxiesForDomain(input) {
922
931
  to: proxy.to,
923
932
  cwd: process.cwd(),
924
933
  createdAt,
934
+ pid: process.pid,
925
935
  pathRewrites: proxy.pathRewrites
926
936
  }, void 0, verbose);
927
937
  if (!activeRpxRegistryIds.includes(proxy.id))
@@ -218,6 +218,91 @@ export function doctor(buddy) {
218
218
  message: `Could not audit storage config: ${err instanceof Error ? err.message : String(err)}`
219
219
  });
220
220
  }
221
+ try {
222
+ if (process.platform !== "darwin")
223
+ checks.push({ name: "Dev domains (rpx)", status: "pass", message: "Not macOS (skipped)" });
224
+ else {
225
+ const fs = await import("node:fs"), os = await import("node:os"), path = await import("node:path"), isAlive = (pid) => {
226
+ try {
227
+ process.kill(pid, 0);
228
+ return !0;
229
+ } catch (err) {
230
+ return err.code === "EPERM";
231
+ }
232
+ }, registryDir = path.join(os.homedir(), ".stacks", "rpx", "registry.d"), registered = new Set, deadRegistryFiles = [];
233
+ if (fs.existsSync(registryDir))
234
+ for (const file of fs.readdirSync(registryDir)) {
235
+ if (!file.endsWith(".json"))
236
+ continue;
237
+ try {
238
+ const entry = JSON.parse(fs.readFileSync(path.join(registryDir, file), "utf8"));
239
+ if (entry.to && (entry.pid === void 0 || isAlive(entry.pid)))
240
+ registered.add(entry.to.toLowerCase());
241
+ if (typeof entry.pid === "number" && !isAlive(entry.pid))
242
+ deadRegistryFiles.push(file);
243
+ } catch {}
244
+ }
245
+ const staleHosts = new Set, staleResolvers = [], hostsPath = "/etc/hosts", hostsLines = fs.existsSync(hostsPath) ? fs.readFileSync(hostsPath, "utf8").split(`
246
+ `) : [];
247
+ for (let i = 0;i < hostsLines.length; i++) {
248
+ const line = hostsLines[i];
249
+ if (line.trim() === "# Added by rpx") {
250
+ for (let j = i + 1;j < hostsLines.length; j++) {
251
+ const blockLine = hostsLines[j].trim();
252
+ if (blockLine === "" || blockLine.startsWith("#"))
253
+ break;
254
+ const names = blockLine.split("#")[0]?.trim().split(/\s+/).slice(1) ?? [];
255
+ for (const name of names)
256
+ if (!registered.has(name.toLowerCase()))
257
+ staleHosts.add(name);
258
+ }
259
+ continue;
260
+ }
261
+ const hash = line.indexOf("#");
262
+ if (hash === -1)
263
+ continue;
264
+ const marker = /^rpx(?::pid=(\d+))?$/.exec(line.slice(hash + 1).trim());
265
+ if (!marker)
266
+ continue;
267
+ const names = line.slice(0, hash).trim().split(/\s+/).slice(1), pid = marker[1] ? Number.parseInt(marker[1], 10) : null;
268
+ if (pid !== null ? !isAlive(pid) : names.every((n) => !registered.has(n.toLowerCase())))
269
+ for (const name of names)
270
+ staleHosts.add(name);
271
+ }
272
+ const resolverDir = "/etc/resolver";
273
+ if (fs.existsSync(resolverDir))
274
+ for (const file of fs.readdirSync(resolverDir))
275
+ try {
276
+ const content = fs.readFileSync(path.join(resolverDir, file), "utf8");
277
+ if (!content.includes("127.0.0.1") || !content.includes("15353"))
278
+ continue;
279
+ const domain = file.toLowerCase();
280
+ if (![...registered].some((host) => host === domain || host.endsWith(`.${domain}`)))
281
+ staleResolvers.push(file);
282
+ } catch {}
283
+ if (staleHosts.size > 0 || staleResolvers.length > 0 || deadRegistryFiles.length > 0) {
284
+ const parts = [];
285
+ if (staleHosts.size > 0)
286
+ parts.push(`hosts(${[...staleHosts].join(", ")})`);
287
+ if (staleResolvers.length > 0)
288
+ parts.push(`resolver(${staleResolvers.join(", ")})`);
289
+ if (deadRegistryFiles.length > 0)
290
+ parts.push(`registry(${deadRegistryFiles.join(", ")})`);
291
+ checks.push({
292
+ name: "Dev domains (rpx)",
293
+ status: "warn",
294
+ message: `Stale loopback overrides from dead dev sessions: ${parts.join(" ")}. These keep pointing the domain at 127.0.0.1. Remove with: sudo nano /etc/hosts; sudo rm /etc/resolver/<name>; rm ~/.stacks/rpx/registry.d/<file>. Updating @stacksjs/rpx lets the daemon sweep pid-stamped entries automatically.`
295
+ });
296
+ } else
297
+ checks.push({ name: "Dev domains (rpx)", status: "pass", message: "No stale dev-domain overrides" });
298
+ }
299
+ } catch (err) {
300
+ checks.push({
301
+ name: "Dev domains (rpx)",
302
+ status: "warn",
303
+ message: `Could not audit dev-domain overrides: ${err instanceof Error ? err.message : String(err)}`
304
+ });
305
+ }
221
306
  try {
222
307
  const orphans = [];
223
308
  for (const name of FEATURE_NAMES) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.111",
5
+ "version": "0.70.113",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -124,7 +124,7 @@
124
124
  "@stacksjs/payments": "^0.70.23",
125
125
  "@stacksjs/realtime": "^0.70.23",
126
126
  "@stacksjs/router": "^0.70.23",
127
- "@stacksjs/rpx": "^0.11.13",
127
+ "@stacksjs/rpx": "^0.11.29",
128
128
  "@stacksjs/search-engine": "^0.70.23",
129
129
  "@stacksjs/security": "^0.70.23",
130
130
  "@stacksjs/server": "^0.70.23",