@wooksjs/http-body 0.7.25 → 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 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
- const contentIsSlot = (0, _wooksjs_event_core.cachedBy)((type, ctx) => {
41
- const contentType = (0, _wooksjs_event_http.useHeaders)(ctx)["content-type"] || "";
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 = (0, _wooksjs_event_http.useHeaders)(ctx)["content-type"] || "";
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) => contentIsSlot(type, ctx),
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, cachedBy, defineWook } from "@wooksjs/event-core";
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
- const contentIsSlot = cachedBy((type, ctx) => {
40
- const contentType = useHeaders(ctx)["content-type"] || "";
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 = useHeaders(ctx)["content-type"] || "";
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) => contentIsSlot(type, ctx),
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.25",
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.25",
46
- "@wooksjs/event-http": "^0.7.25"
45
+ "@wooksjs/event-core": "^0.7.26",
46
+ "@wooksjs/event-http": "^0.7.26"
47
47
  },
48
48
  "peerDependencies": {
49
- "@wooksjs/event-core": "^0.7.25",
50
- "@wooksjs/event-http": "^0.7.25"
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"