@nwire/koa 0.12.0 → 0.12.1
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/dist/http-koa.js +34 -2
- package/package.json +7 -7
package/dist/http-koa.js
CHANGED
|
@@ -35,6 +35,24 @@ import bodyParser from "koa-bodyparser";
|
|
|
35
35
|
import cors from "@koa/cors";
|
|
36
36
|
import helmet from "koa-helmet";
|
|
37
37
|
import KoaRouter from "@koa/router";
|
|
38
|
+
/** Zod throws a `ZodError` (name + `issues[]`) on a failed `.parse()`. */
|
|
39
|
+
function isZodError(e) {
|
|
40
|
+
return (typeof e === "object" &&
|
|
41
|
+
e !== null &&
|
|
42
|
+
e.name === "ZodError" &&
|
|
43
|
+
Array.isArray(e.issues));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A human-readable summary of the first validation issue. Safe to expose:
|
|
47
|
+
* it describes the caller's own input, not internal state.
|
|
48
|
+
*/
|
|
49
|
+
function zodSummary(e) {
|
|
50
|
+
const first = e.issues[0];
|
|
51
|
+
if (!first)
|
|
52
|
+
return "validation failed";
|
|
53
|
+
const path = (first.path ?? []).join(".") || "(body)";
|
|
54
|
+
return `${path}: ${first.message}`;
|
|
55
|
+
}
|
|
38
56
|
function isHttpBinding(b) {
|
|
39
57
|
return (typeof b === "object" &&
|
|
40
58
|
b !== null &&
|
|
@@ -77,6 +95,15 @@ export function httpKoa(config = {}) {
|
|
|
77
95
|
catch (err) {
|
|
78
96
|
const e = err;
|
|
79
97
|
const isNwireError = e?.$kind === "error";
|
|
98
|
+
if (isZodError(err)) {
|
|
99
|
+
// A schema `.parse()` that escaped the per-route catch (e.g. an
|
|
100
|
+
// action's own input schema). Validation failures are the
|
|
101
|
+
// caller's fault → 400 with a readable summary, never an opaque
|
|
102
|
+
// 500. The message is about their input, so it's safe to expose.
|
|
103
|
+
kctx.status = 400;
|
|
104
|
+
kctx.body = { error: { code: "validation_failed", summary: zodSummary(err) } };
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
80
107
|
kctx.status = typeof e.status === "number" ? e.status : 500;
|
|
81
108
|
if (isNwireError) {
|
|
82
109
|
kctx.body = {
|
|
@@ -249,7 +276,7 @@ export function httpKoa(config = {}) {
|
|
|
249
276
|
kctx.body = {
|
|
250
277
|
error: {
|
|
251
278
|
code: "validation_failed",
|
|
252
|
-
summary: err.message,
|
|
279
|
+
summary: isZodError(err) ? zodSummary(err) : err.message,
|
|
253
280
|
},
|
|
254
281
|
};
|
|
255
282
|
return;
|
|
@@ -274,7 +301,12 @@ export function httpKoa(config = {}) {
|
|
|
274
301
|
: (kc.request.headers["x-tenant"] ?? undefined));
|
|
275
302
|
const envelopePartial = {
|
|
276
303
|
tenant: resolveTenant(kctx),
|
|
277
|
-
|
|
304
|
+
// Prefer an explicit `state.userId`, but fall back to the
|
|
305
|
+
// user object's id so handlers reading `envelope.userId` work
|
|
306
|
+
// whenever auth middleware populated `state.user`.
|
|
307
|
+
userId: kctx.state.userId ??
|
|
308
|
+
kctx.state.user?.id ??
|
|
309
|
+
undefined,
|
|
278
310
|
// Pass the full user object through when adopter auth middleware
|
|
279
311
|
// populated it. Without this, transport-agnostic RBAC has to
|
|
280
312
|
// reach into `ctx.koa.state.user` and couples the handler to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/koa",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Nwire — Koa-backed HTTP adopter. Consumes wires from @nwire/wires/http and serves them as Koa routes under endpoint lifecycle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adopter",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"koa-bodyparser": "^4.4.1",
|
|
34
34
|
"koa-helmet": "^8.0.1",
|
|
35
35
|
"zod": "^4.0.0",
|
|
36
|
-
"@nwire/
|
|
37
|
-
"@nwire/
|
|
38
|
-
"@nwire/logger": "0.12.
|
|
39
|
-
"@nwire/
|
|
36
|
+
"@nwire/endpoint": "0.12.1",
|
|
37
|
+
"@nwire/wires": "0.12.1",
|
|
38
|
+
"@nwire/logger": "0.12.1",
|
|
39
|
+
"@nwire/container": "0.12.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@asteasolutions/zod-to-openapi": "^8.0.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"@types/node": "^22.19.9",
|
|
48
48
|
"typescript": "^5.9.3",
|
|
49
49
|
"vitest": "^4.0.18",
|
|
50
|
-
"@nwire/app": "0.12.
|
|
51
|
-
"@nwire/handler": "0.12.
|
|
50
|
+
"@nwire/app": "0.12.1",
|
|
51
|
+
"@nwire/handler": "0.12.1"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@asteasolutions/zod-to-openapi": "^8.0.0"
|