@teamscala/service-boot 0.23.470 → 0.23.472
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 +1 -1
- package/src/mount-orpc-router.ts +41 -0
package/package.json
CHANGED
package/src/mount-orpc-router.ts
CHANGED
|
@@ -40,6 +40,36 @@ export async function mountOrpcRouter(
|
|
|
40
40
|
);
|
|
41
41
|
const schemaPath = join(process.cwd(), "prisma/schema.prisma");
|
|
42
42
|
|
|
43
|
+
// ORPC forward mode: when SCALA_ORPC_FORWARD_URL is set and there is no
|
|
44
|
+
// local bundle, /api/rpc/* proxies to the central ORPC surface instead of
|
|
45
|
+
// mounting a per-service router. This is the collapse's consolidation path —
|
|
46
|
+
// services shed their local bundle and the central surface serves all
|
|
47
|
+
// row-composed procedures.
|
|
48
|
+
const forwardUrl = process.env.SCALA_ORPC_FORWARD_URL;
|
|
49
|
+
if (!await Bun.file(routerPath).exists() && forwardUrl) {
|
|
50
|
+
const base = forwardUrl.replace(/\/+$/, "");
|
|
51
|
+
app.all("/api/rpc/*", async (c) => {
|
|
52
|
+
const url = new URL(c.req.url);
|
|
53
|
+
const target = `${base}${url.pathname}${url.search}`;
|
|
54
|
+
const res = await fetch(target, {
|
|
55
|
+
method: c.req.method,
|
|
56
|
+
headers: c.req.raw.headers,
|
|
57
|
+
body: c.req.raw.body,
|
|
58
|
+
// @ts-ignore — duplex is required for streaming bodies in Bun
|
|
59
|
+
duplex: "half",
|
|
60
|
+
});
|
|
61
|
+
return new Response(res.body, {
|
|
62
|
+
status: res.status,
|
|
63
|
+
statusText: res.statusText,
|
|
64
|
+
headers: res.headers,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
log.info(
|
|
68
|
+
`[scala-install:${serviceSlug}] ORPC forward mounted at /api/rpc/* → ${base}`,
|
|
69
|
+
);
|
|
70
|
+
return { routeCount: 0 };
|
|
71
|
+
}
|
|
72
|
+
|
|
43
73
|
// No generated ORPC router is the EXPECTED state for daemon-only / boot-hook
|
|
44
74
|
// services (session-picker, dep-sync, etc.) that own no DB schema or
|
|
45
75
|
// api_routes — they serve no /api/rpc surface. Skip silently: importing a
|
|
@@ -204,6 +234,17 @@ export async function mountOrpcRouter(
|
|
|
204
234
|
context: { prisma, db8, headers: c.req.raw.headers, resolveUser },
|
|
205
235
|
});
|
|
206
236
|
if (!result?.matched) return c.notFound();
|
|
237
|
+
// ORPC swallows procedure errors into a 500 response with a
|
|
238
|
+
// generic INTERNAL_SERVER_ERROR body. Without logging here the
|
|
239
|
+
// only signal is a fleet-wide spike in 500s with no traceback —
|
|
240
|
+
// measured 2026-09-04 when the regenerated prisma client shipped
|
|
241
|
+
// an empty orm and every model action returned a silent 500.
|
|
242
|
+
// This makes every 500 LOUD with the request path for routing.
|
|
243
|
+
if (result.response.status >= 500) {
|
|
244
|
+
log.error(
|
|
245
|
+
`[ORPC] ${c.req.method} ${c.req.path} → ${result.response.status}`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
207
248
|
return result.response;
|
|
208
249
|
},
|
|
209
250
|
);
|