@koa-stack/body 0.7.0

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.
@@ -0,0 +1,65 @@
1
+ import readRawBody from 'raw-body';
2
+ import inflate from 'inflation';
3
+ import formidable from 'formidable';
4
+ import Koa, { Context } from 'koa';
5
+ declare module 'koa' {
6
+ interface BaseContext {
7
+ payload: Promise<LazyBody>;
8
+ }
9
+ }
10
+ export interface OwnOpts {
11
+ formidable?: formidable.Options;
12
+ inflate?: inflate.Options;
13
+ form?: (data: string) => any;
14
+ json?: (data: string) => any;
15
+ xml?: (data: string) => any;
16
+ }
17
+ export type LazyBodyOpts = readRawBody.Options & OwnOpts;
18
+ /**
19
+ * Request body class
20
+ * json
21
+ *
22
+ */
23
+ export declare class LazyBody {
24
+ ctx: Context;
25
+ type: FormType;
26
+ data: any;
27
+ raw: string;
28
+ files: formidable.Files | null | undefined;
29
+ constructor(ctx: Context, type: FormType, data: any, raw: string, files: formidable.Files | null | undefined);
30
+ assertJSON(statusCode: number, message: string): void;
31
+ assertXML(statusCode: number, message: string): void;
32
+ /**
33
+ * Assert body type is urlencoded
34
+ * @param {*} statusCode
35
+ * @param {*} message
36
+ */
37
+ assertForm(statusCode: number, message: string): void;
38
+ assertMultipart(statusCode: number, message: string): void;
39
+ assertText(statusCode: number, message: string): void;
40
+ /**
41
+ * Assert that body type is either multipart or urlencoded
42
+ * @param {*} statusCode
43
+ * @param {*} message
44
+ */
45
+ assertParams(statusCode: number, message: string): void;
46
+ get text(): string;
47
+ get json(): any;
48
+ get xml(): any;
49
+ get params(): any;
50
+ get isForm(): boolean;
51
+ get isJSON(): boolean;
52
+ get isXML(): boolean;
53
+ get isMultipart(): boolean;
54
+ get isText(): boolean;
55
+ static install(koa: Koa, opts?: LazyBodyOpts): void;
56
+ }
57
+ declare enum FormType {
58
+ 'multipart' = 0,
59
+ 'form' = 1,
60
+ 'json' = 2,
61
+ 'xml' = 3,
62
+ 'text' = 4
63
+ }
64
+ export {};
65
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,UAAU,CAAC;AACnC,OAAO,OAAO,MAAM,WAAW,CAAC;AAEhC,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,GAAG,EAAE,EAAE,OAAO,EAAW,MAAM,KAAK,CAAC;AAE5C,OAAO,QAAQ,KAAK,CAAC;IACjB,UAAU,WAAW;QACjB,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;KAC7B;CACJ;AAED,MAAM,WAAW,OAAO;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7B,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC;CAC/B;AACD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;AAgCzD;;;;GAIG;AACH,qBAAa,QAAQ;IAEjB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,GAAG,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;gBAE/B,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI,GAAG,SAAS;IAQ5G,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM9C,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM7C;;;;OAIG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM9C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAMnD,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM9C;;;;OAIG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAMhD,IAAI,IAAI,WAEP;IAED,IAAI,IAAI,QAEP;IAED,IAAI,GAAG,QAEN;IAED,IAAI,MAAM,QAOT;IAED,IAAI,MAAM,YAET;IAED,IAAI,MAAM,YAET;IAED,IAAI,KAAK,YAER;IAED,IAAI,WAAW,YAEd;IAED,IAAI,MAAM,YAET;IAED,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,YAAiB,GAAG,IAAI;CA0B1D;AACD,aAAK,QAAQ;IACT,WAAW,IAAA;IAAE,MAAM,IAAA;IAAE,MAAM,IAAA;IAAE,KAAK,IAAA;IAAE,MAAM,IAAA;CAC7C"}
package/build/index.js ADDED
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LazyBody = void 0;
7
+ const raw_body_1 = __importDefault(require("raw-body"));
8
+ const inflation_1 = __importDefault(require("inflation"));
9
+ const qs_1 = __importDefault(require("qs"));
10
+ const formidable_1 = __importDefault(require("formidable"));
11
+ function parseMultipartBody(koaRequest, opts) {
12
+ const form = (0, formidable_1.default)(opts.formidable);
13
+ return new Promise((resolve, reject) => {
14
+ form.parse(koaRequest.req, (err, fields, files) => {
15
+ if (err) {
16
+ reject(err);
17
+ }
18
+ else {
19
+ resolve({
20
+ params: fields,
21
+ files: files
22
+ });
23
+ }
24
+ });
25
+ });
26
+ }
27
+ async function getRawBodyText(koaRequest, opts) {
28
+ const len = koaRequest.length;
29
+ let encoding = koaRequest.headers['content-encoding'];
30
+ if (len && !encoding) {
31
+ opts.length = len;
32
+ }
33
+ // TODO how to detect custom encoding on content type to ser encoding for readRawBody
34
+ return (await (0, raw_body_1.default)((0, inflation_1.default)(koaRequest.req, opts.inflate), opts)).toString( /*which charset?*/);
35
+ }
36
+ /**
37
+ * Request body class
38
+ * json
39
+ *
40
+ */
41
+ class LazyBody {
42
+ constructor(ctx, type, data, raw, files) {
43
+ this.ctx = ctx;
44
+ this.type = type;
45
+ this.data = data;
46
+ this.raw = raw;
47
+ this.files = files;
48
+ }
49
+ assertJSON(statusCode, message) {
50
+ if (this.type !== FormType.json) {
51
+ this.ctx.throw(statusCode || 415, message);
52
+ }
53
+ }
54
+ assertXML(statusCode, message) {
55
+ if (this.type !== FormType.xml) {
56
+ this.ctx.throw(statusCode || 415, message);
57
+ }
58
+ }
59
+ /**
60
+ * Assert body type is urlencoded
61
+ * @param {*} statusCode
62
+ * @param {*} message
63
+ */
64
+ assertForm(statusCode, message) {
65
+ if (this.type !== FormType.form) {
66
+ this.ctx.throw(statusCode || 415, message);
67
+ }
68
+ }
69
+ assertMultipart(statusCode, message) {
70
+ if (this.type !== FormType.multipart) {
71
+ this.ctx.throw(statusCode || 415, message);
72
+ }
73
+ }
74
+ assertText(statusCode, message) {
75
+ if (this.type !== FormType.text) {
76
+ this.ctx.throw(statusCode || 415, message);
77
+ }
78
+ }
79
+ /**
80
+ * Assert that body type is either multipart or urlencoded
81
+ * @param {*} statusCode
82
+ * @param {*} message
83
+ */
84
+ assertParams(statusCode, message) {
85
+ if (this.type !== FormType.form && this.type !== FormType.multipart) {
86
+ this.ctx.throw(statusCode || 415, message);
87
+ }
88
+ }
89
+ get text() {
90
+ return this.raw;
91
+ }
92
+ get json() {
93
+ return this.isJSON ? this.data : undefined;
94
+ }
95
+ get xml() {
96
+ return this.isXML ? this.data : undefined;
97
+ }
98
+ get params() {
99
+ switch (this.type) {
100
+ case FormType.form:
101
+ case FormType.multipart:
102
+ return this.data;
103
+ default: return undefined;
104
+ }
105
+ }
106
+ get isForm() {
107
+ return this.type === FormType.form;
108
+ }
109
+ get isJSON() {
110
+ return this.type === FormType.json;
111
+ }
112
+ get isXML() {
113
+ return this.type === FormType.xml;
114
+ }
115
+ get isMultipart() {
116
+ return this.type === FormType.multipart;
117
+ }
118
+ get isText() {
119
+ return this.type === FormType.text;
120
+ }
121
+ static install(koa, opts = {}) {
122
+ opts.encoding = opts.encoding || 'utf8';
123
+ opts.limit = opts.limit || '1mb';
124
+ Object.defineProperty(koa.request, 'body', {
125
+ get() {
126
+ if (!this._body) {
127
+ this._body = createBody(this, opts);
128
+ }
129
+ return this._body;
130
+ },
131
+ // be able to work along code using koa2-formidable (wich sets the body property pf the request)
132
+ set(body) {
133
+ this._body = body;
134
+ }
135
+ });
136
+ // payload is an alias to request.body
137
+ Object.defineProperty(koa.context, 'payload', {
138
+ get() {
139
+ return this.request.body;
140
+ },
141
+ set(body) {
142
+ this.request.body = body;
143
+ }
144
+ });
145
+ }
146
+ }
147
+ exports.LazyBody = LazyBody;
148
+ var FormType;
149
+ (function (FormType) {
150
+ FormType[FormType["multipart"] = 0] = "multipart";
151
+ FormType[FormType["form"] = 1] = "form";
152
+ FormType[FormType["json"] = 2] = "json";
153
+ FormType[FormType["xml"] = 3] = "xml";
154
+ FormType[FormType["text"] = 4] = "text";
155
+ })(FormType || (FormType = {}));
156
+ async function createBody(koaRequest, opts) {
157
+ let type, data, raw, files = null;
158
+ if (koaRequest.is('multipart')) {
159
+ raw = '';
160
+ const result = await parseMultipartBody(koaRequest, opts);
161
+ data = result.params || {};
162
+ files = result.files || {};
163
+ type = FormType.multipart;
164
+ }
165
+ else if (koaRequest.is('urlencoded')) {
166
+ // by default we use qs. You can replace the querystring parser using opts.form
167
+ raw = await getRawBodyText(koaRequest, opts);
168
+ data = opts.form ? opts.form(raw) : qs_1.default.parse(raw);
169
+ type = FormType.form;
170
+ }
171
+ else if (koaRequest.is('json', '+json')) {
172
+ // by default we use JSON.parse. You can replace the json parser using opts.json
173
+ raw = await getRawBodyText(koaRequest, opts);
174
+ data = opts.json ? opts.json(raw) : JSON.parse(raw);
175
+ type = FormType.json;
176
+ }
177
+ else if (koaRequest.is('xml', '+xml')) {
178
+ // by default fast-xml-parser is used - to change the parser you should provde an xml parser through opts.xml
179
+ raw = await getRawBodyText(koaRequest, opts);
180
+ data = opts.xml ? opts.xml(raw) : require('fast-xml-parser').parse(raw);
181
+ type = FormType.xml;
182
+ }
183
+ else if (koaRequest.is('text/*')) {
184
+ raw = await getRawBodyText(koaRequest, opts);
185
+ type = FormType.text;
186
+ }
187
+ else {
188
+ koaRequest.ctx.throw(500, 'Attempting to read text body from an usupported request content type: ' + koaRequest.headers['content-type']);
189
+ throw '';
190
+ }
191
+ return new LazyBody(koaRequest.ctx, type, data, raw, files);
192
+ }
193
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@koa-stack/body",
3
+ "version": "0.7.0",
4
+ "description": "Expose the request body as a promise in koa Context.body",
5
+ "main": "./build/index.js",
6
+ "scripts": {
7
+ "build": "npm run clean && tsc --build",
8
+ "clean": "rimraf ./build ./tsconfig.tsbuildinfo",
9
+ "prepublish": "npm run build",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "files": [
13
+ "build",
14
+ "src"
15
+ ],
16
+ "keywords": [
17
+ "koa",
18
+ "body",
19
+ "parser",
20
+ "lazy"
21
+ ],
22
+ "author": "stefanescu.bogdan@gmail.com",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "formidable": "^3.5.1",
26
+ "inflation": "^2.0.0",
27
+ "qs": "^6.11.2",
28
+ "raw-body": "^2.5.2"
29
+ },
30
+ "devDependencies": {
31
+ "@types/formidable": "^3.4.2",
32
+ "@types/inflation": "^2.0.1",
33
+ "@types/qs": "^6.9.8"
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,222 @@
1
+ import readRawBody from 'raw-body';
2
+ import inflate from 'inflation';
3
+ import qs from 'qs';
4
+ import formidable from 'formidable';
5
+ import Koa, { Context, Request } from 'koa';
6
+
7
+ declare module 'koa' {
8
+ interface BaseContext {
9
+ payload: Promise<LazyBody>
10
+ }
11
+ }
12
+
13
+ export interface OwnOpts {
14
+ formidable?: formidable.Options,
15
+ inflate?: inflate.Options,
16
+ form?: (data: string) => any,
17
+ json?: (data: string) => any,
18
+ xml?: (data: string) => any,
19
+ }
20
+ export type LazyBodyOpts = readRawBody.Options & OwnOpts;
21
+
22
+
23
+ function parseMultipartBody(koaRequest: Request, opts: LazyBodyOpts): Promise<{
24
+ params: formidable.Fields,
25
+ files: formidable.Files
26
+ }> {
27
+ const form = formidable(opts.formidable);
28
+ return new Promise((resolve, reject) => {
29
+ form.parse(koaRequest.req, (err, fields, files) => {
30
+ if (err) {
31
+ reject(err);
32
+ } else {
33
+ resolve({
34
+ params: fields,
35
+ files: files
36
+ });
37
+ }
38
+ });
39
+ });
40
+ }
41
+
42
+ async function getRawBodyText(koaRequest: Request, opts: LazyBodyOpts): Promise<string> {
43
+ const len = koaRequest.length;
44
+ let encoding = koaRequest.headers['content-encoding'];
45
+ if (len && !encoding) {
46
+ opts.length = len;
47
+ }
48
+ // TODO how to detect custom encoding on content type to ser encoding for readRawBody
49
+ return (await readRawBody(inflate(koaRequest.req, opts.inflate), opts)).toString(/*which charset?*/);
50
+ }
51
+
52
+ /**
53
+ * Request body class
54
+ * json
55
+ *
56
+ */
57
+ export class LazyBody {
58
+
59
+ ctx: Context;
60
+ type: FormType;
61
+ data: any;
62
+ raw: string;
63
+ files: formidable.Files | null | undefined;
64
+
65
+ constructor(ctx: Context, type: FormType, data: any, raw: string, files: formidable.Files | null | undefined) {
66
+ this.ctx = ctx;
67
+ this.type = type;
68
+ this.data = data;
69
+ this.raw = raw;
70
+ this.files = files;
71
+ }
72
+
73
+ assertJSON(statusCode: number, message: string) {
74
+ if (this.type !== FormType.json) {
75
+ this.ctx.throw(statusCode || 415, message);
76
+ }
77
+ }
78
+
79
+ assertXML(statusCode: number, message: string) {
80
+ if (this.type !== FormType.xml) {
81
+ this.ctx.throw(statusCode || 415, message);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Assert body type is urlencoded
87
+ * @param {*} statusCode
88
+ * @param {*} message
89
+ */
90
+ assertForm(statusCode: number, message: string) {
91
+ if (this.type !== FormType.form) {
92
+ this.ctx.throw(statusCode || 415, message);
93
+ }
94
+ }
95
+
96
+ assertMultipart(statusCode: number, message: string) {
97
+ if (this.type !== FormType.multipart) {
98
+ this.ctx.throw(statusCode || 415, message);
99
+ }
100
+ }
101
+
102
+ assertText(statusCode: number, message: string) {
103
+ if (this.type !== FormType.text) {
104
+ this.ctx.throw(statusCode || 415, message);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Assert that body type is either multipart or urlencoded
110
+ * @param {*} statusCode
111
+ * @param {*} message
112
+ */
113
+ assertParams(statusCode: number, message: string) {
114
+ if (this.type !== FormType.form && this.type !== FormType.multipart) {
115
+ this.ctx.throw(statusCode || 415, message);
116
+ }
117
+ }
118
+
119
+ get text() {
120
+ return this.raw;
121
+ }
122
+
123
+ get json() {
124
+ return this.isJSON ? this.data : undefined;
125
+ }
126
+
127
+ get xml() {
128
+ return this.isXML ? this.data : undefined;
129
+ }
130
+
131
+ get params() {
132
+ switch (this.type) {
133
+ case FormType.form:
134
+ case FormType.multipart:
135
+ return this.data;
136
+ default: return undefined;
137
+ }
138
+ }
139
+
140
+ get isForm() {
141
+ return this.type === FormType.form;
142
+ }
143
+
144
+ get isJSON() {
145
+ return this.type === FormType.json;
146
+ }
147
+
148
+ get isXML() {
149
+ return this.type === FormType.xml;
150
+ }
151
+
152
+ get isMultipart() {
153
+ return this.type === FormType.multipart;
154
+ }
155
+
156
+ get isText() {
157
+ return this.type === FormType.text;
158
+ }
159
+
160
+ static install(koa: Koa, opts: LazyBodyOpts = {}): void {
161
+ opts.encoding = opts.encoding || 'utf8';
162
+ opts.limit = opts.limit || '1mb';
163
+ Object.defineProperty(koa.request, 'body', {
164
+ get() {
165
+ if (!this._body) {
166
+ this._body = createBody(this, opts);
167
+ }
168
+ return this._body;
169
+ },
170
+ // be able to work along code using koa2-formidable (wich sets the body property pf the request)
171
+ set(body) {
172
+ this._body = body;
173
+ }
174
+ });
175
+ // payload is an alias to request.body
176
+ Object.defineProperty(koa.context, 'payload', {
177
+ get() {
178
+ return this.request.body;
179
+ },
180
+ set(body) {
181
+ this.request.body = body;
182
+ }
183
+ });
184
+ }
185
+
186
+ }
187
+ enum FormType {
188
+ 'multipart', 'form', 'json', 'xml', 'text'
189
+ }
190
+
191
+ async function createBody(koaRequest: Request, opts: LazyBodyOpts) {
192
+ let type: FormType, data: any, raw: string, files: formidable.Files | null | undefined = null;
193
+ if (koaRequest.is('multipart')) {
194
+ raw = '';
195
+ const result = await parseMultipartBody(koaRequest, opts);
196
+ data = result.params || {};
197
+ files = result.files || {};
198
+ type = FormType.multipart;
199
+ } else if (koaRequest.is('urlencoded')) {
200
+ // by default we use qs. You can replace the querystring parser using opts.form
201
+ raw = await getRawBodyText(koaRequest, opts);
202
+ data = opts.form ? opts.form(raw) : qs.parse(raw);
203
+ type = FormType.form;
204
+ } else if (koaRequest.is('json', '+json')) {
205
+ // by default we use JSON.parse. You can replace the json parser using opts.json
206
+ raw = await getRawBodyText(koaRequest, opts);
207
+ data = opts.json ? opts.json(raw) : JSON.parse(raw);
208
+ type = FormType.json;
209
+ } else if (koaRequest.is('xml', '+xml')) {
210
+ // by default fast-xml-parser is used - to change the parser you should provde an xml parser through opts.xml
211
+ raw = await getRawBodyText(koaRequest, opts);
212
+ data = opts.xml ? opts.xml(raw) : require('fast-xml-parser').parse(raw);
213
+ type = FormType.xml;
214
+ } else if (koaRequest.is('text/*')) {
215
+ raw = await getRawBodyText(koaRequest, opts);
216
+ type = FormType.text;
217
+ } else {
218
+ koaRequest.ctx.throw(500, 'Attempting to read text body from an usupported request content type: ' + koaRequest.headers['content-type']);
219
+ throw '';
220
+ }
221
+ return new LazyBody(koaRequest.ctx, type, data, raw, files);
222
+ }