webhoster 0.3.2 → 0.3.4

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.
Files changed (37) hide show
  1. package/.github/workflows/publish.yml +29 -0
  2. package/.test/index.js +1 -1
  3. package/README.md +4 -4
  4. package/data/CookieObject.js +1 -1
  5. package/{types/index.js → data/custom-types.js} +1 -1
  6. package/{types/typings.d.ts → data/middleware.d.ts} +1 -1
  7. package/helpers/HttpListener.js +20 -94
  8. package/helpers/RequestReader.js +1 -12
  9. package/helpers/ResponseHeaders.js +9 -10
  10. package/lib/HttpHandler.js +17 -7
  11. package/lib/HttpRequest.js +7 -4
  12. package/lib/HttpResponse.js +11 -5
  13. package/lib/HttpTransaction.js +2 -2
  14. package/middleware/AutoHeadersMiddleware.js +2 -2
  15. package/middleware/CORSMiddleware.js +3 -3
  16. package/middleware/CaseInsensitiveHeadersMiddleware.js +2 -2
  17. package/middleware/ContentDecoderMiddleware.js +12 -8
  18. package/middleware/ContentEncoderMiddleware.js +6 -5
  19. package/middleware/ContentLengthMiddleware.js +2 -2
  20. package/middleware/HashMiddleware.js +2 -2
  21. package/middleware/HeadMethodMiddleware.js +3 -3
  22. package/middleware/MethodMiddleware.js +3 -3
  23. package/middleware/PathMiddleware.js +4 -4
  24. package/middleware/ReadFormData.js +1 -1
  25. package/middleware/SendJsonMiddleware.js +4 -4
  26. package/middleware/SendStringMiddleware.js +3 -3
  27. package/package.json +20 -3
  28. package/templates/starter.js +2 -2
  29. package/tsconfig.json +18 -1
  30. package/utils/headers.js +1 -1
  31. package/errata/index.js +0 -1
  32. package/index.js +0 -4
  33. package/lib/index.js +0 -3
  34. package/middleware/ContentReaderMiddleware.js +0 -249
  35. package/middleware/ContentWriterMiddleware.js +0 -161
  36. package/middleware/SendHeadersMiddleware.js +0 -47
  37. package/middleware/index.js +0 -11
@@ -1,9 +1,10 @@
1
1
  import { Transform } from 'node:stream';
2
2
  import {
3
+ // @ts-expect-error Bad typings
3
4
  BrotliDecompress, Gunzip, Inflate,
4
5
  } from 'node:zlib';
5
6
 
6
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
7
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
7
8
 
8
9
  /**
9
10
  * @typedef ContentDecoderMiddlewareOptions
@@ -69,17 +70,20 @@ export default class ContentDecoderMiddleware {
69
70
  const gzipOptions = { chunkSize: this.chunkSize };
70
71
  const newDownstream = new Transform({
71
72
 
72
- read: (...args) => {
73
+ read: (...arguments_) => {
73
74
  if (!initialized) {
74
75
  /** @type {import("zlib").Gzip} */
