@kizunajs/express 2.0.0-beta.2 → 2.0.0-beta.21

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/README.md CHANGED
@@ -30,7 +30,6 @@ import express from 'express';
30
30
  import kizuna from '../kizuna.config';
31
31
 
32
32
  const app = express();
33
- app.use(express.json());
34
33
 
35
34
  kizuna.api.mount(app);
36
35
 
package/dist/index.cjs CHANGED
@@ -3,6 +3,51 @@ let express = require("express");
3
3
  let node_stream = require("node:stream");
4
4
  let kizunajs_adapter = require("kizunajs/adapter");
5
5
  //#region src/server.ts
6
+ /**
7
+ * Keep the body as it was sent, for routes with `rawBody: true`. Pass it as the
8
+ * `verify` option of an `express.json()` your app runs before kizuna.
9
+ *
10
+ * @example
11
+ * app.use(
12
+ * express.json({
13
+ * verify: keepRawBody,
14
+ * })
15
+ * );
16
+ */
17
+ const keepRawBody = (req, _res, buffer) => {
18
+ req.rawBody = buffer;
19
+ };
20
+ /**
21
+ * Parse a kizuna route's body, unless the app already parsed it. The parser
22
+ * keeps the text as well, for routes with `rawBody: true`.
23
+ */
24
+ const bodyParserFor = (route, jsonOptions) => {
25
+ if (route.contentType === "multipart/form-data") return void 0;
26
+ const parse = route.contentType === "application/x-www-form-urlencoded" ? (0, express.urlencoded)({
27
+ extended: false,
28
+ verify: keepRawBody
29
+ }) : (0, express.json)({
30
+ ...jsonOptions,
31
+ verify: keepRawBody
32
+ });
33
+ return (req, res, next) => {
34
+ if (req.body !== void 0) {
35
+ next();
36
+ return;
37
+ }
38
+ req.kizunaParsedBody = true;
39
+ parse(req, res, next);
40
+ };
41
+ };
42
+ /**
43
+ * The body text for a `rawBody` route: what the parser kept, an empty body when
44
+ * kizuna parsed and there was none, or `undefined` when the app's own parser
45
+ * consumed it without keeping it.
46
+ */
47
+ const rawBodyOf = (req) => {
48
+ if (req.rawBody !== void 0) return req.rawBody.toString("utf8");
49
+ return req.kizunaParsedBody === true ? "" : void 0;
50
+ };
6
51
  const writeStream = (open, res, validate) => {
7
52
  const controller = new AbortController();
8
53
  res.on("close", () => {
@@ -29,7 +74,7 @@ const writeWebResponse = async (response, res) => {
29
74
  }
30
75
  node_stream.Readable.fromWeb(response.body).pipe(res);
31
76
  };
32
- const adapter = (0, kizunajs_adapter.createAdapter)({
77
+ const adapter = (0, kizunajs_adapter.defineAdapter)({
33
78
  buildHandlerContext: (adapterRequest, { res }) => ({
34
79
  req: adapterRequest.request,
35
80
  res
@@ -81,10 +126,12 @@ function mountExpress(api, app, options) {
81
126
  const jobRunner = (0, kizunajs_adapter.jobRunnerFrom)(jobsMeta);
82
127
  const expressRouter = (0, express.Router)();
83
128
  const mountRoute = (routeKey, route, routes, router) => {
84
- expressRouter[route.method.toLowerCase()](route.path, (req, _res, next) => {
129
+ const method = route.method.toLowerCase();
130
+ const parseBody = bodyParserFor(route, options?.json);
131
+ expressRouter[method](route.path, (req, _res, next) => {
85
132
  req.kizunaRoute = route;
86
133
  next();
87
- }, async (req, res, next) => {
134
+ }, ...parseBody === void 0 ? [] : [parseBody], async (req, res, next) => {
88
135
  const adapterRequest = {
89
136
  request: req,
90
137
  method: req.method,
@@ -96,7 +143,8 @@ function mountExpress(api, app, options) {
96
143
  },
97
144
  query: req.query,
98
145
  headers: req.headers,
99
- readBody: () => req.body
146
+ readBody: () => req.body,
147
+ readRawBody: () => rawBodyOf(req)
100
148
  };
101
149
  await adapter.handle({
102
150
  routes,
@@ -153,4 +201,5 @@ const expressAdapter = (defaults) => ({
153
201
  });
154
202
  //#endregion
155
203
  exports.expressAdapter = expressAdapter;
204
+ exports.keepRawBody = keepRawBody;
156
205
  exports.mountExpress = mountExpress;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { Request, Response, Router } from "express";
1
+ import { Request, Response, Router, json } from "express";
2
+ import { IncomingMessage, ServerResponse } from "node:http";
2
3
  import { Adapter, ApiWithRouter, ErrorFormatter, GUARDS_META, JOBS_META, REQUEST_CONTEXT_META, RouteDefinition, Routes, SCHEMES_META } from "kizunajs/adapter";
3
4
 
4
5
  //#region src/server.d.ts
@@ -23,9 +24,30 @@ declare global {
23
24
  namespace Express {
24
25
  interface Request {
25
26
  kizunaRoute?: RouteDefinition;
27
+ /**
28
+ * The body as it was sent, kept by {@link keepRawBody}.
29
+ */
30
+ rawBody?: Buffer;
31
+ /**
32
+ * Set when kizuna parsed this request's body itself.
33
+ */
34
+ kizunaParsedBody?: true;
26
35
  }
27
36
  }
28
37
  }
38
+ /**
39
+ * Keep the body as it was sent, for routes with `rawBody: true`. Pass it as the
40
+ * `verify` option of an `express.json()` your app runs before kizuna.
41
+ *
42
+ * @example
43
+ * app.use(
44
+ * express.json({
45
+ * verify: keepRawBody,
46
+ * })
47
+ * );
48
+ */
49
+ declare const keepRawBody: (req: IncomingMessage, _res: ServerResponse, buffer: Buffer) => void;
50
+ type JsonOptions = NonNullable<Parameters<typeof json>[0]>;
29
51
  interface ExpressOptions {
30
52
  /**
31
53
  * Validate handler return values against the route's response schemas.
@@ -39,6 +61,17 @@ interface ExpressOptions {
39
61
  * {@link ErrorFormatter}.
40
62
  */
41
63
  formatError?: ErrorFormatter<Request>;
64
+ /**
65
+ * The options for `express.json()`, like `limit` and `strict`, used to parse
66
+ * the bodies of kizuna's own routes. An `express.json()` your app runs
67
+ * first takes precedence, and kizuna uses the body it parsed.
68
+ *
69
+ * @example
70
+ * json: {
71
+ * limit: '5mb',
72
+ * }
73
+ */
74
+ json?: JsonOptions;
42
75
  }
43
76
  interface AppLike {
44
77
  use: (router: Router) => unknown;
@@ -65,4 +98,4 @@ declare function mountExpress(api: ExpressApi, app: AppLike, options?: ExpressOp
65
98
  */
66
99
  declare const expressAdapter: (defaults?: ExpressOptions) => Adapter<ExpressHandlerContext, [app: AppLike, options?: ExpressOptions], Router>;
67
100
  //#endregion
68
- export { AppLike, ExpressApi, ExpressHandlerContext, ExpressOptions, expressAdapter, mountExpress };
101
+ export { AppLike, ExpressApi, ExpressHandlerContext, ExpressOptions, expressAdapter, keepRawBody, mountExpress };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
- import { Request, Response, Router } from "express";
1
+ import { Request, Response, Router, json } from "express";
2
2
  import { Adapter, ApiWithRouter, ErrorFormatter, GUARDS_META, JOBS_META, REQUEST_CONTEXT_META, RouteDefinition, Routes, SCHEMES_META } from "kizunajs/adapter";
3
+ import { IncomingMessage, ServerResponse } from "node:http";
3
4
 
4
5
  //#region src/server.d.ts
5
6
  type ExpressApi<R extends Routes = Routes> = ApiWithRouter<R> & {
@@ -23,9 +24,30 @@ declare global {
23
24
  namespace Express {
24
25
  interface Request {
25
26
  kizunaRoute?: RouteDefinition;
27
+ /**
28
+ * The body as it was sent, kept by {@link keepRawBody}.
29
+ */
30
+ rawBody?: Buffer;
31
+ /**
32
+ * Set when kizuna parsed this request's body itself.
33
+ */
34
+ kizunaParsedBody?: true;
26
35
  }
27
36
  }
28
37
  }
38
+ /**
39
+ * Keep the body as it was sent, for routes with `rawBody: true`. Pass it as the
40
+ * `verify` option of an `express.json()` your app runs before kizuna.
41
+ *
42
+ * @example
43
+ * app.use(
44
+ * express.json({
45
+ * verify: keepRawBody,
46
+ * })
47
+ * );
48
+ */
49
+ declare const keepRawBody: (req: IncomingMessage, _res: ServerResponse, buffer: Buffer) => void;
50
+ type JsonOptions = NonNullable<Parameters<typeof json>[0]>;
29
51
  interface ExpressOptions {
30
52
  /**
31
53
  * Validate handler return values against the route's response schemas.
@@ -39,6 +61,17 @@ interface ExpressOptions {
39
61
  * {@link ErrorFormatter}.
40
62
  */
41
63
  formatError?: ErrorFormatter<Request>;
64
+ /**
65
+ * The options for `express.json()`, like `limit` and `strict`, used to parse
66
+ * the bodies of kizuna's own routes. An `express.json()` your app runs
67
+ * first takes precedence, and kizuna uses the body it parsed.
68
+ *
69
+ * @example
70
+ * json: {
71
+ * limit: '5mb',
72
+ * }
73
+ */
74
+ json?: JsonOptions;
42
75
  }
43
76
  interface AppLike {
44
77
  use: (router: Router) => unknown;
@@ -65,4 +98,4 @@ declare function mountExpress(api: ExpressApi, app: AppLike, options?: ExpressOp
65
98
  */
66
99
  declare const expressAdapter: (defaults?: ExpressOptions) => Adapter<ExpressHandlerContext, [app: AppLike, options?: ExpressOptions], Router>;
67
100
  //#endregion
68
- export { AppLike, ExpressApi, ExpressHandlerContext, ExpressOptions, expressAdapter, mountExpress };
101
+ export { AppLike, ExpressApi, ExpressHandlerContext, ExpressOptions, expressAdapter, keepRawBody, mountExpress };
package/dist/index.mjs CHANGED
@@ -1,7 +1,52 @@
1
- import { Router } from "express";
1
+ import { Router, json, urlencoded } from "express";
2
2
  import { Readable, pipeline } from "node:stream";
3
- import { GUARDS_META, JOBS_META, REQUEST_CONTEXT_META, ROUTER_META, SCHEMES_META, createAdapter, jobRouter, jobRoutes, jobRunnerFrom, pluginExportsOf, pluginRouterOf, pluginRoutesOf, renderJsonResult } from "kizunajs/adapter";
3
+ import { GUARDS_META, JOBS_META, REQUEST_CONTEXT_META, ROUTER_META, SCHEMES_META, defineAdapter, jobRouter, jobRoutes, jobRunnerFrom, pluginExportsOf, pluginRouterOf, pluginRoutesOf, renderJsonResult } from "kizunajs/adapter";
4
4
  //#region src/server.ts
5
+ /**
6
+ * Keep the body as it was sent, for routes with `rawBody: true`. Pass it as the
7
+ * `verify` option of an `express.json()` your app runs before kizuna.
8
+ *
9
+ * @example
10
+ * app.use(
11
+ * express.json({
12
+ * verify: keepRawBody,
13
+ * })
14
+ * );
15
+ */
16
+ const keepRawBody = (req, _res, buffer) => {
17
+ req.rawBody = buffer;
18
+ };
19
+ /**
20
+ * Parse a kizuna route's body, unless the app already parsed it. The parser
21
+ * keeps the text as well, for routes with `rawBody: true`.
22
+ */
23
+ const bodyParserFor = (route, jsonOptions) => {
24
+ if (route.contentType === "multipart/form-data") return void 0;
25
+ const parse = route.contentType === "application/x-www-form-urlencoded" ? urlencoded({
26
+ extended: false,
27
+ verify: keepRawBody
28
+ }) : json({
29
+ ...jsonOptions,
30
+ verify: keepRawBody
31
+ });
32
+ return (req, res, next) => {
33
+ if (req.body !== void 0) {
34
+ next();
35
+ return;
36
+ }
37
+ req.kizunaParsedBody = true;
38
+ parse(req, res, next);
39
+ };
40
+ };
41
+ /**
42
+ * The body text for a `rawBody` route: what the parser kept, an empty body when
43
+ * kizuna parsed and there was none, or `undefined` when the app's own parser
44
+ * consumed it without keeping it.
45
+ */
46
+ const rawBodyOf = (req) => {
47
+ if (req.rawBody !== void 0) return req.rawBody.toString("utf8");
48
+ return req.kizunaParsedBody === true ? "" : void 0;
49
+ };
5
50
  const writeStream = (open, res, validate) => {
6
51
  const controller = new AbortController();
7
52
  res.on("close", () => {
@@ -28,7 +73,7 @@ const writeWebResponse = async (response, res) => {
28
73
  }
29
74
  Readable.fromWeb(response.body).pipe(res);
30
75
  };
31
- const adapter = createAdapter({
76
+ const adapter = defineAdapter({
32
77
  buildHandlerContext: (adapterRequest, { res }) => ({
33
78
  req: adapterRequest.request,
34
79
  res
@@ -80,10 +125,12 @@ function mountExpress(api, app, options) {
80
125
  const jobRunner = jobRunnerFrom(jobsMeta);
81
126
  const expressRouter = Router();
82
127
  const mountRoute = (routeKey, route, routes, router) => {
83
- expressRouter[route.method.toLowerCase()](route.path, (req, _res, next) => {
128
+ const method = route.method.toLowerCase();
129
+ const parseBody = bodyParserFor(route, options?.json);
130
+ expressRouter[method](route.path, (req, _res, next) => {
84
131
  req.kizunaRoute = route;
85
132
  next();
86
- }, async (req, res, next) => {
133
+ }, ...parseBody === void 0 ? [] : [parseBody], async (req, res, next) => {
87
134
  const adapterRequest = {
88
135
  request: req,
89
136
  method: req.method,
@@ -95,7 +142,8 @@ function mountExpress(api, app, options) {
95
142
  },
96
143
  query: req.query,
97
144
  headers: req.headers,
98
- readBody: () => req.body
145
+ readBody: () => req.body,
146
+ readRawBody: () => rawBodyOf(req)
99
147
  };
100
148
  await adapter.handle({
101
149
  routes,
@@ -151,4 +199,4 @@ const expressAdapter = (defaults) => ({
151
199
  })
152
200
  });
153
201
  //#endregion
154
- export { expressAdapter, mountExpress };
202
+ export { expressAdapter, keepRawBody, mountExpress };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kizunajs/express",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.21",
4
4
  "description": "Express adapter for Kizuna.js",
5
5
  "keywords": [
6
6
  "kizunajs",
@@ -55,17 +55,17 @@
55
55
  "access": "public"
56
56
  },
57
57
  "peerDependencies": {
58
+ "kizunajs": "2.0.0-beta.21",
58
59
  "express": "^5.0.0",
59
- "zod": "^4.0.0",
60
- "kizunajs": "2.0.0-beta.2"
60
+ "zod": "^4.0.0"
61
61
  },
62
62
  "devDependencies": {
63
+ "kizunajs": "2.0.0-beta.21",
63
64
  "@types/express": "^5.0.0",
64
65
  "express": "^5.0.0",
65
66
  "tsdown": "^0.21.0",
66
67
  "typescript": "^6.0.3",
67
- "zod": "^4.0.0",
68
- "kizunajs": "2.0.0-beta.2"
68
+ "zod": "^4.0.0"
69
69
  },
70
70
  "scripts": {
71
71
  "build": "tsdown src/index.ts --format esm,cjs --dts --clean --external kizunajs --external express --external zod",