@wooksjs/http-body 0.7.24 → 0.7.26
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/index.cjs +60 -10
- package/dist/index.d.ts +45 -2
- package/dist/index.mjs +62 -13
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
let _wooksjs_event_core = require("@wooksjs/event-core");
|
|
3
3
|
let _wooksjs_event_http = require("@wooksjs/event-http");
|
|
4
|
+
let buffer = require("buffer");
|
|
4
5
|
|
|
5
6
|
//#region packages/http-body/src/utils/safe-json.ts
|
|
6
7
|
const ILLEGAL_KEYS = [
|
|
@@ -37,14 +38,11 @@ const CONTENT_TYPE_MAP = {
|
|
|
37
38
|
"form-data": "multipart/form-data",
|
|
38
39
|
urlencoded: "application/x-www-form-urlencoded"
|
|
39
40
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const mime = CONTENT_TYPE_MAP[type] || type;
|
|
43
|
-
return contentType.includes(mime);
|
|
44
|
-
});
|
|
41
|
+
/** The body's content type: the request's `content-type` header, or what {@link seedBody} set. */
|
|
42
|
+
const contentTypeSlot = (0, _wooksjs_event_core.cached)((ctx) => (0, _wooksjs_event_http.useHeaders)(ctx)["content-type"] || "");
|
|
45
43
|
const parsedBodySlot = (0, _wooksjs_event_core.cached)(async (ctx) => {
|
|
46
44
|
const { rawBody } = (0, _wooksjs_event_http.useRequest)(ctx);
|
|
47
|
-
const contentType =
|
|
45
|
+
const contentType = ctx.get(contentTypeSlot);
|
|
48
46
|
const contentIs = (type) => contentType.includes(type);
|
|
49
47
|
const sBody = (await rawBody()).toString();
|
|
50
48
|
if (contentIs("application/json")) return jsonParser(sBody);
|
|
@@ -131,14 +129,66 @@ function urlEncodedParser(v) {
|
|
|
131
129
|
*
|
|
132
130
|
* @returns Object with `is(type)` checker, `parseBody` function, and `rawBody` accessor.
|
|
133
131
|
*/
|
|
134
|
-
const useBody = (0, _wooksjs_event_core.defineWook)((ctx) =>
|
|
132
|
+
const useBody = (0, _wooksjs_event_core.defineWook)((ctx) => bodyApi(ctx, false));
|
|
133
|
+
/** `ownParse`: parse in `ctx` itself, never reading a parent's parsed body through. */
|
|
134
|
+
function bodyApi(ctx, ownParse) {
|
|
135
135
|
const { rawBody } = (0, _wooksjs_event_http.useRequest)(ctx);
|
|
136
136
|
return {
|
|
137
|
-
is: (type) =>
|
|
138
|
-
parseBody: () => ctx.get(parsedBodySlot),
|
|
137
|
+
is: (type) => ctx.get(contentTypeSlot).includes(CONTENT_TYPE_MAP[type] || type),
|
|
138
|
+
parseBody: () => ownParse ? ctx.getOwn(parsedBodySlot) : ctx.get(parsedBodySlot),
|
|
139
139
|
rawBody
|
|
140
140
|
};
|
|
141
|
-
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Seeds the request body of `ctx` with an already parsed value: `useBody(ctx).parseBody()`
|
|
144
|
+
* resolves to `body`, `useBody(ctx).rawBody()` / `useRequest(ctx).rawBody()` to its raw bytes,
|
|
145
|
+
* and `useBody(ctx).is()` checks the seeded content type — the incoming request stream is never
|
|
146
|
+
* read.
|
|
147
|
+
*
|
|
148
|
+
* Use it for a child event context (`new EventContext({ logger, parent })`) that runs a handler
|
|
149
|
+
* with its own payload: everything else (request, headers, auth) is still read through the
|
|
150
|
+
* parent, the body never is. Call it before anything in the child reads the body.
|
|
151
|
+
*
|
|
152
|
+
* @param ctx - The context to seed — usually a child of the current HTTP event
|
|
153
|
+
* @param body - The parsed body value
|
|
154
|
+
* @param opts - Raw bytes and content type (see {@link TSeedBodyOptions})
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const child = new EventContext({ logger: parent.logger, parent })
|
|
159
|
+
* seedBody(child, { ids: [1, 2] })
|
|
160
|
+
* await run(child, async () => {
|
|
161
|
+
* await useBody().parseBody() // { ids: [1, 2] }
|
|
162
|
+
* useBody().is('json') // true
|
|
163
|
+
* })
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
function seedBody(ctx, body, opts) {
|
|
167
|
+
const defaults = seedDefaults(body, opts?.raw);
|
|
168
|
+
(0, _wooksjs_event_http.seedRawBody)(ctx, defaults.raw);
|
|
169
|
+
const contentType = opts?.contentType ?? defaults.contentType;
|
|
170
|
+
if (contentType !== void 0) ctx.setOwn(contentTypeSlot, contentType);
|
|
171
|
+
const parseRaw = body === void 0 && opts?.raw !== void 0;
|
|
172
|
+
if (!parseRaw) ctx.setOwn(parsedBodySlot, Promise.resolve(body));
|
|
173
|
+
if (parseRaw || ctx.parent) ctx.setOwn(useBody._slot, bodyApi(ctx, true));
|
|
174
|
+
}
|
|
175
|
+
/** The raw bytes (`raw`, else derived from `body`) and default content type of a seeded body. */
|
|
176
|
+
function seedDefaults(body, raw) {
|
|
177
|
+
if (body === void 0) return { raw: raw ?? "" };
|
|
178
|
+
if (typeof body === "string") return {
|
|
179
|
+
raw: raw ?? body,
|
|
180
|
+
contentType: CONTENT_TYPE_MAP.text
|
|
181
|
+
};
|
|
182
|
+
if (buffer.Buffer.isBuffer(body)) return {
|
|
183
|
+
raw: raw ?? body,
|
|
184
|
+
contentType: CONTENT_TYPE_MAP.binary
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
raw: raw ?? JSON.stringify(body) ?? "",
|
|
188
|
+
contentType: CONTENT_TYPE_MAP.json
|
|
189
|
+
};
|
|
190
|
+
}
|
|
142
191
|
|
|
143
192
|
//#endregion
|
|
193
|
+
exports.seedBody = seedBody;
|
|
144
194
|
exports.useBody = useBody;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as _wooksjs_event_core from '@wooksjs/event-core';
|
|
2
|
+
import { EventContext } from '@wooksjs/event-core';
|
|
3
|
+
import { Buffer } from 'buffer';
|
|
2
4
|
|
|
3
5
|
/** Short names for common Content-Type values. */
|
|
4
6
|
type KnownContentType = 'json' | 'html' | 'xml' | 'text' | 'binary' | 'form-data' | 'urlencoded';
|
|
@@ -23,6 +25,47 @@ declare const useBody: _wooksjs_event_core.WookComposable<{
|
|
|
23
25
|
parseBody: <T>() => Promise<T>;
|
|
24
26
|
rawBody: () => Promise<Buffer<ArrayBufferLike>>;
|
|
25
27
|
}>;
|
|
28
|
+
/** Options for {@link seedBody}. */
|
|
29
|
+
interface TSeedBodyOptions {
|
|
30
|
+
/**
|
|
31
|
+
* The raw bytes `rawBody()` returns. Default: `body` itself when it is a string or a
|
|
32
|
+
* `Buffer`, otherwise `JSON.stringify(body)`. With `body` `undefined`, `parseBody()` parses
|
|
33
|
+
* these bytes by the content type (as for a real request).
|
|
34
|
+
*/
|
|
35
|
+
raw?: Buffer | string;
|
|
36
|
+
/**
|
|
37
|
+
* The content type `useBody().is()` checks against (and `parseBody()` parses `raw` by).
|
|
38
|
+
* Default: `'text/plain'` for a string, `'application/octet-stream'` for a `Buffer`,
|
|
39
|
+
* `'application/json'` for other values; with `body` `undefined`, the request's
|
|
40
|
+
* `Content-Type`. Request headers (`useHeaders()`) are not changed.
|
|
41
|
+
*/
|
|
42
|
+
contentType?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Seeds the request body of `ctx` with an already parsed value: `useBody(ctx).parseBody()`
|
|
46
|
+
* resolves to `body`, `useBody(ctx).rawBody()` / `useRequest(ctx).rawBody()` to its raw bytes,
|
|
47
|
+
* and `useBody(ctx).is()` checks the seeded content type — the incoming request stream is never
|
|
48
|
+
* read.
|
|
49
|
+
*
|
|
50
|
+
* Use it for a child event context (`new EventContext({ logger, parent })`) that runs a handler
|
|
51
|
+
* with its own payload: everything else (request, headers, auth) is still read through the
|
|
52
|
+
* parent, the body never is. Call it before anything in the child reads the body.
|
|
53
|
+
*
|
|
54
|
+
* @param ctx - The context to seed — usually a child of the current HTTP event
|
|
55
|
+
* @param body - The parsed body value
|
|
56
|
+
* @param opts - Raw bytes and content type (see {@link TSeedBodyOptions})
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const child = new EventContext({ logger: parent.logger, parent })
|
|
61
|
+
* seedBody(child, { ids: [1, 2] })
|
|
62
|
+
* await run(child, async () => {
|
|
63
|
+
* await useBody().parseBody() // { ids: [1, 2] }
|
|
64
|
+
* useBody().is('json') // true
|
|
65
|
+
* })
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function seedBody(ctx: EventContext, body: unknown, opts?: TSeedBodyOptions): void;
|
|
26
69
|
|
|
27
|
-
export { useBody };
|
|
28
|
-
export type { KnownContentType };
|
|
70
|
+
export { seedBody, useBody };
|
|
71
|
+
export type { KnownContentType, TSeedBodyOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { cached,
|
|
2
|
-
import { EHttpStatusCode, HttpError, WooksURLSearchParams, useHeaders, useRequest } from "@wooksjs/event-http";
|
|
1
|
+
import { cached, defineWook } from "@wooksjs/event-core";
|
|
2
|
+
import { EHttpStatusCode, HttpError, WooksURLSearchParams, seedRawBody, useHeaders, useRequest } from "@wooksjs/event-http";
|
|
3
|
+
import { Buffer } from "buffer";
|
|
3
4
|
|
|
4
5
|
//#region packages/http-body/src/utils/safe-json.ts
|
|
5
6
|
const ILLEGAL_KEYS = [
|
|
@@ -36,14 +37,11 @@ const CONTENT_TYPE_MAP = {
|
|
|
36
37
|
"form-data": "multipart/form-data",
|
|
37
38
|
urlencoded: "application/x-www-form-urlencoded"
|
|
38
39
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const mime = CONTENT_TYPE_MAP[type] || type;
|
|
42
|
-
return contentType.includes(mime);
|
|
43
|
-
});
|
|
40
|
+
/** The body's content type: the request's `content-type` header, or what {@link seedBody} set. */
|
|
41
|
+
const contentTypeSlot = cached((ctx) => useHeaders(ctx)["content-type"] || "");
|
|
44
42
|
const parsedBodySlot = cached(async (ctx) => {
|
|
45
43
|
const { rawBody } = useRequest(ctx);
|
|
46
|
-
const contentType =
|
|
44
|
+
const contentType = ctx.get(contentTypeSlot);
|
|
47
45
|
const contentIs = (type) => contentType.includes(type);
|
|
48
46
|
const sBody = (await rawBody()).toString();
|
|
49
47
|
if (contentIs("application/json")) return jsonParser(sBody);
|
|
@@ -130,14 +128,65 @@ function urlEncodedParser(v) {
|
|
|
130
128
|
*
|
|
131
129
|
* @returns Object with `is(type)` checker, `parseBody` function, and `rawBody` accessor.
|
|
132
130
|
*/
|
|
133
|
-
const useBody = defineWook((ctx) =>
|
|
131
|
+
const useBody = defineWook((ctx) => bodyApi(ctx, false));
|
|
132
|
+
/** `ownParse`: parse in `ctx` itself, never reading a parent's parsed body through. */
|
|
133
|
+
function bodyApi(ctx, ownParse) {
|
|
134
134
|
const { rawBody } = useRequest(ctx);
|
|
135
135
|
return {
|
|
136
|
-
is: (type) =>
|
|
137
|
-
parseBody: () => ctx.get(parsedBodySlot),
|
|
136
|
+
is: (type) => ctx.get(contentTypeSlot).includes(CONTENT_TYPE_MAP[type] || type),
|
|
137
|
+
parseBody: () => ownParse ? ctx.getOwn(parsedBodySlot) : ctx.get(parsedBodySlot),
|
|
138
138
|
rawBody
|
|
139
139
|
};
|
|
140
|
-
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Seeds the request body of `ctx` with an already parsed value: `useBody(ctx).parseBody()`
|
|
143
|
+
* resolves to `body`, `useBody(ctx).rawBody()` / `useRequest(ctx).rawBody()` to its raw bytes,
|
|
144
|
+
* and `useBody(ctx).is()` checks the seeded content type — the incoming request stream is never
|
|
145
|
+
* read.
|
|
146
|
+
*
|
|
147
|
+
* Use it for a child event context (`new EventContext({ logger, parent })`) that runs a handler
|
|
148
|
+
* with its own payload: everything else (request, headers, auth) is still read through the
|
|
149
|
+
* parent, the body never is. Call it before anything in the child reads the body.
|
|
150
|
+
*
|
|
151
|
+
* @param ctx - The context to seed — usually a child of the current HTTP event
|
|
152
|
+
* @param body - The parsed body value
|
|
153
|
+
* @param opts - Raw bytes and content type (see {@link TSeedBodyOptions})
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const child = new EventContext({ logger: parent.logger, parent })
|
|
158
|
+
* seedBody(child, { ids: [1, 2] })
|
|
159
|
+
* await run(child, async () => {
|
|
160
|
+
* await useBody().parseBody() // { ids: [1, 2] }
|
|
161
|
+
* useBody().is('json') // true
|
|
162
|
+
* })
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
function seedBody(ctx, body, opts) {
|
|
166
|
+
const defaults = seedDefaults(body, opts?.raw);
|
|
167
|
+
seedRawBody(ctx, defaults.raw);
|
|
168
|
+
const contentType = opts?.contentType ?? defaults.contentType;
|
|
169
|
+
if (contentType !== void 0) ctx.setOwn(contentTypeSlot, contentType);
|
|
170
|
+
const parseRaw = body === void 0 && opts?.raw !== void 0;
|
|
171
|
+
if (!parseRaw) ctx.setOwn(parsedBodySlot, Promise.resolve(body));
|
|
172
|
+
if (parseRaw || ctx.parent) ctx.setOwn(useBody._slot, bodyApi(ctx, true));
|
|
173
|
+
}
|
|
174
|
+
/** The raw bytes (`raw`, else derived from `body`) and default content type of a seeded body. */
|
|
175
|
+
function seedDefaults(body, raw) {
|
|
176
|
+
if (body === void 0) return { raw: raw ?? "" };
|
|
177
|
+
if (typeof body === "string") return {
|
|
178
|
+
raw: raw ?? body,
|
|
179
|
+
contentType: CONTENT_TYPE_MAP.text
|
|
180
|
+
};
|
|
181
|
+
if (Buffer.isBuffer(body)) return {
|
|
182
|
+
raw: raw ?? body,
|
|
183
|
+
contentType: CONTENT_TYPE_MAP.binary
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
raw: raw ?? JSON.stringify(body) ?? "",
|
|
187
|
+
contentType: CONTENT_TYPE_MAP.json
|
|
188
|
+
};
|
|
189
|
+
}
|
|
141
190
|
|
|
142
191
|
//#endregion
|
|
143
|
-
export { useBody };
|
|
192
|
+
export { seedBody, useBody };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wooksjs/http-body",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.26",
|
|
4
4
|
"description": "@wooksjs/http-body",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"vitest": "^3.2.7",
|
|
45
|
-
"@wooksjs/event-core": "^0.7.
|
|
46
|
-
"@wooksjs/event-http": "^0.7.
|
|
45
|
+
"@wooksjs/event-core": "^0.7.26",
|
|
46
|
+
"@wooksjs/event-http": "^0.7.26"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@wooksjs/event-
|
|
50
|
-
"@wooksjs/event-
|
|
49
|
+
"@wooksjs/event-http": "^0.7.26",
|
|
50
|
+
"@wooksjs/event-core": "^0.7.26"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "rolldown -c ../../rolldown.config.mjs"
|