75
76
  switch (contentEncoding) {
76
77
  case 'deflate':
78
+ // @ts-expect-error Bad typings
77
79
  gzipStream = new Inflate(gzipOptions);
78
80
  break;
79
81
  case 'gzip':
82
+ // @ts-expect-error Bad typings
80
83
  gzipStream = new Gunzip(gzipOptions);
81
84
  break;
82
85
  case 'br':
86
+ // @ts-expect-error Bad typings
83
87
  gzipStream = new BrotliDecompress(gzipOptions);
84
88
  break;
85
89
  default:
@@ -89,7 +93,7 @@ export default class ContentDecoderMiddleware {
89
93
  // To newDownstream <= gzipStream < =inputStream
90
94
 
91
95
  // Forward errors
92
- gzipStream.on('error', (err) => inputStream.emit('error', err));
96
+ gzipStream.on('error', (error) => inputStream.emit('error', error));
93
97
  gzipStream.on('data', (chunk) => newDownstream.push(chunk));
94
98
 
95
99
  inputStream.on('end', () => gzipStream.end());
@@ -104,12 +108,13 @@ export default class ContentDecoderMiddleware {
104
108
  initialized = true;
105
109
  }
106
110
 
107
- Transform.prototype._read.call(this, ...args);
111
+ // eslint-disable-next-line no-underscore-dangle
112
+ Transform.prototype._read.call(this, ...arguments_);
108
113
  },
109
114
  transform: (chunk, chunkEncoding, callback) => {
110
- gzipStream.write(chunk, (err) => {
111
- if (err) console.error(err);
112
- callback(err);
115
+ gzipStream.write(chunk, (error) => {
116
+ if (error) console.error(error);
117
+ callback(error);
113
118
  });
114
119
  },
115
120
  flush: (callback) => {
@@ -129,7 +134,6 @@ export default class ContentDecoderMiddleware {
129
134
  },
130
135
  });
131
136
 
132
- newDownstream.tag = 'ContentDecoder';
133
137
  inputStream = request.addDownstream(newDownstream, { autoPause: true });
134
138
 
135
139
  return CONTINUE;
@@ -15,8 +15,8 @@ const { BROTLI_OPERATION_FLUSH, Z_SYNC_FLUSH } = ZlibContants;
15
15
  /** @typedef {import('http').IncomingHttpHeaders} IncomingHttpHeaders */
16
16
  /** @typedef {import('../lib/HttpRequest.js').default} HttpRequest */
17
17
  /** @typedef {import('../lib/HttpResponse.js').default} HttpResponse */
18
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
19
- /** @typedef {import('../types').ResponseFinalizer} ResponseFinalizer */
18
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
19
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
20
20
 
21
21
  /** @typedef {'br'|'gzip'|'deflate'|'identity'|'*'} COMPATIBLE_ENCODING */
22
22
 
@@ -75,8 +75,9 @@ export default class ContentEncoderMiddleware {
75
75
  let encoding = COMPATIBLE_ENCODINGS[0];
76
76
  const allowWildcards = (encodings.get('*')?.q !== 0);
77
77
  const encodingEntries = [...encodings.entries()];
78
- // @ts-expect-error Cannot cast to COMPATIBLE_ENCODINGS
79
- encoding = (encodingEntries.find(([value, spec]) => spec.q !== 0 && COMPATIBLE_ENCODINGS.includes(value))?.[0]);
78
+ encoding = /** @type {COMPATIBLE_ENCODING} */ (encodingEntries
79
+ .find(([value, spec]) => spec.q !== 0
80
+ && COMPATIBLE_ENCODINGS.includes(/** @type {COMPATIBLE_ENCODING} */ (value)))?.[0]);
80
81
  if (allowWildcards && (encoding === '*' || !encoding)) {
81
82
  // Server preference
82
83
  // Get first compatible encoding not specified
@@ -154,7 +155,7 @@ export default class ContentEncoderMiddleware {
154
155
  encoding = getContentEncoding().toLowerCase?.();
155
156
  }
156
157
 
157
- const isEventStream = response.headers['content-type']?.includes('text/event-stream');
158
+ const isEventStream = /** @type {string|null} */ (response.headers['content-type'])?.includes('text/event-stream');
158
159
 
159
160
  let newStream;
160
161
  switch (encoding) {
@@ -1,6 +1,6 @@
1
1
  /** @typedef {import('../lib/HttpResponse.js').default} HttpResponse */
2
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
3
- /** @typedef {import('../types').ResponseFinalizer} ResponseFinalizer */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
3
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
4
4
 
5
5
  import { Transform } from 'node:stream';
6
6
 
@@ -4,8 +4,8 @@ import { Transform } from 'node:stream';
4
4
  /** @typedef {import('node:crypto').BinaryToTextEncoding} BinaryToTextEncoding */
5
5
  /** @typedef {import('../lib/HttpRequest.js').default} HttpRequest */
6
6
  /** @typedef {import('../lib/HttpResponse.js').default} HttpResponse */
7
- /** @typedef {import('../types/index.js').MiddlewareFunction} MiddlewareFunction */
8
- /** @typedef {import('../types/index.js').ResponseFinalizer} ResponseFinalizer */
7
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
8
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
9
9
 
10
10
  const DEFAULT_ALGORITHM = 'sha1';
11
11
  /** @type {BinaryToTextEncoding} */
@@ -1,6 +1,6 @@
1
- /** @typedef {import('../types/index.js').HttpTransaction} HttpTransaction */
2
- /** @typedef {import('../types/index.js').MiddlewareFunction} MiddlewareFunction */
3
- /** @typedef {import('../types/index.js').ResponseFinalizer} ResponseFinalizer */
1
+ /** @typedef {import('../data/custom-types.js').HttpTransaction} HttpTransaction */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
3
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
4
4
 
5
5
  import { Transform } from 'node:stream';
6
6
 
@@ -1,6 +1,6 @@
1
- /** @typedef {import('../types').HttpTransaction} HttpTransaction */
2
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
3
- /** @typedef {import('../types').RequestMethod} RequestMethod */
1
+ /** @typedef {import('../data/custom-types.js').HttpTransaction} HttpTransaction */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
3
+ /** @typedef {import('../data/custom-types.js').RequestMethod} RequestMethod */
4
4
 
5
5
  /** @typedef {RegExp|RequestMethod} MethodEntry */
6
6
 
@@ -1,9 +1,9 @@
1
1
  import { posix } from 'node:path';
2
2
 
3
- /** @typedef {import('../types').HttpTransaction} HttpTransaction */
4
- /** @typedef {import('../types').IMiddleware} IMiddleware */
5
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
6
- /** @typedef {import('../types').RequestMethod} RequestMethod */
3
+ /** @typedef {import('../data/custom-types.js').HttpTransaction} HttpTransaction */
4
+ /** @typedef {import('../data/custom-types.js').IMiddleware} IMiddleware */
5
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
6
+ /** @typedef {import('../data/custom-types.js').RequestMethod} RequestMethod */
7
7
 
8
8
  /** @typedef {RegExp|string} PathEntry */
9
9
 
@@ -1,5 +1,5 @@
1
1
  /** @typedef {import('../lib/HttpRequest.js').default} HttpRequest */
2
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
3
3
 
4
4
  /**
5
5
  * The application/x-www-form-urlencoded format is in many ways an aberrant monstrosity,
@@ -1,7 +1,7 @@
1
- /** @typedef {import('../types').IMiddleware} IMiddleware */
2
- /** @typedef {import('../types').MiddlewareResponseFunction} MiddlewareResponseFunction */
3
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
4
- /** @typedef {import('../types/index.js').ResponseFinalizer} ResponseFinalizer */
1
+ /** @typedef {import('../data/custom-types.js').IMiddleware} IMiddleware */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareResponseFunction} MiddlewareResponseFunction */
3
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
4
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
5
5
 
6
6
  /**
7
7
  * @typedef {Object} SendJsonMiddlewareOptions
@@ -1,6 +1,6 @@
1
- /** @typedef {import('../types/index.js').IMiddleware} IMiddleware */
2
- /** @typedef {import('../types/index.js').MiddlewareFunction} MiddlewareFunction */
3
- /** @typedef {import('../types/index.js').ResponseFinalizer} ResponseFinalizer */
1
+ /** @typedef {import('../data/custom-types.js').IMiddleware} IMiddleware */
2
+ /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
3
+ /** @typedef {import('../data/custom-types.js').ResponseFinalizer} ResponseFinalizer */
4
4
 
5
5
  /**
6
6
  * @typedef {Object} SendStringMiddlewareOptions
package/package.json CHANGED
@@ -24,7 +24,16 @@
24
24
  "node": ">v16.13"
25
25
  },
26
26
  "exports": {
27
- "./*": "./*"
27
+ "./data/*": "./data/*",
28
+ "./errata/*": "./errata/*",
29
+ "./examples/*": "./examples/*",
30
+ "./helpers/*": "./helpers/*",
31
+ "./lib/*": "./lib/*",
32
+ "./middleware/*": "./middleware/*",
33
+ "./polyfills/*": "./polyfills/*",
34
+ "./templates/*": "./templates/*",
35
+ "./types/*": "./types/*",
36
+ "./utils/*": "./utils/*"
28
37
  },
29
38
  "keywords": [
30
39
  "http",
@@ -42,6 +51,7 @@
42
51
  "scripts": {
43
52
  "debug-test": "ava --serial",
44
53
  "test": "c8 ava",
54
+ "prepublishOnly": "rm -Rf types && tsc --emitDeclarationOnly",
45
55
  "pretestallsync": "rimraf coverage",
46
56
  "testallsync": "scripts/test-all-sync.sh",
47
57
  "posttestallsync": "c8 report",
@@ -50,5 +60,12 @@
50
60
  "posttestall": "c8 report"
51
61
  },
52
62
  "type": "module",
53
- "version": "0.3.2"
54
- }
63
+ "typesVersions": {
64
+ "*": {
65
+ "*": [
66
+ "types/*"
67
+ ]
68
+ }
69
+ },
70
+ "version": "0.3.4"
71
+ }
@@ -13,8 +13,8 @@ import HeadMethodMiddleware from '../middleware/HeadMethodMiddleware.js';
13
13
  * @param {Object} options
14
14
  * @param {string} [options.host='0.0.0.0']
15
15
  * @param {number} [options.port=8080]
16
- * @param {import('../types').Middleware[]} [options.middleware]
17
- * @param {import('../types').MiddlewareErrorHandler[]} [options.errorHandlers]
16
+ * @param {import('../data/custom-types.js').Middleware[]} [options.middleware]
17
+ * @param {import('../data/custom-types.js').MiddlewareErrorHandler[]} [options.errorHandlers]
18
18
  * @return {Promise<import('../helpers/HttpListener.js').default>}
19
19
  */
20
20
  export async function start(options) {
package/tsconfig.json CHANGED
@@ -1,12 +1,29 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "allowJs": true,
3
4
  "checkJs": true,
4
5
  "noImplicitAny": true,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "declarationDir": "types",
9
+ "outDir": "/dev/null",
5
10
  "target": "es2022",
6
11
  "module": "esnext",
7
12
  "moduleResolution": "node"
8
13
  },
14
+ "include": [
15
+ "./data/*",
16
+ "./errata/*",
17
+ "./examples/*",
18
+ "./helpers/*",
19
+ "./lib/*",
20
+ "./middleware/*",
21
+ "./polyfills/*",
22
+ "./templates/*",
23
+ "./utils/*"
24
+ ],
9
25
  "exclude": [
10
- "**/node_modules/*"
26
+ "**/node_modules/*",
27
+ "**/test/*"
11
28
  ]
12
29
  }
package/utils/headers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  *
3
3
  * @param {?string} contentType
4
- * @return {import('../types/index.js').MediaType}
4
+ * @return {import('../data/custom-types.js').MediaType}
5
5
  */
6
6
  export function parseContentType(contentType) {
7
7
  let type;
package/errata/index.js DELETED
@@ -1 +0,0 @@
1
- export * as socketio from './socketio.js';
package/index.js DELETED
@@ -1,4 +0,0 @@
1
- export * as lib from './lib/index.js';
2
- export * as helpers from './helpers/index.js';
3
- export * as middleware from './middleware/index.js';
4
- export * as errata from './errata/index.js';
package/lib/index.js DELETED
@@ -1,3 +0,0 @@
1
- export { default as HttpRequest } from './HttpRequest.js';
2
- export { default as HttpResponse } from './HttpResponse.js';
3
- export { default as HttpHandler } from './HttpHandler.js';
@@ -1,249 +0,0 @@
1
- import { PassThrough, Transform } from 'stream';
2
-
3
- /** @typedef {import('stream').Readable} Readable */
4
- /** @typedef {import('../types').IMiddleware} IMiddleware */
5
- /** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
6
- /** @typedef {import('../types').MiddlewareFunctionParams} MiddlewareFunctionParams */
7
- /** @typedef {import('../types').MiddlewareFunctionResult} MiddlewareFunctionResult */
8
-
9
- /**
10
- * @typedef {Object} ContentReaderMiddlewareOptions
11
- * @prop {string} [defaultMediaType]
12
- * Assumed mediatype if not specified
13
- * @prop {boolean} [parseJSON=false]
14
- * Automatically parses JSON if `application/json` mediatype
15
- * @prop {'entries'|'object'|'string'|'none'} [formURLEncodedFormat='none']
16
- * Automatically converts to object if `application/x-www-form-urlencoded` mediatype
17
- * @prop {boolean} [buildString=false]
18
- * Automatically builds string into one `read()` response
19
- * @prop {boolean|string} [cache=false]
20
- * Caches content in req.local.content or req.local[cacheName]
21
- */
22
-
23
- /** @implements {IMiddleware} */
24
- export default class ContentReaderMiddleware {
25
- /** @param {ContentReaderMiddlewareOptions} [options] */
26
- constructor(options = {}) {
27
- this.defaultMediaType = options.defaultMediaType;
28
- this.parseJSON = options.parseJSON === true;
29
- this.formURLEncodedFormat = options.formURLEncodedFormat || 'none';
30
- this.buildString = options.buildString === true;
31
- this.cache = options.cache;
32
- }
33
-
34
- /**
35
- * @param {string} charset
36
- * @return {BufferEncoding}
37
- */
38
- static charsetAsBufferEncoding(charset) {
39
- switch (charset) {
40
- case 'iso-8859-1':
41
- case 'ascii':
42
- case 'binary':
43
- case 'latin1':
44
- return 'latin1';
45
- case 'utf-16le':
46
- case 'ucs-2':
47
- case 'ucs2':
48
- case 'utf16le':
49
- return 'utf16le';
50
- default:
51
- case 'utf-8':
52
- case 'utf8':
53
- return 'utf-8';
54
- case 'base64':
55
- case 'hex':
56
- return /** @type {BufferEncoding} */ (charset);
57
- }
58
- }
59
-
60
- /**
61
- * The application/x-www-form-urlencoded format is in many ways an aberrant monstrosity,
62
- * the result of many years of implementation accidents and compromises leading to a set of
63
- * requirements necessary for interoperability, but in no way representing good design practices.
64
- * In particular, readers are cautioned to pay close attention to the twisted details
65
- * involving repeated (and in some cases nested) conversions between character encodings and byte sequences.
66
- * Unfortunately the format is in widespread use due to the prevalence of HTML forms. [HTML]
67
- * @param {Buffer} buffer
68
- * @param {string} charset
69
- * @return {[string, string][]} Tuple
70
- */
71
- static readUrlEncoded(buffer, charset) {
72
- // https://url.spec.whatwg.org/#urlencoded-parsing
73
- const bufferEncoding = ContentReaderMiddleware.charsetAsBufferEncoding(charset);
74
-
75
- const sequences = [];
76
- let startIndex = 0;
77
- for (let i = 0; i < buffer.length; i += 1) {
78
- if (buffer[i] === 0x26) {
79
- sequences.push(buffer.subarray(startIndex, i));
80
- startIndex = i + 1;
81
- }
82
- if (i === buffer.length - 1) {
83
- sequences.push(buffer.subarray(startIndex, i + 1));
84
- break;
85
- }
86
- }
87
- /** @type {[string, string][]} */
88
- const output = [];
89
- sequences.forEach((bytes) => {
90
- if (!bytes.length) return;
91
-
92
- // Find 0x3D and replace 0x2B in one loop for better performance
93
- let indexOf0x3D = -1;
94
- for (let i = 0; i < bytes.length; i += 1) {
95
- switch (bytes[i]) {
96
- case 0x3D:
97
- if (indexOf0x3D === -1) {
98
- indexOf0x3D = i;
99
- }
100
- break;
101
- case 0x2B:
102
- // Replace bytes on original stream for memory conservation
103
- // eslint-disable-next-line no-param-reassign
104
- bytes[i] = 0x20;
105
- break;
106
- default:
107
- }
108
- }
109
- let name;
110
- let value;
111
- if (indexOf0x3D === -1) {
112
- name = bytes;
113
- value = bytes.subarray(bytes.length, 0);
114
- } else {
115
- name = bytes.subarray(0, indexOf0x3D);
116
- value = bytes.subarray(indexOf0x3D + 1);
117
- }
118
- const nameString = decodeURIComponent(name.toString(bufferEncoding));
119
- const valueString = decodeURIComponent(value.toString(bufferEncoding));
120
- output.push([nameString, valueString]);
121
- });
122
- return output;
123
- }
124
-
125
- /**
126
- * @param {MiddlewareFunctionParams} params
127
- * @return {MiddlewareFunctionResult}
128
- */
129
- execute({ req }) {
130
- switch (req.method) {
131
- case 'HEAD':
132
- case 'GET':
133
- return 'continue';
134
- default:
135
- }
136
-
137
- const contentType = (req.headers['content-type']);
138
- /** @type {string} */
139
- let mediaType;
140
- /** @type {string} */
141
- let charset;
142
- if (contentType) {
143
- contentType.split(';').forEach((directive) => {
144
- const parameters = directive.split('=');
145
- if (parameters.length === 1) {
146
- mediaType = directive.trim().toLowerCase();
147
- return;
148
- }
149
- if (parameters[0].trim().toLowerCase() !== 'charset') {
150
- return;
151
- }
152
- charset = parameters[1]?.trim().toLowerCase();
153
- const firstQuote = charset.indexOf('"');
154
- const lastQuote = charset.lastIndexOf('"');
155
- if (firstQuote !== -1 && lastQuote !== -1) {
156
- charset = charset.substring(firstQuote + 1, lastQuote);
157
- }
158
- });
159
- }
160
-
161
- if (!mediaType) {
162
- mediaType = this.defaultMediaType;
163
- }
164
- const isFormUrlEncoded = mediaType === 'application/x-www-form-urlencoded';
165
- const isJSON = /application\/(.+\+)?json/i.test(mediaType);
166
- if (!charset) {
167
- if (!mediaType) {
168
- return 'continue';
169
- }
170
- if (isFormUrlEncoded && (this.formURLEncodedFormat || 'none') === 'none') {
171
- return 'continue';
172
- }
173
- if (!isFormUrlEncoded && !isJSON && !mediaType.startsWith('text/')) {
174
- return 'continue';
175
- }
176
- }
177
-
178
- const readAll = isJSON || isFormUrlEncoded;
179
-
180
- let fullString = '';
181
- /** @type {Buffer[]} */
182
- const pendingChunks = [];
183
- const source = req.stream;
184
- const {
185
- buildString, formURLEncodedFormat, cache, parseJSON,
186
- } = this;
187
- const newReadable = new Transform({
188
- objectMode: true,
189
- read(...args) {
190
- if (source.isPaused()) source.resume();
191
- // eslint-disable-next-line no-underscore-dangle
192
- Transform.prototype._read.call(this, ...args);
193
- },
194
- transform(chunk, encoding, callback) {
195
- if (typeof chunk === 'string') {
196
- if (readAll || buildString) {
197
- fullString += chunk;
198
- } else {
199
- this.push(chunk);
200
- }
201
- } else if (isFormUrlEncoded) {
202
- pendingChunks.push(chunk);
203
- } else {
204
- this.push(chunk);
205
- }
206
- callback();
207
- },
208
- flush(callback) {
209
- let result = null;
210
- if (isFormUrlEncoded) {
211
- const combinedBuffer = Buffer.concat(pendingChunks);
212
- if (formURLEncodedFormat === 'object') {
213
- result = Object.fromEntries(ContentReaderMiddleware.readUrlEncoded(combinedBuffer, charset));
214
- } else if (formURLEncodedFormat === 'string') {
215
- result = combinedBuffer.toString(ContentReaderMiddleware.charsetAsBufferEncoding(charset));
216
- } else {
217
- result = ContentReaderMiddleware.readUrlEncoded(combinedBuffer, charset);
218
- }
219
- } else if (isJSON && parseJSON) {
220
- try {
221
- result = JSON.parse(fullString);
222
- } catch {
223
- result = fullString;
224
- }
225
- } else if (fullString) {
226
- result = fullString;
227
- }
228
- if (cache && result) {
229
- const cacheName = cache === true ? 'content' : cache;
230
- req.locals[cacheName] = result;
231
- }
232
- callback(null, result);
233
- },
234
- });
235
- req.replaceStream(newReadable);
236
- if (!isFormUrlEncoded) {
237
- // Data read from source will be decoded as a string
238
- const encoding = ContentReaderMiddleware.charsetAsBufferEncoding(charset);
239
- const stringDecoder = new PassThrough({ encoding });
240
- newReadable.setDefaultEncoding(encoding);
241
- source.pipe(stringDecoder).pipe(newReadable);
242
- } else {
243
- source.pipe(newReadable);
244
- }
245
- source.pause();
246
-
247
- return 'continue';
248
- }
249
- }