@teamscala/service-boot 0.23.295 → 0.23.297

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.
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "name": "@teamscala/service-boot",
8
8
  "type": "module",
9
- "version": "0.23.295",
9
+ "version": "0.23.297",
10
10
  "sideEffects": false,
11
11
  "files": [
12
12
  "src"
@@ -97,7 +97,10 @@ async function scalaToolsCurrencyWatch(_ctx: { config: ScheduledJobConfig }): Pr
97
97
  );
98
98
  const install = await spawn({
99
99
  name: "scala-tools-currency-watch:deps-install",
100
- command: ["scala-tools", "deps", "install", "--force-resolve"],
100
+ // A plain install fetches the current version, which is the whole point
101
+ // of this watch. Re-resolving the entire tree is an emergency hatch, not
102
+ // a currency step (operator ruling 2026-07-31).
103
+ command: ["scala-tools", "deps", "install"],
101
104
  cwd: SENTINEL_DIR,
102
105
  timeoutMs: 300_000,
103
106
  });
@@ -14,6 +14,8 @@ import { join } from "node:path";
14
14
  import type { Hono } from "hono";
15
15
  import { etag } from "hono/etag";
16
16
  import { buildErrorReport } from "@teamscala/os/errors/error-report";
17
+ import { collectHeapReport } from "@teamscala/os/observability/heap-report";
18
+ import { isLoopbackHost } from "@teamscala/os/observability/loopback-host";
17
19
  import { handleAssetProxy } from "./asset-proxy";
18
20
  import { resolveBrandFaviconPath, warmBrandFaviconCache } from "./resolve-brand-favicon";
19
21
 
@@ -37,41 +39,20 @@ export async function registerStaticAssets(
37
39
  }),
38
40
  );
39
41
 
40
- // Localhost-gated heap-attribution probe — restores the object-composition
41
- // data the removed /_healthz surface exposed: the heapSize-vs-RSS split
42
- // (JS heap vs native — OTel/Prisma-engine/buffers/compiled code) plus the
43
- // top object types by count. Read-only, no secrets; gated to a localhost
44
- // Host so it never answers over the public domain. Registered here (before
45
- // auth + page routes) so it is reachable on every service kind, like
46
- // /health (`verify-via-127.0.0.1`, sibling of `runtime-metrics-via-railway-cli`).
42
+ // Localhost-gated heap-attribution probe — the heapSize-vs-RSS split (JS heap
43
+ // vs native: OTel/Prisma-engine/buffers/compiled code) plus the top object
44
+ // types by count. Read-only, no secrets; gated to a localhost Host so it never
45
+ // answers over the public domain. Registered here (before auth + page routes)
46
+ // so it is reachable on every service kind, like /health
47
+ // (`verify-via-127.0.0.1`, sibling of `runtime-metrics-via-railway-cli`).
48
+ //
49
+ // The payload shape lives in @teamscala/os so standalone `Bun.serve` daemons
50
+ // (scala-guard) answer this probe identically — `scala-tools heap <slug>` must
51
+ // not mean different things depending on which process replied.
47
52
  app.get("/_heap", async (c) => {
48
- const host = c.req.header("host") ?? "";
49
- const isLocal =
50
- host.startsWith("127.0.0.1") || host.startsWith("localhost") || host.startsWith("[::1]");
51
- if (!isLocal) return c.json({ error: "localhost only" }, 403);
53
+ if (!isLoopbackHost(c.req.header("host"))) return c.json({ error: "localhost only" }, 403);
52
54
  try {
53
- const { heapStats } = await import("bun:jsc");
54
- const s = heapStats() as unknown as {
55
- heapSize: number;
56
- heapCapacity: number;
57
- objectCount: number;
58
- objectTypeCounts?: Record<string, number>;
59
- };
60
- const rss = process.memoryUsage.rss();
61
- const topObjectTypes = Object.fromEntries(
62
- Object.entries(s.objectTypeCounts ?? {})
63
- .sort((a, b) => b[1] - a[1])
64
- .slice(0, 40),
65
- );
66
- return c.json({
67
- service: serviceSlug,
68
- rss,
69
- heapSize: s.heapSize,
70
- heapCapacity: s.heapCapacity,
71
- objectCount: s.objectCount,
72
- nativeApprox: Math.max(0, rss - s.heapSize),
73
- topObjectTypes,
74
- });
55
+ return c.json(await collectHeapReport(serviceSlug));
75
56
  } catch (err) {
76
57
  const report = buildErrorReport(err, { service: serviceSlug, url: c.req.url });
77
58
  return new Response(JSON.stringify(report.http.body), {
@@ -113,7 +113,11 @@ async function checkCurrency(): Promise<{ healthy: boolean; details?: Record<str
113
113
  });
114
114
  const result = await spawn({
115
115
  name: "scala-tools-currency-repair",
116
- command: ["node_modules/.bin/scala-tools", "deps", "install", "--dir", cwd, "--force-resolve"],
116
+ // A stale installed version needs the current one fetched, which a plain
117
+ // install does. Re-resolving the whole tree to achieve that is an emergency
118
+ // escape hatch, not a currency step, and running it automatically burns host
119
+ // resources for no gain (operator ruling 2026-07-31).
120
+ command: ["node_modules/.bin/scala-tools", "deps", "install", "--dir", cwd],
117
121
  cwd,
118
122
  timeoutMs: cfg.command_timeout_ms,
119
123
  captureStderr: true,